坑爹代码 | 这样的日志封装到底是坑爹还是有用呢?

红薯
 红薯
发布于 2019年06月19日
收藏 35

玩 Java 的人都知道 Java 提供很多的日志框架,诸如 Log4j、SLF4J、Commons-logging 等等

更多的 Java Logging 框架请看这里

那么怎么用日志框架才是最佳的实践呢?

下面这段代码难道真的只是一个无用的封装吗?

import org.slf4j.Marker;
// 当时完全不懂包装slf4j什么意思,还以为自己针对市面上的logger进行了封装处理,上次开会的时候这么说,我为了logger的时候像andriod能写个tag,WTF,难道你logger.info('tag:{},xxxxx',tag)这样不行么?为了六个字符创建一个logger和一个loggerFactory
public class Logger {

    /** 日志处理 */
    private org.slf4j.Logger logger;

    public static String separator = " ";

    public Logger(Class clazz) {
        logger = org.slf4j.LoggerFactory.getLogger(clazz);
    }

    public Logger(String name) {
        logger = org.slf4j.LoggerFactory.getLogger(name);
    }

    public String getName() {
        return logger.getName();
    }

    public boolean isTraceEnabled() {
        return logger.isTraceEnabled();
    }

    public void trace(String msg) {
        logger.trace(msg);
    }

    public void trace(String format, Object arg) {
        logger.trace(format, arg);
    }

    public void trace(String format, Object arg1, Object arg2) {
        logger.trace(format, arg1, arg2);
    }

    public void trace(String format, Object... arguments) {
        logger.trace(format, arguments);
    }

在我看来,起码有一个用处就是,当我们决定要更换一个日志框架的时候,直接修改该类就可以了,而不用修改项目中成千上万的文件。

你怎么看呢?

请移步下面链接发表评论,领取奖品:

https://gitee.com/oschina/bullshit-codes/blob/master/java/Logger.java

码云 6 周年,我们正在征集各种坑爹代码,很多奖品等你来拿

详细的参与方法请看  https://gitee.com/oschina/bullshit-codes

------ 分割线 ------

其他坑爹代码吐槽:

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处,尊重他人劳动共创开源社区。
转载请注明:文章转载自 OSCHINA 社区 [http://www.oschina.net]
本文标题:坑爹代码 | 这样的日志封装到底是坑爹还是有用呢?
加载中

精彩评论

木有龙井茶
木有龙井茶
都用slf4j了,底层还不是随便换,换个jar包换个配置文件就行,根本不用动代码。为啥自己再封一层,换实现还得改代码,自己给自己挖坑嘛😄
路小磊
路小磊
这样封装无无可厚非,尤其是开源的框架中间件。比如著名的#Netty#就是做了一层包装(见:https://github.com/netty/netty/blob/4.1/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java)。

不过需要说明的是,这段代码的“坑爹”之处是简单做了包装,这会导致打印出来的日志行号和类都指向这个类,解决方法是传入FQCN指向当前类,然后调用LocationAwareLogger.log方法来转发日志。详细实现可以参考#Hutool#的实现:https://gitee.com/loolly/hutool/blob/v4-master/hutool-log/src/main/java/cn/hutool/log/dialect/slf4j/Slf4jLog.java

好的,下面是广告时间。#Hutool#对日志的封装绝不仅限于此,它不仅包装了Slf4j,而且包装了包括#Apache Commons Logging##LOG4J##Log4j2##tinylog##JBoss Logging#以及命令行实现,通过LogFactory自动判断引入的jar从而自动选择相应的日志实现来打印日志,代码见:https://gitee.com/loolly/hutool/blob/v4-master/hutool-log/src/main/java/cn/hutool/log/LogFactory.java

同时,#Hutool#通过CallerUtil自动查找到了当前类类名,从而省掉了每次创建日志对象需要传入类名的繁琐。更多改进见我的博客:【Java日志那点儿事儿】https://my.oschina.net/looly/blog/543000
Soi
Soi
我就遇到过,老大不让改,日志打印行号永远都在这个类
行走在赤道
行走在赤道
netty 这完全是因为历史兼容的原因造成的。官方早就对 slf4j-api 替代内部的 InternalLogger 进行为讨论,其中有很多人都支持 slf4j-api。后面是因为要修改代码,而带来的好处仅仅是使用了 slf4j-api 就搁置了。可不是因为什么“无可厚非”。https://netty.io/news/2011/11/22/slf4j-or-not.html

至于 hutool-log 有啥的优点?解决了什么实际痛点?
ibrucekong
ibrucekong
给出坑爹代码时,最好能给出相应的重构或者替代方案,这样也是学习到了,不只是吐槽:bowtie:

最新评论(48

水溶C100
水溶C100
完全为了封装而封装,人家已经开源通的的类,直接调用就好了,.自己封装,名字又不一样,人家看代码每次要转到引用看你这个方法是做什么用的,slf4j 大家都了解,一看都懂。
B
Bingou211
代码审查走一波,
花树堆雪
花树堆雪
有点多余,slf4j本来就是日志门面
闲大赋
闲大赋
说一下极端情况下,这种封装能发挥的用处吧
1) 首先format这种方式我记得性能并不是最好,我记得是没有预编译过程,所以封装提供了一个预编译可能,比如某个format很常用,我先预编译以下,得出日志格式
2)对以Exception,可能有特殊处理,比如空指针异常,虚拟机会优化消除异常栈,那你的正常日志就会看不到问题再哪儿,你可以特殊处理一下,如果Exception.getStackTrance[].length==0,你可以间隔(比如100条)打印一次上次有异常栈得异常
3)这里也许能国际化统一做
路小磊
路小磊
这样封装无无可厚非,尤其是开源的框架中间件。比如著名的#Netty#就是做了一层包装(见:https://github.com/netty/netty/blob/4.1/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java)。

不过需要说明的是,这段代码的“坑爹”之处是简单做了包装,这会导致打印出来的日志行号和类都指向这个类,解决方法是传入FQCN指向当前类,然后调用LocationAwareLogger.log方法来转发日志。详细实现可以参考#Hutool#的实现:https://gitee.com/loolly/hutool/blob/v4-master/hutool-log/src/main/java/cn/hutool/log/dialect/slf4j/Slf4jLog.java

好的,下面是广告时间。#Hutool#对日志的封装绝不仅限于此,它不仅包装了Slf4j,而且包装了包括#Apache Commons Logging##LOG4J##Log4j2##tinylog##JBoss Logging#以及命令行实现,通过LogFactory自动判断引入的jar从而自动选择相应的日志实现来打印日志,代码见:https://gitee.com/loolly/hutool/blob/v4-master/hutool-log/src/main/java/cn/hutool/log/LogFactory.java

同时,#Hutool#通过CallerUtil自动查找到了当前类类名,从而省掉了每次创建日志对象需要传入类名的繁琐。更多改进见我的博客:【Java日志那点儿事儿】https://my.oschina.net/looly/blog/543000
talent-tan
talent-tan
只能默默地给路神点个赞!
行走在赤道
行走在赤道
netty 这完全是因为历史兼容的原因造成的。官方早就对 slf4j-api 替代内部的 InternalLogger 进行为讨论,其中有很多人都支持 slf4j-api。后面是因为要修改代码,而带来的好处仅仅是使用了 slf4j-api 就搁置了。可不是因为什么“无可厚非”。https://netty.io/news/2011/11/22/slf4j-or-not.html

至于 hutool-log 有啥的优点?解决了什么实际痛点?
闲大赋
闲大赋
比如log.error(Exception ex),这种,封装可能会对Exception做一些特殊处理,比如对NullpointException 需要更多处理,还有日志的国际化?
路小磊
路小磊
几个优势:
1、很多遗留项目并没有用slf4j,你为了引入一个库被强行关联引入不合适吧,Hutool自动发现项目中的日志库,然后转发日志,做到老项目的最大兼容。
2、Hutool可以做到StaticLog.error等调用日志
3、Hutool可以做到Log log = LogFactory.get(); 而不用传类名。
4、log.error依旧可以使用消息模板,slf4j做不到。
行走在赤道
行走在赤道
1. 如果认为引入 hutool-log 比引入 slf4j-api 更合适,我没必要说啥。
4. 完全没看明白。

关于你说的 2/3 好吧,实在不想做评价,刚才大概看了一眼 hutool-log 的实现。

((LocationAwareLogger) this.logger).log(null, fqcn, level_int, StrUtil.format(msgTemplate, arguments), null, t);

@Override
public void debug(Throwable t, String format, Object... arguments) {
if (false == locationAwareLog(LocationAwareLogger.DEBUG_INT, t, format, arguments)) {
logger.debug(StrUtil.format(format, arguments), t);
}
}

上面的代码是从你的项目中 copy 过来,你的实现中根本没有判断日志 level,《就直接对 message 进行了 format》,这是一个 logging-api/adapter ? my god.

