--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Handler;
+import java.util.logging.Logger;
+
+/**
+ * The central agent managing the JUL and Log4j handlers.
+ *
+ * @author David Goulet
+ * @deprecated Applications are now expected to manage their Logger and Handler
+ * objects.
+ */
+@Deprecated
+public class LTTngAgent {
+
+ private static LTTngAgent instance = null;
+
+ /**
+ * Public getter to acquire a reference to this singleton object.
+ *
+ * @return The agent instance
+ */
+ public static synchronized LTTngAgent getLTTngAgent() {
+ if (instance == null) {
+ instance = new LTTngAgent();
+ }
+ return instance;
+ }
+
+ /**
+ * Dispose the agent. Applications should call this once they are done
+ * logging. This dispose function is non-static for backwards
+ * compatibility purposes.
+ */
+ @SuppressWarnings("static-method")
+ public void dispose() {
+ synchronized (LTTngAgent.class) {
+ if (instance != null) {
+ instance.disposeInstance();
+ instance = null;
+ }
+ }
+ return;
+ }
+
+ private ILttngHandler julHandler = null;
+ private ILttngHandler log4jAppender = null;
+
+ /**
+ * Private constructor. This is a singleton and a reference should be
+ * acquired using {@link #getLTTngAgent()}.
+ */
+ private LTTngAgent() {
+ initJulHandler();
+ initLog4jAppender();
+ }
+
+ /**
+ * "Destructor" method.
+ */
+ private void disposeInstance() {
+ disposeJulHandler();
+ disposeLog4jAppender();
+ }
+
+ /**
+ * Create a LTTng-JUL handler, and attach it to the JUL root logger.
+ */
+ private void initJulHandler() {
+ try {
+ Class<?> julHandlerClass = Class.forName("org.lttng.ust.agent.jul.LttngLogHandler");
+ /*
+ * It is safer to use Constructor.newInstance() rather than
+ * Class.newInstance(), because it will catch the exceptions thrown
+ * by the constructor below (which happens if the Java library is
+ * present, but the matching JNI one is not).
+ */
+ Constructor<?> julHandlerCtor = julHandlerClass.getConstructor();
+ julHandler = (ILttngHandler) julHandlerCtor.newInstance();
+
+ /* Attach the handler to the root JUL logger */
+ Logger.getLogger("").addHandler((Handler) julHandler);
+
+ /*
+ * If any of the following exceptions happen, it means we could not
+ * find or initialize LTTng JUL classes. We will not setup LTTng JUL
+ * tracing in this case.
+ */
+ } catch (SecurityException e) {
+ } catch (IllegalAccessException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (ClassNotFoundException e) {
+ } catch (NoSuchMethodException e) {
+ } catch (InstantiationException e) {
+ } catch (InvocationTargetException e) {
+ }
+ }
+
+ /**
+ * Create a LTTng-logj4 appender, and attach it to the log4j root logger.
+ */
+ private void initLog4jAppender() {
+ /*
+ * Since Log4j is a 3rd party library, we first need to check if we can
+ * load any of its classes.
+ */
+ if (!testLog4jClasses()) {
+ return;
+ }
+
+ try {
+ Class<?> log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender");
+ Constructor<?> log4jAppendCtor = log4jAppenderClass.getConstructor();
+ log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance();
+
+ /*
+ * If any of the following exceptions happen, it means we could not
+ * find or initialize LTTng log4j classes. We will not setup LTTng
+ * log4j tracing in this case.
+ */
+ } catch (SecurityException e) {
+ return;
+ } catch (ClassNotFoundException e) {
+ return;
+ } catch (NoSuchMethodException e) {
+ return;
+ } catch (IllegalArgumentException e) {
+ return;
+ } catch (InstantiationException e) {
+ return;
+ } catch (IllegalAccessException e) {
+ return;
+ } catch (InvocationTargetException e) {
+ return;
+ }
+
+ /*
+ * Attach the appender to the root Log4j logger. Slightly more tricky
+ * here, as log4j.Logger is not in the base Java library, and we do not
+ * want the "common" package to depend on log4j. So we have to obtain it
+ * through reflection too.
+ */
+ try {
+ Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
+ Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
+
+ Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
+ Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass);
+
+ Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
+ addAppenderMethod.invoke(rootLogger, log4jAppender);
+
+ /*
+ * We have checked for the log4j library version previously, none of
+ * the following exceptions should happen.
+ */
+ } catch (SecurityException e) {
+ throw new IllegalStateException(e);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Check if log4j >= 1.2.15 library is present.
+ */
+ private static boolean testLog4jClasses() {
+ Class<?> loggingEventClass;
+
+ try {
+ loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent");
+ } catch (ClassNotFoundException e) {
+ /*
+ * Log4j classes not found, no need to create the relevant objects
+ */
+ return false;
+ }
+
+ /*
+ * Detect capabilities of the log4j library. We only support log4j >=
+ * 1.2.15. The getTimeStamp() method was introduced in log4j 1.2.15, so
+ * verify that it is available.
+ *
+ * We can't rely on the getPackage().getImplementationVersion() call
+ * that would retrieves information from the manifest file found in the
+ * JAR since the manifest file shipped from upstream is known to be
+ * broken in several versions of the library.
+ *
+ * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370
+ */
+ try {
+ loggingEventClass.getDeclaredMethod("getTimeStamp");
+ } catch (NoSuchMethodException e) {
+ System.err.println(
+ "Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled.");
+ return false;
+ } catch (SecurityException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Detach the JUL handler from its logger and close it.
+ */
+ private void disposeJulHandler() {
+ if (julHandler == null) {
+ /* The JUL handler was not activated, we have nothing to do */
+ return;
+ }
+ Logger.getLogger("").removeHandler((Handler) julHandler);
+ julHandler.close();
+ julHandler = null;
+ }
+
+ /**
+ * Detach the log4j appender from its logger and close it.
+ */
+ private void disposeLog4jAppender() {
+ if (log4jAppender == null) {
+ /* The log4j appender was not active, we have nothing to do */
+ return;
+ }
+
+ /*
+ * Detach the appender from the log4j root logger. Again, we have to do
+ * this via reflection.
+ */
+ try {
+ Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
+ Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
+
+ Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
+ Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass);
+
+ Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
+ removeAppenderMethod.invoke(rootLogger, log4jAppender);
+
+ /*
+ * We were able to attach the appender previously, we should not
+ * have problems here either!
+ */
+ } catch (SecurityException e) {
+ throw new IllegalStateException(e);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+
+ /* Close the appender */
+ log4jAppender.close();
+ log4jAppender = null;
+ }
+
+}