添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

之前对一个项目进行调优,将原先项目中的日志配置logBack,修改为log4j2,网上查找各种资料,官方文档,遇到很多坑,最终实现了修改,至此,特记录一下。

1.导入依赖,pom文件配置

SpringBoot官方文档:

docs.spring.io/spring-boot…

一个干净的项目如果要使用log4j2,可以采用下面的方式引入依赖:

        <!-- 排除 Spring-boot-starter 默认的日志配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</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>

  因为在老项目中,日志依赖混杂,在项目中引入依赖情况:

		<!-- slf4j的实现:log4j2,用来取代logback -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
			<version>1.5.9.RELEASE</version>
		</dependency>
                <!-- log4j转 slf4j 输出-->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
			<version>1.7.25</version>
		</dependency>
		<!--异步日志依赖-->
		<dependency>
			<groupId>com.lmax</groupId>
			<artifactId>disruptor</artifactId>
			<version>3.3.4</version>
		</dependency>

  总结:排干净项目中的Logback依赖,否则会导致日志在控制台打印却不能输出到文件中。

2.log4j2配置文件

参考(官方)文档:

www.docs4dev.com/docs/zh/log…

docs.spring.io/spring-boot…

  在项目resource目录下,新建log4j2.xml 或者 log4j2-spring.xm l。建议使用log4j2-spring.xml,否则还需在application.properties或者.yml中配置好logging.config=你的配置文件名。

  网上找来的xml模版:

<?xml version="1.0" encoding="UTF-8"?>
<!--      Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,
     当设置成trace时,可以看到log4j2内部各种详细输出
<!-- monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
<configuration monitorInterval="5">
    <!-- 日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
    <!-- 变量配置 -->
    <Properties>
            格式化输出:
            %d表示日期,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %msg:日志消息,%n是换行符
            %logger{36} 表示 Logger 名字最长36个字符
        <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level}[%thread] %style{%logger{36}}{cyan} : %msg%n" />
        <!-- 定义日志存储的路径,不要配置相对路径 -->
        <property name="FILE_PATH" value="此处更换为你的日志文件存储路径" />
        <property name="FILE_NAME" value="此处更换为你的项目名称" />
    </Properties>
    <appenders>
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="${LOG_PATTERN}" disableAnsi="false" noConsoleNoAnsi="false"/>
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>
          这个会打印出所有的info及以下级别的信息,每次大小超过size,
          则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="20MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖 -->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>
        <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="20MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>
        <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="20MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>
    </appenders>
    <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
    <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <!--监控系统信息-->
        <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root




    
>
    </loggers>
</configuration>

3.配置文件解读

