springboot2 log4j2 如何动态记录日志,并将日志根据api接口路径,保存到对应路径的文件中?

场景:
1、
api接口地址:/payPage/createOrder/addSave
springboot2 收到请求后,如何把日志保存到:jar包目录/logs/payPage/createOrder/addSave/YYYY-MM-dd.log文件中

2、
api接口地址:/merchant/goodlist/getListByQuery?currentPage=1&size=10
springboot2 收到请求后,如何把日志保存到:jar包目录/logs/merchant/goodlist/getListByQuery/YYYY-MM-dd.log文件中

我目前查到的方法有:

1、
在程序main函数入口中,设置:
System.setProperty("log4fFile", "runtimeTest.log");

2、
在 log4j-spring.xml 中,设置文件名,加入变量:

<RollingFile name="RollingFile" fileName="${LOG_PATH}/${sys:log4fFile}/${date:yyyy-MM-dd}_${APP_NAME}.log"

filePattern="${LOG_BACKUP_PATH}/${date:yyyy-MM-dd}/${APP_NAME}-%d{yyyy-MM-dd}_%i.log.zip">

<!--输出日志的格式, 不设置默认为:%m%n-->

<PatternLayout pattern="${PATTERN_FILE}"/>

<!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->

<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>

<!--归档设置-->

<Policies>

<!--按时间间隔归档:

1. interval=时间间隔, 单位由filePattern的%d日期格式指定, 此处配置代表每一天归档一次

2. modulate="true" 是否对interval取模,决定了下一次触发的时间点

-->

<TimeBasedTriggeringPolicy interval="1" modulate="true"/>

<!-- 按照日志文件的大小: size表示当前日志文件的最大size,支持单位:KB/MB/GB-->

<SizeBasedTriggeringPolicy size="50MB"/>

</Policies>

<!-- 历史日志配置: 该属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->

<DefaultRolloverStrategy max="30"/>

</RollingFile>

3、
我使用了拦截器,并进行了注册

public class Log4j2Interceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

String traceId = UUID.randomUUID().toString().replaceAll("-", "");

// org.slf4j.MDC

MDC.put("logid", traceId);// 用来给日志文件使用

// org.apache.logging.log4j.ThreadContext

ThreadContext.put("logid", traceId); // 经测试,这两行都可行。

System.setProperty("log4fFile", traceId);

return true;

}

}

注册

@Configuration

public class InterceptorConfig implements WebMvcConfigurer {

@Override

public void addInterceptors(InterceptorRegistry registry) {

InterceptorRegistration log4j2Registration = registry.addInterceptor(useLog4j2Inter());

log4j2Registration

.addPathPatterns("/**");

}

@Bean

public Log4j2Interceptor useLog4j2Inter() {

return new Log4j2Interceptor();

}

}

测试结果
1:main函数入口 获取不到api接口的地址程序启动后,接口请求不会访问到main函数入口。-->在主入口动态修改log4fFile失败。
2、在拦截器中动态修改log4fFile失败。
由此,因为无法动态log4fFile的值,由此失败。

2023年3月21日
经过测试
提供大佬提供的思路完美解决,运行中发现一个小的问题 百度后猜测:
1、是启动时,初试值为空,但我在Log4j2Interceptor.preHandle设置后依然报错,
2、文件夹没有权限,但我用的windows,应该没有权限问题。
想请大佬帮我看看。

我在log4j-spring.xml中配置:

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

<!-- 扫描配置文件是否被改动过 monitorInterval=5秒 -->

<Configuration status="DEBUG" monitorInterval="5">

<Appenders>

<Routing name="Routing">

<Routes pattern="$${ctx:ROUTINGKEY}">

<Route>

<RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="./logs/${ctx:ROUTINGKEY}/${date:yyyy-MM}.log"

filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz">

<PatternLayout>

<pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>

</PatternLayout>

<Policies>

<TimeBasedTriggeringPolicy interval="6" modulate="true"/>

<SizeBasedTriggeringPolicy size="10 MB"/>

</Policies>

</RollingFile>

</Route>

</Routes>

</Routing>

</Appenders>

<Loggers>

<Root level="info">

<AppenderRef ref="Routing"/>

</Root>

</Loggers>

</Configuration>

启动后会报错:
2023-03-21 11:13:17,938 Thread-22 ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.RollingFileAppender for element RollingFile: java.lang.IllegalStateException: ManagerFactory [org.apache.logging.log4j.core.appender.rolling.RollingFileManager$RollingFileManagerFactory@47981a5] unable to create manager for [./logs/${ctx:ROUTINGKEY}/2023-03.log] with data [org.apache.logging.log4j.core.appender.rolling.RollingFileManager$FactoryData@30b5c0bd[pattern=./logs/2023-03/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz, append=true, bufferedIO=true, bufferSize=8192, policy=CompositeTriggeringPolicy(policies=[TimeBasedTriggeringPolicy(nextRolloverMillis=0, interval=6, modulate=true), SizeBasedTriggeringPolicy(size=10485760)]), strategy=DefaultRolloverStrategy(min=1, max=7, useMax=true), advertiseURI=null, layout=%d{ISO8601} [%t] %p %c{3} - %m%n, filePermissions=null, fileOwner=null]] java.lang.IllegalStateException: ManagerFactory [org.apache.logging.log4j.core.appender.rolling.RollingFileManager$RollingFileManagerFactory@47981a5] unable to create manager for [./logs/${ctx:ROUTINGKEY}/2023-03.log] with data [org.apache.logging.log4j.core.appender.rolling.RollingFileManager$FactoryData@30b5c0bd[pattern=./logs/2023-03/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz, append=true, bufferedIO=true, bufferSize=8192, policy=CompositeTriggeringPolicy(policies=[TimeBasedTriggeringPolicy(nextRolloverMillis=0, interval=6, modulate=true), SizeBasedTriggeringPolicy(size=10485760)]), strategy=DefaultRolloverStrategy(min=1, max=7, useMax=true), advertiseURI=null, layout=%d{ISO8601} [%t] %p %c{3} - %m%n, filePermissions=null, fileOwner=null]]


