Skip to content

Commit ba4b732

Browse files
committed
Revamp AbstractLogger in log4j-sdk
In order for Log4j Core 3.x not to depend on the utility classes in Log4j API 2.x, we provide a new `AbstractLogger` in `log4j-sdk`.
1 parent ce28b4c commit ba4b732

File tree

19 files changed

+4803
-93
lines changed

19 files changed

+4803
-93
lines changed

log4j-async-logger/pom.xml

+13
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,21 @@
2828
<name>Apache Log4j Async Logger</name>
2929
<description>Alternative implementation of logger that uses LMAX Disruptor.</description>
3030

31+
<properties>
32+
<bnd-extra-package-options>
33+
<!-- JSpecify is optional at runtime -->
34+
org.jspecify.annotations.*;resolution:=optional
35+
</bnd-extra-package-options>
36+
</properties>
37+
3138
<dependencies>
3239

40+
<dependency>
41+
<groupId>org.jspecify</groupId>
42+
<artifactId>jspecify</artifactId>
43+
<scope>provided</scope>
44+
</dependency>
45+
3346
<dependency>
3447
<groupId>org.apache.logging.log4j</groupId>
3548
<artifactId>log4j-core</artifactId>

log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLogger.java

+25-17
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@
3333
import org.apache.logging.log4j.core.impl.ContextDataFactory;
3434
import org.apache.logging.log4j.core.time.Clock;
3535
import org.apache.logging.log4j.core.time.NanoClock;
36+
import org.apache.logging.log4j.message.FlowMessageFactory;
3637
import org.apache.logging.log4j.message.Message;
3738
import org.apache.logging.log4j.message.MessageFactory;
38-
import org.apache.logging.log4j.spi.AbstractLogger;
39+
import org.apache.logging.log4j.sdk.logger.AbstractLogger;
3940
import org.apache.logging.log4j.spi.recycler.Recycler;
41+
import org.apache.logging.log4j.spi.recycler.RecyclerFactory;
4042
import org.apache.logging.log4j.util.StackLocatorUtil;
4143
import org.apache.logging.log4j.util.StringMap;
44+
import org.jspecify.annotations.NullMarked;
45+
import org.jspecify.annotations.Nullable;
4246

