【运维篇】三、SLF4J与Logback

文章目录

  • 0、Java的门面设计模式
  • 1、SLF4J
  • 2、作用
  • 3、调试
  • 4、SpringBoot采用SLF4J+Logback
  • 5、切换SpringBoot的日志框架
  • 6、logback的配置加载
  • 7、logback的配置组成
  • 8、logback之logger
  • 9、logback之appender
  • 10、logback之pattern
  • 11、appender的Filter
  • 12、logback.xml全解释
  • 13、logback常用配置备份

请添加图片描述

0、Java的门面设计模式

因为SLF4J就是门面设计模式的应用,因此先整理下这种设计模式。参考原文:https://blog.csdn.net/jason0539/article/details/22775311

部分截图:

在这里插入图片描述
在这里插入图片描述

核心就是外部(客户端)与一个子系统通信时,通过一个统一的外观对象进行,从而隐藏子系统的具体逻辑,使得子系统更易于使用。客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。

在这里插入图片描述
门面对象有以下几个特点:

  • 知道所有子角色的功能和责任
  • 将客户端发来的请求委派到子系统中,没有实际业务逻辑
  • 不参与子系统内业务逻辑的实现

1、SLF4J

SLF4J,Simple Logging Facade For Java,即简单日志门面,是一套存取日志的标准接口

注意是接口,不是具体实现,就像JDBC一样只是统一的接口,想要使用就需要搭配其他具体的日志实现方案,如logback、log4j

在这里插入图片描述

slf4j-simple、logback都是slf4j的具体实现,log4j并不直接实现slf4j,但是有专门的一层桥接slf4j-log4j12来实现slf4j。关于这些具体实现框架的信息,贴个图:

在这里插入图片描述

2、作用

关于SLF4J的使用目的,看这个场景:

- 自己开发的系统中,日志框架使用logback实现
- 这个系统中使用了A.jar,A.jar中使用了log4j框架
- 这个系统也使用了B.jar,B.jar中使用了slf4j-simple

此时,我们就得维护多套日志框架,使用不便。就像这样:

// 使用log4j,需要log4j.jar
import org.apache.log4j.Logger;
Logger logger_log4j = Logger.getLogger(Test.class);
logger_log4j.info("Hello World!");// 使用log4j2,需要log4j-api.jar、log4j-core.jar
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Logger logger_log4j2 = LogManager.getLogger(Test.class);
logger_log4j2.info("Hello World!");// logback,需要logback-classic.jar、logback-core.jar
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
Logger logger_logback = new LoggerContext().getLogger(Test.class);
logger_logback.info("Hello World!");

使用不同的日志框架,就要引入不同的jar包,使用不同的代码获取Logger。如果项目升级需要更换不同的框架,那么就需要修改所有的地方来获取新的Logger,这将会产生巨大的工作量。 因此,加入门面的设计模式,让调用端只关心如何调用,而不关心怎么实现打印日志,这个抽象层就是slf4j。slf4j只是一个日志标准,并不是日志系统的具体实现,它只负责:

  • 提供日志接口
  • 提供获取具体日志对象的方法

3、调试

先看下只有slf4j依赖的情况,创建个小模块,仅引入单元测试与slf4j的依赖,不引入任何日志的具体实现的依赖。

在这里插入图片描述

pom.xml内容:

在这里插入图片描述

在单元测试中打印行日志看下效果:

@Test
public void testSlf4j(){Logger logger = LoggerFactory.getLogger(Object.class);logger.info("log...");
}

运行单元测试:

在这里插入图片描述

在没有给slf4j提供具体的实现时,打印日志失败,大致意思是找不到绑定器和实现。接下来随便再引入一个日志实现框架的依赖:

<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic<artifactId><version>1.2.3<version>
</dependency>

再运行单元测试,日志打印成功:

在这里插入图片描述
再引入一个其他的日志实现:

<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple<artifactId><version>1.7.25<version>
</dependency>

再运行上面的UT,发现控制台显示有多个SLF4J绑定器,最终生效的是logback下的:

在这里插入图片描述

4、SpringBoot采用SLF4J+Logback

默认情况下,SpringBoot使用了 SLF4J+logback 的日志框架组合。查看SpringBoot起步依赖:

在这里插入图片描述

往下跟logging-starter:

