Add instrumentation service
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index abc1764..4c25898 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -41,12 +41,14 @@
import org.apache.logging.log4j.core.config.NullConfiguration;
import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.core.jmx.Server;
import org.apache.logging.log4j.core.util.Cancellable;
import org.apache.logging.log4j.core.util.ExecutorServices;
import org.apache.logging.log4j.core.util.NetUtils;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.LoggerContextFactory;
import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
@@ -800,6 +802,12 @@
// LOG4J2-151: changed visibility from private to protected
protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
- return new Logger(ctx, name, messageFactory);
+ // TODO: Adapt after
+ // https://github.com/apache/logging-log4j2/issues/2379
+ // is fixed.
+ final MessageFactory actualMessageFactory = InstrumentationService.getInstance()
+ .instrumentMessageFactory(
+ name, messageFactory != null ? messageFactory : ParameterizedMessageFactory.INSTANCE);
+ return new Logger(ctx, name, actualMessageFactory);
}
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java
index 463067a..7e2106b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java
@@ -48,6 +48,7 @@
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
import org.apache.logging.log4j.core.filter.AbstractFilterable;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.spi.AbstractLogger;
/**
@@ -121,7 +122,9 @@
} else if (errorRef == null) {
throw new ConfigurationException("No appenders are available for AsyncAppender " + getName());
}
- asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create();
+ asyncQueueFullPolicy = InstrumentationService.getInstance()
+ .instrumentQueueFullPolicy(
+ InstrumentationService.ASYNC_APPENDER, getName(), AsyncQueueFullPolicyFactory.create());
dispatcher.start();
super.start();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
index ba5fd4d..cc8518e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
@@ -36,6 +36,7 @@
import org.apache.logging.log4j.core.impl.LogEventFactory;
import org.apache.logging.log4j.core.impl.MutableLogEvent;
import org.apache.logging.log4j.core.impl.ReusableLogEventFactory;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
import org.apache.logging.log4j.core.util.Log4jThread;
import org.apache.logging.log4j.core.util.Log4jThreadFactory;
@@ -242,7 +243,9 @@
return result;
}
};
- asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create();
+ asyncQueueFullPolicy = InstrumentationService.getInstance()
+ .instrumentQueueFullPolicy(
+ InstrumentationService.ASYNC_LOGGER_CONFIG, "Default", AsyncQueueFullPolicyFactory.create());
translator = mutable ? MUTABLE_TRANSLATOR : TRANSLATOR;
factory = mutable ? MUTABLE_FACTORY : FACTORY;
@@ -449,6 +452,9 @@
*/
@Override
public RingBufferAdmin createRingBufferAdmin(final String contextName, final String loggerConfigName) {
- return RingBufferAdmin.forAsyncLoggerConfig(disruptor.getRingBuffer(), contextName, loggerConfigName);
+ final RingBufferAdmin ringBufferAdmin =
+ RingBufferAdmin.forAsyncLoggerConfig(disruptor.getRingBuffer(), contextName, loggerConfigName);
+ InstrumentationService.getInstance().instrumentRingBuffer(ringBufferAdmin);
+ return ringBufferAdmin;
}
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java
index 7b6b0d8..d9e0fc3 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java
@@ -31,6 +31,7 @@
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.AbstractLifeCycle;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
import org.apache.logging.log4j.core.util.Log4jThread;
import org.apache.logging.log4j.core.util.Log4jThreadFactory;
@@ -130,7 +131,9 @@
return result;
}
};
- asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create();
+ asyncQueueFullPolicy = InstrumentationService.getInstance()
+ .instrumentQueueFullPolicy(
+ InstrumentationService.ASYNC_LOGGER, getContextName(), AsyncQueueFullPolicyFactory.create());
disruptor = new Disruptor<>(
RingBufferLogEvent.FACTORY, ringBufferSize, threadFactory, ProducerType.MULTI, waitStrategy);
@@ -219,7 +222,9 @@
*/
public RingBufferAdmin createRingBufferAdmin(final String jmxContextName) {
final RingBuffer<RingBufferLogEvent> ring = disruptor == null ? null : disruptor.getRingBuffer();
- return RingBufferAdmin.forAsyncLogger(ring, jmxContextName);
+ final RingBufferAdmin ringBufferAdmin = RingBufferAdmin.forAsyncLogger(ring, jmxContextName);
+ InstrumentationService.getInstance().instrumentRingBuffer(ringBufferAdmin);
+ return ringBufferAdmin;
}
EventRoute getEventRoute(final Level logLevel) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index 014ebcc..08ef194 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -56,6 +56,7 @@
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor;
import org.apache.logging.log4j.core.lookup.Interpolator;
@@ -129,8 +130,8 @@
private Node advertiserNode;
private Object advertisement;
private String name;
- private ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>();
- private ConcurrentMap<String, LoggerConfig> loggerConfigs = new ConcurrentHashMap<>();
+ private final ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>();
+ private final ConcurrentMap<String, LoggerConfig> loggerConfigs = new ConcurrentHashMap<>();
private List<CustomLevelConfig> customLevels = Collections.emptyList();
private final ConcurrentMap<String, String> propertyMap = new ConcurrentHashMap<>();
private final Interpolator tempLookup = new Interpolator(propertyMap);
@@ -693,15 +694,22 @@
}
}
} else if ("Appenders".equalsIgnoreCase(child.getName())) {
- appenders = child.getObject();
+ final InstrumentationService instrumentation = InstrumentationService.getInstance();
+ final Map<String, Appender> appenders = child.getObject();
+ appenders.forEach((name, appender) -> {
+ this.appenders.computeIfAbsent(name, ignored -> instrumentation.instrumentAppender(appender));
+ });
} else if (child.isInstanceOf(Filter.class)) {
addFilter(child.getObject(Filter.class));
} else if (child.isInstanceOf(Loggers.class)) {
final Loggers l = child.getObject();
- loggerConfigs = l.getMap();
+ final InstrumentationService instrumentation = InstrumentationService.getInstance();
+ l.getMap().forEach((key, value) -> {
+ loggerConfigs.put(key, instrumentation.instrumentLoggerConfig(value));
+ });
setLoggers = true;
if (l.getRoot() != null) {
- root = l.getRoot();
+ root = instrumentation.instrumentLoggerConfig(l.getRoot());
setRoot = true;
}
} else if (child.isInstanceOf(CustomLevels.class)) {
@@ -842,7 +850,8 @@
@Override
public void addAppender(final Appender appender) {
if (appender != null) {
- appenders.putIfAbsent(appender.getName(), appender);
+ appenders.computeIfAbsent(appender.getName(), ignored -> InstrumentationService.getInstance()
+ .instrumentAppender(appender));
}
}
@@ -893,15 +902,19 @@
return;
}
final String loggerName = logger.getName();
- appenders.putIfAbsent(appender.getName(), appender);
+ final Appender actualAppender =
+ appenders.computeIfAbsent(appender.getName(), ignored -> InstrumentationService.getInstance()
+ .instrumentAppender(appender));
final LoggerConfig lc = getLoggerConfig(loggerName);
if (lc.getName().equals(loggerName)) {
- lc.addAppender(appender, null, null);
+ lc.addAppender(actualAppender, null, null);
} else {
- final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive());
- nlc.addAppender(appender, null, null);
- nlc.setParent(lc);
- loggerConfigs.putIfAbsent(loggerName, nlc);
+ loggerConfigs.computeIfAbsent(loggerName, name -> {
+ final LoggerConfig config = new LoggerConfig(name, lc.getExplicitLevel(), lc.isAdditive());
+ config.addAppender(actualAppender, null, null);
+ config.setParent(lc);
+ return InstrumentationService.getInstance().instrumentLoggerConfig(config);
+ });
setParents();
logger.getContext().updateLoggers();
}
@@ -923,10 +936,12 @@
if (lc.getName().equals(loggerName)) {
lc.addFilter(filter);
} else {
- final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive());
- nlc.addFilter(filter);
- nlc.setParent(lc);
- loggerConfigs.putIfAbsent(loggerName, nlc);
+ loggerConfigs.computeIfAbsent(loggerName, name -> {
+ final LoggerConfig config = new LoggerConfig(name, lc.getExplicitLevel(), lc.isAdditive());
+ config.addFilter(filter);
+ config.setParent(lc);
+ return InstrumentationService.getInstance().instrumentLoggerConfig(config);
+ });
setParents();
logger.getContext().updateLoggers();
}
@@ -949,9 +964,11 @@
if (lc.getName().equals(loggerName)) {
lc.setAdditive(additive);
} else {
- final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), additive);
- nlc.setParent(lc);
- loggerConfigs.putIfAbsent(loggerName, nlc);
+ loggerConfigs.computeIfAbsent(loggerName, name -> {
+ final LoggerConfig config = new LoggerConfig(name, lc.getExplicitLevel(), additive);
+ config.setParent(lc);
+ return InstrumentationService.getInstance().instrumentLoggerConfig(config);
+ });
setParents();
logger.getContext().updateLoggers();
}
@@ -1052,7 +1069,8 @@
*/
@Override
public synchronized void addLogger(final String loggerName, final LoggerConfig loggerConfig) {
- loggerConfigs.putIfAbsent(loggerName, loggerConfig);
+ loggerConfigs.computeIfAbsent(
+ loggerName, ignored -> InstrumentationService.getInstance().instrumentLoggerConfig(loggerConfig));
setParents();
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
index b22bd79..3feab2e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
@@ -47,6 +47,7 @@
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.impl.LogEventFactory;
import org.apache.logging.log4j.core.impl.ReusableLogEventFactory;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.core.util.Booleans;
import org.apache.logging.log4j.core.util.Constants;
@@ -237,9 +238,9 @@
* Default constructor.
*/
public LoggerConfig() {
- this.logEventFactory = LOG_EVENT_FACTORY;
- this.level = Level.ERROR;
this.name = Strings.EMPTY;
+ this.logEventFactory = InstrumentationService.getInstance().instrumentLogEventFactory(name, LOG_EVENT_FACTORY);
+ this.level = Level.ERROR;
this.properties = null;
this.propertiesRequireLookup = false;
this.config = null;
@@ -254,8 +255,8 @@
* @param additive true if the Logger is additive, false otherwise.
*/
public LoggerConfig(final String name, final Level level, final boolean additive) {
- this.logEventFactory = LOG_EVENT_FACTORY;
this.name = name;
+ this.logEventFactory = InstrumentationService.getInstance().instrumentLogEventFactory(name, LOG_EVENT_FACTORY);
this.level = level;
this.additive = additive;
this.properties = null;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java
index ae25109..a301b17 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategyFactory.java
@@ -16,6 +16,7 @@
*/
package org.apache.logging.log4j.core.config;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;
@@ -43,9 +44,14 @@
* configuration change
*/
public static ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfig) {
+ return InstrumentationService.getInstance()
+ .instrumentReliabilityStrategy(
+ loggerConfig.getName(), createFromProperties(loggerConfig, PropertiesUtil.getProperties()));
+ }
- final String strategy =
- PropertiesUtil.getProperties().getStringProperty("log4j.ReliabilityStrategy", "AwaitCompletion");
+ private static ReliabilityStrategy createFromProperties(
+ final LoggerConfig loggerConfig, final PropertiesUtil properties) {
+ final String strategy = properties.getStringProperty("log4j.ReliabilityStrategy", "AwaitCompletion");
if ("AwaitCompletion".equals(strategy)) {
return new AwaitCompletionReliabilityStrategy(loggerConfig);
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/InstrumentationService.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/InstrumentationService.java
new file mode 100644
index 0000000..a2447cb
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/InstrumentationService.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.core.instrumentation;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.async.AsyncQueueFullPolicy;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.ReliabilityStrategy;
+import org.apache.logging.log4j.core.impl.LogEventFactory;
+import org.apache.logging.log4j.core.instrumentation.internal.CompositeInstrumentationService;
+import org.apache.logging.log4j.core.jmx.RingBufferAdminMBean;
+import org.apache.logging.log4j.message.MessageFactory;
+
+/**
+ * Service class to influence the way Log4j components are created
+ * <p>
+ * Integrators that wish to instrument Log4j Core to provide metrics and other services, should implement this
+ * class and register it with {@link java.util.ServiceLoader}.
+ * </p>
+ * @since 2.24.0
+ */
+public interface InstrumentationService {
+
+ /**
+ * Indicates that the component is used by an {@link org.apache.logging.log4j.core.appender.AsyncAppender}.
+ */
+ String ASYNC_APPENDER = "ASYNC_APPENDER";
+ /**
+ * Indicates that the component is used by an {@link org.apache.logging.log4j.core.async.AsyncLogger}.
+ */
+ String ASYNC_LOGGER = "ASYNC_LOGGER";
+ /**
+ * Indicates that the component is used by an {@link org.apache.logging.log4j.core.async.AsyncLoggerConfig}.
+ */
+ String ASYNC_LOGGER_CONFIG = "ASYNC_LOGGER_CONFIG";
+
+ /**
+ * @return The default instance of {@code InstrumentationService}
+ */
+ static InstrumentationService getInstance() {
+ return CompositeInstrumentationService.getInstance();
+ }
+
+ /**
+ * Allows to instrument the creation of {@link org.apache.logging.log4j.message.Message}s by the logger.
+ * <p>
+ * This callback is called each time a new {@link org.apache.logging.log4j.core.Logger} is created by Log4j
+ * Core.
+ * </p>
+ * @param loggerName The name of the logger to be created.
+ * @param messageFactory The message factory provided by the user or the default one.
+ * @return The actual message factory to use.
+ */
+ default MessageFactory instrumentMessageFactory(final String loggerName, final MessageFactory messageFactory) {
+ return messageFactory;
+ }
+
+ /**
+ * Allows to instrument the delivery process of log events during a reconfiguration.
+ * <p>
+ * This callback is called each time a new {@link LoggerConfig} is created by Log4j Core.
+ * </p>
+ * @param loggerName The name of the logger configuration to be created.
+ * @param strategy The reliability strategy configured by the user.
+ * @return The actual reliability strategy to use.
+ */
+ default ReliabilityStrategy instrumentReliabilityStrategy(
+ final String loggerName, final ReliabilityStrategy strategy) {
+ return strategy;
+ }
+
+ /**
+ * Allows to instrument the creation of {@link org.apache.logging.log4j.core.LogEvent}s.
+ * <p>
+ * This callback is called each time a new {@link LoggerConfig} is created by Log4j Core.
+ * </p>
+ * @param loggerName The name of the logger configuration to be created.
+ * @param logEventFactory The log event factory configured by the user.
+ * @return The actual log event factory to use.
+ */
+ default LogEventFactory instrumentLogEventFactory(final String loggerName, final LogEventFactory logEventFactory) {
+ return logEventFactory;
+ }
+
+ /**
+ * Allows to instrument the handling of queue full events.
+ * <p>
+ * This event is called, when as new queue full policy is created.
+ * </p>
+ * @param parentType The type of the component that will use the queue full policy.
+ * <p>
+ * Currently the following constants are supported:
+ * </p>
+ * <ol>
+ * <li>{@link #ASYNC_APPENDER},</li>
+ * <li>{@link #ASYNC_LOGGER},</li>
+ * <li>{@link #ASYNC_LOGGER_CONFIG}.</li>
+ * </ol>
+ * @param parentName The name of
+ * @param queueFullPolicy The reliability strategy configured by the user.
+ * @return The actual policy to use.
+ */
+ default AsyncQueueFullPolicy instrumentQueueFullPolicy(
+ final String parentType, final String parentName, final AsyncQueueFullPolicy queueFullPolicy) {
+ return queueFullPolicy;
+ }
+
+ /**
+ * Allows to instrument the ring buffer of a disruptor.
+ * <p>
+ * Whenever a new {@link com.lmax.disruptor.RingBuffer} is created, this method is called.
+ * </p>
+ * @param ringBufferAdmin An object that gives access to the characteristics of a ring buffer,
+ */
+ default void instrumentRingBuffer(final RingBufferAdminMBean ringBufferAdmin) {}
+
+ /**
+ * Allows to instrument a {@link LoggerConfig}.
+ * <p>
+ *
+ * </p>
+ * @param loggerConfig The logger configuration to instrument.
+ * @return The same logger configuration or a wrapper.
+ */
+ default LoggerConfig instrumentLoggerConfig(final LoggerConfig loggerConfig) {
+ return loggerConfig;
+ }
+
+ /**
+ * Allows to instrument an {@link Appender}.
+ * @param appender The appender to instrument.
+ * @return The same appender or a wrapper.
+ */
+ default Appender instrumentAppender(final Appender appender) {
+ return appender;
+ }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/internal/CompositeInstrumentationService.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/internal/CompositeInstrumentationService.java
new file mode 100644
index 0000000..48bab72
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/internal/CompositeInstrumentationService.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.core.instrumentation.internal;
+
+import java.util.ServiceLoader;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.async.AsyncQueueFullPolicy;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.ReliabilityStrategy;
+import org.apache.logging.log4j.core.impl.LogEventFactory;
+import org.apache.logging.log4j.core.instrumentation.InstrumentationService;
+import org.apache.logging.log4j.core.jmx.RingBufferAdminMBean;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Lazy;
+import org.apache.logging.log4j.util.ServiceLoaderUtil;
+
+public final class CompositeInstrumentationService implements InstrumentationService {
+
+ private static final Lazy<InstrumentationService> INSTANCE =
+ Lazy.lazy(CompositeInstrumentationService::createInstance);
+
+ private static InstrumentationService createInstance() {
+ final InstrumentationService[] services = ServiceLoaderUtil.safeStream(
+ InstrumentationService.class,
+ ServiceLoader.load(InstrumentationService.class, Loader.getClassLoader()),
+ StatusLogger.getLogger())
+ .toArray(InstrumentationService[]::new);
+ return new CompositeInstrumentationService(services);
+ }
+
+ public static InstrumentationService getInstance() {
+ return INSTANCE.get();
+ }
+
+ private final InstrumentationService[] services;
+
+ private CompositeInstrumentationService(final InstrumentationService[] services) {
+ this.services = services;
+ }
+
+ @Override
+ public MessageFactory instrumentMessageFactory(final String loggerName, final MessageFactory messageFactory) {
+ MessageFactory result = messageFactory;
+ for (final InstrumentationService service : services) {
+ result = service.instrumentMessageFactory(loggerName, result);
+ }
+ return result;
+ }
+
+ @Override
+ public ReliabilityStrategy instrumentReliabilityStrategy(
+ final String loggerName, final ReliabilityStrategy strategy) {
+ ReliabilityStrategy result = strategy;
+ for (final InstrumentationService service : services) {
+ result = service.instrumentReliabilityStrategy(loggerName, result);
+ }
+ return result;
+ }
+
+ @Override
+ public LogEventFactory instrumentLogEventFactory(final String loggerName, final LogEventFactory logEventFactory) {
+ LogEventFactory result = logEventFactory;
+ for (final InstrumentationService service : services) {
+ result = service.instrumentLogEventFactory(loggerName, result);
+ }
+ return result;
+ }
+
+ @Override
+ public AsyncQueueFullPolicy instrumentQueueFullPolicy(
+ final String parentType, final String parentName, final AsyncQueueFullPolicy queueFullPolicy) {
+ AsyncQueueFullPolicy result = queueFullPolicy;
+ for (final InstrumentationService service : services) {
+ result = service.instrumentQueueFullPolicy(parentType, parentName, result);
+ }
+ return result;
+ }
+
+ @Override
+ public void instrumentRingBuffer(final RingBufferAdminMBean ringBufferAdmin) {
+ for (final InstrumentationService service : services) {
+ service.instrumentRingBuffer(ringBufferAdmin);
+ }
+ }
+
+ @Override
+ public LoggerConfig instrumentLoggerConfig(final LoggerConfig loggerConfig) {
+ LoggerConfig result = loggerConfig;
+ for (final InstrumentationService service : services) {
+ result = service.instrumentLoggerConfig(result);
+ }
+ return result;
+ }
+
+ @Override
+ public Appender instrumentAppender(final Appender appender) {
+ Appender result = appender;
+ for (final InstrumentationService service : services) {
+ result = service.instrumentAppender(result);
+ }
+ return result;
+ }
+}