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

8 浏览
0 Comments

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

java.util.logging.Logger生成的日志语句中能否打印线程名称?\n一种替代方案是执行以下操作:\n

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

\n但这种方式重复且日志框架应该处理这个问题。

0
0 Comments

问题的出现原因是在使用java.util.logging时,想要在日志消息中打印线程名称,但默认的日志格式化器无法提供这个功能。

根据stackoverflow上的回答,可以通过扩展java.util.logging.Formatter来解决这个问题。扩展的Formatter类中,可以通过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();
    }
    
    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);
    }
}

这样,在日志记录时,会将线程名称添加到日志消息中,以便区分不同线程的日志记录。

0
0 Comments

使用Java的日志记录(logging)库来打印线程名字,可以通过自定义Formatter来实现。在LogRecord对象中包含了产生日志消息的线程的ID,我们可以通过自定义Formatter来获取LogRecord对象,然后通过线程ID获取线程名字。

下面是一个自定义Formatter的示例,它只打印线程名字和日志消息:

private static Formatter getMinimalFormatter() {
    return new Formatter() {
        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";
        }
    };
}

要使用自定义的Formatter,可以修改默认的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 getDefaultConsoleHandler() {
    var rootLogger = Logger.getLogger("");
    return first(Arrays.asList(rootLogger.getHandlers()));
}
static  Optional first(List list) {
    return list.isEmpty() ?
            Optional.empty() :
            Optional.ofNullable(list.get(0));
}

使用上述的自定义Formatter,可以得到包含线程名字的日志消息:

main: Hello from the main thread

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

此外,还可以自定义更多的内容来打印日志消息,下面是一个示例的Formatter:

private static Formatter getCustomFormatter() {
    return new Formatter() {
        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);
            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;
}

此Formatter将产生类似下面的日志消息:

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

通过自定义Formatter,我们可以方便地打印线程名字和其他所需的信息来进行日志记录。

0
0 Comments

使用java.util.logging库时,无法直接打印线程名称,这是一个很尴尬的问题。默认的java.util.logging.SimpleFormatter根本不支持记录线程名称,而java.util.logging.FileHandler只支持一些模板占位符,其中没有线程名称。

最接近的是java.util.logging.XMLFormatter,但它只记录线程ID,而不是线程名称。

如果你认为我们已经接近解决方法了,那你就错了。LogRecord类只保存线程ID,而不是线程名称,这样并没有什么用处。

确实,SimpleFormatter不使用线程ID。但是,可以很容易地定义一个自定义格式化器,并通过线程ID获取线程名称。没有什么可尴尬的了。

解决方法是定义一个自定义格式化器,通过线程ID获取线程名称。以下是一个示例代码:

import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public class CustomFormatter extends Formatter {
    @Override
    public String format(LogRecord record) {
        String threadName = Thread.currentThread().getName();
        return "[" + threadName + "] " + record.getMessage() + "\n";
    }
}

然后,可以将自定义格式化器应用于日志记录器:

import java.util.logging.ConsoleHandler;
import java.util.logging.Logger;
public class Main {
    private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
    public static void main(String[] args) {
        ConsoleHandler consoleHandler = new ConsoleHandler();
        consoleHandler.setFormatter(new CustomFormatter());
        LOGGER.addHandler(consoleHandler);
        LOGGER.info("Test");
    }
}

通过这种方式,就可以在日志中打印线程名称了。

0