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;
+    }
+}