log4j2 日志介绍和使用

前言

日志对于项目的重要性不言而喻,现在市面上的日志框架多种多样:Log4j、Log4j2、Slf4j、JDKLog、Logback 等等。Log4j 目前已经停止更新。Apache 推出了新的 Log4j2 代替 Log4j,Log4j2 是 Log4j 的升级,与其前身Log4j 相比有了显着的改进,并提供了许多 Logback 可用的改进,因此 Log4j2 + Slf4j 应该是未来的大势所趋。

日志作用

在项目开发中,都不可避免的使用到日志。没有日志虽然不会影响项目的正确运行,但是没有日志的项目可以说是不完整的。日志在调试,错误或者异常定位,数据分析中的作用是不言而喻的。

  1. 调试

在Java项目调试时,查看栈信息可以方便地知道当前程序的运行状态,输出的日志便于记录程序在之前的运行结果。如果你大量使用 System.out 或者 System.err,这是一种最方便最有效的方法,但显得不够专业。

  1. 错误定位

项目在运行一段时候后,可能由于数据问题,网络问题,内存问题等出现异常。这时日志可以帮助开发或者运维人员快速定位错误位置,提出解决方案。

  1. 数据分析

日志中蕴含了大量的用户数据,包括点击行为,兴趣偏好等,用户画像对于公司下一步的战略方向有一定指引作用。

日志级别

Log4j2中日志有六个级别(level):

  • trace:追踪,是最低的日志级别,相当于追踪程序的执行,一般不怎么使用
  • debug:调试,一般在开发中,都将其设置为最低的日志级别
  • info:信息,输出重要的信息,使用较多
  • warn:警告,有些时候,虽然程序不会报错,但是还是需要告诉程序员的
  • error:错误,这个在开发中也挺常用的
  • fatal:严重错误,这个一旦发生,程序基本上也要停止了

当日志级别设置为某个值的时候,低于它的日志信息将不会被记录,只有高于设置的级别的信息会被记录。

Spring Boot 集成 log4j2

  1. pom.xml

Spring Boot默认使用LogBack,但是我们没有看到显示依赖的jar包,其实是因为所在的jar包spring-boot-starter-logging都是作为spring-boot-starter-web或者spring-boot-starter依赖的一部分。

如果这里要使用Log4j2,需要从spring-boot-starter-web中去掉spring-boot-starter-logging依赖,同时显示声明使用Log4j2的依赖jar包,具体如下:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<!-- 去掉默认配置 -->
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<!-- 引入log4j2依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
  1. log4j2.xml

在application.properties中指定log4j2.xml的位置

logging.config=classpath:log4j2.xml
logging.level.root=info

log4j2.xml 示例

<?xml version="1.0" encoding="UTF-8" ?>

<!--status的值有trace、debug、info、warn、error和fatal,用于控制log4j2日志框架本身的日志级别,
monitorInterval,含义是每隔多少秒重新读取配置文件,可以不重启应用的情况下修改配置-->
<configuration status="warn" monitorInterval="5">

    <!-- 集中配置属性进行管理,使用时通过 ${name} 使用 -->
    <properties>
        <property name="LOG_HOME">D:/logs</property>
    </properties>

    <!-- 日志处理 -->
    <Appenders>
        <!-- 控制台输出 appender,SYSTEM_OUT输出黑色,SYSTEM_ERR输出红色 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!-- 日志消息格式 -->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
        </Console>

        <!-- 日志文件输出 appender -->
        <File name="file" fileName="${LOG_HOME}/myfile.log">
            <!-- 日志消息格式 -->
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
        </File>

        <!-- 使用随机读写流的日志文件输出 appender,性能提高 -->
        <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
            <!-- 控制台或文件输出源 -->
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
        </RandomAccessFile>

        <!-- 按照一定规则拆分的日志文件的appender -->
        <!-- 拆分后的文件 filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log"> -->
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                     filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
            <!-- 日志级别过滤器 -->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 日志消息格式 -->
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n"/>
            <Policies>
                <!-- 在系统启动时,出发拆分规则,生产一个新的日志文件 -->
                <OnStartupTriggeringPolicy/>
                <!-- 按照文件大小拆分,10MB -->
                <SizeBasedTriggeringPolicy size="10 MB"/>
                <!-- 按照时间节点拆分,规则根据filePattern定义的 -->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <!-- 在同一个目录下,文件的个限定为 30个,超过进行覆盖 -->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        <!-- 异步 appender -->
        <Async name="Async">
            <AppenderRef ref="file"/>
        </Async>
    </Appenders>

    <!-- logger 定义 -->
    <Loggers>
        <!-- 使用 rootLogger 配置 -->
        <!-- 总是存在一个rootLogger,即使没有显示配置也是存在的,并且默认输出级别为DEBUG,所有其他的Logger都默认继承自rootLogger -->
        <Root level="info">
            <!-- 指定日志使用的处理器 -->
            <AppenderRef ref="Console"/>
            <AppenderRef ref="file"/>
            <AppenderRef ref="rollingFile"/>
            <AppenderRef ref="accessFile"/>
            <AppenderRef ref="Async"/>
        </Root>
        <!-- 自定义的Logger(子Loggger)继承自rootLogger -->
        <Logger name="com.example.canal.yang" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
    </Loggers>
</configuration>

PatternLayout 各标记符详细含义如下:

%d 输出时间 
    %d{HH:mm:ss.SSS} 表示输出到毫秒的时间 
%t 输出当前线程名称 
%p 输出日志级别 
    %-5p -5表示左对齐并且固定输出5个字符,如果不足在右边补0
%c 日志消息所在类名 
%m 消息内容 
%n 换行 
%F 输出所在的类文件名,如Log4j2Test.java 
%L 输出行号 
%M 输出所在方法名 
%l 输出语句所在的行数, 包括类名、方法名、文件名、行数 
%logger 输出logger名称,如果没有名称,就不输出 
  1. 打印日志

只使用log4j2的话,获取Logger对象一般是使用LogManager去获取的,如果使用slf4j的话,使用LoggerFactory去获取

public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);

private final static org.slf4j.Logger logger = LoggerFactory.getLogger(Log4j2Controller.class); 
 
LOGGER.trace("trace level");
LOGGER.debug("debug level");
logger.info("info level");
logger.warn("warn level");
logger.error("error level");

使用lombok插件(详细安装方法请上网查询),可以简化Logger对象的获取,首先在相关的类上面添加@Slf4j注解(该注解由lombok提供),然后通过下面的方式打印日志:

log.trace("trace level");
log.debug("debug level");
log.info("info level");
log.warn("warn level");
log.error("error level");

日志重复打印问题

如果Root中的日志包含了Logger中的日志信息,并且AppenderRef是一样的配置,则日志会打印两次。

这是 log4j2 继承机制问题,在Log4j2中,logger 是有继承关系的,root是根节点,在log4j2中,有个 additivity 的属性,它是子 Logger 是否继承 父 Logger 的 输出源(appender) 的属性。具体说,默认情况下子 Logger 会继承父 Logger 的appender,也就是说子 Logger 会在父 Logger 的 appender 里输出。若是 additivity 设为 false,则子 Logger 只会在自己的 appender 里输出,而不会在父 Logger 的 appender 里输出。

要打破这种传递性,也非常简单,在 logger 中添加 additivity = “false”。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值