日志规范

简介

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
    
  1. 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的配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值