(转)Java 日志框架解析(下) - 最佳实践

上一篇文章中, 讲了Java经常使用的日志库以及之间的关系,如今来讲说咱们在项目中怎么使用日志库。

apache

1. 老是使用Log Facade,而不是具体Log Implementationmaven

正如以前所说的,使用 Log Facade 能够方便的切换具体的日志实现。并且,若是依赖多个项目,使用了不一样的Log Facade,还能够方便的经过 Adapter 转接到同一个实现上。若是依赖项目使用了多个不一样的日志实现,就麻烦的多了。函数

具体来讲,如今推荐使用 Log4j-API 或者 SLF4j,不推荐继续使用 JCL。url

2. 只添加一个 Log Implementation依赖spa

毫无疑问,项目中应该只使用一个具体的 Log Implementation,建议使用 Logback 或者Log4j2。若是有依赖的项目中,使用的 Log Facade不支持直接使用当前的 Log Implementation,就添加合适的桥接器依赖。具体的桥接关系能够看上一篇文章的图debug

3. 具体的日志实现依赖应该设置为optional和使用runtime scope日志

在项目中,Log Implementation的依赖强烈建议设置为runtime scope,而且设置为optional。例如项目中使用了 SLF4J 做为 Log Facade,而后想使用 Log4j2 做为 Implementation,那么使用 maven 添加依赖的时候这样设置:code

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.version}</version>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>${log4j.version}</version>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

 

设为optional,依赖不会传递,这样若是你是个lib项目,而后别的项目使用了你这个lib,不会被引入不想要的Log Implementation 依赖;orm

Scope设置为runtime,是为了防止开发人员在项目中直接使用Log Implementation中的类,而不适用Log Facade中的类。blog

4. 若是有必要, 排除依赖的第三方库中的Log Impementation依赖

这是很常见的一个问题,第三方库的开发者未必会把具体的日志实现或者桥接器的依赖设置为optional,而后你的项目继承了这些依赖——具体的日志实现未必是你想使用的,好比他依赖了Log4j,你想使用Logback,这时就很尴尬。另外,若是不一样的第三方依赖使用了不一样的桥接器和Log实现,也极容易造成环。

这种状况下,推荐的处理方法,是使用exclude来排除全部的这些Log实现和桥接器的依赖,只保留第三方库里面对Log Facade的依赖。

好比阿里的JStorm就没有很好的处理这个问题,依赖jstorm会引入对Logback和log4j-over-slf4j的依赖,若是你想在本身的项目中使用Log4j或其余Log实现的话,就须要加上excludes:

<dependency>
    <groupId>com.alibaba.jstorm</groupId>
    <artifactId>jstorm-core</artifactId>
    <version>2.1.1</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </exclusion>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </exclusion>
    </exclusions>
</dependency>

 

5. 避免为不会输出的log付出代价

Log库均可以灵活的设置输出界别,因此每一条程序中的log,都是有可能不会被输出的。这时候要注意不要额外的付出代价。

先看两个有问题的写法:

logger.debug("start process request, url: " + url);
logger.debug("receive request: {}", toJson(request));

 

第一条是直接作了字符串拼接,因此即便日志级别高于debug也会作一个字符串链接操做;第二条虽然用了SLF4J/Log4j2 中的懒求值方式来避免没必要要的字符串拼接开销,可是toJson()这个函数倒是都会被调用而且开销更大。

推荐的写法以下:

logger.debug("start process request, url:{}", url); // SLF4J/LOG4J2
logger.debug("receive request: {}", () -> toJson(request)); // LOG4J2
logger.debug(() -> "receive request: " + toJson(request)); // LOG4J2
if (logger.isDebugEnabled()) { // SLF4J/LOG4J2
    logger.debug("receive request: " + toJson(request)); 
}

 

6. 日志格式中最好不要使用行号,函数名等字段

缘由是,为了获取语句所在的函数名,或者行号,log库的实现都是获取当前的stacktrace,而后分析取出这些信息,而获取stacktrace的代价是很昂贵的。若是有不少的日志输出,就会占用大量的CPU。在没有特殊须要的状况下,建议不要在日志中输出这些这些字段。

最后, log中不要输出稀奇古怪的字符!

部分开发人员为了方便看到本身的log,会在log语句中加上醒目的前缀,好比:

logger.debug("========================start process request=============");

 

虽然对于本身来讲是方便了,可是若是全部人都这样来作的话,那log输出就无法看了!正确的作法是使用grep 来看只本身关心的日志。

转载自:https://zhuanlan.zhihu.com/p/24275518

相关文章
相关标签/搜索