logback日志使用及常用配置

 

目录

为什么要使用logback ?

Logback 与 Log4J

Logback的结构

快速上手

一个简单的例子

Logger,Appenders 与 Layouts

Logger

分层命名规则

有效级别即级别继承

Appender

Layout

补充

常用配置


为什么要使用logback ?

在开发中不建议使用System.out因为大量的使用会增加资源的消耗。因为使用System.out是在当前线程执行的,写入文件也是写入完毕之后才继续执行下面的程序。而使用Log工具不但可以控制日志是否输出,怎么输出,它的处理机制也是通知写日志,继续执行后面的代码不必等日志写完。

Logback 与 Log4J

Slf4j是The Simple Logging Facade for Java的简称,是一个简单日志门面抽象框架,它本身只提供了日志Facade API和一个简单的日志类实现,一般常配合Log4j,LogBack,java.util.logging使用。Slf4j作为应用层的Log接入时,程序可以根据实际应用场景动态调整底层的日志实现框架(Log4j/LogBack/JdkLog...);

LogBack和Log4j都是开源日记工具库,LogBack是Log4j的改良版本,比Log4j拥有更多的特性,同时也带来很大性能提升。详细数据可参照下面地址:Reasons to prefer logback over log4j LogBack官方建议配合Slf4j使用,这样可以灵活地替换底层日志框架。

  • 同样的代码路径,Logback 执行更快
  • 更充分的测试
  • 原生实现了 SLF4J API(Log4J 还需要有一个中间转换层)
  • 内容更丰富的文档
  • 支持 XML 或者 Groovy 方式配置
  • 配置文件自动热加载
  • 从 IO 错误中优雅恢复
  • 自动删除日志归档
  • 自动压缩日志成为归档文件
  • 支持 Prudent 模式,使多个 JVM 进程能记录同一个日志文件
  • 支持配置文件中加入条件判断来适应不同的环境
  • 更强大的过滤器
  • 支持 SiftingAppender(可筛选 Appender)
  • 异常栈信息带有包信息

默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台。

2018-06-15 17:11:43.510  INFO 11956 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6c130c45: startup date [Fri Jun 15 17:11:43 CST 2018]; root of context hierarchy
2018-06-15 17:11:43.649  INFO 11956 --- [           main] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-06-15 17:11:43.671  INFO 11956 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [class org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$e47494a7] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.1.RELEASE)