<dependencies><!--logback--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version><scope>compile<www.dongdongrji.cn /scope></dependency><!--log4j 向 slf4j 转换--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-to-slf4j</artifactId><version>2.11.1</version><scope>compile</scope></dependency><!--Java 自带的日志框架转换为 slf4j--><dependency><groupId>org.slf4j</groupId><artifactId>jul-to-slf4j</artifactId><version>1.7.25</version><scope>compile</scope></dependency></dependencies>

Maven依赖图:

在这里插入图片描述

5、切换SpringBoot的日志框架

切换日志框架,即切换实现,就是排除默认的依赖,引入自己需要的框架的依赖。

logback切换为log4j2:

<dependencies><dependency><!‐‐starter‐web里面自动添加starter‐logging ,向下就是logback的依赖‐‐><groupId>org.springframework.boot</groupId><artifactId>spring‐boot‐starter‐web</artifactId><exclusions><!‐‐排除starter‐logging 也就是logback的依赖‐‐><exclusion><artifactId>spring‐boot‐starter‐logging</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions></dependency><!‐‐Log4j2的场景启动器‐‐><dependency><groupId>org.springframework.boot</groupId><artifactId>spring‐boot‐starter‐log4j2</artifactId></dependency>
</dependencies>

再添加log4j2的配置文件log4j2-spring.xml即可。

logback切换为log4j:

要将logback的桥接器排除,添加log4j的桥接器:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><artifactId>logback-classic</artifactId><groupId>ch.qos.logback</groupId></exclusion></exclusions></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></dependency>

再添加log4j的配置文件。

关于桥接:

在这里插入图片描述

6、logback的配置加载

logback是log4j的作者写的另一日志框架,对之前的框架进行了完善和优化。关于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

在这里插入图片描述

7、logback的配置组成

整个配置拆为三大块:Appender、Logger、Pattern。先看下一个logback的 <configuration>的三个属性。

<configuration>的三个属性:

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

8、logback之logger

<logger>用来设置某一个包或者具体某一个类的日志打印级别(分组的那个味儿)、以及指定所用的<appender>

<logger>包含一个元素和三个属性:

  • <appender-ref>元素:0个或多个,用来说明这个logger分组下的日志往哪儿输出、怎么输出
  • name属性:name必填,name不是随便起的,它表示的是LoggerFactory.getLogger(XXX.class),XXX类的包路径,包路径越少越是父级,传入的xxx.class用哪个logger,就是看这个类属于哪个name包下
  • level:用来设置打印级别,可选,未设置时继承上级logger的级别,<root>也是<logger>元素,但是它是根logger,只有一个level属性,因为它的name就是ROOT
  • additivity:是否向上级logger传递打印信息,默认为true

写个UT来测试下效果:

public class Slf4jTest {@Testpublic void testSlf4j() {Logger logger = LoggerFactory.getLogger(Object.class);logger.trace("=====trace=====");logger.debug("=====debug=====");logger.info("=====info=====");logger.warn("=====warn=====");logger.error("=====error=====");}}

类路径下写logback.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false" scanPeriod="60000" debug="false"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern></layout></appender><root level="info"><appender-ref ref="STDOUT" /></root></configuration>

以上这个配置,即root这个logger把日志级别大于info的信息交给STDOUT这个appender处理(STDOUT将信息打印到控制台),此时运行上面的UT:

在这里插入图片描述

再调整下上面的logback.xml,新加一个logger:

<logger name="java" additivity="false" />

此时运行UT,无输出结果:

在这里插入图片描述
结果分析:

  • 首先logger的name是java,我UT传入的class为java.lang.Object,属于这个logger
  • 其次,level没写,继承父级logger的level,即info
  • additivity=false,即不向父级logger<root>传递
  • 而它自己却又没有配置任何的appender-ref,所以无输出
  • additivity改为true,则传递到root这个根logger,用它的appender-ref,就能输出info及以上信息

再调整下上面的logback.xml,再新加一个logger:

<logger name="java" additivity="false" />
<!--新增-->
<logger name="java.lang" level="warn"><appender-ref ref="STDOUT" />
</logger>

运行UT:

在这里插入图片描述
结果分析:

  • 传入的Object.class属于java.lang这个logger
  • java.lang的logger配有appender,成功打印warn及以上
  • 没写additivity,默认为true,传递到父级name=java的logger
  • java这个logger无appender,也不向上传递,那不打印任何信息

最后,注意这种导致重复打印的不合理写法:

在这里插入图片描述

9、logback之appender

