使用 java.util.logging 打印线程名称

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6889057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 17:46:09  来源:igfitidea点击:

Printing thread name using java.util.logging

javalogging

提问by Majid Alfifi

Is it possible to print the thread name in the log statements generated by java.util.logging.Logger?

是否可以在生成的日志语句中打印线程名称java.util.logging.Logger

One alternative is to do something like the following:

一种替代方法是执行以下操作:

logger.info(thread.getName() + " some useful info");

but it's repetitive and the logging framework should handle it.

但它是重复的,日志框架应该处理它。

采纳答案by Tomasz Nurkiewicz

Embarrassingly, but looks like java.util.loggingcan't do this...

尴尬,但看起来java.util.logging不能这样做......

The default java.util.logging.SimpleFormatterdoesn't have the ability to log thread name at all. The java.util.logging.FileHandlersupports few template placeholders, none of them is thread name.

默认情况下java.util.logging.SimpleFormatter根本无法记录线程名称。该java.util.logging.FileHandler支持几个模板占位符,它们都不是线程名。

java.util.logging.XMLFormatteris the closest one, but only logs thread id:

java.util.logging.XMLFormatter是最接近的,但只记录线程 ID:

<record>
  <date>2011-07-31T13:15:32</date>
  <millis>1312110932680</millis>
  <sequence>0</sequence>
  <logger></logger>
  <level>INFO</level>
  <class>java.util.logging.LogManager$RootLogger</class>
  <method>log</method>
  <thread>10</thread>
  <message>Test</message>
</record>

If you think we're getting close - we're not. LogRecordclass only holds the thread ID, not its name - not very useful.

如果你认为我们正在接近 - 我们不是。LogRecord类只保存线程 ID,而不是它的名称 - 不是很有用。

回答by l245c4l

I had similar problem. As answered here How to align log messages using java.util.loggingyou can extend java.util.logging.Formatterbut instead getting LogRecord#getThreadID()you can get thread name by invoking Thread.currentThread().getName()like this:

我有类似的问题。如此处回答如何使用 java.util.logging 对齐日志消息,您可以扩展,java.util.logging.FormatterLogRecord#getThreadID()可以通过调用Thread.currentThread().getName()这样的方式获取线程名称:

public class MyLogFormatter extends Formatter
{

    private static final MessageFormat messageFormat = new MessageFormat("[{3,date,hh:mm:ss} {2} {0} {5}]{4} \n");

    public MyLogFormatter()
    {
        super();
    }

    @Override
    public String format(LogRecord record)
    {
        Object[] arguments = new Object[6];
        arguments[0] = record.getLoggerName();
        arguments[1] = record.getLevel();
        arguments[2] = Thread.currentThread().getName();
        arguments[3] = new Date(record.getMillis());
        arguments[4] = record.getMessage();
        arguments[5] = record.getSourceMethodName();
        return messageFormat.format(arguments);
    }

}

回答by Matthias Braun

With a custom Formatter

有自定义 Formatter

Luckily, LogRecordcontains the ID of the thread that produced the log message. We can get hold of this LogRecordwhen writing a custom Formatter. Once we have that, we only need to get the thread name via its ID.

幸运的是,LogRecord包含生成日志消息的线程的 ID。我们可以LogRecord在编写自定义Formatter. 一旦我们有了它,我们只需要通过它的 ID 获取线程名称。

There are a couple of waysto get the Threadobject corresponding to that ID, here's mine:

几种方法可以获取Thread与该 ID 对应的对象,这是我的:

static Optional<Thread> getThread(long threadId) {
    return Thread.getAllStackTraces().keySet().stream()
            .filter(t -> t.getId() == threadId)
            .findFirst();
}

The following is a minimal Formatterthat only prints the thread name and the log message:

以下是Formatter仅打印线程名称和日志消息的最小内容:

private static Formatter getMinimalFormatter() {
    return new Formatter() {

        @Override
        public String format(LogRecord record) {

            int threadId = record.getThreadID();
            String threadName = getThread(threadId)
                    .map(Thread::getName)
                    .orElseGet(() -> "Thread with ID " + threadId);

            return threadName + ": " + record.getMessage() + "\n";
        }
    };
}

To use your custom formatter, there are again different options, one way is to modify the default ConsoleHandler:

要使用自定义格式化程序,还有不同的选项,一种方法是修改默认值ConsoleHandler

public static void main(final String... args) {

    getDefaultConsoleHandler().ifPresentOrElse(
            consoleHandler -> consoleHandler.setFormatter(getMinimalFormatter()),
            () -> System.err.println("Could not get default ConsoleHandler"));

    Logger log = Logger.getLogger(MyClass.class.getName());
    log.info("Hello from the main thread");
    SwingUtilities.invokeLater(() -> log.info("Hello from the event dispatch thread"));
}

static Optional<Handler> getDefaultConsoleHandler() {
    // All the loggers inherit configuration from the root logger. See:
    // https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html#a1.3
    var rootLogger = Logger.getLogger("")
    // The root logger's first handler is the default ConsoleHandler
    return first(Arrays.asList(rootLogger.getHandlers()));
}

static <T> Optional<T> first(List<T> list) {
    return list.isEmpty() ?
            Optional.empty() :
            Optional.ofNullable(list.get(0));
}

Your minimal Formattershould then produce the folowing log messages containing the thread name:

Formatter然后你的minimal应该产生包含线程名称的以下日志消息:

main: Hello from the main thread

main:来自主线程的你好

and

AWT-EventQueue-0: Hello from the event dispatch thread

AWT-EventQueue-0:来自事件调度线程的Hello



This is a Formatterthat shows how to log more than thread name and log message:

