• Logback 快速入门 / 使用详解


    官方文档: http://logback.qos.ch/manual/index.html

    一、简介

    Java 开源日志框架,以继承改善 log4j 为目的而生,是 log4j 创始人 Ceki Gülcü 的开源产品。
    它声称有极佳的性能,占用空间更小,且提供其他日志系统缺失但很有用的特性。
    其一大特色是,在 logback-classic 中本地(native)实现了 SLF4J API(也表示依赖 slf4j-api)

    二、架构 / Logback知识

    1. 项目分为三个模块:

    • logback-core:其他俩模块基础模块,作为通用模块 其中没有 logger 的概念
    • logback-classic:日志模块,完整实现了 SLF4J API
    • logback-access:配合Servlet容器,提供 http 访问日志功能

    2. 在 logback 中主要概念(Logger、Appender、Layout、Encoder)

    · Logger

    日志记录器,logback-classic 的部分
    每个 Logger 都附加到一个 LoggerContext 上,该 Context 负责构造 Logger 以及将其安排在层级结构中。

    1. 命名及层级关系
      Logger 名称区分大小写,并遵循层级命名规则。
      层级关系用 "." 表示,如:"com.foo" 是 "com.foo.Bar" 的父Logger

      且所有 Logger 都可通过LoggerFactory#getlogger(String name)来获取,且相同名称返回的实例相同

    2. 根 Logger
      是所有层级结构的顶部Logger,可通过名称检索获取
      Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

    3. Level 与 继承关系
      Logger 可以被分配级别(TRACE、DEBUG、INFO、WARN、ERROR),可在 ch.qos.logback.classic 中查看
      若没有给 Logger 分配级别,则它将从最近的分配了等级的祖先处继承等级
      例:

      Logger name Assigned level Effective level
      root DEBUG DEBUG
      X INFO INFO
      X.Y none INFO
      X.Y.Z ERROR ERROR
    4. Level 与 log 规则(basic selection rule)
      Logger 只会启用等级 ≥ Logger等级的日志请求
      等级排序(严重程度,而非优先级):TRACE < DEBUG < INFO < WARN < ERROR
      如:

      Logger logger = LoggerFactory.getLogger("com.foo");
      logger.setLevel(Level. INFO);
      
      // 启用,因为 WARN >= INFO
      logger.warn("Low fuel level.");
      // 禁用, 因为 DEBUG < INFO. 
      logger.debug("Starting search for nearest gas station.");
      

    · Appender

    Logback 通过Appender#doAppend(E event)将日志事件打印到目的地(允许附加多个Appender即即多个目的地
    目前官方已提供 console、文件、远程socket服务、JMS、远程UNIX Syslog进程、MySQL/PostgreSQL/Orcale等数据库的 appender 支持.
    注意:

    • 一个 Logger 可以通过Logger#addAppender方法可被附加多个 appender
    • appender 同样适用于继承结构,且是以追加的方式而非覆盖;
      但继承行为可被 Logger 的 additivity 标志影响是否继承(通过Logger#setAdditive设置)
    • additivity 标志本身也是可继承的

    继承示例如下:

    Logger Name Attached Appenders Additivity Flag Output Targets
    root A1 not applicable A1
    x A-x1, A-x2 true A1, A-x1, A-x2
    x.y none true A1, A-x1, A-x2
    x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1
    security A-sec false A-sec
    security.access none true A-sec

    · Layout

    表示日志输出格式,其通过接口ch.qos.logback.core#doLayout(E event): String将日志事件格式化为String 返回。
    Logback 官方提供 PatternLayout,允许以类似c语言printf来指定输出格式。
    可通过将 layout 与 appender 关联,来实现自定义输出格式和目的地;但并不是每个 appender 都需要 layout,比如负责序列化的 SocketAppender 自然不需要 doLayout 转字符串

    · Encoder

    Encoder的概念在 Logback 0.9.19 中被引入,通过Encoder#encode可将 LoggingEvent 转为 byte[]
    目前 Logback 仅提供了 PatternLayoutEncoder 这一个可用的实现,其逻辑很简单:内部构造/包装了 PatternLayout 实例,调用PatternLayout#doLayout得到格式化字串后,再调用String#getBytes返回 byte[]

    引入原因:因为Layout#doLayout接口只能将 LoggingEvent 转为 String,这在某些情况下不太灵活,而现在 Encoder 能完全控制字节格式。
    比如,在以前的版本中常在FileAppender中嵌套PatternLayout来使用,但现在仅需依赖Encoder

    三、使用

    1.logback 配置

    Logback 初始化时,根据以下顺序尝试配置:

    1. 类路径下尝试寻找 logback-test.xml
    2. 若没有,类路径下尝试寻找 logback.groovy
    3. 若没有,类路径下尝试寻找 logback.xml
    4. 若没有,尝试基于 Java SPI 机制寻找 com.qos.logback.classic.spi.Configurator 接口的实现
    5. 若以上都没有,Logback 会使用最基本的 BasicConfigurator 配置自己。
      这将使用 TTLLLayout(类似 PatternLayout) 以"%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"模式格式化日志,并将 ConsoleAppender 附加到 root Logger,这会输出到控制台,且 root 被指定为 DEBUG 等级。
      默认配置等效为以下xml配置:
      <configuration>
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
      </appender>
      
      <root level="debug">
      <appender-ref ref="STDOUT" />
      </root>
      </configuration>
      

    可通过以下代码打印当前配置:StatusPrinter.print((LoggerContext) LoggerFactory.getILoggerFactory())
    输出:

    15:25:46,635 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
    15:25:46,635 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
    15:25:46,636 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
    15:25:46,647 |-INFO in ch.qos.logback.classic.BasicConfigurator@5f16132a - Setting up default configuration.
    

    2.log 整体流程

    1. 获取 filter 链决策
    2. 应用 basic selection rule
    3. 创建 LoggingEvent 对象
    4. 调用 appenders
    5. 调 Layout 格式化
    6. 输出到目的地

    四、配置详解

    1.配置文件

    一些写法/背景知识:

    1. 可在值中以 ${KEY:-defaultValue} 的形式引入属性,查找顺序如下:

      • 首先在本地作用域查找(详见<property>
      • 若没有,在上下文作用域查找
      • 若没有,在JVM系统属性查找
      • 最后,在系统环境变量中查找

      既支持名称(KEY)中的嵌套,也支持值/默认值的嵌套引用

    2. 预定义属性:

      • HOSTNAME:系统hostname,是配置时在 context 域中被定义被定义
      • CONTEXT_NAME:上下文名
    3. 一些标签涉及到类的标签(如<definer>、<appender>、<encoder>),其子标签除了规定之外,还可定义与 JavaBean 属性同名,这将调用相应setter注入。

    顶级标签

    <configuration>
    属性:

    • debug:获知 Logback 内部状态,官方推荐在<configuration>上设置debug属性,而非在代码中调用 StatusPrinter。例:<configuration debug="true">
      这也等同于设置状态监听器 OnConsoleStatusListener
      例:
      <configuration>
      	<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
      	<!-- … -->
      </configuration>
      
    • scan / scanPeriod:可定期扫描配置文件的更改,并在更改时自动应用。默认每分钟一次
      例:<configuration scan="true" scanPeriod="30 seconds" >
    • packagingData:可令每一行StackTrace输出其对应jar包。需注意,其计算成本高昂
      例:<configuration packagingData="true">
      输出示例:
      14:28:48.835 [btpool0-7] INFO  c.q.l.demo.prime.PrimeAction - 99 is not a valid value
      java.lang.Exception: 99 is invalid
      at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
      at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
      at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
      at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
      

    <configuration>子标签

    • <include>:从另外的文件引入配置,目标文件必须将其标签放入<included>下。(SpringBoot基础配置便是如此,可参考spring-boot:2.4.0包中base.xml)

      • file
      • resource
      • url
      • optional:[true | false],默认若找不到目标文件,将输出警告;可设置为可选避免该行为
    • <contextName>:设置上下文名称,可作为区分不同多个应用程序到同一target的区分,默认为"default"
      示例:

    <contextName>myAppName</contextName>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    	<encoder>
    	<pattern>%d %contextName [%t] %level %logger{36} - %msg%n</pattern>
    </encoder>
    </appender>
    
    • <property>:配置或引入外部属性,别名

      • name
      • value
      • file:引用文件系统的 .properties 文件
      • resource:引用类路径上的 variables.properties 文件
      • scope:
        • local:默认,会在配置解释时定义,并在结束后清除。
        • context:上下文作用域,属性将插入到LoggerContext中直到LoggerContext被清除,因此在所有日志事件中都可用。
        • system:系统作用域,属性将插入到JVM系统属性中
    • <define>:代码动态定义属性,以 PropertyDefiner#getPropertyValue 方法返回其值

      • name
      • class:PropertyDefiner实现全类名
    • <conversionRule>:自定义转换词

      • conversionWord:转换词
      • converterClass:ClassicConverter 实现类全类名
    • <appender>

      • *name:声明Appender名称
      • *class:要使用的Appender的完全限定名
      • <appender>、<layout>子标签:
        • <layout>:0或1个
          • class:若未声明,表示 PatternLayout
        • <encoder>:0或多个
          • class:若未声明,表示 PatternLayoutEncoder
        • <filter>:0或多个
    • <logger>:0或多个,配置 Logger

      • name(必须)
      • level:[TRACE | DEBUG | INFO | WARN | ERROR | ALL | OFF],或者 INHERITED / NULL 表示从上级继承。
      • additivity:[true | false]
      • 子标签:
        • <appender-ref> :0或多个,表附加appender
          • ref:指定先前的名称
    • <root>:配置根Logger

      • level:根Logger级别不能设置为 INHERITED / NULL
      • 子标签:
        • <appender-ref>

    2.JVM系统属性

    • logback.configurationFile:指定配置文件位置,可以是 URL、类路径资源或外部文件系统。也可以在代码中设置(System#setProperty),但必须在创建任意Logger实例之前。
      文件扩展名必须是 .xml 或 .groovy
    • logback.statusListenerClass:可设置为希望注册的 StatusListener 名称

    3. PatternLayout 的格式化消息编写规则

    基础规则

    • 转换符 '%' 可后跟规定的word,来获取数据字段,如 logger名称,日期,线程名等

    • 转换符可后接格式修饰符(Format modifiers)来指定每个字段最小、最大宽度 以及 是否左对齐
      示例:

      写法 左对齐 最小宽度 最大宽度
      %20logger false 20 none
      %.30logger NA none 30
      %-20.30logger true 20 30
    • 括号"()" 可将部分内容分组,常配合格式修饰符使用
      如:
      %d{HH:mm:ss.SSS} [%thread]

      输出:
      13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
      13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
      

      %-30(%d{HH:mm:ss.SSS} [%thread])
      image

    • 一些关键词可使用转义符来输出

    常用转换词

    • 日志事件的消息:m / msg / messages

    • 日志级别:p / le / level

    • Logger 名
      语法:

      c{length}
      lo{length}
      logger{length}
      

      含义/示例: length表示推荐最大长度,logback会根据该长度适当缩写Logger名,但始终会保证最右段不会缩写

      示例 Logger原名称 打印结果
      %logger mainPackage.sub.sample.Bar mainPackage.sub.sample.Bar
      %logger{0} mainPackage.sub.sample.Bar Bar
      %logger{5} mainPackage.sub.sample.Bar m.s.s.Bar
      %logger{16} mainPackage.sub.sample.Bar m.sub.sample.Bar
    • 上下文名:contextName

    • 日志事件的线程名: t / thread

    • 从"应用启动"到"产生该日志事件"经过的毫秒数:r / relative

    • 日期
      语法:

      d{pattern}
      date{pattern}
      d{pattern, timezone}
      date{pattern, timezone}
      

      含义/示例:输出日志事件的日期,其 pattern 语法与 java.text.SimpleDateFormat 格式兼容
      在没有 timezone 的情况下,使用 Java 平台默认时区

      示例 结果
      %d 2006-10-20 14:06:49,812
      %date{HH:mm:ss.SSS} 14:06:49.812
      %date{dd MMM yyyy;HH:mm:ss.SSS} 20 oct. 2006;14:06:49.812
      %date{HH:mm:ss.SSS, Australia/Perth}
    • 换行符:n

    • 异常:
      语法:

      ex{depth}
      exception{depth}
      throwable{depth}
      
      ex{depth, evaluator-1, ..., evaluator-n}
      exception{depth, evaluator-1, ..., evaluator-n}
      throwable{depth, evaluator-1, ..., evaluator-n}
      

      含义/示例:输出与日志事件关联的异常的stacktrace信息,默认输出完整的stacktrace
      image

    • 属性:property{key}

    • 正则替换:replace(p){r,t} 将消息模式p中的r替换为t
      如:%replace(%logger %msg){'.', '/'}将所有"."替换为"/"

    • MDC(Mapped Diagnostic Contex)字段:%X{filedName}

    • 自定义转换词:

      1. 自定义实现 ClassicConverter,重写 convert(ILoggingEvent) 方法
        public class MySampleConverter extends ClassicConverter {
        	long start = System.nanoTime();
        
        	@Override
        	public String convert(ILoggingEvent event) {
        		long nowInNanos = System.nanoTime();
        		return Long.toString(nowInNanos-start);
        	}
        }
        
      2. 声明转换词
        <configuration>
        	<conversionRule conversionWord="nanos" converterClass="chapters.layouts.MySampleConverter" />
        	<!-- … -->
        </configuration>
        

    性能考虑,应避免使用的转换词

    • 类名:C{length} / class{length}
    • 文件名:F / file
    • 行号:L / line
    • 方法名:M / method
    • 包含jar包信息的异常stacktrace:xEx{depth} / xException{depth}…
      及root异常顺序反转的 rEx{depth} / rootException{depth}…

    4. 着色(Coloring)

    基于ANSI颜色代码,Logback可使用颜色关键词来为分组设置颜色。内置了:"%highlight","%black", "%red", "%green","%yellow","%blue", "%magenta","%cyan", "%white", "%gray", "%boldRed","%boldGreen", "%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite"
    例:
    <pattern>%d ${HOSTNAME} [%t] %highlight(%level) %logger{36} - %msg%n %ex{full}</pattern>

    踩坑:官方说明使用 jansi 可以做到兼容不兼容ANSI的终端,但 Windows 下始终报错,即便引入了推荐版本 仍然报错

    <dependency>
    	<groupId>org.fusesource.jansi</groupId>
    	<artifactId>jansi</artifactId>
    	<version>1.17</version>
    </dependency>
    

    5.异步Appender

    可通过声明AsyncAppender来执行异步日志打印,它只进行日志事件分发,因此必须引用另一个实际Appender才有意义

    五、常见需求

    1. 根据环境变量动态附加Appender
      参考:How to select Logback appender based on property file or environment variable
      1. 使用 logback 官方支持的 语法,但同样需要引入 Janino library。参考:configuration
      2. Appender 设置 <filter>,并使用环境变量语法动态配置Filter的Level【有些曲线救国,应该仍有性能损耗
      3. 使用 SpringProfile
        例:
        application.yaml
        spring:
          profiles:
        	active:
        	  - ${ENV:classiclogs}
        
        logback.xml
        <configuration>
        	<!-- appender conf…-->
        	<springProfile name="jsonlogs">
        		<root level="info">
        			<appender-ref ref="stdout-json" />
        		</root>
        	</springProfile>
        	<springProfile name="classiclogs">
        		<root level="info">
        			<appender-ref ref="stdout-classic" />
        		</root>
        	</springProfile>
        </configuration>
        

    六、踩坑

    1. 对于嵌套 .xml 配置文件,默认域"local"重复声明的话是无效的,不会覆盖。
      比如在我的logback.xml中声明,无法覆盖Spring自带的配置。

    撰文参考:
    http://logback.qos.ch/manual/index.html

    本文为博主原创文章,如需转载请注明链接出处! 如有帮助到你,还请留言支持一下,谢谢:) 若有问题,也欢迎讨论指正。
  • 相关阅读:
    python 垃圾回收装置
    文件夹同步/备份软件推荐 (SyncToy/FreeFileSync/Compare Advance/GoodSync/Allway Sync/Compare Advance)
    WCF的ABC
    NPOI导出多表头Execl(通过html表格遍历表头)
    分布式应用处理方式
    再探ASP.NET 5(转载)
    应聘.net开发工程师常见的面试题(五)
    应聘.net开发工程师常见的面试题(四)
    应聘.net开发工程师常见的面试题(二)(转载)
    应聘.net开发工程师常见的面试题(一)(转载)
  • 原文地址:https://www.cnblogs.com/simpleito/p/15133654.html
Copyright © 2020-2023  润新知