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参数传递变量的方式。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hbbliyong

Spring Boot搭建Web项目常用功能

     首先要弄清楚为什么要包装统一结构结果数据,这是因为当任意的ajax请求超时或者越权操作时,系统能返回统一的错误信息给到前端,前端通过封装统一的ajax...

35120
来自专栏技术墨客

Spring核心——纯Java运行与@Bean

在3.0之前的Spring核心框架中,我们启动一个Spring容器必须使用一个XML文件。而到了3.X之后的版本Spring为创建容器新增了一个入口类——Ann...

8730
来自专栏逆向技术

COM_第四讲_保存GUID_优化使用代码

    优化以前的代码,让使用者更方便 一丶 优化思路 1.我们可以将我们写的GUID(类工厂的ID)保存到注册表中,并且保存一下DLL的文件路径,遍历注册表去...

23400
来自专栏美团技术团队

这个Spring高危漏洞,你修补了吗?

前言 2009年9月Spring 3.0 RC1发布后,Spring就引入了SpEL(Spring Expression Language)。对于开发者而言,引...

1.5K110
来自专栏杨建荣的学习笔记

一些“简单”的linux命令(r2笔记46天)

有些linux命令看起来极其简单,只包含2个字符,但确有很强的功能性。看起来还是有些陌生的命令,不过在工作中别忘记它们的存在。 ab 这条命令式做为性能测试所...

31680
来自专栏芋道源码1024

【追光者系列】HikariCP 源码分析之 allowPoolSuspension

摘要: 原创出处 https://mp.weixin.qq.com/s/-WGg22lUQU41c_8lx6kyQA 「渣渣王子」欢迎转载,保留摘要,

16600
来自专栏一个会写诗的程序员的博客

《Spring Boot极简教程》第9章 Spring Boot集成Scala混合Java开发参考资料

本章我们使用Spring Boot集成Scala混合Java开发一个Web性能测试平台。

21720
来自专栏Java 技术分享

SpringMVC(一)

12920
来自专栏微服务生态

Flume-NG源码分析-整体结构及配置载入分析

终于开始Flume源码的分析研究工作了,我也是边学边和大家分享,内容上难免有不足之处,望大家见谅。

15640
来自专栏黑泽君的专栏

day33_Spring学习回顾_01

1、导入jar包:4 + 1 --> beans/core/context/expression + commons-logging

8320

扫码关注云+社区

领取腾讯云代金券