这是一个Formatter显示如何记录线程名称和日志消息的更多信息:

private static Formatter getCustomFormatter() {
    return new Formatter() {

        @Override
        public String format(LogRecord record) {

            var dateTime = ZonedDateTime.ofInstant(record.getInstant(), ZoneId.systemDefault());

            int threadId = record.getThreadID();
            String threadName = getThread(threadId)
                    .map(Thread::getName)
                    .orElse("Thread with ID " + threadId);

            // See also: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Formatter.html
            var formatString = "%1$tF %1$tT %2$-7s [%3$s] %4$s.%5$s: %6$s %n%7$s";

            return String.format(
                    formatString,
                    dateTime,
                    record.getLevel().getName(),
                    threadName,
                    record.getSourceClassName(),
                    record.getSourceMethodName(),
                    record.getMessage(),
                    stackTraceToString(record)
            );
        }
    };
}

private static String stackTraceToString(LogRecord record) {
    final String throwableAsString;
    if (record.getThrown() != null) {
        var stringWriter = new StringWriter();
        var printWriter = new PrintWriter(stringWriter);
        printWriter.println();
        record.getThrown().printStackTrace(printWriter);
        printWriter.close();
        throwableAsString = stringWriter.toString();
    } else {
        throwableAsString = "";
    }
    return throwableAsString;
}

That Formatterproduces log messages like these:

Formatter会产生这样的日志消息:

2019-04-27 13:21:01 INFO [AWT-EventQueue-0] package.ClassName.method: The log message

2019-04-27 13:21:01 INFO [AWT-EventQueue-0] package.ClassName.method:日志消息

回答by irreputable

java.util.logging has many curious peculiarities. you can add a facade API to tweak its behaviors

java.util.logging 有许多奇怪的特性。你可以添加一个外观 API 来调整它的行为

public class Log

    Logger logger;

    static public Log of(Class clazz)
        return new Log( Logger.getLogger( clazz.getName() ));

    public void error(Throwable thrown, String msg, Object... params)
    {
        log(ERROR, thrown, msg, params);
    }

    void log(Level level, Throwable thrown, String msg, Object... params)
    {
        if( !logger.isLoggable(level) ) return;

        // bolt on thread name somewhere
        LogRecord record = new LogRecord(...);
        record.setXxx(...);
        ...
        logger.log(record);
    }

----

static final Log log = Log.of(Foo.class);
....
log.error(...);

People use java's logging mostly because they don't want to have 3rd party dependencies. That's also why they can't depend on existing logging facades like apache's or slf4j.

人们使用 Java 的日志记录主要是因为他们不想拥有 3rd 方依赖项。这也是为什么他们不能依赖现有的日志记录外观,如 apache 或 slf4j。

回答by home

Some application servers implicitly log the thread ID (I know of WebSphere). You can create your own LogFormatter. The records passed to the formatter contain the Thread ID, see here. I implemented that approach for Tomcat several times, but it'll work in Java SE environments as well.

一些应用程序服务器隐式地记录线程 ID(我知道 WebSphere)。您可以创建自己的LogFormatter。传递给格式化程序的记录包含线程 ID,请参见此处。我多次为 Tomcat 实现了这种方法,但它也适用于 Java SE 环境。

BTW: The Thread name is not available to LogRecord.

顺便说一句:线程名称对 LogRecord 不可用。

回答by Johann Tienhaara

A couple of the answers above suggest that LogRecord.getThreadId() returns a meaningful thread ID, and all we're missing is a way to correlate that to the thread's name.

上面的几个答案表明 LogRecord.getThreadId() 返回一个有意义的线程 ID,而我们所缺少的只是一种将其与线程名称相关联的方法。

Unfortunately LogRecord.getThreadId() returns an int value which does not correspond to the long id of the thread which induced the log message.

不幸的是 LogRecord.getThreadId() 返回一个 int 值,该值与引发日志消息的线程的 long id 不对应。

So we cannot just use ManagementFactory.getThreadMXBean() to resolve the thread name. It results in random thread names.

所以我们不能只使用 ManagementFactory.getThreadMXBean() 来解析线程名称。它导致随机线程名称。

If you are certain that your Logging facility always formats in the same thread as the caller, then you can create a custom Formatter as proposed above, and call Thread.currentThread().getName().

如果您确定您的日志记录工具总是在与调用者相同的线程中进行格式化,那么您可以按照上面的建议创建一个自定义的格式化程序,并调用 Thread.currentThread().getName()。

It seems that a Logging facade or a third party library are the only completely safe options.

似乎 Logging 门面或第三方库是唯一完全安全的选择。

回答by Pedro Reis

To complement @l245c4l answer: Instead of using SimpleFormatter() use:

补充@l245c4l 答案:而不是使用 SimpleFormatter() 使用:

//fileHandler.setFormatter(new SimpleFormatter());

class MyFormatter extends Formatter {
    private final MessageFormat messageFormat = new MessageFormat("{0,date}, {0,time} {1} {2}: {3} [T:{4}] {5}\n");

    public String format(LogRecord record)
    {
        Object[] arguments = new Object[6];
        arguments[0] = new Date( record.getMillis() );
        arguments[1] = record.getSourceClassName();
        arguments[2] = record.getSourceMethodName();
        arguments[3] = record.getLevel();
        arguments[4] = Long.toString( Thread.currentThread().getId() );
        arguments[5] = record.getMessage();

        return messageFormat.format(arguments);
    }
}

fileHandler.setFormatter( new MyFormatter() ); 

Logger myLogger = Logger.getLogger("<LOGGER_NAME>");
myLogger.addHandler(fileHandler);

where T:{4}is the thread id (argument 4).

T:{4}线程 id在哪里(参数 4)。