2018-06-15 17:11:44.070  INFO 11956 --- [           main] c.csizg.iot.model.CsizgModelApplication  : No active profile set, falling back to default profiles: default
2018-06-15 17:11:44.084  INFO 11956 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@246f8b8b: startup date [Fri Jun 15 17:11:44 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@6c130c45
2018-06-15 17:11:45.501  INFO 11956 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2018-06-15 17:11:45.716  INFO 11956 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=ce09d350-18eb-3a03-8c8c-ed3394b359ad

从上图可以看到,日志输出内容元素具体如下:

  • 时间日期:精确到毫秒
  • 日志级别:ERROR, WARN, INFO, DEBUG or TRACE
  • 进程ID
  • 分隔符:--- 标识实际日志的开始
  • 线程名:方括号括起来(可能会截断控制台输出)
  • Logger名:通常使用源代码的类名
  • 日志内容

Logback的结构

LogBack被分为3个组件,logback-core, logback-classic 和 logback-access.

其中logback-core提供了LogBack的核心功能,是另外两个组件的基础。

logback-classic则实现了Slf4j的API,所以当想配合Slf4j使用时,需要将logback-classic加入classpath。

可以启动服务的时候指定 profile (如不指定使用默认),如指定prod 的方式为:

java -jar xxx.jar --spring.profiles.active=prod

如果在 logback.xml 和 application.properties 中定义了相同的配置(如都配置了 org.springframework.web)但是输出级别不同,则实际上 application.properties 的优先级高于 logback.xml

Logback 可以使用 {} 占位符来拼接字符串,而不需要使用 ““+”” 来连接字符串。 @Slf4j 就不用 private static final Logger logger = LogManager.getLogger(ApproveTaskController.class); logging.file ,设置文件,可以是绝对路径,也可以是相对路径。如: logging.file=my.log logging.path ,设置目录,会在该目录下创建 spring.log 文件,并写入日志内容,如: logging.path=/var/log

快速上手

     
    
        org.slf4j
        slf4j-api
    
    
    
        ch.qos.logback
        logback-core
    
    
    
        ch.qos.logback
        logback-classic
    

一个简单的例子

package com.csizg.iot.portal.v1.controller;

import com.alibaba.fastjson.JSONObject;
import com.csizg.iot.portal.v1.domain.dto.user.SafetyUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author limengying-8088
 * @version v1.0
 * @date 16:15 2018/6/15
 */
@RestController
@RequestMapping("/v1/portal/logDemo/")
public class LogDemoController {
    private static final Logger logger = LoggerFactory.getLogger(LogDemoController.class);
    @PostMapping("/addLog")
    public SafetyUser addLog(@RequestBody SafetyUser safetyUser) {
        logger.info("========我是log");
        return safetyUser;
    }
}

控制台结果

2018-06-16 13:57:32.591  INFO 10044 --- [nio-8005-exec-1] c.c.i.p.v1.controller.LogDemoController  : ========我是log

LoggerFactory 的 getLogger() 方法接收一个参数,以这个参数决定 logger 的名字,这里传入了 LogDemoController 这个类的 Class 实例,那么 logger 的名字便是 LogDemoController 这个类的全限定类名:com.csizg.iot.portal.v1.controlle.LogDemoController

Logger,Appenders 与 Layouts

在 logback 里,最重要的三个类分别是 Logger Appender Layout

Logger

分层命名规则

每个 logger 都有一个 name,这个 name 的格式与 Java 语言中的包名格式相同。这也是前面的例子中直接把一个 class 对象传进 LoggerFactory.getLogger() 方法作为参数的原因。

例如, 命名为 com.foo 的 logger,是命名为 com.foo.Bar 的 logger 的父亲,是命名为 com.foo.Bar.demo 的 logger 的祖先。

在 logger 上下文中,有一个 root logger,作为所有 logger 的祖先,这是 logback 内部维护的一个 logger,并非开发者自定义的 logger。

可通过以下方式获得这个 logger :

Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

有效级别即级别继承

日志打印级别从低级到高级排序的顺序是: TRACE < DEBUG < INFO < WARN < ERROR

如果一个 logger 允许打印一条具有某个日志级别的信息,那么它也必须允许打印具有比这个日志级别更高级别的信息,而不允许打印具有比这个日志级别更低级别的信息。

public void testLevel(){
    //这里强制类型转换时为了能设置 logger 的 Level
    ch.qos.logback.classic.Logger logger =
            (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.foo");
    logger.setLevel(Level.INFO);
    // barlogger 是 logger 的一个子 logger
    // 它继承了 logger 的级别 INFO
    Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");
    // 这个语句能打印,因为 WARN > INFO
    logger.warn("can be printed because WARN > INFO");
    // 这个语句不能打印,因为 DEBUG < INFO.
    logger.debug("can not be printed because DEBUG < INFO");
    // 以下语句能打印,因为 INFO >= INFO
    barlogger.info("can be printed because INFO >= INFO");
    // 以下语句不能打印,因为 DEBUG < INFO
    barlogger.debug("can not be printed because DEBUG < INFO");
}

控制台结果

2018-06-16 18:01:02.354  WARN 9908 --- [nio-8005-exec-5] com.foo                                  : can be printed because WARN > INFO
2018-06-16 18:01:02.354  INFO 9908 --- [nio-8005-exec-5] com.foo.Bar                              : can be printed because INFO >= INFO

Appender

logback允许打印记录请求到多个目的地,目前有控制台、文件、远程套接字服务器、MySQL等多种appender。

Appender 是绑定在 logger 上的,同时,一个 logger 可以绑定多个 Appender,意味着一条信息可以同时打印到不同的目的地去。例如,常见的做法是,日志信息既输出到控制台,同时也记录到日志文件中,这就需要为 logger 绑定两个不同的 logger,而 logger 又有继承关系,因此一个 logger 打印信息时的目的地 Appender 需要参考它的父亲和祖先。。在 logback 中,默认情况下,如果一个 logger 打印一条信息,那么这条信息首先会打印至它自己的 Appender,然后打印至它的父亲和父亲以上的祖先的 Appender,但如果它的父亲设置了 additivity = false,那么这个 logger 除了打印至它自己的 Appender 外,只会打印至其父亲的 Appender

name指定的名称 class指定的全限定名

 
        
            %d - %msg%n
        
    

是0.9.19版本之后引进的,以前的版本使用,logback极力推荐的是使用而不是

Layout

日志打印格式

if(logger.isDebugEnabled()) { 
  logger.debug("the message is " + msg + " from " + somebody);
}

无论日志级别是什么,程序总要先执行 "the message is " + msg + " from " + somebody 这段字符串的拼接操作

一种改进的打印日志方式

if(logger.isDebugEnabled()) { 
  logger.debug("the message is " + msg + " from " + somebody);
}

当日志级别为 DEBUG 时,那么打印这行消息,需要判断两次日志级别。一次是logger.isDebugEnabled(),另一次是 logger.debug() 方法内部也会做的判断。

logger.debug("the message {} is from {}", msg, somebody);

In Logger, the logging methods are overloaded with forms that accept one, two or more values.[9] Occurrences of the simple pattern {} in the log message are replaced in turn with the values. This is simple to use yet provides a performance benefit when the values have expensive toString() methods. When logging is disabled at the DEBUG level, the logging framework does not need to evaluate the string representation of the values. In the following example, the values count or userAccountList only need to be evaluated when DEBUG is enabled; otherwise the overhead of the debug call is trivial.

  • 原因1,第二种使用占位符只有当日志级别是debug,才会执行count和userAccountlist的toString方法,而第一种非debug级别也会执行。
  • 原因2,更具可读性。

logback日志使用及常用配置_第1张图片

根节点是 configuration,可包含0个或多个 appender,0个或多个 logger,最多一个 root。

logback加载 我们简单分析一下logback加载过程,当我们使用logback-classic.jar时,应用启动,那么logback会按照如下顺序进行扫描:

  • 在系统配置文件System Properties中寻找是否有logback.configurationFile对应的value
  • 在classpath下寻找是否有logback.groovy(即logback支持groovy与xml两种配置方式)
  • 在classpath下寻找是否有logback-test.xml
  • 在classpath下寻找是否有logback.xml

以上任何一项找到了,就不进行后续扫描,按照对应的配置进行logback的初始化,具体代码实现可见ch.qos.logback.classic.util.ContextInitializer类的findURLOfDefaultConfigurationFile方法。

当所有以上四项都找不到的情况下,logback会调用ch.qos.logback.classic.BasicConfigurator的configure方法,构造一个ConsoleAppender用于向控制台输出日志,默认日志输出格式为”%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} – %msg%n”。

logback的configuration

  • scan:当scan被设置为true时,当配置文件发生改变,将会被重新加载,默认为true
  • scanPeriod:检测配置文件是否有修改的时间间隔,默认为毫秒,当scan=true时这个值生效,默认时间间隔为1分钟
  • debug:当被设置为true时,将打印出logback内部日志信息,实时查看logback运行信息,默认为false

...

  • name:用来指定受此logger约束的某一个包或者具体的某一个类
  • level:用来设置打印级别,五个常用打印级别从低至高依次为TRACE、DEBUG、INFO、WARN、ERROR,如果是"INHERITED"或"NULL",会继承上级的级别
  • additivity:是否向上级logger传递打印信息,默认为true

有一个level属性,没有name属性,因为已经被命名为"ROOT" 也是元素,但是它是根loger。只有一个level属性,应为已经被命名为"root". level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。默认是DEBUG。

可以包含零个或多个元素,标识这个appender将会添加到这个loger。

节点负责两件事情:

  1. 把日志信息转换为字节数组
  2. 把字节数组写到输出流
  • %m 输出代码中指定的消息
  • %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
  • %r 输出自应用启动到输出该log信息耗费的毫秒数
  • %c 输出所属的类目,通常就是所在类的全名
  • %t 输出产生该日志事件的线程名
  • %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
  • %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS}输出类似:2002年10月18日 22:10:28,921
  • %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
  • %thread 线程名