3.1 日志级别

  在log4j2中, 共有8个级别,按照从低到高为:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。

  • All:最低等级的,用于打开所有日志记录;
  • Trace:是追踪,就是程序推进一下;
  • Debug:指出细粒度信息事件对调试应用程序是非常有帮助的;
  • Info:消息在粗粒度级别上突出强调应用程序的运行过程;
  • Warn:输出警告及warn以下级别的日志;
  • Error:输出错误信息日志;
  • Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志;
  • OFF:最高等级的,用于关闭所有日志记录。
  • Event Level LoggerConfig Level TRACE DEBUG ERROR FATAL TRACE DEBUG ERROR FATAL

    3.2 xml配置解读

    根节点Configuration
    	有两个属性:
    		status:指定log4j2本身的打印日志的级别.
    		monitorinterval:指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.
    	有两个子节点:	
    		Appenders
    		Loggers
    Appenders节点
    	常见的有三种子节点:Console、RollingFile、File
    		Console节点用来定义输出到控制台的Appender.
    			name:指定Appender的名字.
    			target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.
    			PatternLayout:输出格式,不设置默认为:%m%n.
    		File节点用来定义输出到指定位置的文件的Appender.
    			name:指定Appender的名字.
    			fileName:指定输出日志的目的文件带全路径的文件名.
    			PatternLayout:输出格式,不设置默认为:%m%n.
    		RollingFile节点用来定义超过指定条件自动删除旧的创建新的Appender.
    			name:指定Appender的名字.
    			fileName:指定输出日志的目的文件带全路径的文件名.
    			PatternLayout:输出格式,不设置默认为:%m%n.
    			filePattern : 指定当发生Rolling时,文件的转移和重命名规则.
    			Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.
    			TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am.
    			SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
    			DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。
    Loggers节点
    	Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
    		level:日志输出级别
        AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender
    	Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
    		level:日志输出级别
    		name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
    		AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。
    

    4.log4j2-spring.xml读取.properties中的属性值

      Spring Environment property: logging.config.并不能为log4j2-spring提供属性,我们需要通过MDC为其获取.properties中的属性。

    4.1 写一个监听类

    import org.slf4j.MDC;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
    import org.springframework.boot.context.event.ApplicationFailedEvent;
    import org.springframework.boot.context.event.ApplicationPreparedEvent;
    import org.springframework.boot.context.event.ApplicationStartingEvent;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.context.event.GenericApplicationListener;
    import org.springframework.core.Ordered;
    import org.springframework.core.ResolvableType;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.PropertySource;
    public class ApplicationStartedEventListener implements GenericApplicationListener {
        public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
        private static Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
                ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class,
                ContextClosedEvent.class, ApplicationFailedEvent.class };
        private static Class<?>[] SOURCE_TYPES = { SpringApplication.class,
                ApplicationContext.class };
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ApplicationEnvironmentPreparedEvent) {
                ConfigurableEnvironment envi = ((ApplicationEnvironmentPreparedEvent) event).getEnvironment();
                PropertySource<?> propertySource = envi.getPropertySources().get("读取到的配置文件,比如applicationConfig: [classpath:/application.properties]");
                //你自己的逻辑,切记MDC.put(key,value)
                MDC.put("LOG_PATH", loggingPath);
         * (non-Javadoc)
         * @see org.springframework.core.Ordered#getOrder()
        @Override
        public int getOrder() {
            // TODO Auto-generated method stub
            return DEFAULT_ORDER;
         * (non-Javadoc)
         * @see org.springframework.context.event.GenericApplicationListener#
         * supportsEventType(org.springframework.core.ResolvableType)
        @Override
        public boolean supportsEventType(ResolvableType resolvableType) {
            return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
        @Override
        public boolean supportsSourceType(Class<?> sourceType) {
            return isAssignableFrom(sourceType, SOURCE_TYPES);
        private boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
            if (type != null) {
                for (Class<?> supportedType : supportedTypes) {
                    if (supportedType.isAssignableFrom(type)) {
                        return true;
            return false;
    

    4.2 在Application启动类中添加自定义的启动监听ApplicationStartedEventListener

     	SpringApplication app = new SpringApplication(AppMain.class);
            ApplicationStartedEventListener asel = new ApplicationStartedEventListener();
    	app.addListeners(asel);
    	app.run(args);
    

    4.3 在log4j2.xml中使用MDC定义的属性

      使用方式 ${ctx:key}

    5. 异步日志

      参考文档;logging.apache.org/log4j/2.x/m…

    5.1 让所有的日志异步输出

      jvm启动参数增加:

    -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
    

      log4j2配置文件不需要变动

    5.2 同步和异步混合使用

      不需要增加5.1中的jvm启动参数。

      官网的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- No need to set system property "log4j2.contextSelector" to any value
         when using <asyncLogger> or <asyncRoot>. -->
    <Configuration status="WARN">
      <Appenders>
        <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
        <RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log"
                  immediateFlush="false" append="false">
          <PatternLayout>
            <Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern>
          </PatternLayout>
        </RandomAccessFile>
      </Appenders>
      <Loggers>
        <!-- pattern layout actually uses location, so we need to include it -->
        <AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
          <AppenderRef ref="RandomAccessFile"/>
        </AsyncLogger>
        <Root level="info" includeLocation="true">
          <AppenderRef ref="RandomAccessFile"/>
        </Root>
      </Loggers>
    </Configuration>
    

    6.混合使用同异步日志,异步不输出至控制台和文件的原因

      之前配置log4j2.xml的时候,没有设置AsyncLogger、root的 level。这时,设置了AsyncLogger的类(name="类/包")不打印到控制台,也不输出到文件里,经过排查注释掉AsyncLogger,恢复了正常,于是暂时采用了完全异步模式输出日志。

      建立一个新module只针对日志进行测试,移植以前的配置,发现在注释掉AsyncLogger后,控制台竟只输出ERROR级别的日志(当时只设置了INFO、WARN、ERROR三种级别),随后发现配置文件中缺少了logging.level.root=info(此处再给自己挖个坑,如果log4j2-spring.xml无法直接读取*.properties文件中的配置,那么root的日志级别是怎么设置的,AsyncLogger的日志级别是否也可以通过此方式进行配置),增加root的level,配置为info,此时控制台能够全部输出,增加AsyncLogger并设置level="info",解决问题。

      总结:缺少设置AsyncLogger的level(目前测试默认是ERROR)。additivity="true"(此时控制台也可以输出)。

      附上填坑过程代码;

    package com.xyz.study.log;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import javax.annotation.PostConstruct;
     * @author gem.xu
    @Component
    public class LogTest {
        private Logger log = LoggerFactory.getLogger(LogTest.class);
        @PostConstruct
        public void init(){
            log.info("加载中 这是info信息");
            log.warn("加载中 这是warn信息");
            log.error("加载中 这是error信息");
            //以为是被最后一个覆盖了,结果还是输出了error
            log.info("加载中 这是info信息");
    
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="OFF">
        <Properties>
            <property name="CONSOLE_LOG_PATTERN"
                      value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %highlight{%-5level} %logger{36} - %msg%n"/>
            <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <property name="FILE_PATH" value="${ctx:LOG_PATH}"/>
            <property name="FILE_NAME" value="logtest"/>
        </Properties>
        <appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout>
                    <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
                </PatternLayout>
            </Console>
            <!--  日志输出配置 file文件方式   immediateFlush="true"-->
            <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}.log"
                         filePattern="${FILE_PATH}/%d{yyyy-MM-dd}/${FILE_NAME}-%d{yyyy-MM-dd}-%i.log.gz"
                         immediateFlush="false">
                <PatternLayout>
                    <Pattern>${LOG_PATTERN}</Pattern>
                </PatternLayout>
                <!-- 记录日志级别-->
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
                <Policies>
                    <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                    <TimeBasedTriggeringPolicy interval="24"/>
                    <!--    日志文件达到y阈值开始滚动    -->
                    <SizeBasedTriggeringPolicy size="500MB"/>
                </Policies>
                <!-- 同一文件夹最多多少 开始覆盖 默认7 这这里设置30-->
                <DefaultRolloverStrategy max="30"/>
            </RollingFile>
            <!-- 日志输出配置 file文件方式-->
            <RollingFile name="ERROR" fileName="${FILE_PATH}/${FILE_NAME}_error.log"
                         filePattern="${FILE_PATH}/%d{yyyy-MM-dd}/${FILE_NAME}-%d{yyyy-MM-dd}-%i.log.gz"
                <PatternLayout>
                    <Pattern>${LOG_PATTERN}</Pattern>
                </PatternLayout>
                <!-- 记录日志级别-->
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
                <Policies>
                    <!--interval属性用来指定多久滚动一次,默认是24 hour-->
                    <TimeBasedTriggeringPolicy interval="24"/>
                    <!--    日志文件达到y阈值开始滚动    -->
                    <SizeBasedTriggeringPolicy size="500MB"/>
                </Policies>
                <!-- 同一文件夹最多多少 开始覆盖 默认7 这这里设置30-->
                <DefaultRolloverStrategy max="30"/>
            </RollingFile>
        </appenders>
        <loggers>
            <!--  异步日志  -->
            <AsyncLogger level="info" name="com.xyz.study.log.LogTest" includeLocation="true" additivity="true" >
                <appender-ref ref="RollingFileInfo"/>
                <appender-ref ref="ERROR"/>
            </AsyncLogger>
                <appender-ref level="info" ref="Console"/>
    <!--            <appender-ref ref="RollingFileInfo"/>-->
    <!--            <appender-ref ref="ERROR"/>-->
            </root>
        </loggers>
    </configuration>
    复制代码
    分类:
    后端
    标签: