使用 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
Printing thread name using java.util.logging
提问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.logging
can't do this...
尴尬,但看起来java.util.logging
不能这样做......
The default java.util.logging.SimpleFormatter
doesn't have the ability to log thread name at all. The java.util.logging.FileHandler
supports few template placeholders, none of them is thread name.
默认情况下java.util.logging.SimpleFormatter
根本无法记录线程名称。该java.util.logging.FileHandler
支持几个模板占位符,它们都不是线程名。
java.util.logging.XMLFormatter
is 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. LogRecord
class 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.Formatter
but instead getting LogRecord#getThreadID()
you can get thread name by invoking Thread.currentThread().getName()
like this:
我有类似的问题。如此处回答如何使用 java.util.logging 对齐日志消息,您可以扩展,java.util.logging.Formatter
但LogRecord#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, LogRecord
contains the ID of the thread that produced the log message. We can get hold of this LogRecord
when 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 Thread
object 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 Formatter
that 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 Formatter
should 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 Formatter
that 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 Formatter
produces 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)。