<appender>是负责写日志的组件。<appender>有两个必要属性name和class:

  • name指定<appender>的名称
  • class指定<appender>的全限定名

常用的appender有:

  • ConsoleAppender
  • FileAppender
  • RollingFileAppender
  • AsyncAppender

ConsoleAppender用来将日志输出到控制台:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern></encoder>
</appender>

FileAppender用于将日志写到文件中:

<appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>D:/server.log</file><append>true</append><encoder><pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern></encoder>
</appender>

其中:

  • file元素指定文件名,可以用相对路径,也可绝对路径
  • appender元素为true,及追加,不是清空文件(Linux的>和>>)

RollingFileAppender的作用是滚动记录文件,先将日志记录到指定文件,当符合某个条件(如达到每个文件的大小)时再将日志记录到其他文件:

<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/data-svc-error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/data-svc-error.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>7</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder></appender>

其中:

  • fileNamePattern指定日志文件格式
  • maxHistory设置滚动历史时长,保留近maxHistory天的日志文件,当然也有按文件大小切割的配置

AsyncAppender采用异步写日志的方式,减少性能损耗: 之前的appender,如RollingFileAppender,每写一次日志就发生一次磁盘IO

<!-- 异步输出 -->  
<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">  <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->  <discardingThreshold>0</discardingThreshold>  <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->  <queueSize>256</queueSize>  <!-- 添加附加的appender,最多只能添加一个 -->  <appender-ref ref ="myRollingFileAppender"/>  
</appender>

异步写入实现原理:

  • 当我们配置了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来做事。(如上面引用了定义的RollingAppender类型的appender)

基于这个原理,看这个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使用哪个具体的<appender>进行日志输出

10、logback之pattern

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern></layout>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern></encoder>
</appender>

encoder表示对参数进行格式化,上面第一种使用了<layout>定义<pattern>,第二种使用了<encoder>定义<pattern>,区别是:

  • <encoder>是0.9.19版本之后引进的,以前的版本使用<layout>,logback极力推荐的是使用<encoder>而不是<layout>
  • 最常用的FileAppender和它的子类的期望是使用<encoder>而不再使用<layout>

11、appender的Filter

<filter><appender>的一个子节点,表示在当前给到的日志级别下再进行一次过滤,最基本的Filter有:

  • ch.qos.logback.classic.filter.LevelFilter
  • ch.qos.logback.classic.filter.ThresholdFilter

LevelFilter这种过滤器:

<configuration scan="false" scanPeriod="60000" debug="false"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>WARN</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><logger name="java" additivity="false" /><logger name="java.lang" level="DEBUG"><appender-ref ref="STDOUT" /></logger><root level="INFO"><appender-ref ref="STDOUT" /></root></configuration>

以上即,过滤WARN级别的日志,匹配时接收这个记录,不匹配时拒绝这个记录,因此最后的输出只有WARN日志:

在这里插入图片描述
注意,有了过滤器之后,这里最终输出的信息,是logger和Filter交集的关系,不是logger配info就输出info及以上了。再看ThresholdFilter过滤器,Threshold即门槛:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false" scanPeriod="60000" debug="false"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>INFO</level></filter></appender><logger name="java" additivity="false" /><logger name="java.lang" level="DEBUG"><appender-ref ref="STDOUT" /></logger><root level="INFO"><appender-ref ref="STDOUT" /></root></configuration>

此时,门槛设置为INFO,则小于INFO级别的被过滤掉了,尽管logger中设置了DEBUG,最终输出的结果也没有DEBUG:

在这里插入图片描述

12、logback.xml全解释

