专栏首页IT杂记Slf4j+Logback配置文件变量使用小记

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 条评论
登录 后参与评论

相关文章

  • Mapreduce程序中reduce的Iterable参数迭代出是同一个对象

    今天在对reduce的参数Iterable进行迭代时,发现一个问题,即Iterator的next()方法每次返回的是同一个对象,next()只是修改了Writa...

    囚兔
  • zip文件操作导致JVM crash

    1. 概况     程序运行操作系统: CentOS6.5 64bit     JDK版本:7 2. 测试 2.1 准备测试程序 测试程序很简单,就一个类一个m...

    囚兔
  • TThreadedSelectorServer介绍及Direct Memory OOM分析

    一、TThreadedSelectorServer线程模型: 该服务会启动1个AcceptThread, 2个SelectorThread(默认为2个,可设置)...

    囚兔
  • 白话AI

    过去我们用通过编写设定程序来直接让计算机完成某些特定任务,现在,我们还可以训练计算机,就像我们训练宠物一样。这就是用大白话来解释机器学习。当然了,对于一些相对简...

    后端技术探索
  • 漫画版:什么是机器学习?

    这段机器学习基础视频[2]将帮助您了解什么是机器学习,机器学习有哪些类型-有监督,无监督和强化学习,如何通过简单的示例学习机器学习以及如何在各个行业中使用机器学...

    用户4131414
  • 【一分钟论文】轻松解读Semi-supervised Sequence Learning半监督序列学习

    一个月前和实验室的伙伴们打了一个跨领域半监督依存句法分析的比赛,比赛成绩出乎意料,在封闭测试下是第一名。这也是我第一次接触半监督学习。最近师兄在写这个评测论文,...

    zenRRan
  • 机器学习分类

    机器学习通常分为四类 监督学习 无监督学习 半监督学习 强化学习 监督学习 监督学习是从标记的训练数据来推断一个功能的机器学习任务。在监督学习中,每个实例都是由...

    听城
  • 人工智能时代,我们还会有工作吗?

    19世纪80年代,两匹马在聊天。甲马说:“听说人类发明了内燃机。以前我们干的活儿现在都要用机器来干了”。乙马说:“别担心,就像轮子和犁被发明时一样,我们不会被替...

    企鹅号小编
  • 搞机器学习/AI有什么必备的数学基础?| 经验之谈+资源大全

    雷刚 发自 凹非寺 量子位 报道 | 公众号 QbitAI 今天是开学第一天!心里只有学习的量子位,发现Hacker News上又有高分话题,而且还跟学习有关...

    量子位
  • 深度学习要走向更深?Gary Marcus称DL与经典AI结合才是出路

    自1950年代以来,AI的发展一直承载了过多的未能实现的期待。尽管近年来由于深度学习的突破而实现了质的飞跃,当今的AI领域仍然有诸多问题亟待突破:面对攻击时的脆...

    新智元

扫码关注云+社区

领取腾讯云代金券