参考 https://www.jianshu.com/p/a33902d58530
参考 https://blog.csdn.net/Alex19961223/article/details/103878721 - 这篇讲得好。

配置 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="120 seconds"><appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) %X{C} - %msg%n</pattern></encoder></appender><appender name="defaultAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${HOME}/Documents/rpl-task/polarx.log</file><append>true</append><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${HOME}/Documents/rpl-task/%d{yyyy-MM-dd}/polarx.%i.log.gz</fileNamePattern><timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>200MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern><![CDATA[%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n]]></pattern><charset class="java.nio.charset.Charset">UTF-8</charset></encoder></appender><appender name="commitAppender" class="ch.qos.logback.classic.sift.SiftingAppender"><discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"><Key>taskName</Key><DefaultValue>default</DefaultValue></discriminator><sift><appender name="commitAppender-${taskName}"class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${user.home}/logs/rpl/${taskName}/commit.log</file><append>true</append><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- rollover daily --><fileNamePattern>${user.home}/logs/rpl/${taskName}/%d{yyyy-MM-dd}/commit.%i.log.gz</fileNamePattern><maxFileSize>200MB</maxFileSize><maxHistory>30</maxHistory><totalSizeCap>5GB</totalSizeCap><delayMinute>5</delayMinute></rollingPolicy><encoder><pattern><![CDATA[%m%n]]></pattern><charset class="java.nio.charset.Charset">UTF-8</charset></encoder></appender></sift></appender><appender name="checkResultAppender" class="ch.qos.logback.classic.sift.SiftingAppender"><discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"><Key>taskName</Key><DefaultValue>default</DefaultValue></discriminator><sift><appender name="checkResultAppender-${taskName}"class="ch.qos.logback.core.FileAppender"><file>${user.home}/logs/rpl/${taskName}/check.log</file><append>true</append><encoder><pattern><![CDATA[%m%n]]></pattern><charset class="java.nio.charset.Charset">UTF-8</charset></encoder></appender></sift></appender><logger name="defaultLogger" level="INFO"><appender-ref ref="defaultAppender"/></logger><logger name="consoleLogger" level="INFO"><appender-ref ref="consoleAppender"/></logger><logger name="commitLogger" additivity="false"><level value="INFO"/><appender-ref ref="commitAppender" additivity="false"/></logger><logger name="checkResultLogger" additivity="false"><level value="INFO"/><appender-ref ref="checkResultAppender" additivity="false"/></logger><root level="INFO"><appender-ref ref="defaultAppender"/></root>
</configuration>

