log4j2的异步使用及添加自定义参数方式

 更新时间:2021年12月22日 23:11  点击:239 作者:住街对面的查理

log4j2异步使用及添加自定义参数

关于log4j2的性能和原理就不赘述了,这篇主要讲使用,配置文件解读,和添加自定义参数,偏应用的一篇文章。

相比与其他的日志系统,log4j2丢数据这种情况少;disruptor技术,在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生;

目前看来,log4j2的性能最突出。

添加依赖(这里省略了版本号)

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

然后再各个项目中添加文件log4j2.xml,在要使用的类上添加@slf4j 注解(lombok的),即可使用log对象。

log4j 2.0与以往的1.x有一个明显的不同,其配置文件只能采用.xml, .json或者 .jsn。在默认情况下,系统选择configuration文件的优先级如下:(classpath为src文件夹)

  • classpath下名为 log4j-test.json 或者log4j-test.jsn文件
  • classpath下名为 log4j2-test.xml
  • classpath下名为 log4j.json 或者log4j.jsn文件
  • classpath下名为 log4j2.xml

level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

下面写一个生产可用的log4j2.xml的模板

<?xml version="1.0" encoding="UTF-8"?>
<!-- Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!-- monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN">
<Properties>
<!--常用变量配置 供下文中使用-->
<property name="APP_NAME">项目名称</property>
<property name="LOGGER_LEVEL">INFO</property>
<!--日志路径 对应服务器路径-->
<property name="LOGGER_PATH">/data/logs</property> 
<Property name="LOG_HOME">${LOGGER_PATH}/${APP_NAME}</Property>
<!--文件大小-->
<Property name="FILE_SIZE">10M</Property>
<!--日志格式-->
<Property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</Property>
<!--日志文件命名格式-->
<Property name="rolling_file_name">-%d{yyyy-MM-dd}.%i.zip</Property>
<!--日志留存最大文件数-->
<Property name="rollover_strategy_max">30</Property>
<Property name="LOG_HOME_PROJECT">${LOG_HOME}/${APP_NAME}-project</Property>
<Property name="LOG_HOME_PROJECT_ERROR">${LOG_HOME}/${APP_NAME}-project-error</Property>
<Property name="LOG_HOME_SQL">${LOG_HOME}/${APP_NAME}-sql</Property>
</Properties>
<appenders>
<!--控制台打印 及格式-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${log_pattern}" />
</Console>
<!--定义rolling() 供下文使用-->
<RollingRandomAccessFile name="projectRolling"
fileName="${LOG_HOME_PROJECT}.log"
filePattern="${LOG_HOME_PROJECT}${rolling_file_name}"
immediateFlush="false" append="true">
<PatternLayout>
<Pattern>${log_pattern}</Pattern>
<Charset>UTF-8</Charset>
</PatternLayout>
<Policies>
<!--滚动规则 时间或者文件大小 滚动后将按照filePattern命名-->
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="24"/>
<SizeBasedTriggeringPolicy size="${FILE_SIZE}"/>
</Policies>
<DefaultRolloverStrategy max="${rollover_strategy_max}" />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="projectErrorRolling"
fileName="${LOG_HOME_PROJECT_ERROR}.log"
filePattern="${LOG_HOME_PROJECT_ERROR}${rolling_file_name}"
immediateFlush="false" append="true">
<Filters>
<!-- 只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)可以使用多个ThresholdFilter 达到精准过滤某个级别的日志-->
<ThresholdFilter level="${LOGGER_LEVEL}" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<PatternLayout>
<Pattern>${log_pattern}</Pattern>
<Charset>UTF-8</Charset>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="${FILE_SIZE}"/>
</Policies>
<DefaultRolloverStrategy max="${rollover_strategy_max}" />
</RollingRandomAccessFile>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="sqlRolling"
fileName="${LOG_HOME_SQL}.log"
filePattern="${LOG_HOME_SQL}${rolling_file_name}"
immediateFlush="false" append="true">
<PatternLayout>
<Pattern>${log_pattern}</Pattern>
<Charset>UTF-8</Charset>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="${FILE_SIZE}"/>
</Policies>
<DefaultRolloverStrategy max="${rollover_strategy_max}" />
</RollingRandomAccessFile>
</appenders>
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
<loggers>
<!--异步日志 区别于普通使用的logger root 搭配-->
<!--name 为包名 对应配置日志输出等级level -->
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
<!--将org.springframework包下的日志打印到Console控制台,projectRolling文件,projectErrorRolling(error级别单独一个文件)-->
<AsyncLogger name="org.springframework" level="${LOGGER_LEVEL}" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="projectRolling"/>
<appender-ref ref="projectErrorRolling"/>
</AsyncLogger>
<AsyncLogger name="com.alibaba.dubbo" level="${LOGGER_LEVEL}" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="projectRolling"/>
<appender-ref ref="projectErrorRolling"/>
</AsyncLogger>
<AsyncLogger name="druid.sql" level="${LOGGER_LEVEL}" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="sqlRolling"/>
</AsyncLogger>
<AsyncLogger name="org.mybatis" level="${LOGGER_LEVEL}" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="sqlRolling"/>
</AsyncLogger>
<AsyncLogger name="com.项目包名" level="${LOGGER_LEVEL}" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="projectRolling"/>
<appender-ref ref="projectErrorRolling"/>
</AsyncLogger>
<AsyncRoot level="${LOGGER_LEVEL}">
<appender-ref ref="Console"/>
<appender-ref ref="projectRolling" />
<appender-ref ref="projectErrorRolling" />
</AsyncRoot>
</loggers>
</configuration>