logback.xml文件各项配置的含义备份:

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false"><!-- 定义日志的根目录为项目的根目录,前面不要加"/",加了会默认会认为是根目录,提示 classnotfond --><property name="LOG_HOME" value="app/log"/><!-- 定义日志文件名称 --><property name="appName" value="logbackBootText"/><!-- ConsoleAppender 用于控制台输出 --><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><!--日志输出格式:%d表示日期时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度%logger{50} 表示logger名字最长50个字符,否则按照句点分割。%msg:日志消息,%n是换行符--><layout class="ch.qos.logback.classic.PatternLayout"><!--springProfile可以指定某段配置只在某个环境下生效--><!--如果使用logback.xml作为日志配置文件,还要使用profile功能,会有以下错误no applicable action for [springProfile]--><springProfile name="dev"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern></springProfile><springProfile name="!dev"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern></springProfile></layout></appender><!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --><appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"><!--定义日志输出的路径和名称--><!-- 指定日志文件的名称 --><!--这里的scheduler.manager.server.home 没有在上面的配置中设定,所以会使用java启动时配置的值--><!--比如通过 java -Dscheduler.manager.server.home=/path/to XXXX 配置该属性--><!--<file>${scheduler.manager.server.home}/${LOG_HOME}/${appName}.log</file>--><!--当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。“%d”可以包含一个java.text.SimpleDateFormat指定的时间格式,--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动%i:当文件大小超过maxFileSize时,按照i进行文件滚动--><fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern><!--可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。--><MaxHistory>365</MaxHistory><!-- 该属性在 1.1.6版本后 才开始支持,日志量最大20GB--><totalSizeCap>20GB</totalSizeCap><!--当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置 SizeBasedTriggeringPolicy 是无法实现按文件大小进行滚动的,必须配置 timeBasedFileNamingAndTriggeringPolicy--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!-- 日志输出格式: --><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] ->> [ %-5level ] [ %logger{50} : %line ] ->> %msg%n</pattern></layout></appender><!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --><appender name="appLogAppenderBoot" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_HOME}/boot-${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern><MaxHistory>365</MaxHistory><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] -> [ %-5level ] [ %logger{50} : %line ] -> %msg%n</pattern></layout></appender><!--root是默认的logger,root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 --><root level="DEBUG"><!--定义了三个appender,日志会通过往这这些appender里面写--><appender-ref ref="stdout"/><appender-ref ref="appLogAppender"/><appender-ref ref="appLogAppenderBoot"/></root><!--logger主要用于存放日志对象,也可以定义日志类型、级别--><!--name:表示匹配的logger类型前缀,也就是包的前半部分--><!--level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR 越往后越精简--><!--additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,--><!--false:表示只用当前logger的appender-ref,true:表示当前logger的appender-ref和rootLogger的appender-ref都有效--><!--    Spring framework logger 记录 spring 的日志 --><logger name="org.springframework.boot" level="debug" additivity="false"><appender-ref ref="appLogAppenderBoot"/></logger><!--通过 LoggerFactory.getLogger("mytest") 可以获取到这个logger--><!--由于这个logger自动继承了root的appender,root中已经有stdout的appender了,自己这边又引入了stdout的appender--><!--如果没有设置 additivity="false" ,就会导致一条日志在控制台输出两次的情况,原因是:没有写additivity会用 rootlogger 输出日志,而它下面有三个appender,单appLogAppenderBoot经过自定义,不会输出,stdout则会打印两遍--><!--additivity表示要不要使用rootLogger配置的appender进行输出--><logger name="com.tuniu" level="debug" additivity="false"><!--即输出到appLogAppender,又输出到stdout--><appender-ref ref="appLogAppender"/><appender-ref ref="stdout"/></logger><!--对于类路径以 com.tuniu 开头的Logger,输出级别设置为 warn,--><!--这个logger没有指定appender,它会继承root节点中定义的那些appender--><!--注意,如果名称相同,两个 logger 会有属性互补机制;而且以最后一个加载的属性为准,可以理解为 boot的 bootstrap.properties与 application.yml--><!--<logger name="com.tuniu" level="warn"/>如果将上面的这段配置放开,就如同写了:<logger name="com.tuniu" level="warn" additivity="false"><appender-ref ref="appLogAppender"/></logger>--><!--由于设置了 additivity="false" ,所以输出时不会使用rootLogger的appender--><!--但是这个logger本身又没有配置 appender,所以使用这个logger输出日志的话就不会输出到任何地方-->
<!--    <logger name="mytest2" level="info" additivity="false"/>--></configuration>

13、logback常用配置备份

贴个精简版的,目前工作中服务正在用的:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false"><!-- 日志存放路径 --><property name="log.path" value="/logs"/><!-- 日志输出格式 --><property name="log.pattern"value="%d{HH:mm:ss.SSS} [%thread] %-5level [%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}] %logger{20} - [%method,%line] - %msg%n"/><!-- 控制台输出 --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${log.pattern}</pattern></encoder></appender><!-- 系统日志输出 --><appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/data-service-ent-svc-info.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/data-service-xxx-svc-info.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>7</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 --><level>INFO</level><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch></filter></appender><appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/data-service-xxx-svc-error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/data-service-ent-svc-error.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>7</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 --><level>ERROR</level><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch></filter></appender><!-- 系统模块日志级别控制  --><logger name="com.plat" level="info"/><!-- Spring日志级别控制  --><logger name="org.springframework" level="warn"/><root level="info"><appender-ref ref="console"/></root><!--系统操作日志--><root level="info"><appender-ref ref="file_info"/><appender-ref ref="file_error"/></root>
</configuration>

