【问题标题】:In java, is it possible to save a variable, local to a thread?在java中,是否可以保存一个线程本地的变量?
【发布时间】:2025-12-05 00:40:01
【问题描述】:

我正在尝试使用 log4j 从基于 servlet 的 java 应用程序中进行一些日志记录。我想记录额外的信息,如用户 ID 等。有没有办法将局部变量放在线程中?并且在记录时,是否可以获取当前线程,并获取设置的局部变量?

【问题讨论】:

  • 你可以通过你的 log4j 配置,然后我会告诉你如何添加线程名称

标签: java servlets


【解决方案1】:

与其直接使用线程本地存储,不如使用 Log4j 的 NDCMDC(嵌套和映射的诊断上下文)。它们已经是线程本地的,您可以使用%x%X 模式将它们的数据包含在日志消息中,因此无需显式地将数据从线程本地上下文中提取出来以将其包含在日志消息中。

【讨论】:

    【解决方案2】:

    Log4J MDC 方法

    这是使用 MDC 的方法

    public class MdcExample implements Runnable {
        private static final Logger logger  = Logger.getLogger(MdcExample.class);
    
        public static void main(final String[] args) {
            final MdcExample threadLocalExample = new MdcExample();
    
            final Thread thread1 = new Thread(threadLocalExample);
            final Thread thread2 = new Thread(threadLocalExample);
            final Thread thread3 = new Thread(threadLocalExample);
    
            thread1.start(); thread2.start(); thread3.start();
        }
    
        @Override
        public void run() {
    
            while (true) {
                MDC.put("random", "" + Math.random());
    
                try {
                    logger.info("My log message, prefixed with MDC");
                    TimeUnit.SECONDS.sleep(1);
                }
                catch (final InterruptedException e) {
                }
            }
        }
    }
    

    Log4J 设置:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    
        <!-- Appenders -->
        <appender name="console" class="org.apache.log4j.ConsoleAppender">
            <param name="Target" value="System.out" />
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%-5p %X{random}:- %m%n" />
            </layout>
        </appender> 
    
        <!-- Root Logger -->
        <root>
            <priority value="info" />
            <appender-ref ref="console" />
            <appender-ref ref="file" />
        </root>
    
    </log4j:configuration>
    

    线程本地方法(如果您的业务逻辑需要)

    如果您想将值(如用户)本地存储到线程,请使用ThreadLocal

    import java.util.concurrent.TimeUnit;
    
    public class ThreadLocalExample implements Runnable {
        ThreadLocal<Double> myRandom    = new ThreadLocal<>();
    
        public static void main(final String[] args) {
            final ThreadLocalExample threadLocalExample = new ThreadLocalExample();
    
            final Thread thread1 = new Thread(threadLocalExample);
            final Thread thread2 = new Thread(threadLocalExample);
            final Thread thread3 = new Thread(threadLocalExample);
    
            thread1.start(); thread2.start(); thread3.start();
        }
    
        @Override
        public void run() {
    
            this.myRandom.set(Math.random());
    
            while (true) {
                System.out.println(Thread.currentThread().getId() + " " + this.myRandom.get());
    
                try {
                    TimeUnit.SECONDS.sleep(1);
                }
                catch (final InterruptedException e) {
                }
            }
        }
    }
    

    http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html

    将线程 ID 添加到 log4j 输出

    关于您的 log4j 问题,您可以使用 t-Placeholder 作为线程名称:

    这是一个可以使用的示例 Appender:

    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%5p %d{ISO8601} [%t][%x] %c - %m%n" />
        </layout>
    </appender>
    

    http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html

    【讨论】:

    • @user187809 你需要smth吗?还有吗?
    【解决方案3】:

    ThreadLocal 是要走的路。 ThreadLocal 提供了保存变量的功能,这些变量不在线程之间共享,并且在线程的整个生命周期中都可用。

    ThreadLocal 就其本质而言,此功能可帮助您摆脱同步块。

    【讨论】:

      【解决方案4】:

      您应该为此使用MDC。您可以存储命名属性,然后直接以日志格式引用它们。示例见What is the difference between Log4j's NDC and MDC facilities?

      【讨论】:

      • 这看起来很整洁,因为我不必在我的日志记录函数中附加任何字符串!我正在寻找使用 MDC 的完整示例,您知道任何教程/示例吗?谢谢。
      • Mapped Diagnostic Context 在 slf4j 的上下文中解释它,并举例说明。
      最近更新 更多