如上配置会产生3个日志文件

  • 项目名称-project.log
  • 项目名称-project-error.log
  • 项目名称-sql.log

补充知识

onMatch和onMismatch都有三个属性值,分别为Accept、DENY和NEUTRAL

分别介绍这两个配置项的三个属性值:

  • onMatch=“ACCEPT” 表示匹配该级别及以上
  • onMatch=“DENY” 表示不匹配该级别及以上
  • onMatch=“NEUTRAL” 表示该级别及以上的,由下一个filter处理,如果当前是最后一个,则表示匹配该级别及以上
  • onMismatch=“ACCEPT” 表示匹配该级别以下
  • onMismatch=“NEUTRAL” 表示该级别及以下的,由下一个filter处理,如果当前是最后一个,则不匹配该级别以下的
  • onMismatch=“DENY” 表示不匹配该级别以下的

自定义日志格式

  • %d{HH:mm:ss.SSS} 表示输出到毫秒的时间
  • %logger{36} 简单理解为类名
  • %thread 输出当前线程名称
  • %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
  • %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
  • %msg 日志文本
  • %n 换行
  • %X{xxx} xxx为自定义参数

如何在日志中添加自己想传的参数?

定义拦截器(web服务拦controller,dubbo服务拦api),每次请求过来,拦住,然后将自定义参数传入。至于自定义参数怎么存,就是另一个问题了。

eg: traceId 跟踪号 对应log4j2.xml中的 %X{traceId}

下面是关键代码:

public class ContextFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String traceId = UUID.randomUUID().toString().replaceAll("-", "");
// org.slf4j.MDC 
        MDC.put(CommonConsts.TRACE_ID_LOG, traceId);// 用来给日志文件使用
// org.apache.logging.log4j.ThreadContext
    ThreadContext.put(CommonConsts.TRACE_ID_LOG, traceId); //经测试,这两行都可行。
    filterChain.doFilter(servletRequest, servletResponse);
}

最后的日志打印效果如下:

2019-05-29 12:04:30.122 [http-nio-8080-exec-2] [2333333] INFO com.core.web.filter.ContextFilter - 接口调用时间:245毫秒

log4j 输入自定义参数

使用log4j、log4j2输入日志时,有时想追加打印自定义参数(比如客户端环境:手机型号、浏览器数据,request数据、用户数据等),以便于快速定位问题所在。

亦或在多线程环境中,快速定位哪些日志是由同一用户输出,便于其他工具进行日志分析。

log4j提供了ThreadContext 线程上下文类,用于存储自定义数据,以便在输入日志时,包含指定数据。

测试代码如下

