From f5ef464fdaaf2dfdb3aca2925b4c777cf89634ab Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Mon, 8 Apr 2024 14:01:07 +0200 Subject: [PATCH] Add instrumentation service --- .../logging/log4j/core/LoggerContext.java | 10 +- .../log4j/core/appender/AsyncAppender.java | 5 +- .../async/AsyncLoggerConfigDisruptor.java | 10 +- .../core/async/AsyncLoggerDisruptor.java | 9 +- .../core/config/AbstractConfiguration.java | 58 ++++--- .../log4j/core/config/LoggerConfig.java | 7 +- .../config/ReliabilityStrategyFactory.java | 10 +- .../InstrumentationService.java | 151 ++++++++++++++++++ .../CompositeInstrumentationService.java | 119 ++++++++++++++ 9 files changed, 348 insertions(+), 31 deletions(-) create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/InstrumentationService.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/instrumentation/internal/CompositeInstrumentationService.java 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 abc1764b854..4c25898fe89 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 @@ private void initApiModule() { // 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 463067ae948..7e2106b081b 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 @@ public void start() { } 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 ba5fd4d54aa..cc8518e769d 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 @@ public Thread newThread(final Runnable r) { 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 @@ private LogEvent ensureImmutable(final LogEvent event) { */ @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 7b6b0d8dc13..d9e0fc3beea 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 @@ public Thread newThread(final Runnable r) { 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 @@ private static boolean hasBacklog(final Disruptor theDisruptor) { */ public RingBufferAdmin createRingBufferAdmin(final String jmxContextName) { final RingBuffer 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 014ebcca013..08ef1947ef0 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 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement private Node advertiserNode; private Object advertisement; private String name; - private ConcurrentMap appenders = new ConcurrentHashMap<>(); - private ConcurrentMap loggerConfigs = new ConcurrentHashMap<>(); + private final ConcurrentMap appenders = new ConcurrentHashMap<>(); + private final ConcurrentMap loggerConfigs = new ConcurrentHashMap<>(); private List customLevels = Collections.emptyList(); private final ConcurrentMap propertyMap = new ConcurrentHashMap<>(); private final Interpolator tempLookup = new Interpolator(propertyMap); @@ -693,15 +694,22 @@ protected void doConfigure() { } } } else if ("Appenders".equalsIgnoreCase(child.getName())) { - appenders = child.getObject(); + final InstrumentationService instrumentation = InstrumentationService.getInstance(); + final Map 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 @@ public Map getAppenders() { @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 @@ public synchronized void addLoggerAppender( 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 @@ public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Log 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 @@ public synchronized void setLoggerAdditive( 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 @@ public LoggerConfig getLogger(final String loggerName) { */ @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 b22bd799959..3feab2ee195 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 @@ public B asBuilder() { * 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 @@ public LoggerConfig() { * @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 ae25109d3c1..a301b17681e 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 @@ private ReliabilityStrategyFactory() {} * 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 00000000000..a2447cb306d --- /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 + *

+ * 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}. + *

+ * @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. + *

+ * This callback is called each time a new {@link org.apache.logging.log4j.core.Logger} is created by Log4j + * Core. + *

+ * @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. + *

+ * This callback is called each time a new {@link LoggerConfig} is created by Log4j Core. + *

+ * @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. + *

+ * This callback is called each time a new {@link LoggerConfig} is created by Log4j Core. + *

+ * @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. + *

+ * This event is called, when as new queue full policy is created. + *

+ * @param parentType The type of the component that will use the queue full policy. + *

+ * Currently the following constants are supported: + *

+ *
    + *
  1. {@link #ASYNC_APPENDER},
  2. + *
  3. {@link #ASYNC_LOGGER},
  4. + *
  5. {@link #ASYNC_LOGGER_CONFIG}.
  6. + *
+ * @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. + *

+ * Whenever a new {@link com.lmax.disruptor.RingBuffer} is created, this method is called. + *

+ * @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}. + *

+ * + *

+ * @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 00000000000..48bab72e346 --- /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 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; + } +}