Logback中%X的使用

本文详细介绍了在Logback中如何使用%X来输出与当前线程相关的NDC信息,通过实例展示了如何在代码中给MDC添加key/value以增加新值,并解释了%X的不同用法及与%replace的配合使用,帮助读者深入理解日志输出的高级配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 参考资料

2. 环境

Java: jdk1.8.0_144
Logback: 1.0.13

3. %X 的使用方法

%X用于输出和当前线程相关联的NDC(嵌套诊断环境),在代码中给org.slf4j.MDC添加key/value即可增加新值

示例说明
%X输出所有值
%X{testKey}输出testKey所对应的value,且无默认值
%X{testKey:-}输出testKey所对应的value,默认为空
%X{testKey:-aaa}输出testKey所对应的value,默认为aaa

测试代码

public class AbstractLogWrapper<T> {
    private final T job;
    private final Map<?, ?> context;

    public AbstractLogWrapper(T t) {
        this.job = t;
        this.context = MDC.getCopyOfContextMap();
    }

    public void setLogContext() {
        if (this.context != null) {
            MDC.setContextMap(this.context);
        }
    }

    public void clearLogContext() {
        MDC.clear();
    }

    public T getJob() {
        return this.job;
    }
}

public class LogSupplier<T> extends AbstractLogWrapper<Supplier<T>> implements Supplier<T> {
    public LogSupplier(Supplier<T> supplier) {
        super(supplier);
    }

    @Override
    public T get() {
        this.setLogContext();
        try {
            return getJob().get();
        } finally {
            this.clearLogContext();
        }
    }
}

@RunWith(MockitoJUnitRunner.class)
public class LogSupplierTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(LogSupplierTest.class);
    private ExecutorService executorService;

    @Before
    public void setUp() {
        executorService = Executors.newFixedThreadPool(2);
    }

    @Test
    public void testGet() {
        AtomicInteger counter = new AtomicInteger(0);
        Supplier<String> supplier = () -> {
            String rtn = String.valueOf(counter.incrementAndGet());
            // 往MDC中添加内容
            MDC.put(RunnabeTestHelper.RUNNABLE, rtn);
            LOGGER.info("This is {} supplier.", rtn);
            return rtn;
        };

        LogSupplier<String> logSupplier = Mockito.spy(new LogSupplier<>(supplier));
        Set<String> set = new HashSet<>();
        Mockito.doAnswer(invocation -> set.add(invocation.getMethod().getName())).when(logSupplier).setLogContext();
        Mockito.doAnswer(invocation -> set.add(invocation.getMethod().getName())).when(logSupplier).clearLogContext();

        List<CompletableFuture<String>> futures = IntStream.rangeClosed(0, 4).mapToObj(index -> CompletableFuture.supplyAsync(logSupplier, executorService)).collect(Collectors.toList());
        futures.forEach(CompletableFuture::join);
        Assert.assertEquals("[setLogContext, clearLogContext]", set.toString());
    }

}

class RunnabeTestHelper {
    static final String RUNNABLE = "runn_able";
}
  • 输出所有
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="stdot" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%X %m%n</pattern>
        </layout>
    </appender>
    <root level="debug">
        <appender-ref ref="stdot"/>
    </root>
</configuration>

结果

runn_able=1 This is 1 supplier.
runn_able=3 This is 3 supplier.
runn_able=2 This is 2 supplier.
runn_able=4 This is 4 supplier.
runn_able=5 This is 5 supplier.
  • 输出特定值(不指定默认值/默认空/默认非空)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="stdot" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%X{runn_able} %X{runn_able:-} %X{runnable:-aaa} %m%n</pattern>
        </layout>
    </appender>
    <root level="debug">
        <appender-ref ref="stdot"/>
    </root>
</configuration>

结果

1 1 aaa This is 1 supplier.
2 2 aaa This is 2 supplier.
3 3 aaa This is 3 supplier.
5 5 aaa This is 5 supplier.
4 4 aaa This is 4 supplier.

4. 与%replace的配合使用

示例说明
%replace(p ){r, t}p 为日志内容,r 是正则表达式,将p 中符合r 的内容替换为t
  • 把只有key没有value内容从日志中替换掉
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="stdot" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%replace(Test_Method=%X{method} runn-able=%X{runn_able}){'\S+=( |$)', ''} -> %m%n</pattern>
        </layout>
    </appender>
    <root level="debug">
        <appender-ref ref="stdot"/>
    </root>
</configuration>

结果

