Add a Log4j 2.x Java agent
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-log4j2 / org / lttng / ust / agent / log4j2 / LttngLog4j2Agent.java
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Agent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j2/org/lttng/ust/agent/log4j2/LttngLog4j2Agent.java
new file mode 100644 (file)
index 0000000..cb7c35a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015-2022 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.log4j2;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.impl.Log4jContextFactory;
+import org.apache.logging.log4j.core.selector.ContextSelector;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.lttng.ust.agent.AbstractLttngAgent;
+
+/**
+ * Agent implementation for Log4j 2.x.
+ */
+class LttngLog4j2Agent extends AbstractLttngAgent<LttngLogAppender> {
+
+       private static LttngLog4j2Agent instance = null;
+
+       private LttngLog4j2Agent() {
+               super(Domain.LOG4J);
+       }
+
+       public static synchronized LttngLog4j2Agent getInstance() {
+               if (instance == null) {
+                       instance = new LttngLog4j2Agent();
+               }
+               return instance;
+       }
+
+       @Override
+       public Collection<String> listAvailableEvents() {
+               Set<String> eventNames = new TreeSet<>();
+
+               LoggerContextFactory contextFactory = LogManager.getFactory();
+               if (!(contextFactory instanceof Log4jContextFactory)) {
+                       /* Using a custom ContextFactory is not supported. */
+                       StatusLogger.getLogger().error("Can't list events with custom ContextFactory");
+                       return eventNames;
+               }
+
+               ContextSelector selector = ((Log4jContextFactory) contextFactory).getSelector();
+
+               for (LoggerContext logContext : selector.getLoggerContexts()) {
+                       Collection<? extends Logger> loggers = logContext.getLoggers();
+                       for (Logger logger : loggers) {
+                               /*
+                                * Check if that logger has at least one LTTng log4j appender attached.
+                                */
+                               if (hasLttngAppenderAttached(logger)) {
+                                       eventNames.add(logger.getName());
+                               }
+                       }
+               }
+               return eventNames;
+       }
+
+       /*
+        * Check if a logger has an LttngLogAppender attached.
+        *
+        * @param logger the Logger to check, null returns false
+        * @return true if the logger or its parent has at least one LttngLogAppender attached
+        */
+       private static boolean hasLttngAppenderAttached(Logger logger) {
+
+               if (logger == null) {
+                       return false;
+               }
+
+               /*
+                * Check all the appenders associated with the logger and return true if one of
+                * them is an LttngLogAppender.
+                */
+               Map<String, Appender> appenders = logger.getAppenders();
+               for (Map.Entry<String, Appender> appender : appenders.entrySet()) {
+                       if (appender.getValue() instanceof LttngLogAppender) {
+                               return true;
+                       }
+               }
+
+               /*
+                * A parent logger, if any, may be connected to an LTTng handler. In this case,
+                * we will want to include this child logger in the output, since it will be
+                * accessible by LTTng.
+                *
+                * Despite the doc, getParent can return null based on the implementation as of
+                * log4j 2.17.1.
+                *
+                * The getParent function is there as a backward compat for 1.x. It is not clear
+                * in which context it should be used. The cost of doing the lookup is minimal
+                * and mimics what was done for the 1.x agent.
+                */
+               return hasLttngAppenderAttached(logger.getParent());
+
+       }
+}
This page took 0.025226 seconds and 4 git commands to generate.