上面日志存放路径是/logs,而deploy部署文件则是将这个路径持久化到hostpath中,以便后续排查问题:

apiVersion: apps/v1
kind: Deployment
metadata:name: xxx-svc-deploymentnamespace: mynamespace
spec:selector:matchLabels:app: xxx-svc-deploymenttemplate:metadata:labels:app: xxx-svc-deploymentspec:dnsPolicy: ClusterFirstcontainers:- name: xxx-svc-deploymentimage: repoistory.xxx-svc-deployment:release-2.0ports:- containerPort: 9527volumeMounts:- mountPath: /logsname: go-logs     # 持久化到宿主机的hostpathvolumes:- name: go-logshostPath:path: /data/logs


参考文章:

  • https://www.cnblogs.com/xrq730/p/8619156.html
  • https://www.cnblogs.com/xrq730/p/8628945.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/137053.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【校招VIP】测试方案之测试需求分析

考点介绍&#xff1a; 需求分析就是要弄清楚用户需要的是什么功能&#xff0c;用户会怎样使用系统。这样我们测试的时候才能更加清楚的知道系统该怎么样运行&#xff0c;才能更好的设计测试用例&#xff0c;才能更好的测试。 测试方案之测试需求分析-相关题目及解析内容可点击…

Qt事件处理

1. 事件 众所周知Qt是一个基于C的框架&#xff0c;主要用来开发带窗口的应用程序&#xff08;不带窗口的也行&#xff0c;但不是主流&#xff09;。我们使用的基于窗口的应用程序都是基于事件&#xff0c;其目的主要是用来实现回调&#xff08;因为只有这样程序的效率才是最高…

优化系统报错提示信息,提高人机交互(一)

1、常规报错及处理 package com.example.demo.controller;import com.example.demo.service.IDemoService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.w…

机器视觉应用系统包括哪些硬件?

导语&#xff1a;机器视觉是一个软硬件相结合的综合技术&#xff0c;需要软件对图像做处理&#xff0c;也需要硬件提供稳定和高质量的图像&#xff0c;两者同样重要。作为开发&#xff0c;软件模块比较熟悉&#xff0c;硬件系统的相关知识储备弱一些&#xff0c;本文对机器视觉…

卷运维不如卷网络安全

最近发现很多从事运维的选择了辞职&#xff0c;重新规划自己的职业发展方向。运维工程师这个岗位在IT行业里面确实是处于最底层的&#xff0c;不管什么环节出现问题&#xff0c;基本都是运维背锅。背锅也就罢了&#xff0c;薪资水平也比不上别的岗位。 一般运维的薪资水平大多数…

LeetCode 2596. 检查骑士巡视方案【数组,模拟】1448

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

云计算的未来:云原生架构和自动化运维的崭露头角

文章目录 云原生架构&#xff1a;重新定义应用开发和部署什么是云原生架构&#xff1f;为什么云原生架构重要&#xff1f;1. 弹性和伸缩性2. 故障隔离3. 更快的交付4. 资源利用率5. 多云支持 云原生架构的实践步骤 1&#xff1a;容器化步骤 2&#xff1a;微服务步骤 3&#xff…

JavaWeb后端开发 JWT令牌解析 登录校验 通用模板/SpringBoot整合

目录 实现思路 相关技术的解析 ​编辑会话跟踪三个方案 JWT令牌技术 ​生成令牌 校验令牌 登录下发令牌 实现思路 通过登录成功的标记来检测,在每个接口前做一个标记判断是否登录,若没登录则返回错误信息,并使前端退出.但这样较为繁琐,因此我们可以通过一种统一拦截的技…

R语言-关于颜色

目录 颜色 示例 R 颜色板 参考&#xff1a; 颜色 什么场景会用到颜色&#xff1f;比如在绘图过程中&#xff0c;为了让图更好看&#xff0c;有的时候&#xff0c;需要选择使用不同的颜色进行绘制或者填充。本文提供了R颜色的相关参数。 在R中&#xff0c;可以通过颜色下标…