runn-able=2 -> This is 2 supplier.
runn-able=3 -> This is 3 supplier.
runn-able=4 -> This is 4 supplier.
runn-able=5 -> This is 5 supplier.
runn-able=1 -> This is 1 supplier.

转载于:https://www.cnblogs.com/hiver/p/9887116.html

<think>首先,用户的问题是关于如何在logback使用MarkerFilter的。用户提供了系统级指令,我必须遵守这些指令: 1. 所有行内数学表达式必须使用$...$格式。例如,$x^2$。 2. 独立公式必须使用$$...$$格式并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如:[^1]。 此外,在用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户还提供了三个引用,这些是关于logback的MarkerFilter的。引用[1]描述了MarkerFilter的基本用法,引用[2]给出了配置示例,引用[3]讨论了过滤器的一般结构。我应该参考这些来回答问题。 用户的问题是:“我想了解如何在logback使用MarkerFilter 请问logback MarkerFilter 使用方法及示例” 所以,我的回答需要包括: 1. 解释MarkerFilter是什么。 2. 提供使用方法和示例。 3. 结构清晰,逐步引导。 4. 最后生成相关问题。 步骤: 1. **介绍MarkerFilter**:基于引用[1],MarkerFilter用于支持slf4j的Marker,通过MarkerFactory.getMarker获取marker,并在配置文件中配置。 2. **使用方法**:从引用[2]中,我可以提取配置示例。例如,定义过滤器,设置onMatch和onMismatch行为。 3. **示例**:提供一个logback.xml配置文件的示例,展示如何添加MarkerFilter。 4. **代码示例**:如果需要,可以展示Java代码中如何使用marker。 5. **引用**:在回答中自然地添加引用标识。例如,当提到配置时,添加[^2]。 6. **生成相关问题**:在最后,添加“§§相关问题§§”部分,列出相关问题。 确保回答用中文。 结构大纲: - **开头**:简要介绍MarkerFilter。 - **步骤1**:解释MarkerFilter的作用。 - **步骤2**:描述配置方法。 - **步骤3**:提供配置文件示例。 - **步骤4**:可能添加Java代码示例。 - **结尾**:总结或注意事项。 - **相关问题**:生成2-3个相关问题。 引用处理: - 引用[1]:聊聊logback的MarkerFilter,提到基本用法。 - 引用[2]:解锁新技能,提供配置示例。 - 引用[3]:常规过滤器继承Filter类,但用户问题具体是MarkerFilter,所以可能不直接相关,但可以提及。 在回答中,当引用信息时,添加[^1], [^2]等。 例如:“MarkerFilter可以通过MarkerFactory.getMarker获取marker[^1]。” 最后,生成相关问题,如: 1. MarkerFilter与其他过滤器有什么区别? 2. 如何动态添加MarkerFilter? 3. MarkerFilter在日志管理中的实际应用场景? 确保没有数学表达式,因为用户问题是关于logback的,不涉及数学。但指令说所有行内数学表达式必须用$...$,但在这个上下文中可能不需要。所以,我可以忽略数学部分,除非必要。 完整回答: - 用中文。 - 结构:先解释,然后方法,示例,相关问题。</think>### logback MarkerFilter 使用方法及示例 MarkerFilter 是 logback 提供的一个过滤器,用于基于 SLF4J 的 Marker 接口来过滤日志事件。Marker 允许您为日志添加自定义标记(例如,用于区分业务日志或错误类型),MarkerFilter 则通过配置文件定义对这些标记的处理规则(如接受或拒绝特定标记的日志)。这在日志分类和精细化控制中非常有用。下面我将逐步解释使用方法,并提供详细示例。参考引用信息,我将确保回答可靠,并基于 logback 官方文档和社区实践[^1][^2]。 #### 步骤 1: 理解 MarkerFilter 的基本概念 - **Marker 是什么?** Marker 是 SLF4J 提供的一个接口,用于为日志添加额外标记(例如,标识敏感操作或特定模块)。您可以通过 `MarkerFactory.getMarker("markerName")` 创建 Marker[^1]。 - **MarkerFilter 的作用** MarkerFilter 继承自 logback 的 `TurboFilter`,它根据日志事件的 Marker 值进行过滤。在配置文件中,您可以设置: - `onMatch`:当 Marker 匹配时如何处理(例如,`ACCEPT` 接受日志,`DENY` 拒绝日志)。 - `onMismatch`:当 Marker 不匹配时如何处理(例如,`NEUTRAL` 表示不干预,交由其他过滤器决定)[^1][^2]。 这允许您实现精细的日志路由,例如只记录带有特定标记的日志到文件。 #### 步骤 2: 配置 MarkerFilter 的方法 在 logback 的配置文件(通常是 `logback.xml`)中添加 MarkerFilter。以下是关键步骤: 1. **定义 MarkerFilter**:在 `<configuration>` 标签内,使用 `<turboFilter>` 元素定义 MarkerFilter。 2. **设置参数**: - `class`:指定过滤器类为 `ch.qos.logback.classic.turbo.MarkerFilter`。 - `marker`:指定要匹配的 Marker 名称(例如,`"SECURITY"`)。 - `onMatch` 和 `onMismatch`:设置过滤行为,值可以是 `ACCEPT`, `DENY`, 或 `NEUTRAL`。 3. **添加到上下文**:MarkerFilter 会自动注册到 logback 的 LoggerContext 中,无需额外代码[^2]。 注意:MarkerFilter 是 TurboFilter 的一种,与常规过滤器(如 `LevelFilter`)不同,它作用于日志事件早期阶段,效率更高[^3]。 #### 步骤 3: 完整示例 下面是一个实际示例,展示如何在 `logback.xml` 中配置 MarkerFilter,并结合 Java 代码使用。假设我们有两个 Marker:`"AUDIT"`(用于审计日志)和 `"DEBUG"`(用于调试日志),我们只希望记录 `"AUDIT"` 标记的日志到文件,拒绝 `"DEBUG"` 标记的日志。 ##### 配置文件示例 (`logback.xml`) ```xml <configuration> <!-- 定义 MarkerFilter:接受 "AUDIT" 标记的日志 --> <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> <marker>AUDIT</marker> <onMatch>ACCEPT</onMatch> <!-- 匹配时接受日志 --> <onMismatch>NEUTRAL</onMismatch> <!-- 不匹配时不干预 --> </turboFilter> <!-- 定义另一个 MarkerFilter:拒绝 "DEBUG" 标记的日志 --> <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> <marker>DEBUG</marker> <onMatch>DENY</onMatch> <!-- 匹配时拒绝日志 --> <onMismatch>NEUTRAL</onMismatch> </turboFilter> <!-- 配置 Appender(例如,文件输出) --> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>application.log</file> <encoder> <pattern>%date %level [%marker] - %msg%n</pattern> </encoder> </appender> <!-- 设置根 Logger,使用上述 Appender --> <root level="INFO"> <appender-ref ref="FILE" /> </root> </configuration> ``` **解释**: - 第一个 MarkerFilter 接受带有 `"AUDIT"` 标记的日志(`onMatch: ACCEPT`),其他日志由后续过滤器处理。 - 第二个 MarkerFilter 拒绝带有 `"DEBUG"` 标记的日志(`onMatch: DENY`),防止它们被记录。 - 在日志输出中,`%marker` 在 pattern 中显示 Marker 名称,便于调试[^2]。 ##### Java 代码示例 在应用程序中,使用 SLF4J 的 Logger 和 MarkerFactory 添加标记: ```java import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; public class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class); // 创建 Marker private static final Marker AUDIT_MARKER = MarkerFactory.getMarker("AUDIT"); private static final Marker DEBUG_MARKER = MarkerFactory.getMarker("DEBUG"); public static void main(String[] args) { // 记录带有 AUDIT 标记的日志(会被接受并记录到文件) logger.info(AUDIT_MARKER, "这是一条审计日志。"); // 记录带有 DEBUG 标记的日志(会被拒绝,不记录) logger.debug(DEBUG_MARKER, "这是一条调试日志,不会出现在文件中。"); // 记录无标记的日志(根据 onMismatch 设置,可能被记录) logger.info("普通日志,可能被记录。"); } } ``` **运行结果**: - 文件 `application.log` 中只包含 `AUDIT` 标记的日志和普通日志(如果 `onMismatch` 允许)。 - `DEBUG` 标记的日志被过滤掉,不会输出[^1][^2]。 #### 注意事项 - **性能影响**:MarkerFilter 是高效的,但过多过滤器可能轻微增加日志处理时间。建议在需要精细控制时使用。 - **错误处理**:如果 Marker 名称在配置文件中拼写错误,logback 会忽略该过滤器(不会抛出异常),需检查日志输出。 - **与其他过滤器结合**:MarkerFilter 可以与 `LevelFilter` 等组合使用,实现更复杂的规则(例如,只记录 ERROR 级别且带有特定标记的日志)[^3]。 通过以上步骤,您可以灵活地在 logback使用 MarkerFilter 来管理日志。如果您在测试中遇到问题,建议查看 logback 官方文档或调试日志上下文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值