多应用中间的日志使用,log4j,log4j2,logback三个日志框架为例
分别使用三个日志框架建好测试
log4j
依赖:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
测试代码
import org.apache.log4j.Logger;
//import org.slf4j.LoggerFactory;
public class MyLog4j {
public void test1() {
Logger logger = Logger.getLogger(MyLog4j.class);
logger.info("MyLog4j "+ logger.getClass().getName());
logger.error("MyLog4j "+ logger.getClass().getName());
}
// public void test2(){
// org.slf4j.Logger logger = LoggerFactory.getLogger(MyLog4j.class);
// logger.info("MyLog4j test2 clzz={} ",logger.getClass().getName());
// }
public static void main(String[] args) {
new MyLog4j().test1();
// new MyLog4j().test2();
}
}
效果
log4j2
依赖:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
测试代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//import org.slf4j.LoggerFactory;
public class MyLog4j2 {
public void test(){
Logger logger = LogManager.getLogger();
logger.info("MyLog4j2 clzz={}",logger.getClass().getName());
}
public void test2(){
// org.slf4j.Logger logger = LoggerFactory.getLogger(MyLog4j2.class);
// logger.info("MyLog4j test2 clzz={} ",logger.getClass().getName());
}
public static void main(String[] args) {
new MyLog4j2().test();
new MyLog4j2().test2();
}
}
效果
logback
依赖:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version>
</dependency>
测试代码
在这里插入代码片
配置文件:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyLogBack {
public void test(){
Logger logger = LoggerFactory.getLogger(MyLogBack.class);
logger.info("MyLogBack clzz={}",logger.getClass().getName());
}
public static void main(String[] args) {
new MyLogBack().test();
}
}
效果
上面的三个同时引入一个应用中
添加依赖:
<dependency>
<groupId>log</groupId>
<artifactId>mylog4j</artifactId>
<version>1.0-SNAPSHOT</version>
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>org.slf4j</groupId>-->
<!--<artifactId>slf4j-log4j12</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->
</dependency>
<dependency>
<groupId>log</groupId>
<artifactId>MyLog4j2</artifactId>
<version>1.0-SNAPSHOT</version>
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>org.apache.logging.log4j</groupId>-->
<!--<artifactId>log4j-slf4j-impl</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->
</dependency>
<dependency>
<groupId>log</groupId>
<artifactId>myLogBack</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
public class SDK2 {
public static void main(String[] args) {
new MyLog4j2().test();
new MyLog4j().test1();
new MyLogBack().test();
// new MyLog4j2().test2();
// new MyLog4j().test2();
// new MyLogBack().test();
}
}
从上面我们发现完全是可以的每个应用使用自己的日志,但是这样我们发现日志打出来就是五花八门,不能使用同一的格式输入,我们如何操作
2 . 将日志统一使用slf4j标准
slf4j 官网; https://slf4j.org/
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
从上面可以看到只引用slf4j是不能打印日志的,因为slf4j是门面不包含实现框架,需要我们增加绑定器:
这官网给的一个逻辑图
总结:
- 使用slf4j作为日志门面,统一给应用提供日志输出
- 提供日志实现依赖,否则不能实现日志输出,其中,logback,slf4j-simple,slf4j-nop都遵从了slf4j的规范,直接依赖即可
- log4j,jcl等没有遵从的框架需要使用相应的适配器
2.1 slf4j 绑定各个日志实现
slf4j 是日志门面必须绑定日志实现不然会报下面的错误
2.1.1 使用jcl绑定器
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jcl -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.7.32</version>
</dependency>
绑定器为啥可以打印我们看看其中的依赖关系:
由此可以看出slf4j-jcl依赖了commons-logging这个实现日志
2.1.2 绑定log4j
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.32</version>
</dependency>
这个是log4j没有实现slf4j的标准,是通过适配器模式实现的
适配器层面实现:
2.1.3 绑定 logback
后面看logback的时候我们知道logback实现了slf4j标准,其实就不用这么一个搞法
<!--<dependency>-->
<!--<groupId>ch.qos.logback</groupId>-->
<!--<artifactId>logback-core</artifactId>-->
<!--<version>1.2.5</version>-->
<!--</dependency>-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version>
</dependency>
上面的二选其一都可以
由此可见logback自身实现了slf4j标准
2.1. 4 绑定log4j2
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.3</version>
</dependency>
由下图可以看出log4j-slf4j-impl 中直接依赖了slf4j(slf4j-api)
log4j2适配器层面代码实现
2.1.5 总结:
根据这个手写方案进行修改:
将上面的代码注释部分放开即可,这里不贴代码了
说明一定logback已经实现了slf4j的标准:所以不需要处理
使用sIf4j的日志绑定流程
1.添加slf4j-api的依赖
2.使用sIf4j的API在项目中进行统一的日志记录
3.绑定具体的日志实现框架
- 绑定已经实现了sIf4j的日志框架,直接添加对应依赖
- 绑定没有实现sIf4的日志框架,先添加日志的适配器,再添加实现类的依赖
4.sIf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)
2.2 slf4j的桥接
这种往往就是那种代码没有使用slf4j模块或者三方jar包,但是我们又想让他们统一输出日志。
通常,您依赖的某些组件依赖于SLF4以外的日志记录API。您也可以假设这些组件在不久的将来不会切换到SLF4]。为了解决这种情况,SLF4附带了几个桥接模块,这些模块将log4j,JCL和java.util.loggingAPI的调用重定向,就好像它们是对SLF4API一样。
- 1.先去除之前老的日志框架的依赖
- 2.添加SLF4提供的桥接组件
- 3.为项目添加SLF4的具体实现
2.2.1 log4j 桥接到logback
<dependencies>
<!--我们把不是使用logback的模块或者三方jar包都桥接到logback-->
<!-- slf4j 日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!-- logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version>
</dependency>
<!--log4j 实现模块-->
<dependency>
<groupId>org.example</groupId>
<artifactId>log-log4j-self</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j 桥接 slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
public class Slf4jOverMain {
private static final Logger logger = LoggerFactory.getLogger(Slf4jOverMain.class);
public static void main(String[] args) {
logger.error("Slf4j Over");
new Log4jSelfMain().test1();
}
}
根据日志发现打印是logback格式
2.2.2 每个模块都不一样如何最终统一输出-slf4j桥接的综合应用
我们有log4j log4j2 logback日志,也有log4j ,log4j2 ,logback 按照统一的slf4j输出的6个模块,我们最后在log-slf4j-over2中实现统一的输出
上面这个警告比较好处理排除掉就可以:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>log-slf4j</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>log-slf4j-over2</artifactId>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>log-logback</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>log-log4j-self</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>log-log4j-slf4j</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 把log4j的依赖排除掉增加桥接器,是log4j满足slf4j的条件-->
<!-- log4j 桥接 slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>log-log4j2-self</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>log-log4j2-slf4j</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
想使用那个就把不需要使用的排除掉:
这已经很方便了发现,也可以完全达到我们的目的了,格式统一,但是有一个问题就是,我们这个地方是我们自己写的应用可以随时修改pom文件,直接统一升级到slf4j标准,但是如果我们使用的是第三方jar包如何办,里面的pom文件没法升级,同时jar也没有实现slf4j标准
注意问题:
1.jcl-over-slf4j.jar和sif4j-jcl.jar不能同时部署。前一个jar文件将导致JCL将日志系统的选择委托给SLF4],后一个jar文件将导致SLF4将日志系统的选择委托给JCL,从而导致无限循环。
2.log4j-over-slf4j.jar和slf4j-log4j12.jar不能同时出现
3.jul-to-slf4j.jar和slf4j-jdk14.jar不能同时出现象,将无法产生效果。
总之: 桥接器和适配器不能同时使用
2.2.3 不能修改pom文件如何操作统一日志格式
这里我使用my2log4j 和 mylogback两个模块演示
这两个分别使用log4j 和 mylogback
sdk3 进行测试:
依赖:
<dependencies>
<dependency>
<groupId>log</groupId>
<artifactId>my2log4j</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>log</groupId>
<artifactId>myLogBack</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
思路就是:讲log4j中的依赖排除掉,使用桥接器来实现,
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.32</version>
</dependency>
也达到格式统一的要求了;
依赖关系:
总结:
1.自己使用日志,全部使用slf4j
2.如果引用第三方jar,第三方没有使用slf4j这种日志门面,而是直接使用的日志实现框架
a。如果可以修改源码的情况可以将日志第三方jar升级到slf4j标准
b。如果不可以修改源码的情况下,我们引用第三方jar,排除其使用的日志实现,利用slf4j的桥接器来实现升级到slf4j
ps: 在引用jar包时一定要排除日志实现jar,不然会导致不起作用
slf4j的桥接器如下图:
2.4 SLF4J原理解析
1.SLF4通过LoggerFactory加载日志具体的实现对象。
2.LoggerFactory在初始化的过程中,会通过performlnitialization(方法绑定具体的日志实现。
3.在绑定具体实现的时候,通过类加载器,加载org/slf4j/impl/StaticLoggerBinder.class
4.所以,只要是一个日志实现框架,在org.slf4j.impl包中提供一个自已的StaticLoggerBinder类,在其中提供具体日志实现的LoggerFactory就可以被SLF4]所加载
3. logback 日志实现框架之一
Logback是由log4j创始人设计的另一个开源日志组件,性能比log4要好。
官方网站:https://logback.qos.ch
Logback主要分为三个模块:
- logback-core:其它两个模块的基础模块
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4jAPI
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能
3.1 logback 入门
<dependencies>
<!-- slf4j 日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!-- logback 日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version>
</dependency>
</dependencies>
package com.wfg.log.logback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author wufagang
* @description
* @date 2023年07月08日 14:53
*/
public class LogBackTest {
public void test(){
Logger logger = LoggerFactory.getLogger(LogBackTest.class);
logger.debug("debug");
logger.info("info");
logger.error("error");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}",logger.getClass().getName());
}
public void test1(){
for (int i = 0; i < 1000; i++) {
Logger logger = LoggerFactory.getLogger(LogBackTest.class);
logger.debug("debug");
logger.info("info");
logger.error("error");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}",logger.getClass().getName());
}
}
public static void main(String[] args) {
LogBackTest logBackTest = new LogBackTest();
logBackTest.test();
logBackTest.test1();
}
}
3.2 logback 配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 配置集中管理熟悉-->
<!--
日志输出格式:
%-5level 日志级别
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%C 类的完整名称
%M 为method
%L 为行号
%thread 线程名称
%m或者%msg 为信息
%n 换行
-->
<property name="pattern1" value="%thread [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %C %M %L %m %n"></property>
<property name="pattern2" value="%-4relative [%thread] %-5level %logger{35} - %msg %n"></property>
<property name="pattern3" value="%thread%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%C%M%L%m"></property>
<property name="fileUrl" value="/Users/wufagang/logs/logback"/>
<!-- 控制日志输出的 appender-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 控制输出流对象 默认System.out 修改成System.err-->
<traget>
System.err
</traget>
<!-- 日志格式配置-->
<encoder>
<pattern>${pattern1}</pattern>
</encoder>
</appender>
<!-- 日志文件输出的 appender-->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>${fileUrl}/logback.log</file>
<encoder>
<pattern>${pattern1}</pattern>
</encoder>
</appender>
<!-- html 格式日志文件输出的 appender-->
<appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
<file>${fileUrl}/logback.html</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>${pattern3}</pattern>
</layout>
</encoder>
</appender>
<!-- 日志拆分和归档压缩的appender-->
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${fileUrl}/rolling_logback.log</file>
<!-- 日志格式配置-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern1}</pattern>
</encoder>
<!-- 按照时间和压缩格式拆分文件名 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${fileUrl}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log.%i.gz</fileNamePattern>
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
<!-- 日志级别过滤器-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 我们过滤error级别 -->
<level>error</level>
<!-- 满足要求接受-->
<onMatch>ACCEPT</onMatch>
<!-- 不满足过滤-->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步日志-->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="rollingFile"/>
</appender>
<!-- root logger 配置 只能有一个root-->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="file"/>
<appender-ref ref="htmlFile"/>
<appender-ref ref="async"/>
</root>
<!-- 自定义logger 对象
additivity="false" 表示自定义 logger 是否继承 root logger
-->
<logger name="com.wfg" level = "error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
</configuration>
3.3 logback-access 模块的使用
4. 配置参考: https://logback.qos.ch/manual/configuration.html
4. log4j2 日志实现框架之一
ApacheLog4j2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
- 异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。
- ·性能提升,log4j2相较于log4j和logback都具有很明显的性能提升,后面会有官方测试的数据。
- ·自动重载配置,参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
- 无垃圾机制,log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的ivm gc。
- 官网:https://logging.apache.org/log4j/2.x/
4.1 log4j2 入门
目前市面上最主流的日志门面就是SLF4,虽然Log4j2也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将Log4j2看作是日志的实现,SIf4j+Log4j2应该是未来的大势所趋。
4.1.1 log4j2 使用自己的门面
<dependencies>
<!-- log4j2 日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
<!-- log4j2 日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
package com.wfg.log.log4j2.self;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @author wufagang
* @description
* @date 2023年07月08日 18:01
*/
public class Log4j2 {
private static Logger logger = LogManager.getLogger(Log4j2.class);
public void test(){
logger.fatal("fatat");
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}",logger.getClass().getName());
}
public void test1(){
for (int i = 0; i < 1000; i++) {
logger.fatal("fatat");
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}",logger.getClass().getName());
}
}
public static void main(String[] args) {
Log4j2 log4j2 = new Log4j2();
log4j2.test();
}
}
4.1.2 log4j2 和 slf4j 结合使用
<dependencies>
<!-- slf4j 日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!-- log4j2 日志适配器-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.0</version>
</dependency>
<!-- log4j2 日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<!-- log4j2 日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
</dependencies>
package com.wfg.log.log4j2.slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author wufagang
* @description
* @date 2023年07月08日 18:11
*/
public class Log4j2 {
Logger logger = LoggerFactory.getLogger(Log4j2.class);
public void test(){
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}",logger.getClass().getName());
}
public void test1(){
for (int i = 0; i < 1000; i++) {
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}",logger.getClass().getName());
}
}
public static void main(String[] args) {
Log4j2 log4j2 = new Log4j2();
log4j2.test();
}
}
4.2 log4j2 日志文件配置
<?xml version="1.0" encoding="UTF-8"?>
<!--status 框架自身日志级别
monitorInterval 自动加载配置时间间隔,最小5秒
-->
<Configuration status="info" monitorInterval="30">
<!-- 配置集中管理熟悉-->
<!--
日志输出格式:
%-5level 日志级别
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%C 类的完整名称
%M 为method
%L 为行号
%t 线程名称
%m或者%msg 为信息
%n 换行
-->
<properties>
<property name="pattern1" value="%thread [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %C %M %L %m %n"></property>
<property name="pattern2" value="%-4relative [%thread] %-5level %logger{35} - %msg %n"></property>
<property name="pattern3" value="%thread%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%C%M%L%m"></property>
<property name="pattern4" value="%d{HH:mm:ss.SSS} %t %-5level %logger{35}:%L---%msg%n"></property>
<property name="fileUrl" value="/Users/wufagang/logs/log4j2"/>
</properties>
<!-- 日志处理-->
<Appenders>
<!-- 控制台 -->
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %t %-5level %logger{35}:%L---%msg%n" />
</Console>
<!-- 日志打印到root.log -->
<File name="file" fileName="${fileUrl}/root.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
<!-- 使用随机读写流的日志文件输出,性能提高-->
<RandomAccessFile name="randomAccess" fileName="${fileUrl}/randomAccess.log">
<PatternLayout pattern="${pattern1}"/>
</RandomAccessFile>
<!-- 按照一定的规则进行拆分
filePattern 文档归类文件名称规则
-->
<RollingFile name="rolling" fileName="${fileUrl}/rolling.log" filePattern = "${fileUrl}/$${date:yyyy-MM-dd}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log.%i.log">
<ThresholdFilter level = "error" onMatch="ACCEPT" onMisMatch="DENY"></ThresholdFilter>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<!-- 拆分规则-->
<Policies>
<!-- 在系统启动时,出发拆分规则,生产一个新的日志文件-->
<OnStartupTriggeringPolicy/>
<!-- 按照文件大小拆分,10MB-->
<SizeBasedTriggeringPolicy size="1KB"/>
<!-- 按照时间节点拆分,规则根据filePattern定义的-->
<TimeBasedTriggeringPolicy/>
</Policies>
<!--在同一个目录下,文件的个数限定为30个,超过进行覆盖-->
<DefaultRolloverStrategy useMax="30"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<!-- 输出到控制台 -->
<AppenderRef ref="console" />
<!-- 输出到root.log-->
<AppenderRef ref="file" />
<AppenderRef ref="randomAccess" />
<AppenderRef ref="rolling" />
</Root>
<!-- 自定义logger
<logger name="StockServiceThaiImpl" level="info">
<AppenderRef ref="xxx" />
</logger-->
</Loggers>
</Configuration>
4.3 Log4j2异步日志
异步日志
log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用log4j2的异步日志。
- 同步日志
- 异步日志
Log4j2提供了两种实现日志的方式: - 一个是通过AsyncAppender,
- 一个是通过AsyncLogger,
分别对应前面我们说的Appender组件和Logger组件。
性能说明:https://logging.apache.org/log4j/2.x/performance.html
从上图可以看出:
AsyncLogger 性能要远远好于AsyncAppender,因此大部分情况我们使用AsyncLogger组件, AsyncLogger 又分为全异步和混合异步
增加依赖:
</dependency>
<!-- 异步日志依赖-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
4.3.1 AsyncAppender 方式
增加一个 Async
4.3.2 AsyncLogger 方式
AsyncLogger才是log4j2的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的更快。你可以有两种选择:全局异步和混合异步。
- 全局异步
就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一
个log4j2.component.properties配置文件; 配置信息如下:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
- 混合异步 就是你可以使用同步和异步同时使用
<AsyncLogger name="com.wfg" level="trace" includeLocation="false" additivity="false">
<AppenderRef ref="console" />
</AsyncLogger>
使用异步日志需要注意的问题
1.如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现。性能会和AsyncAppender一致,降至最低。
2.设置includeLocation=false,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。
4.4 log4j2的性能
5 . springboot 中的日志
5.1 入门
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
@SpringBootApplication
public class LogSpringboot1Application {
private static Logger logger = LoggerFactory.getLogger(LogSpringboot1Application.class);
public static void main(String[] args) {
SpringApplication.run(LogSpringboot1Application.class, args);
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}", logger.getClass().getName());
}
}
5.2 springboot 完成log4j的桥接
public static void main(String[] args) {
SpringApplication.run(LogSpringboot1Application.class, args);
logger.error("error");
logger.info("info");
logger.debug("debug");
logger.warn("warn");
logger.trace("trace");
logger.info("LogBackTest clzz={}", logger.getClass().getName());
// log4j 通过桥接器 slf4j 门面和 logback 实现
org.apache.logging.log4j.Logger log4j2Logger = LogManager.getLogger(LogSpringboot1Application.class);
log4j2Logger.error("error log4j2 日志");
logger.info("LogBackTest clzz={}", log4j2Logger.getClass().getName());
}
log4j2 打印格式和logback完全一样证明桥接成功
5.3 spring boot 自身日志配置
# 指定自定义logger 日志级别
logging.level.com.wfg=debug
#控制台输出日志格式
logging.pattern.console=%thread [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %C %M %L ====== %m %n
#指定文件的具体路径
#logging.file.name=/Users/wufagang/logs/srpingboot/springboot.log
# 指定日志文件路径,默认是spring.log
logging.file.path=/Users/wufagang/logs/srping
# 文件日志格式
logging.pattern.file=%thread [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %C %M %L ++++%m %n
5.4 指定日志文件实现
5.5 将spring boot 日志实现切换成 log4j2
结尾
logback 官网: https://logback.qos.ch
log4j2官网:https://logging.apache.org/log4j/2.x/
slf4j 官网; https://slf4j.org/