首页 > 其他分享 >[日志] lo4j2之自定义日志格式变量

[日志] lo4j2之自定义日志格式变量

时间:2023-12-28 14:59:05浏览次数:26  
标签:layout log 自定义 lo4j2 apache org 日志 appender

1 PatternLayout / LogEventPatternConverter : 自定义日志格式及格式变量

在 Log4j 或 Logback 等 Java 日志框架中,PatternLayout 类允许你定义日志输出的格式。
PatternLayout 通过一系列的转换器(PatternConverter) 来定义输出的样式。其中,LogEventPatternConverter(日志格式化转化器)是一种转换器,用于将LogEvent中的信息转换为文本。

以Log4j2为例,下面是一些常用的 LogEventPatternConverter的格式说明。

  • %m : 输出日志消息
  • %p : 输出日志级别
  • %c : 输出日志记录器名称
  • %d : 输出日志时间。可以通过指定日期格式来自定义输出格式
    • 例如:%d{yyyy-MM-dd HH:mm:ss}
  • %t : 输出线程名称
  • %n : 输出一个平台特定的换行符
  • %C : 输出日志记录器的全类名
  • %F : 输出产生日志记录事件的源文件的文件名

这些格式可以按照需要进行组合。以下是一个简单的例子:

log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %c - %m %n

#%thread [%traceId] [${application.name}] [system] [%d{yyyy/MM/dd HH:mm:ss.SSS}] [%-5p] [%t] [%C{1}] %M:%L__|%X{traceId}|__%m%n
# 注1: thread 是我自定义的 ThreadLogEventPatternConverter 的日志格式变量
# 注2: X{traceId} 是我基于org.slf4j.MDC.put/remove("traceId", traceId) 自定义的日志格式变量

需注意,具体的格式和转换器可能会因使用的日志框架及版本而有所不同。请查阅日志框架的相应文档,获取更详细的信息。

2 案例实践

2.1 基于 LogEventPatternConverter 自定义日志格式转换器及日志格式变量:thread/Thread

2.1.1 ThreadLogEventPatternConverter extends LogEventPatternConverter

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.message.Message;

/**
 * org.apache.logging.log4j.core.pattern.MethodLocationPatternConverter
 * @reference-doc
 *  [1] log4j2 源代码片段 - 编写Converter类型的插件 - CSDN - https://blog.csdn.net/zhouzhiande/article/details/111238677
 */

@Plugin(name = "ThreadLogEventPatternConverter", category = PatternLayout.KEY, printObject = true)
@ConverterKeys({"thread", "Thread"}) // 对应模板中的 %thread / %Thread 的日志格式变量
public class ThreadLogEventPatternConverter extends LogEventPatternConverter {
    private static final ThreadLogEventPatternConverter INSTANCE = new ThreadLogEventPatternConverter();

    private ThreadLogEventPatternConverter() {
        super("threadx", "threadxxxx");
    }

    @Override
    public void format(LogEvent event, StringBuilder toAppendTo) {
        final StackTraceElement sourceStackTraceElement = event.getSource();

        Message message = event.getMessage();
        //String resultMessage = message.getFormattedMessage();//%m

        if (sourceStackTraceElement != null) {
            toAppendTo.append("[" + event.getThreadId() + " | " + event.getThreadName() +  " | " + Thread.currentThread().getId() + "]");
        }
    }

    public static ThreadLogEventPatternConverter newInstance(final String[] options) {
        return INSTANCE;
    }
}

2.1.2 日志框架版本及日志输出策略

  • 日志框架版本
  • slf4j : 1.7.25
  • log4j2 : 2.20.0
  • 日志输出策略(log4j2.properties)
##################### [0] 自定义配置(可灵活修改) #####################
property.instance.name=${env:INSTANCE_NAME:-localInstance}

property.log.level=${env:LOG_ACCESS:-INFO}
property.log.access.level=${env:LOG_ACCESS:-INFO}
property.log.operation.level=${env:LOG_OPERATE:-INFO}
property.log.threshold=${log.level}

property.log.consoleAppender=CONSOLE_APPENDER
#property.log.layout=org.apache.log4j.PatternLayout
property.log.layout=PatternLayout
property.log.layout.consolePattern=%thread [%traceId] [${application.name}] [system] [%d{yyyy/MM/dd HH:mm:ss.SSS}] [%-5p] [%t] [%C{1}] %M:%L__|%X{traceId}|__%m%n
property.log.layout.mainPattern=${log.layout.consolePattern}
property.log.dir=/logs/${instance.name}