补充

异步写日志 日志通常来说都以文件形式记录到磁盘,例如使用,这样的话一次写日志就会发生一次磁盘IO,这对于性能是一种损耗,因此更多的,对于每次请求必打的日志(例如请求日志,记录请求API、参数、请求时间),我们会采取异步写日志的方式而不让此次写日志发生磁盘IO,阻塞线程从而造成不必要的性能损耗。

  
      
    0  
      
    256  
      
      

原理:当我们配置了AsyncAppender,系统启动时会初始化一条名为"AsyncAppender-Worker-ASYNC"的线程

当Logging Event进入AsyncAppender后,AsyncAppender会调用appender方法,appender方法中再将event填入Buffer(使用的Buffer为BlockingQueue,具体实现为ArrayBlockingQueye)前,会先判断当前Buffer的容量以及丢弃日志特性是否开启,当消费能力不如生产能力时,AsyncAppender会将超出Buffer容量的Logging Event的级别进行丢弃,作为消费速度一旦跟不上生产速度导致Buffer溢出处理的一种方式。

上面的线程的作用,就是从Buffer中取出Event,交给对应的appender进行后面的日志推送

从上面的描述我们可以看出,AsyncAppender并不处理日志,只是将日志缓冲到一个BlockingQueue里面去,并在内部创建一个工作线程从队列头部获取日志,之后将获取的日志循环记录到附加的其他appender上去,从而达到不阻塞主线程的效果。因此AsyncAppender仅仅充当的是事件转发器,必须引用另外一个appender来做事。

