С пользовательским Formatter
К счастью, LogRecord
содержит идентификатор потока, который создал сообщение журнала.Мы можем получить это LogRecord
при написании пользовательского Formatter
.Как только мы это получим, нам нужно только получить имя потока через его идентификатор.
Существует пара способов , чтобы получить объект Thread
, соответствующийэтот идентификатор, вот мой:
static Optional<Thread> getThread(long threadId) {
return Thread.getAllStackTraces().keySet().stream()
.filter(t -> t.getId() == threadId)
.findFirst();
}
Ниже приведен минимальный 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";
}
};
}
Чтобы использовать свой пользовательский форматтер, естьснова различные параметры , один из способов - изменить значение по умолчанию 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));
}
Ваш минимальный Formatter
должен затем создать следующие сообщения журнала, содержащие имя потока:
main: Привет из основного потока
и
AWT-EventQueue-0: Привет из потока диспетчеризации событий
Это 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;
}
Что Formatter
создает сообщения журнала, подобные этим:
2019-04-27 13:21:01 ИНФОРМАЦИЯ [AWT-EventQueue-0] package.ClassName.method: сообщение журнала