回答:

问题分析

感谢邀请,我也是因为工作原因,好久没动代码了。。。不过这个问题我还是感兴趣的,虽然之前没有碰到过,不过题主你的思路我觉得是对的。

我理解你的需求应该是,根据接口url的路径来保存日志,日志分类与url的路径保持一致。比如有10个url,那理论来说就会根据请求与否动态创建10个日志与其分别对应。

而题主你直接使用RollingFile为啥不行呢?我认为log4j2的常用的Appender,比如RollingFileAppenderFileAppender等,这些都是属于一个萝卜一个坑,也就是配置一个,那最终日志文件也只有一类(不是文件个数,是类型)

而题主想要的应该是配置只有一两个,但是最终生成日志的类型应该动态生成多个。而题主你之前虽然有一个${sys:log4fFile}作为变量配置,看似"动态"了,不过这个只是动态配置,不是动态生成,也就是仅仅动态的生成了最终日志的文件名,一旦根据启动获取到变量值,那这个日志文件名就不变了。

个人思路

所以我的思路是换一个Appender,要不然是官网有提供类似功能的,要么就是自定义Appender

我本能的直接搜索log4j2 dynamic log file,结果第二个搜索结果,是log4j2官网的一个FAQ的提问How do I dynamically write to separate log files,也就是"我如何动态地写入独立的日志文件",这个跟我们想要的结果很像,所以赶紧点进去看一下

可以看到官网是推荐用RoutingAppender,通过定义路由规则,然后运行时结合ThreadContext来设置路由值,让路由进指定的Appender

官网下面有一个示例,贴出来

<Routing name="Routing">

<Routes pattern="$${ctx:ROUTINGKEY}">

<!-- This route is chosen if ThreadContext has value 'special' for key ROUTINGKEY. -->

<Route key="special">

<RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/special-${ctx:ROUTINGKEY}.log"

filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-special-%d{yyyy-MM-dd}-%i.log.gz">

<PatternLayout>

<pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>

</PatternLayout>

<Policies>

<TimeBasedTriggeringPolicy interval="6" modulate="true" />

<SizeBasedTriggeringPolicy size="10 MB" />

</Policies>

</RollingFile>

</Route>

<!-- This route is chosen if ThreadContext has no value for key ROUTINGKEY. -->

<Route key="$${ctx:ROUTINGKEY}">

<RollingFile name="Rolling-default" fileName="logs/default.log"

filePattern="./logs/${date:yyyy-MM}/default-%d{yyyy-MM-dd}-%i.log.gz">

<PatternLayout>

<pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>

</PatternLayout>

<Policies>

<TimeBasedTriggeringPolicy interval="6" modulate="true" />

<SizeBasedTriggeringPolicy size="10 MB" />

</Policies>

</RollingFile>

</Route>

<!-- This route is chosen if ThreadContext has a value for ROUTINGKEY

(other than the value 'special' which had its own route above).

The value dynamically determines the name of the log file. -->

<Route>

<RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/other-${ctx:ROUTINGKEY}.log"

filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz">

<PatternLayout>

<pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>

</PatternLayout>

<Policies>

<TimeBasedTriggeringPolicy interval="6" modulate="true" />

<SizeBasedTriggeringPolicy size="10 MB" />

</Policies>

</RollingFile>

</Route>

</Routes>

</Routing>

从上面配置来看,Routes下面配置了3个Route,也就是3个路由规则,根据匹配模式是ctx:ROUTINGKEY,也就是ThreadContextkeyROUTINGKEY的值来进行路由匹配(注:前两个Route是有key属性的,最后一个Route没有),3个路由规则分别是

  1. ROUTINGKEY的值为special,则按照下级的RollingFile来生成special-${ctx:ROUTINGKEY}.log日志(这里意思就是可以自己设定多个特定keyAppender,这跟普通的RollingFile类似,还是一个萝卜一个坑的形式)
  2. ROUTINGKEY的没有设定值,则按照下级的下级的RollingFile来生成default.log(这里就相当于路由值空值处理)
  3. 最后一个Route没有key,所以直接根据ROUTINGKEY的值动态创建Appender生成other-${ctx:ROUTINGKEY}.log日志,最后这一个应该就是我们想要的效果了

那赶紧的,直接copy过去应该就可以用了,所以我准备了两个api

@GetMapping("/test/hi")

@GetMapping("/demo/u")

然后用上题主的Log4j2Interceptor,里面只是额外再用了postHandle再把ROUTINGKEY的删掉,保证日志不会乱入

最后再把copy过来的日志配置文件中的最后一个Route改成题主的想要的效果

fileName="logs/${ctx:ROUTINGKEY}/${date:yyyy-MM-dd}.log"

最后运行一下效果,很奈斯啊

很开心完美解决,谢谢题主的问题分享~我们应该都有所收获~( =•ω•= )


回答:

日志保存到不同的文件中可以看slf4j的LoggerFactory#getLogger方法,可以指定配置文件中定义的loggerName,配置文件中再定义不同的路径

以上是 springboot2 log4j2 如何动态记录日志,并将日志根据api接口路径,保存到对应路径的文件中? 的全部内容, 来源链接: utcz.com/p/945034.html

回到顶部