从上述原理,我们就能比较清晰地理解几个参数的作用了:

  • discardingThreshold,假如等于20则表示,表示当还剩20%容量时,将丢弃TRACE、DEBUG、INFO级别的Event,只保留WARN与ERROR级别的Event,为了保留所有的events,可以将这个值设置为0,默认值为queueSize/5
  • queueSize比较好理解,BlockingQueue的最大容量,默认为256
  • includeCallerData表示是否提取调用者数据,这个值被设置为true的代价是相当昂贵的,为了提升性能,默认当event被加入BlockingQueue时,event关联的调用者数据不会被提取,只有线程名这些比较简单的数据
  • appender-ref表示AsyncAppender使用哪个具体的进行日志输出

常用配置



    
    
    
    
    
    
    
    

    
    
        
            
            ${CONSOLE_LOG_PATTERN}
        
    
    
    
        
        
            INFO
            ACCEPT
            DENY
        
        
            ${FILE_LOG_PATTERN}
        
        
        
        
            
            ${LOG_HOME}/${INFO_LOG}/user-info-%d{yyyy-MM-dd}.%i.log
            
            
                10MB
            
        
    
    
        
            WARN
            ACCEPT
            DENY
        
        
            ${FILE_LOG_PATTERN}
        
        
            ${LOG_HOME}/${WARN_LOG}/user-warn-%d{yyyy-MM-dd}.log
        
    
    
        ${LOG_HOME}/user-error.log
        
            ERROR
        
        
            ${FILE_LOG_PATTERN}
        
        
            ${LOG_HOME}/${ERROR_LOG}/user-error-%d{yyyy-MM-dd}.%i.log
            
                5MB
            
        
    
    
    
    
    
    
    
    

    
    
        
        
        
        
    

 

你可能感兴趣的:(logback,spring,boot,java)