##################### [1] 定义 Logger #####################
# ------------------- [1.1] 定义 RootLogger 等 全局性配置(不可随意修改) ------------------- #
## rootLogger, 根记录器,所有记录器的父辈
## 指定根日志的级别 | All < Trace < Debug < Info < Warn < Error < Fatal < OFF
rootLogger.level=${log.level}
## 指定输出的appender引用
## 2.17.2 版本以下通过这种方式将 root 和 Appender关联起来
## 2.17.2 版本以上有更简便的写法
rootLogger.appenderRef.stdout.ref=${log.consoleAppender}
rootLogger.appenderRef.rolling.ref=${log.systemFileAppender}

# ------------------- [1.2] 指定个别 Class 的 Logger (可随意修改,建议在 nacos 上修改) ------------------- #
## [format] step1 若想对不同的类输出不同的文件(以cn.com.Test为例),先要在Test.java中定义: private static Log logger = LogFactory.getLog(Test.class);
## [format] step2 然后在log4j.properties中加入: (即 让 cn.com.Test 中的 logger 使用 log4j.appender.test所做的配置 )
## [format] step3 property.logger.cn.com.Test= DEBUG, test
## [format]       property.appender.test=org.apache.log4j.FileAppender
## [format]       property.appender.test.File=${myweb.root}/WEB-INF/log/test.log
## [format]       property.appender.test.layout=org.apache.log4j.PatternLayout
## [format]       property.appender.test.layout.ConversionPattern=%d %p [%c] - %m%n

# org.springframework.context
# org.springframework.core
# org.springframework.beans.factory
logger.spring.name=org.springframework
logger.spring.level=WARN
logger.spring.additivity=false
logger.spring.appenderRef.systemRollingFile.ref=MySystemFileAppender
logger.spring.appenderRef.console.ref=CONSOLE_APPENDER

logger.mybatisplus.name=com.baomidou.mybatisplus
logger.mybatisplus.level=WARN
logger.mybatisplus.additivity=false
logger.mybatisplus.appenderRef.systemRollingFile.ref=MySystemFileAppender
logger.mybatisplus.appenderRef.console.ref=CONSOLE_APPENDER

logger.mybatisplus.name=com.baomidou.mybatisplus
logger.mybatisplus.level=WARN
logger.mybatisplus.additivity=false
logger.mybatisplus.appenderRef.systemRollingFile.ref=MySystemFileAppender
logger.mybatisplus.appenderRef.console.ref=CONSOLE_APPENDER

##################### [2] 定义 Appender #####################

# ------------------- [2.1] CONSOLE Appender ------------------- #
# console
# 指定输出源的类型与名称
appender.console.type=Console
appender.console.name=CONSOLE_APPENDER
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=${log.layout.consolePattern}

# ------------------- [2.2] SYSTEM File Appender ------------------- #

appender.systemRollingFile.type=RollingFile
appender.systemRollingFile.name=MySystemFileAppender
appender.systemRollingFile.fileName=${log.dir}/system.log
appender.systemRollingFile.filePattern=${log.dir}/system-%d{MM-dd-yyyy}-%i.log.gz
appender.systemRollingFile.policies.type=Policies
appender.systemRollingFile.policies.time.type=TimeBasedTriggeringPolicy
appender.systemRollingFile.policies.size.type=SizeBasedTriggeringPolicy
appender.systemRollingFile.policies.size.size=128MB
appender.systemRollingFile.strategy.type=DefaultRolloverStrategy
appender.systemRollingFile.layout.type=PatternLayout
appender.systemRollingFile.layout.pattern=${log.layout.mainPattern}

# ------------------- [2.3] ACCESS File Appender ------------------- #

appender.accessRollingFile.type=RollingFile
appender.accessRollingFile.name=MyAccessFileAppender
appender.accessRollingFile.fileName=${log.dir}/access.log
appender.accessRollingFile.filePattern=${log.dir}/access-%d{MM-dd-yyyy}-%i.log.gz
appender.accessRollingFile.policies.type=Policies
appender.accessRollingFile.policies.time.type=TimeBasedTriggeringPolicy
appender.accessRollingFile.policies.size.type=SizeBasedTriggeringPolicy
appender.accessRollingFile.policies.size.size=128MB
appender.accessRollingFile.strategy.type=DefaultRolloverStrategy
appender.accessRollingFile.layout.type=PatternLayout
appender.accessRollingFile.layout.pattern=${log.layout.mainPattern}