package com.howtodoinjava.log4j2.examples;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
 
public class Log4j2HelloWorldExample {
    private static final Logger LOGGER = LogManager.getLogger(Log4j2HelloWorldExample.class.getName());
    public static void main(String[] args) {
        //Add context information
        ThreadContext.put("id", UUID.randomUUID().toString());
        ThreadContext.put("ipAddress", "192.168.21.9");
 
        LOGGER.debug("Debug Message Logged !!");
        LOGGER.info("Info Message Logged !!");
        LOGGER.debug("Another Debug Message !!");
 
        //Clear the map
        ThreadContext.clearMap();
		
        LOGGER.debug("Thread Context Cleaned up !!");
        LOGGER.debug("Log message with no context information !!");
    }
}

而后在 log4j.xml 中指定上述参数,

单独使用%X以包含地图的全部内容。

使用%X{key}包括指定的键。

使用%x包括堆栈的全部内容。

在实际应用时,一般是在过滤器、拦截器进行上述操作,方法之前,将数据绑定到线程中,方法完成后,清理线程数据。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

原文出处:https://blog.csdn.net/macrun28/article/details/90669513

[!--infotagslink--]

相关文章

  • C#创建自定义控件及添加自定义属性和事件使用实例详解

    这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
  • JS实现自定义简单网页软键盘效果代码

    本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
  • Nest.js参数校验和自定义返回数据格式详解

    这篇文章主要给大家介绍了关于Nest.js参数校验和自定义返回数据格式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-28
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • 自定义jquery模态窗口插件无法在顶层窗口显示问题

    自定义一个jquery模态窗口插件,将它集成到现有平台框架中时,它只能在mainFrame窗口中显示,无法在顶层窗口显示. 解决这个问题的办法: 通过以下代码就可能实现在顶层窗口弹窗 复制代码 代码如下: $(window.top.documen...2014-05-31
  • 解决Springboot get请求是参数过长的情况

    这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
  • 自定义feignClient的常见坑及解决

    这篇文章主要介绍了自定义feignClient的常见坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-20
  • PHP中empty和isset对于参数结构的判断及empty()和isset()的区别

    废话不多说了,直接给大家贴代码了。<&#63;php class test{} $a1 = null; $a2 = ""; //$a3 = $a4 = 0; $a5 = '0'; $a6 = false; $a7 = array(); //var $a8; $a9 = new test(); for ($i=1; $i <=9 ; $i++) {...2015-11-24
  • java正则表达式判断前端参数修改表中另一个字段的值

    这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
  • mysql配置模板(my-*.cnf)参数详细说明

    mysql安装成功后有几个默认的配置模板,列表如下: my-huge.cnf : 用于高端产品服务器,包括1到2GB RAM,主要运行mysql my-innodb-heavy-4G.ini : 用于只有innodb的安装,最多有4GB RAM,支持大的查询和低流量 my-large.cnf : 用于...2015-03-15
  • pytorch 自定义卷积核进行卷积操作方式

    今天小编就为大家分享一篇pytorch 自定义卷积核进行卷积操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-06
  • C#中out参数、ref参数与值参数的用法及区别

    这篇文章主要给大家介绍了关于C#中out参数、ref参数与值参数的用法及区别的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 详解C#泛型的类型参数约束

    这篇文章主要介绍了C#泛型的类型参数约束的相关资料,文中讲解非常细致,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2020-07-31
  • 深入分析C#中的异步和多线程

    这篇文章主要介绍了C#中异步和多线程的相关资料,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2021-01-16
  • PHP YII框架开发小技巧之模型(models)中rules自定义验证规则

    YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证...2015-11-24
  • C#多线程与异步的区别详解

    多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别...2020-06-25
  • 详解C# Socket异步通信实例

    本篇文章主要介绍了C# Socket异步通信,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • jquery自定义插件开发之window的实现过程

    这篇文章主要介绍了jquery自定义插件开发之window的实现过程的相关资料,需要的朋友可以参考下...2016-05-09
  • Java线程池中的各个参数如何合理设置

    这篇文章主要介绍了Java线程池中的各个参数如何合理设置操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-19