4347
/**
4448
* AsyncLogger is a logger designed for high throughput and low latency logging. It does not perform any I/O in the
@@ -59,6 +63,7 @@
5963
* and they will flush to disk at the end of each batch. This means that even with immediateFlush=false, there will
6064
* never be any items left in the buffer; all log events will all be written to disk in a very efficient manner.
6165
*/
66+
@NullMarked
6267
public class AsyncLogger extends Logger {
6368
// Implementation note: many methods in this class are tuned for performance. MODIFY WITH CARE!
6469
// Specifically, try to keep the hot methods to 35 bytecodes or less:
@@ -86,8 +91,11 @@ public class AsyncLogger extends Logger {
8691
final LoggerContext context,
8792
final String name,
8893
final MessageFactory messageFactory,
94+
final FlowMessageFactory flowMessageFactory,
95+
final RecyclerFactory recyclerFactory,
96+
final org.apache.logging.log4j.Logger statusLogger,
8997
final AsyncLoggerDisruptor loggerDisruptor) {
90-
super(context, name, messageFactory);
98+
super(context, name, messageFactory, flowMessageFactory, recyclerFactory, statusLogger);
9199
final Configuration configuration = context.getConfiguration();
92100
this.translatorRecycler = configuration
93101
.getRecyclerFactory()
@@ -107,7 +115,7 @@ public class AsyncLogger extends Logger {
107115
@Override
108116
protected void updateConfiguration(final Configuration newConfig) {
109117
nanoClock = newConfig.getNanoClock();
110-
includeLocation = newConfig.getLoggerConfig(name).isIncludeLocation();
118+
includeLocation = newConfig.getLoggerConfig(getName()).isIncludeLocation();
111119
super.updateConfiguration(newConfig);
112120
}
113121

@@ -149,26 +157,26 @@ public void logMessage(
149157
* This re-uses a {@code RingBufferLogEventTranslator} instance cached in a {@code ThreadLocal} to avoid creating
150158
* unnecessary objects with each event.
151159
*
152-
* @param fqcn fully qualified name of the caller
153-
* @param location the Location of the caller.
154-
* @param level level at which the caller wants to log the message
155-
* @param marker message marker
156-
* @param message the log message
157-
* @param thrown a {@code Throwable} or {@code null}
160+
* @param fqcn fully qualified name of the caller
161+
* @param location the Location of the caller.
162+
* @param level level at which the caller wants to log the message
163+
* @param marker message marker
164+
* @param message the log message
165+
* @param throwable a {@code Throwable} or {@code null}
158166
*/
159167
@Override
160-
public void log(
161-
final Level level,
162-
final Marker marker,
168+
protected void doLog(
163169
final String fqcn,
164-
final StackTraceElement location,
170+
final @Nullable StackTraceElement location,
171+
final Level level,
172+
final @Nullable Marker marker,
165173
final Message message,
166-
final Throwable thrown) {
174+
final @Nullable Throwable throwable) {
167175
// Implementation note: this method is tuned for performance. MODIFY WITH CARE!
168176

169177
final RingBufferLogEventTranslator translator = translatorRecycler.acquire();
170178
try {
171-
initTranslator(translator, fqcn, location, level, marker, message, thrown);
179+
initTranslator(translator, fqcn, location, level, marker, message, throwable);
172180
translator.updateThreadValues();
173181
publish(translator);
174182
} finally {
@@ -220,7 +228,7 @@ private void initTranslator(
220228

221229
translator.setBasicValues(
222230
this,
223-
name,
231+
getName(),
224232
marker,
225233
fqcn,
226234
level,
@@ -246,7 +254,7 @@ private void initTranslator(
246254

247255
translator.setBasicValues(
248256
this,
249-
name,
257+
getName(),
250258
marker,
251259
fqcn,
252260
level,

log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerConfig.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.apache.logging.log4j.plugins.Configurable;
3434
import org.apache.logging.log4j.plugins.Plugin;
3535
import org.apache.logging.log4j.plugins.PluginFactory;
36-
import org.apache.logging.log4j.spi.AbstractLogger;
36+
import org.apache.logging.log4j.sdk.logger.AbstractLogger;
3737
import org.apache.logging.log4j.util.Strings;
3838

3939
/**

log4j-async-logger/src/main/java/org/apache/logging/log4j/async/logger/AsyncLoggerContext.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import org.apache.logging.log4j.core.LoggerContext;
2323
import org.apache.logging.log4j.core.config.Configuration;
2424
import org.apache.logging.log4j.core.config.DefaultConfiguration;
25+
import org.apache.logging.log4j.message.FlowMessageFactory;
2526
import org.apache.logging.log4j.message.MessageFactory;
2627
import org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory;
28+
import org.apache.logging.log4j.spi.recycler.RecyclerFactory;
2729
import org.apache.logging.log4j.status.StatusLogger;
2830

2931
/**
@@ -78,8 +80,15 @@ private AsyncWaitStrategyFactory createAsyncWaitStrategyFactory() {
7880
}
7981

8082
@Override
81-
protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
82-
return new AsyncLogger(ctx, name, messageFactory, loggerDisruptor);
83+
protected Logger newInstance(
84+
final LoggerContext ctx,
85+
final String name,
86+
final MessageFactory messageFactory,
87+
final FlowMessageFactory flowMessageFactory,
88+
final RecyclerFactory recyclerFactory,
89+
final org.apache.logging.log4j.Logger statusLogger) {
90+
return new AsyncLogger(
91+
ctx, name, messageFactory, flowMessageFactory, recyclerFactory, statusLogger, loggerDisruptor);
8392
}
8493

8594
@Override

log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerContextTest.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.apache.logging.log4j.core.LoggerContext;
2323
import org.apache.logging.log4j.core.test.CoreLoggerContexts;
2424
import org.apache.logging.log4j.core.test.categories.AsyncLoggers;
25+
import org.apache.logging.log4j.internal.recycler.DummyRecyclerFactoryProvider;
26+
import org.apache.logging.log4j.status.StatusLogger;
2527
import org.junit.Test;
2628
import org.junit.experimental.categories.Category;
2729

@@ -30,7 +32,14 @@ public class AsyncLoggerContextTest {
3032

3133
@Test
3234
public void testNewInstanceReturnsAsyncLogger() {
33-
final Logger logger = new AsyncLoggerContext("a").newInstance(new LoggerContext("a"), "a", null);
35+
final Logger logger = new AsyncLoggerContext("a")
36+
.newInstance(
37+
new LoggerContext("a"),
38+
"a",
39+
null,
40+
null,
41+
new DummyRecyclerFactoryProvider().createForEnvironment(null),
42+
StatusLogger.getLogger());
3443
assertTrue(logger instanceof AsyncLogger);
3544

3645
CoreLoggerContexts.stopLoggerContext(); // stop async thread

log4j-async-logger/src/test/java/org/apache/logging/log4j/async/logger/AsyncLoggerEventTranslationExceptionTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
3131
import org.apache.logging.log4j.message.Message;
3232
import org.apache.logging.log4j.message.ReusableSimpleMessage;
33-
import org.apache.logging.log4j.spi.AbstractLogger;
33+
import org.apache.logging.log4j.sdk.logger.AbstractLogger;
3434
import org.junit.jupiter.api.Tag;
3535
import org.junit.jupiter.api.Test;
3636
import org.junitpioneer.jupiter.SetSystemProperty;

log4j-core/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
com.lmax.disruptor.*;resolution:=optional,
4040
org.apache.commons.compress.*;resolution:=optional,
4141
org.fusesource.jansi;resolution:=optional,
42+
org.jspecify.annotations.*;resolution:=optional,
4243
<!-- Optional Java modules -->
4344
<!-- java.management -->
4445
java.lang.management;resolution:=optional,
@@ -73,6 +74,10 @@
7374
<groupId>org.apache.logging.log4j</groupId>
7475
<artifactId>log4j-plugins</artifactId>
7576
</dependency>
77+
<dependency>
78+
<groupId>org.apache.logging.log4j</groupId>
79+
<artifactId>log4j-sdk</artifactId>
80+
</dependency>
7681
<!-- The dependency on the plugin processor is only required to ensure the processor is built before core. We
7782
don't want the dependency to be included by consumers -->
7883
<dependency>

log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java

+31-37
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@
2828
import org.apache.logging.log4j.core.config.LoggerConfig;
2929
import org.apache.logging.log4j.core.config.ReliabilityStrategy;
3030
import org.apache.logging.log4j.core.filter.CompositeFilter;
31+
import org.apache.logging.log4j.message.FlowMessageFactory;
3132
import org.apache.logging.log4j.message.Message;
3233
import org.apache.logging.log4j.message.MessageFactory;
33-
import org.apache.logging.log4j.message.SimpleMessage;
34-
import org.apache.logging.log4j.spi.AbstractLogger;
34+
import org.apache.logging.log4j.sdk.logger.AbstractLogger;
35+
import org.apache.logging.log4j.spi.recycler.RecyclerFactory;
3536
import org.apache.logging.log4j.util.Strings;
3637
import org.apache.logging.log4j.util.Supplier;
38+
import org.jspecify.annotations.NullMarked;
39+
import org.jspecify.annotations.Nullable;
3740

3841
/**
3942
* The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an implementation
@@ -43,40 +46,37 @@
4346
* unit tests or bridging legacy Log4j 1.x code. Future versions of this class may or may not include the various
4447
* methods that are noted as not being part of the public API.
4548
*/
49+
@NullMarked
4650
public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
4751

4852
/**
4953
* Config should be consistent across threads.
5054
*/
5155
protected volatile PrivateConfig privateConfig;
5256

53-
// FIXME: ditto to the above
5457
private final LoggerContext context;
5558

5659
/**
5760
* The constructor.
5861
*
5962
* @param context The LoggerContext this Logger is associated with.
60-
* @param messageFactory The message factory.
6163
* @param name The name of the Logger.
64+
* @param messageFactory The message factory to use for logging methods.
65+
* @param flowMessageFactory The flow message factory to use for flow logging methods.
66+
* @param recyclerFactory The recycler to use for log builder instances.
6267
*/
63-
protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
64-
super(name, messageFactory);
68+
protected Logger(
69+
final LoggerContext context,
70+
final String name,
71+
final MessageFactory messageFactory,
72+
final FlowMessageFactory flowMessageFactory,
73+
final RecyclerFactory recyclerFactory,
74+
final org.apache.logging.log4j.Logger statusLogger) {
75+
super(name, messageFactory, flowMessageFactory, recyclerFactory, statusLogger);
6576
this.context = context;
6677
privateConfig = new PrivateConfig(context.getConfiguration(), this);
6778
}
6879

69-
/**
70-
* This is used to construct an InternalLoggerContext, which makes SimpleLoggerContext conmpatible with core.
71-
* @param context the InternalLoggerContext.
72-
* @param name the Logger name.
73-
*/
74-
protected Logger(final LoggerContext context, final String name) {
75-
super(name);
76-
this.context = context;
77-
privateConfig = null;
78-
}
79-
8080
/**
8181
* This method is only used for 1.x compatibility. Returns the parent of this Logger. If it doesn't already exist
8282
* return a temporary Logger.
@@ -95,10 +95,7 @@ private Logger getParent(final PrivateConfig config) {
9595
}
9696
final String lcName = lc.getName();
9797
final MessageFactory messageFactory = getMessageFactory();
98-
if (context.hasLogger(lcName, messageFactory)) {
99-
return context.getLogger(lcName, messageFactory);
100-
}
101-
return new Logger(context, lcName, messageFactory);
98+
return context.getLogger(lcName, messageFactory);
10299
}
103100

104101
/**
@@ -149,35 +146,32 @@ protected boolean requiresLocation() {
149146
}
150147

151148
@Override
152-
public void logMessage(
153-
final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
154-
final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
155-
final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
156-
strategy.log(this, getName(), fqcn, marker, level, msg, t);
157-
}
158-
159-
@Override
160-
protected void log(
161-
final Level level,
162-
final Marker marker,
149+
protected void doLog(
163150
final String fqcn,
164-
final StackTraceElement location,
165-
final Message message,
166-
final Throwable throwable) {
151+
final @Nullable StackTraceElement location,
152+
final Level level,
153+
final @Nullable Marker marker,
154+
final @Nullable Message message,
155+
final @Nullable Throwable throwable) {
167156
final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
168157
strategy.log(this, getName(), fqcn, location, marker, level, message, throwable);
169158
}
170159

171160
@Override
172-
public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
173-
return privateConfig.filter(level, marker, message, t);
161+
public boolean isEnabled(final Level level, final Marker marker) {
162+
return privateConfig.filter(level, marker, null);
174163
}
175164

176165
@Override
177166
public boolean isEnabled(final Level level, final Marker marker, final String message) {
178167
return privateConfig.filter(level, marker, message);
179168
}
180169

170+
@Override
171+
public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
172+
return privateConfig.filter(level, marker, message, t);
173+
}
174+
181175
@Override
182176
public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
183177
return privateConfig.filter(level, marker, message, params);

0 commit comments

Comments
 (0)