# ------------------- [2.5] SkyWalkingClient Appender ------------------- #
## @reference-doc :
##  [1] https://skywalking.apache.org/docs/skywalking-java/next/en/setup/service-agent/java-agent/application-toolkit-log4j-2.x/#print-trace-id-in-your-logs
##  [2] https://blog.csdn.net/qq_56042039/article/details/125930502
## 依赖 JAR 包 : org.apache.skywalking:apm-toolkit-log4j-2.x:8.7.0 , org.apache.skywalking:apm-toolkit-trace:8.7.0
## org.apache.skywalking.apm.toolkit.log.log4j.v2.x.log.GRPCLogClientAppender
appender.linkTrace.type=GRPCLogClientAppender
appender.linkTrace.name=MySkyWalkingClientAppender
appender.linkTrace.layout.type=PatternLayout
#appender.linkTrace.layout.pattern=[${application.name}] [${instance.name}] [${env:HOST_IP}] [${env:CONTAINER_IP}] [%d{yyyy/MM/dd HH:mm:ss.SSS}] [%traceId] [%-5p] [%t] [%C{1}.java:%L %M] %m%n
appender.linkTrace.layout.pattern=${log.layout.mainPattern}

2.1.3 日志使用与输出 : Test

public class Test {
    private final static Logger logger = LoggerFactory.getLogger(Test.class);

    public static void main(String[] args) {
        logger.info("hello");
    }
}
  • 输出效果如下:
[1 | main | 1] [TID: N/A] [bdp-xxxx-service] [system] [2023/12/28 14:31:51.633] [INFO ] [main] [Test] main:19__||__hello

2.2 Apache Skywalking 的日志插件(apm-toolkit-log4j-2.x)的自定义日志格式变量traceId

  • 相关依赖
  • skywalking.apm-application-toolkit.version : 8.15.0