Flask框架-1-[群聊]: flask-socketio实现websocket的功能

一、项目结构 flask_websocket |---static |---js |---jquery-3.7.0.min.js |---socket.io_4.3.1.js |---templates |---home |---group_chat.html |---index.html |---app.py 1.1、python环境 python3.9.0 1.2、依赖包 Flask2.1.0 eventlet0.33.3 Flask-SocketIO5.3.4 1.…

gpt扣款失败,openai扣款失败无法使用-如何解决gpt扣款失败的问题?

gpt扣款失败&#xff0c;openai扣款失败无法使用。毕竟你花了钱却无法使用你所期待的服务&#xff0c;这种情况确实令人不快。但是&#xff0c; 为什么gpt扣款失败&#xff1f; 可能是由于支付问题导致的扣款失败。这包括信用卡额度不足、支付信息错误等等。如果你的支付信息…

NI SCXI-1520 控制主板模块

NI SCXI-1520 是 National Instruments&#xff08;NI&#xff09;生产的控制主板模块&#xff0c;通常用于 NI 的 SCXI&#xff08;Signal Conditioning eXtensions for Instrumentation&#xff09;模块化测量和控制系统中&#xff0c;以实现信号调理、数据采集和控制。以下是…

问道管理:机器人产业迎催化 黄金价格或将突破前高

昨日&#xff0c;沪指盘中震动下探&#xff0c;一度跌近1%逼近3100点&#xff0c;尾盘逐步止跌&#xff1b;深成指、创业板指均跌超1%。截至收盘&#xff0c;沪指跌0.45%报3123.07点&#xff0c;深成指跌1.14%报10255.87点&#xff0c;创业板指跌1.14%报2027.73点&#xff0c;科…

全局异常处理器@RestControllerAdvice解析 Springboot项目异常处理 JavaWeb @ExceptionHandler

RestControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class)//指定捕获异常类型:所有public Result ex(Exception ex){ex.printStackTrace();return Result.error("对不起,出现异常,请联系管理员");}}RestControllerAdvice注解在…

淘宝商品详情数据采集

淘宝商品详情数据采集的方法如下&#xff1a; 确定采集目标&#xff1a;明确要采集的商品信息&#xff0c;如商品标题、价格、销量、评论、图片等。选择采集工具&#xff1a;可以选择Scrapy框架、Java的WebMagic框架等。编写爬虫程序&#xff1a;进入目标文件夹&#xff0c;输…

起尔正版虚拟商品交易商城源码系统 第三方交易平台网站源码

起尔网正版虚拟商品交易商城源码系统 Thinkct多商户源码系统商城&#xff0c;采用Thinkphp框架打造&#xff0c;后端采用Thinkadmin开发响应速度控制200ms内 起尔网正版虚拟商品交易商城源码系统 - 起尔网起尔网正版虚拟商品交易商城源码系统 Thinkct多商户源码系统商城&#…

数据结构和算法(7):图应用

双连通分量&#xff1a;判定准则 考查无向图G。若删除顶点v后G所包含的连通域增多&#xff0c;则v称作切割节点或关节点。 不含任何关节点的图称作双连通图。 任一无向图都可视作由若干个极大的双连通子图组合而成&#xff0c;这样的每一子图都称作原图的一个双连通域。 如何…

HTML实现移动端布局与页面自适应

我们所说的布局方式&#xff0c;这里我们通常指的是width和height在不同页面情况下面的改变。 常见页面的布局方式有 静态布局 &#xff08;px布局&#xff0c;就是固定其高宽&#xff0c;不论页面怎样放大缩小&#xff0c;其占领的依旧是&#xff0c;使用px固定了的高宽&…

JumpServer开源堡垒机与爱可生云树数据库完成兼容性认证

近日&#xff0c;中国领先的开源软件提供商FIT2CLOUD飞致云宣布&#xff0c;JumpServer开源堡垒机已经完成与爱可生云树数据库软件的兼容性认证。经过双方联合测试&#xff0c;云树数据库软件&#xff08;简称&#xff1a;ActionDB&#xff09;V1.0与杭州飞致云信息科技有限公司…

Java下打印九九乘法表

这个算法是基于打直角三角型演变而来&#xff0c;代码如下&#xff1a; public class MyWork {public static void main(String[] args) {for (int i 1; i < 10; i) {for (int j 1; j < i; j) {System.out.print(j "x" i "" i*j "\t&qu…