LogUtil 代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class LogUtil {/*** Log4j is thread safe*/private static String SIFT_LOGGER_KEY     = "taskName";private static String COMMIT_LOGGER       = "commitLogger";private static String CHECK_RESULT_LOGGER = "checkResultLogger";public static Logger getCommitLogger() {MDC.put(SIFT_LOGGER_KEY, TaskContext.getInstance().getTaskName());return LoggerFactory.getLogger(COMMIT_LOGGER);}public static Logger getCheckResultLogger() {MDC.put(SIFT_LOGGER_KEY, TaskContext.getInstance().getTaskName());return LoggerFactory.getLogger(CHECK_RESULT_LOGGER);}

问题排查

使用上面代码。发现:

我使用 static final Logger logger = LogUtil.getCommitLogger();

在单线程中 ,log 写入到了正确的文件(文件路径由 MDC 传入。与 TaskContext.getInstance().getTaskName() 绑定)。

但是在多线程中,log 始终写入到了 default 路径。也就是 MDC 传入的 taskName 没有生效。

经过排查:
发现创建 logger 和使用 logger 的不是同一个线程。创建 logger 时 MDC 传入的 taskName 在使用 logger 的线程中已经不起作用了,故 logger 必须在使用它的线程中创建,MDC 才能起作用。

参考:
https://blog.csdn.net/pengjunlee/article/details/88059664

以下摘自:https://blog.csdn.net/Alex19961223/article/details/103878721
MDCBasedDiscriminator 的实现,可以看出之所以是跟线程相关的,因为MDC.put会保存到一个ThreadLocal中去

public class MDCBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> {// 之前的java实例代码中通过<key>taskId</key>设置这里的key是“taskId”private String key;private String defaultValue;public MDCBasedDiscriminator() {}// event即日志事件,这个方法的返回值决定了此日志event后面会有那一个appender处理public String getDiscriminatingValue(ILoggingEvent event) {// 前面说过MDC.put会保存到一个ThreadLocal<Map>中去,这里拿到这个mapMap mdcMap = event.getMDCPropertyMap();if(mdcMap == null) {return this.defaultValue;} else {// 这里拿到当前线程通过MDC.put设置的key的value值String mdcValue = (String)mdcMap.get(this.key);return mdcValue == null?this.defaultValue:mdcValue;}}public void start() {int errors = 0;if(OptionHelper.isEmpty(this.key)) {++errors;this.addError("The \"Key\" property must be set");}if(OptionHelper.isEmpty(this.defaultValue)) {++errors;this.addError("The \"DefaultValue\" property must be set");}if(errors == 0) {this.started = true;}}public String getKey() {return this.key;}public void setKey(String key) {this.key = key;}public String getDefaultValue() {return this.defaultValue;}public void setDefaultValue(String defaultValue) {this.defaultValue = defaultValue;}
}

如果想要进程级别区分的 logger

MDCBasedDiscriminator 是基于 MDC 的,如果要每个线程打出的 log 都到同一个文件夹,则需要每个线程启动时都创建一次 logger。如果整个进程都希望打 log 到同一个文件夹,则需要如下改动:

  • 创建自己的 TaskBasedDiscriminator。其实就是基于 MDCBasedDiscriminator 改动的。
  • 修改 logback.xml 中的 MDCBasedDiscriminator 为 TaskBasedDiscriminator
public class TaskBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> {private String key;private String defaultValue;private static Map<String, String> values = new HashMap<>();public TaskBasedDiscriminator() {}@Overridepublic String getDiscriminatingValue(ILoggingEvent event) {return values.containsKey(key) ? values.get(key) : defaultValue;}@Overridepublic void start() {int errors = 0;if (OptionHelper.isEmpty(this.key)) {++errors;this.addError("The \"Key\" property must be set");}if (OptionHelper.isEmpty(this.defaultValue)) {++errors;this.addError("The \"DefaultValue\" property must be set");}if (errors == 0) {this.started = true;}}public static void put(String key, String value) {values.put(key, value);}@Overridepublic String getKey() {return this.key;}public void setKey(String key) {this.key = key;}public String getDefaultValue() {return this.defaultValue;}public void setDefaultValue(String defaultValue) {this.defaultValue = defaultValue;}
}

Log4j输出格式控制–log4j的PatternLayout参数含义

https://blog.csdn.net/guoquanyou/article/details/5689652

slf4j log4j logback关系详解和相关用法

https://www.cnblogs.com/sinte-beuve/p/5758971.html

Logback - SiftingAppender相关推荐

  1. logback prudent, SiftingAppender, layout, encoder的使用

    [size=small][b]1. 将prudent设置为true 是为了支持多个JVM往同一个日志文件写日志.[/b] 参考[url]http://logback.qos.ch/manual/app ...

  2. Springboot [日志管理LogBack]

    Spring Boot的日志详解 日志框架的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: logb ...

  3. logback的使用和logback.xml详解

    一.logback的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: logback-core:其它两 ...

  4. 日志组件logback的介绍及配置使用方法

    2019独角兽企业重金招聘Python工程师标准>>> 一.logback的介绍 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logb ...

  5. 014_logback中的SiftingAppender

    1. 如其名, SiftingAppender能按照给定的运行时属性, 对记录进行分离或筛选.例如, SiftingAppender能根据用户会话对记录事件进行分离, 这样, 每个用户生成的记录会进入 ...

  6. 003_commons-logging与slf4j和log4j与logback

    1. 日志接口常用的有commons-logging和slf4j, 日志实现常用的有log4j和logback.那么, 我们应该选取什么样的组合呢? 2. Logback是由log4j创始人设计的又一 ...

  7. logback的简单使用

    一.logback的介绍 Logback是由log4j创始人设计的又一个开源日志组件,官方网站: http://logback.qos.ch. logback当前分成三个模块:logback-core ...

  8. 从零开始玩转logback

    为什么80%的码农都做不了架构师?>>>    概述 LogBack是一个日志框架,它与Log4j可以说是同出一源,都出自Ceki Gülcü之手.(log4j的原型是早前由Ceki ...

  9. logback Appender详解

    为什么80%的码农都做不了架构师?>>>    一.简介 <appender>是<configuration>的子节点,是负责写日志的组件. <appe ...

最新文章

  1. 【SLAM建图和导航仿真实例】(三)- 使用RTAB-MAP进行SLAM建图和导航
  2. 转:strcat与strcpy与strcmp与strlen
  3. ESI最新计算机学科统计:中国78所跻身高被引100强,中南大学夺冠
  4. xilinx sg dma 注意事项
  5. 打破你的认知,数字除以 0 一定会崩溃吗?
  6. 详解JavaScript之神奇的Object.defineProperty
  7. 使用有限状态机(FSM)解释shell 命令
  8. 计算机88端口,计算机常用端口一览表
  9. 一个4体低位交叉的存储器_前交叉韧带术后关节粘连的康复策略
  10. Qt工作笔记-主界面传输数据到附属界面(通过信号与槽非构造函数)
  11. Must 和 have to_54
  12. c里面的fflush函数
  13. JavaScript 必会的知识点
  14. 活动: 北京Xamarin分享会第6期(2017年9月9日)
  15. 晶体管扩流电源电路设计
  16. JavaScript学习之面向对象编程-04
  17. mysql学习--sql语句
  18. 西北工业大学计算机学院教授张凯龙,张凯龙
  19. 清除FTP登录的用户名密码
  20. pbootcms如何设置网站xml地图

热门文章

  1. 安全狗技术分享|Web应用防火墙之攻击防护
  2. RecyclerView 多条目
  3. python 调用函数实现——斐波纳契数列
  4. java中的集合详解
  5. 雪花,是冬天凝固的泪
  6. Docker: 现在和未来
  7. pycharm+django创建一个搜索网页
  8. 用python解决数据结构与算法_python中各种数据结构与算法的解决技巧
  9. 51单片机模拟电梯控制(含完整代码)
  10. 【ESP32_8266_WiFi (一)】网络通信基础