From 5a3613346573824f8e89870b6e3562dc275a3a74 Mon Sep 17 00:00:00 2001 From: Ceki Gulcu Date: Fri, 2 Feb 2024 15:25:44 +0100 Subject: [PATCH] fix issues/710 Signed-off-by: Ceki Gulcu --- .../qos/logback/classic/spi/LoggingEvent.java | 127 ++++++++++++------ 1 file changed, 84 insertions(+), 43 deletions(-) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java index 21b98e93c1..a0c9689c41 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java @@ -1,20 +1,20 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.classic.spi; import java.io.IOException; import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.time.Clock; import java.time.Instant; import java.util.ArrayList; @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.EnvUtil; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; import org.slf4j.helpers.MessageFormatter; @@ -34,16 +36,15 @@ import ch.qos.logback.core.spi.SequenceNumberGenerator; /** - * The internal representation of logging events. When an affirmative decision - * is made to log then a LoggingEvent instance is created. This - * instance is passed around to the different logback-classic components. + * The internal representation of logging events. When an affirmative decision is made to log then a + * LoggingEvent instance is created. This instance is passed around to the different logback-classic + * components. *

*

- * Writers of logback-classic components such as appenders should be aware of - * that some of the LoggingEvent fields are initialized lazily. Therefore, an - * appender wishing to output data to be later correctly read by a receiver, - * must initialize "lazy" fields prior to writing them out. See the - * {@link #prepareForDeferredProcessing()} method for the exact list. + * Writers of logback-classic components such as appenders should be aware of that some of the LoggingEvent fields are + * initialized lazily. Therefore, an appender wishing to output data to be later correctly read by a receiver, must + * initialize "lazy" fields prior to writing them out. See the {@link #prepareForDeferredProcessing()} method for the + * exact list. *

* * @author Ceki Gülcü @@ -51,9 +52,11 @@ */ public class LoggingEvent implements ILoggingEvent { + public static final String VIRTUAL_THREAD_NAME_PREFIX = "virtual-"; + public static final String REGULAR_UNNAMED_THREAD_PREFIX = "unnamed-"; + /** - * Fully qualified name of the calling Logger class. This field does not survive - * serialization. + * Fully qualified name of the calling Logger class. This field does not survive serialization. *

*

* Note that the getCallerInformation() method relies on this fact. @@ -73,8 +76,7 @@ public class LoggingEvent implements ILoggingEvent { * Level of logging event. *

*

- * This field should not be accessed directly. You should use the - * {@link #getLevel} method instead. + * This field should not be accessed directly. You should use the {@link #getLevel} method instead. *

*/ private transient Level level; @@ -102,8 +104,7 @@ public class LoggingEvent implements ILoggingEvent { List keyValuePairs; /** - * The number of milliseconds elapsed from 1/1/1970 until logging event was - * created. + * The number of milliseconds elapsed from 1/1/1970 until logging event was created. */ private Instant instant; @@ -205,11 +206,56 @@ public void setLoggerName(String loggerName) { public String getThreadName() { if (threadName == null) { - threadName = (Thread.currentThread()).getName(); + threadName = extractThreadName(Thread.currentThread()); } return threadName; } + /** + * Extracts the name of aThread by calling {@link Thread#getName()}. If the value is null, then use the value + * returned by {@link Thread#getId()} prefixing with {@link #VIRTUAL_THREAD_NAME_PREFIX} if thread is virtual or + * with {@link #REGULAR_UNNAMED_THREAD_PREFIX} if regular. + * + * @param aThread + * @return + * @since 1.5.0 + */ + private String extractThreadName(Thread aThread) { + if (aThread == null) { + return CoreConstants.NA; + } + String threadName = aThread.getName(); + if (threadName != null) + return threadName; + Long virtualThreadId = getVirtualThreadId(aThread); + if (virtualThreadId != null) { + return VIRTUAL_THREAD_NAME_PREFIX + virtualThreadId; + } else { + return REGULAR_UNNAMED_THREAD_PREFIX + aThread.getId(); + } + } + // + + + /** + * Return the threadId if running under JDK 21+ and the thread is a virtual thread, return null otherwise. + * + * @param aThread + * @return Return the threadId if the thread is a virtual thread, return null otherwise. + */ + Long getVirtualThreadId(Thread aThread) { + if (EnvUtil.isJDK21OrHigher()) { + try { + Method isVirtualMethod = Thread.class.getMethod("isVirtual"); + boolean isVirtual = (boolean) isVirtualMethod.invoke(aThread); + if (isVirtual) + return aThread.getId(); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + return null; + } + } + return null; + } + /** * @param threadName The threadName to set. * @throws IllegalStateException If threadName has been already set. @@ -241,12 +287,12 @@ public void setThrowableProxy(ThrowableProxy tp) { } /** - * This method should be called prior to serializing an event. It should also be - * called when using asynchronous or deferred logging. + * This method should be called prior to serializing an event. It should also be called when using asynchronous or + * deferred logging. *

*

- * Note that due to performance concerns, this method does NOT extract caller - * data. It is the responsibility of the caller to extract caller information. + * Note that due to performance concerns, this method does NOT extract caller data. It is the responsibility of the + * caller to extract caller information. */ public void prepareForDeferredProcessing() { this.getFormattedMessage(); @@ -280,7 +326,7 @@ public void setMessage(String message) { /** * Return the {@link Instant} corresponding to the creation of this event. - * + * * @see {@link #getTimeStamp()} * @since 1.3 */ @@ -290,7 +336,7 @@ public Instant getInstant() { /** * Set {@link Instant} corresponding to the creation of this event. - * + * * The value of {@link #getTimeStamp()} will be overridden as well. */ public void setInstant(Instant instant) { @@ -304,9 +350,9 @@ public long getTimeStamp() { return timeStamp; } - /** * Return the number of nanoseconds past the {@link #getTimeStamp() timestamp in seconds}. + * * @since 1.3.0 */ @Override @@ -316,11 +362,9 @@ public int getNanoseconds() { /** * Set the number of elapsed milliseconds since epoch in UTC. - * - * Setting the timestamp will override the value contained in - * {@link #getInstant}. Nanoseconds value will be computed form the provided - * millisecond value. - * + * + * Setting the timestamp will override the value contained in {@link #getInstant}. Nanoseconds value will be + * computed form the provided millisecond value. */ public void setTimeStamp(long timeStamp) { Instant instant = Instant.ofEpochMilli(timeStamp); @@ -344,13 +388,11 @@ public void setLevel(Level level) { } /** - * Get the caller information for this logging event. If caller information is - * null at the time of its invocation, this method extracts location - * information. The collected information is cached for future use. + * Get the caller information for this logging event. If caller information is null at the time of its invocation, + * this method extracts location information. The collected information is cached for future use. *

*

- * Note that after serialization it is impossible to correctly extract caller - * information. + * Note that after serialization it is impossible to correctly extract caller information. *

*/ public StackTraceElement[] getCallerData() { @@ -450,9 +492,8 @@ public String toString() { } /** - * LoggerEventVO instances should be used for serialization. Use - * {@link LoggingEventVO#build(ILoggingEvent) build} method to create the - * LoggerEventVO instance. + * LoggerEventVO instances should be used for serialization. Use {@link LoggingEventVO#build(ILoggingEvent) build} + * method to create the LoggerEventVO instance. * * @since 1.0.11 */