<!-- apache skywalking | start -->
<!-- Skywalking 的 Layout 所支持的`ConversionPattern`核心参数在`apm-toolkit-log4j-1.x`插件中是`T`(即 `traceId`, 区分大小写[T≠t]),在`apm-toolkit-logback-1.x`插件中是`%X{tid}`,需打印到日志中,才能被 Skywalking OAP 服务的`Collertor`收集并识别请求链路中的日志信息,否则日志收集失败。 -->
<!--        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-log4j-1.x</artifactId>
            <version>${skywalking.apm-application-toolkit.version}</version>
        </dependency>-->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-log4j-2.x</artifactId>
    <version>${skywalking.apm-application-toolkit.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>${skywalking.apm-application-toolkit.version}</version>
</dependency>

<!--
    WebFluxSkyWalkingOperators : apm-toolkit-webflux : start version 8.15.0
    https://github.com/apache/skywalking/discussions/10686 -->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-webflux</artifactId>
    <version>${skywalking.apm-application-toolkit.version}</version>
</dependency>

<!-- apache skywalking [start] -->
<!-- Skywalking 的 Layout 所支持的`ConversionPattern`核心参数在`apm-toolkit-log4j-1.x`插件中是`T`(即 `traceId`, 区分大小写[T≠t]),在`apm-toolkit-logback-1.x`插件中是`%X{tid}`,需打印到日志中,才能被 Skywalking OAP 服务的`Collertor`收集并识别请求链路中的日志信息,否则日志收集失败。 -->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-agent-core</artifactId>
    <version>${skywalking.apm.version}</version>
</dependency>
<!-- apache skywalking | end -->
  • org.apache.skywalking.apm.toolkit.log.log4j.v2.x.TraceIdConverter

apm-toolkit-trace-8.15.0.jar

  • log4j2.properties :日志格式变量traceId的使用

  • 日志输出效果

[TID: 15211e39736b44a9a42f13927fae2f2e.1.17037328207610001] [bdp-gateway-service] [system] [2023/12/28 11:07:03.834] [INFO ] [main] [HikariDataSource] $sw$original$getConnection$08spks0:123__||__HikariPool-1 - Start completed.

2.3 基于 MDC与Spring Cloud Gateway#GlobalFilter的TraceGlobalFilter实现自定义日志格式变量X{traceId}

import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.apache.skywalking.apm.toolkit.webflux.WebFluxSkyWalkingOperators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class TraceGlobalFilter implements GlobalFilter /**, Ordered **/{
    private final static Logger logger = LoggerFactory.getLogger(TraceGlobalFilter.class);

    private final static String TRACE_ID_HTTP_HEADER_PARAM = "trace-id";

    public final static String TRACE_ID_MDC_PARAM = "traceId";

    private Integer order;

    public TraceGlobalFilter(Integer order){
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String traceId = WebFluxSkyWalkingOperators.continueTracing(exchange, TraceContext::traceId);
        if(ObjectUtils.isEmpty(traceId)){//get trace id from skywalking framework is empty
            traceId = SkywalkingUtil.getTraceId(exchange.getRequest().getHeaders());
            logger.info("TraceId from skywalking framework is empty! But traceId from request.headers now is : {}", traceId);
        }
        MDC.put(TRACE_ID_MDC_PARAM, traceId);
        exchange.getResponse().getHeaders().set(TRACE_ID_HTTP_HEADER_PARAM, traceId);
        return chain.filter(exchange).doAfterTerminate( () -> {
            MDC.remove(TRACE_ID_MDC_PARAM);
        });
    }
}
  • log4j2.properties
property.log.layout.consolePattern=[${application.name}] [system] [%d{yyyy/MM/dd HH:mm:ss.SSS}] [%-5p] [%t] [%C{1}] %M:%L__|%X{traceId}|__%m%n

X 参考文献

标签:layout,log,自定义,lo4j2,apache,org,日志,appender
From: https://www.cnblogs.com/johnnyzen/p/17932686.html

相关文章

  • 克魔助手:方便查看iPhone应用实时日志和奔溃日志工具
    ​ 克魔助手:方便查看iPhone应用实时日志和奔溃日志工具查看iosapp运行日志摘要本文介绍了一款名为克魔助手的iOS应用日志查看工具,该工具可以方便地查看iPhone设备上应用和系统运行时的实时日志和奔溃日志。同时还提供了奔溃日志分析查看模块,可以对苹果奔溃日志进行符号化、......
  • 阿里 P7 三面凉凉,kafka Borker 日志持久化没答上来
    ......
  • 排查oracle日志增速过快记录
    问题产生:最近同事连接开发环境oracle数据报错,一开始简单更加了下归档空间,每次加5G,语句如下:查看归档空间大小:showparameterdb_recovery;查看使用占比:select*fromv$flash_recovery_area_usage;增加归档空间:altersystemsetdb_recovery_file_dest_size=30G; 后来每......
  • Postgresql中PL/pgSQL的游标、自定义函数、存储过程的使用
    场景Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句:Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句上面讲了基本语法,下面记录游标、自定义函数、存储过程的使用。注:博客:霸道流氓气质_C#,架构之路,SpringBoot实......
  • WPF 使用Log4Net记录日志和显示日志
    一、添加引用 二、添加Log4Net配置文件,设置文件属性如果较新则复制或者始终复制 <?xmlversion="1.0"encoding="utf-8"?><log4net><!--将日志以回滚文件的形式写到文件中--><!--按日期切分日志文件,并将日期作为日志文件的名字--><appendername="Lo......
  • 日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队
    前言随着互联网和大数据的迅猛发展,分布式日志系统和日志分析系统已广泛应用,几乎所有应用程序都使用各种日志框架记录程序运行信息。因此,作为工程师,了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影响,但没有日志的应用程序是不完整的,甚至可以说是有缺陷的......
  • ETCD 下线Member未剔除引发的日志报错
    背景介绍容器化的etcd集群原来具有三个节点分别为etcd-0,etcd-1,etcd-2,在节点etcd-2下线后剩两个节点etcd-0,etcd-1#kubectlgetpod-napisixNAMEREADYSTATUSRESTARTSAGEetcd-0......
  • 哪里有流程自定义表单?
    如果需要提升办公协作效率,可以借助流程自定义表单的功能和价值,快速进入办公流程化发展阶段。那么,哪里有流程自定义表单可以体验?流程自定义表单又有哪些优势特点?通过这篇文章,我们一起来了解流程自定义表单和低代码技术平台的相关特点。我们都知道,随着社会的进步和发展,很多企业已经......
  • MySQL 事务日志
    MySQL事务日志事务有4种特性:原子性,一致性,隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢(是通过什么来控制的呢)?事务的"隔离性"由锁机制实现(通过加锁来实现隔离)。而事务的"原子性","一致性"和"持久性"由事务的redo日志和undo 日志来保证redolog称......
  • Spring Boot学习随笔- 后端实现全局异常处理(HandlerExceptionResolver),前后端解决跨域
    学习视频:【编程不良人】2021年SpringBoot最新最全教程第十七章、异常处理异常处理作用:用来解决整合系统中任意一个控制器抛出异常时的统一处理入口传统方式传统单体架构下的处理方式配置全局异常处理类@ComponentpublicclassGlobalExceptionResolverimplementsHand......