您的位置:首页 > 其它

日志规范

2015-10-19 18:49 555 查看


简介

Java里存在众多的开源日志框架: slf4j, logback, log4j, JCL(Apache Common Logging), JUL(JDK自带的java.util.logging)等。这其中slf4j属于一套简洁的日志API,其并不包含日志的实现(它并不负责日志输出等,JCL也包含API)。slf4j提供了众多的适配器可以适配其他所有开源日志框架,这样让我们在代码中只需面对slf4j的API,然后可以任意切换日志的实现。你可能会讲我们并不需要切换日志框架这种功能,但是我们项目中需要使用大量的第三方库,而这些第三方库使用的日志框架各不相同,不同的日志框架需要不同的配置,不同的配置会导致日志输出到不同的位置。一个应用肯定需要将日志level,日志输出等收纳起来统一管理。对于这种情况,我们可以使用slf4j的适配器将第三方库中各种日志的实现接管,接管之后就可以统一配置这些第三方库中使用的日志了。

在这些日志实现框架中又数logback性能最优,所以在选择日志实现的时候推荐使用logback。


举例

我们的系统中使用了两个开源类库zookeeper.jar和dubbo.jar。假如zookeeper.jar中使用的日志框架是log4j,而dubbo.jar中使用的是JCL。那我们采取何种方式将这两者的日志统一管理呢?参考下面的pom文件(仅为举例):

<!--注意各个依赖的scope -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<!-- 排除dubbo中依赖的JCL -->
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
<!-- 排除zookeeper中依赖的log4j -->
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--使用适配器将log4j接管-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 使用适配器将JCL接管 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 引入slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 在我们的项目中我们选择了logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>


这样我们在classpath下放置一个logback.xml就可以配置系统中所有日志了(包括第三方类库内使用的日志)。而我们的应用中的代码日志都应该采用类似下面的方式:

//我们的代码中只应该直面slf4j的API
private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(Broker.class);
//...
LOGGER.error("Create broker for {} error", name, e);


这里需要注意的是,如果你的日志实现选择log4j,但因为log4j比slf4j先发布,所以需要添加slf4j到log4j的适配:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>



要点

统一使用 slf4j , 避免在代码中直接使用log4j 或 java logging 等实现类

slf4j 本质是 Facade , 便于我们后期随时切换日志实现.

INFO 及以上的系统日志统一输出到Console

我们发现,在生产环境中如果将系统log切分成多个文件, 是非常不利于问题排查的. 不仅要同时追找多个文件, 还要匹配问题发生的时间点. 最要命的是, 有些异常是被print到 console里的.
第二个就是, 我们原先切分日志是为了报警和rolling, 但实施下来其实仍然有问题, 经常会因为添加一个类型的日志而遗失相当一部分报警信息. 现在我们有了唐娟的强大日志分析系统可以更好的满足这部分需求.

业务日志使用独立的路径配置, 不能采用 className

业务日志需要与系统日志明确区分开, 尤其是日志级别, 一定要设置成最低级别, 免得被调试过程中的其它设置捕获到.

所有日志都输出到 catalina.base/logs 下

避免大家到处找日志, 降低沟通成本.

最佳实践

禁止使用 System.out 或 System.error
禁止使用 Apache Commons Logging , Java Util Logging
推荐 slf4j
推荐 Logback
代码中使用 SLF4J (not log4j, not jcl, not jul, not logback):
类库项目不允许引入logback, log4j等日志实现,只允许引入slf4j

参考: http://gordondickens.com/wordpress/2012/07/03/enterprise-spring-best-practices-part-1-project-config/


名词解释


业务日志

业务相关的信息. 特征是不包含异常栈.


系统日志

调试相关的信息, 异常信息. 用于程序员收集系统健康状况信息.


日志级别

编者按: level 定义简直就是一门艺术, 好的定义应该遵循以下原则:

warn以上信息通俗易懂能清楚的告知所有人发生了什么情况.能引起人的重视.

info 应该简洁明确让管理员确认状态.

debug 完整详细的记录流程的关键路径.


TRACE

在qunar用于业务日志记录.


DEBUG 调试

通常,DEBUG 级别应该用于开发人员比较感兴趣的跟踪和调试信息, 生产环境中默认为关闭状态。


INFO

通常,INFO 消息被写入到控制台或与之相当的地方。因此,INFO 级别应该只用于相当重要的,对于最终用户和系统管理员有意义的消息。
关键系统参数的回显、后台服务的初始化状态、需要系统管理员知会确认的关键信息都需要使用INFO级别


WARN

是指示潜在问题的消息级别。
通常,WARNING 消息应该描述最终用户或系统管理员感兴趣的事件,或者那些指示潜在问题的事件。


ERROR

严重失败, 推荐使用FATAL.


FATAL

是指示严重失败的消息级别。
FATAL 消息应该描述相当重要的事件,这些事件会阻止正常程序的执行。它们对于最终用户和系统管理员来说应该是很容易理解的。


Maven 依赖配置(logback)

pom.xml

<!--Java Commons Logging redirect to slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!--Apache log4j redirect to slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<!--Java Util Logging redirect to slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<!--将所有日志归一成 logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>



系统日志配置

INFO 及以上的系统日志统一输出到Console

异常监控、分类以及rolling目前已经有现成的组件支持, 所以这些系统信息输出到同一个文件里更有利于问题排查。

logback.xml

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
<appender name="console">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss} %5p %c:%L] %m%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="console" />
</root>
</configuration>



业务日志配置

业务日志(比如订单信息,用户输入的日志等等), 不能采用 className. 请采用模式: datalog.PRODUCTNAME.LOGNAME

//定义,一定不要使用类名, 以datalog开头的pattern datalog.${productName}.${logName}
private final static Logger logger = LoggerFactory.getLogger("datalog.hoteltts.orderlog");


为避免业务日志错误的被其它捕获器拦截, 业务日志一律使用最低级别的输出 (TRACE).

logger.trace("###############")


设置指向

log4j.logger.datalog.hoteltts.orderlog = TRACE,orderlog

log4j.appender.orderlog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.orderlog.File=${catalina.base}/logs/orderlog.log
log4j.appender.orderlog.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.orderlog.Threshold=TRACE
log4j.appender.orderlog.layout=org.apache.log4j.PatternLayout
log4j.appender.orderlog.layout.ConversionPattern=\[%d{yyyy-MM-dd HH:mm:ss} %m%n



Comments (1)

Hide Comments

Collapse All





2014-01-15


严家强 says:

其中一种常用的配置: 日志API使用slf4j <!-- 引入slf4j --> <dependency> <g...

其中一种常用的配置:

日志API使用slf4j

<!-- 引入slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>


日志实现使用logback

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>


日志配置文件使用log4j的格式,需要适配

<!--使用适配器将log4j接管-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>


如果使用第三方组件使用JUC,那么我们需要再加:

<!-- 使用适配器将JCL接管 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>


这样第三方组件中的日志级别的配置 也就是用了log4j的配置
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: