Slf4j+Logback配置文件变量使用小记
项目中须要根据不同的模块,产生出不同的日志文件名,使用的是同一logback.xml配置文件,这里简单调研,说明两种实现方式,以及两种实现方式的区别。
测试准备
建立一个maven项目,并添加slf4j-api, logback-core, logback-classic 依赖。
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
方式一:变量替换
详细信息参考:https://logback.qos.ch/manual/configuration.html#variableSubstitution
测试一(单线程)
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE"
class="ch.qos.logback.core.FileAppender">
<File>test-logger-${test-variable}.log</File>
<Append>true</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n
</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
测试代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggerJVMParamTest {
private static final Logger LOG = LoggerFactory.getLogger(LoggerJVMParamTest.class);
public void printLog() {
LOG.info("This is test log.");
}
public static void main(String[] args) {
LoggerJVMParamTest test = new LoggerJVMParamTest();
test.printLog();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行程序时加入jvm参数: -Dtest-variable="PROG"
执行结果及说明
生成了test-logger-PROG.log文件
2017-08-02 17:06:29,794 INFO [main] c.s.t.l.LoggerJVMParamTest [LoggerJVMParamTest.java:16] This is test log.
logback 中可以通过jvm参数传递变量来指定文件名, 当然变量的传递方式还有多种方式,这里不展开,可以参考https://logback.qos.ch/manual/configuration.html#variableSubstitution
测试二(多线程)
再看看在多线程环境中是否也都有效,logback.xml同测试一
测试代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class LoggerJVMParamMultiThreadsTest {
private static final Logger LOG = LoggerFactory.getLogger(LoggerJVMParamMultiThreadsTest.class);
public void printLog() {
LOG.info("This is test log.");
}
public static void main(String[] args) {
LoggerJVMParamMultiThreadsTest test = new LoggerJVMParamMultiThreadsTest();
Thread subThd1 = new Thread(new Runnable() {
public void run() {
LOG.info("This is subThd1 log");
}
});
subThd1.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.printLog();
Thread subThd2 = new Thread(new Runnable() {
public void run() {
LOG.info("This is subThd2 log");
}
});
subThd2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行程序时加入jvm参数: -Dtest-variable="PROG"
执行结果及说明
生成了test-logger-PROG.log文件
2017-08-02 17:26:01,786 INFO [Thread-0] c.s.t.l.LoggerJVMParamMultiThreadsTest [LoggerJVMParamMultiThreadsTest.java:27] This is subThd1 log
2017-08-02 17:26:06,791 INFO [main] c.s.t.l.LoggerJVMParamMultiThreadsTest [LoggerJVMParamMultiThreadsTest.java:17] This is test log.
2017-08-02 17:26:06,791 INFO [Thread-1] c.s.t.l.LoggerJVMParamMultiThreadsTest [LoggerJVMParamMultiThreadsTest.java:42] This is subThd2 log
方式二:MDC
详细信息参考:https://logback.qos.ch/manual/mdc.html
测试1(单线程)
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE_MDC" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<Key>test-variable</Key>
<DefaultValue>default</DefaultValue>
</discriminator>
<sift>
<appender name="FILE"
class="ch.qos.logback.core.FileAppender">
<File>test-logger-${test-variable}.log</File>
<Append>true</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</Pattern>
</layout>
</appender>
</sift>
</appender>
<root level="debug">
<appender-ref ref="FILE_MDC" />
</root>
</configuration>
测试代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class LoggerConfVariablesTest {
private static final Logger LOG = LoggerFactory.getLogger(LoggerConfVariablesTest.class);
public void printLog() {
LOG.info("This is test log.");
}
public static void main(String[] args) {
MDC.put("test-variable", "PROG");
LoggerConfVariablesTest test = new LoggerConfVariablesTest();
test.printLog();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果及说明
执行代码,生成日志文件:test-logger-PROG.log
内容:
2017-08-02 13:44:20,779 INFO [main] c.s.t.l.LoggerConfVariablesTest [LoggerConfVariablesTest.java:17] This is test log.
先看下ch.qos.logback.classic.sift.SiftingAppender类的说明:
This appender can contains other appenders which it can build dynamically depending on MDC values. The built appender is specified as part of a configuration file.
就是说SiftingAppender类可以根据MDC的值动态的构建其他的appender,由discriminator来指定MDC的Key和默认值。
根据https://logback.qos.ch/manual/mdc.html中的说明:
MDC operations such as put() and get() affect only the MDC of the current thread, and the children of the current thread. The MDC in other threads remain unaffected.
MDC put(), get()会影响当前线程和子线程的MDC值,但是不会响应其他线程。再考虑上面的测试,那么其他线程应该是拿不到MDC test-variable的值的,打印的日志信息应该是不会输出到test-logger-PROG.log日志文件中去的,那它会输出到哪里呢? 接下来继续测试。
测试二(多线程)
logback.xml配置同测试一
测试代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class LoggerConfVariablesTest {
private static final Logger LOG = LoggerFactory.getLogger(LoggerConfVariablesTest.class);
public void printLog() {
LOG.info("This is test log.");
}
public static void main(String[] args) {
LoggerConfVariablesTest test = new LoggerConfVariablesTest();
Thread subThd1 = new Thread(new Runnable() {
public void run() {
MDC.put("test-variable", "PROG");
LOG.info("This is subThd1 log");
}
});
subThd1.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.printLog();
Thread subThd2 = new Thread(new Runnable() {
public void run() {
LOG.info("This is subThd2 log");
}
});
subThd2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果及说明
生成了两个日志文件
test-logger-PROG.log
2017-08-02 16:45:22,798 INFO [Thread-0] c.s.t.l.LoggerConfVariablesTest [LoggerConfVariablesTest.java:30] This is subThd1 log
test-logger-default.log
2017-08-02 16:45:27,790 INFO [main] c.s.t.l.LoggerConfVariablesTest [LoggerConfVariablesTest.java:17] This is test log.
2017-08-02 16:45:27,790 INFO [Thread-1] c.s.t.l.LoggerConfVariablesTest [LoggerConfVariablesTest.java:45] This is subThd2 log
可以知道
MDC.put("test-variable", "PROG"); 仅在subThd1中生效,其的父线程和兄弟线程取不到PROG这个值所以使用了默认值“default”。
总结
根据测试通过JVM变量方式来设置日志文件名,这个方式适用于单线程和多线程环境,但是MDC不适用多线程环境,特别是当MDC.put()方法的调用是非主线程的情况,比如javaEE项目就不适合这么做。根据对MDC的了解,MDC主要适用于须要在同一个进程中对不同的线程输出不同的日志格式,或输出到不同的文件这种场景,比如web项目,针对不同的用户请求,在日志中加入用户的信息,或者是不同的任务日志输出到不同的文件。
而对于须要将不同模块进程的日志输出到不同的文件,共用logback.xml, 则可以使用jvm参数传递变量的方式。
- 快速入门系列--深入理解C#
- “家庭贷款”域名Home.loans已经建站为家庭贷款相关的网站
- 微信亿级用户异常检测框架的设计与实践
- 程序员必知的6点编程秘诀,编程三板斧将解决90%问题!
- JavaScript之面向对象学习三原型语法升级
- 腾讯入局物业管理,欲改造传统服务?
- JavaScript之面向对象学习五(JS原生引用类型Array、Object、String等等)的原型对象介绍
- SQL学习之分组数据Group by
- SQL学习之数据列去空格函数
- 采用DIV+CSS布局对SEO优化有何好处?
- Sublime快速入门
- SQL学习之汇总数据之聚集函数
- Sedo榜单中,域名“加密世界”CryptoWorld.com七位数夺冠
- ExtJs学习笔记(20)-利用ExtJs的Ajax与服务端WCF交互
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- String equals()方法 源码分析
- centos7宝塔环境安装VeryNginx waf系统实现高级防火墙功能
- 使用curl探测访问响应时长
- dnspod通过接口解析本地ip python脚本
- 如何修复WordPress死亡白屏(WSoD)故障问题
- linux: 添加一个IP地址
- Nginx简易防CC策略规则
- 如何编写和优化WordPress网站的Robots.txt
- 如何处理WordPress网站404状态死链
- (三)RecyclerView简单滑动场景分析
- AWS EC2实例开启IPv6方法 阅读模式
- centos7宝塔环境安装VeryNginx waf系统实现高级防火墙功能
- 腾讯云中关于授权子用户QCloudResourceFullAccess权限后使用api接口创建购买cvm没有支付权限的解决办法
- centos7中systemctl系统服务和资源限制故障
- centos7 安装zabbix并监控windows服务器