个人认识你这个库的优点可有可无。
缺点倒是挺致命的。
路小磊
路小磊
1、我只针对需要用到Hutool的用户,你不想引入Hutool不在讨论范围之内。
4、举个例子,slf4j中只有public void error(String msg, Throwable t);这里没法传入消息模板和变量,而Hutool是public void error(Throwable t, String format, Object... arguments),懂了吧,这算是个小的语法糖。

关于2、3也是语法糖的一部分,本身也是为了简化代码量。

关于你说的致命缺点,我仔细看了下,确实是因为解决slf4j中的一个bug而忽略了判断,感谢。新版会修复此问题。
行走在赤道
行走在赤道
4. 还是看一下 api 吧。https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.26

其他不做评价。推广自身的项目,这没错。但是还是要有原则,理论依据,有客观事实。不要一句“无可厚非”这只会对小白造成误导。
路小磊
路小磊
好的,感谢。Hutool并不完美。欢迎多提供意见和issue。
Maxwell1987
Maxwell1987
把 slf4j 解决的问题在 slf4j 之外又解决了一边,顺便把 slf4j 当问题也包在了里面。
很拽De土豆
很拽De土豆
看了这么多评论,居然大部分都是说用了slf4j了,还封装做什么呢。最简单的假如有一个第三方的日志log4j 3.0或slf4j不支持的日志。那整个系统都要重构了,但是这种情况基本很少出现。还有另外一种考虑就是,slf4j包装后的日志会有性能损耗且消息可转换,拿log4j2来说:http://logging.apache.org/log4j/2.x/log4j-to-slf4j/index.html官网已经直接说明了这一点。另外我觉得封装还有一点,就是我可以在里面统一做一些定制功能,比如我以前会在里面默认创建几个配置好的logger,外面直接用或者说为了性能直接把这里面的slf4j改为log4j2,如果在编辑器里面输logger会默认跳出很多来,有些新人直接拿其中一个就敲起来了,如果我们建一个自己命令前缀的也会很方便,不过现有的slf4j足够使用了,封装一下也没什么错。
w
wailouci
@很拽De土豆 还不是一样,哪天不想用你这个封装还不是得重改所有使用的地方
很拽De土豆
很拽De土豆
不想用的只是封装的里面的改了就完了,为什么要改所有地方?不然封装的作用是什么?
杰睿宁
杰睿宁
自己写了个logutil,在业务代码中使用时不必给每个类创建logger对象,能通过栈自动定位输出位置信息,直接调用LogUtil的静态方法打印日志, LogUtil的常用静态方法有,info、debug、error, LogUtil的特殊方法trackApi(专门输出报文日志)、trackPoint(专门输出pvuv统计埋点日志), 默认info日志中包括所有基本日志输出,同时单独输出一份error日志,info日志和error日志均按照30天滚动

在业务代码中使用LogUtil样例
LogUtil.info("查询参数={}", "1000");

框架代码https://github.com/jeffreyning/nh-logutil
已经提交中央库,可直接maven下载使用。
行走在赤道
行走在赤道
呵呵呵,好像是很有见地啊。
既然出现了 log4j 3.0 你就能默认支持了?不需要实现新的 adapter 吗?
既然要实现 adapter,那直接扩展 slf4j 的实现不行?需要单独定义一套 api?
所以你假设"那整个系统都要重构了"根本就不存在,如果存在这完全是技术能力不够导致的,而不是使用 slf4j 导致的。

so,定义这样一套接口的意义是什么呢?
浪费内存,浪费CPU,找存在感。😂
很拽De土豆
很拽De土豆
那你告诉我,为什么有开源软件会封装一套自己的实现?你用不到不代表别人用不到ok?一楼置顶的hutool已经解释了你的问题了。我只是假设这种情况而已,另外我已经说得很清楚了,这只是其中一种情况。大部分情况下slf4j是足够使用的,只是纯粹的封装本身就显得多余。
Maxwell1987
Maxwell1987
建议先学会清晰表达,看半天没看出来这些场景怎么就自己封装比用别人封装的 slf4j 好了。
f
freezingsky
SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志System.

纯粹是吃饱了没事干 , 又包了一层!
很拽De土豆
很拽De土豆
只能说你对日志的理解还不够深。
f
freezingsky
水平不够, 还望指教.
很拽De土豆
很拽De土豆
我回复了,你可以看一下。个人理解 。
老谭12345
都用了 slf4j 了为什么要换?
黄海彬
黄海彬
同问
robortly
robortly
好啰嗦的代码呀!这是用什么语言写的?
kidfruit
kidfruit
这个封装除非是想做一层aspect,否则完全没有必要,slf4j本来就是一个facade封装……
返回顶部
顶部