Move to kernel style SPDX license identifiers
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / LTTngAgent.java
index e83504d94d8f3f26072cca3b0765da8182597850..f6aae3513ac42acae5834fd833ea1fe3552b0038 100644 (file)
 /*
- * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ * SPDX-License-Identifier: LGPL-2.1-only
  *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License, version 2.1 only,
- * as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
  */
 
 package org.lttng.ust.agent;
 
-import java.io.IOException;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
+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 {
 
-       /* Domains */
-       static enum Domain {
-               JUL(3), LOG4J(4);
-               private int value;
+       private static LTTngAgent instance = null;
 
-               private Domain(int value) {
-                       this.value = value;
+       /**
+        * 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;
+       }
 
-               public int value() {
-                       return value;
+       /**
+        * 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 static final int SEM_TIMEOUT = 3; /* Seconds */
-
-       private static LogFramework julUser;
-       private static LogFramework julRoot;
-       private static LogFramework log4jUser;
-       private static LogFramework log4jRoot;
-
-       /* Sessiond clients */
-       private static LTTngTCPSessiondClient julUserClient;
-       private static LTTngTCPSessiondClient julRootClient;
-       private static LTTngTCPSessiondClient log4jUserClient;
-       private static LTTngTCPSessiondClient log4jRootClient;
-
-       private static Thread sessiondThreadJULUser;
-       private static Thread sessiondThreadJULRoot;
-       private static Thread sessiondThreadLog4jUser;
-       private static Thread sessiondThreadLog4jRoot;
-
-       private boolean useJUL = false;
-       private boolean useLog4j = false;
+       private ILttngHandler julHandler = null;
+       private ILttngHandler log4jAppender = null;
 
-       /* Singleton agent object */
-       private static LTTngAgent curAgent = null;
-
-       /* Indicate if this object has been initialized. */
-       private static boolean initialized = false;
-
-       private static Semaphore registerSem;
-
-       /*
-        * Constructor is private. This is a singleton and a reference should be
-        * acquired using getLTTngAgent().
+       /**
+        * Private constructor. This is a singleton and a reference should be
+        * acquired using {@link #getLTTngAgent()}.
         */
        private LTTngAgent() {
-               initAgentJULClasses();
-
-               /* Since Log4j is a 3rd party JAR, we need to check if we can load any of its classes */
-               Boolean log4jLoaded = loadLog4jClasses();
-               if (log4jLoaded) {
-                       initAgentLog4jClasses();
-               }
-
-               registerSem = new Semaphore(0, true);
+               initJulHandler();
+               initLog4jAppender();
        }
 
-       private static Boolean loadLog4jClasses() {
-               Class<?> logging;
+       /**
+        * "Destructor" method.
+        */
+       private void disposeInstance() {
+               disposeJulHandler();
+               disposeLog4jAppender();
+       }
 
+       /**
+        * Create a LTTng-JUL handler, and attach it to the JUL root logger.
+        */
+       private void initJulHandler() {
                try {
-                       logging = loadClass("org.apache.log4j.spi.LoggingEvent");
+                       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) {
-                       /* 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 {
-                       logging.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 (NullPointerException e) {
-                       /* Should never happen */
-                       return false;
-               } catch (SecurityException e) {
-                       return false;
+               } catch (InstantiationException e) {
+               } catch (InvocationTargetException e) {
                }
-
-               return true;
        }
 
-       private static Class<?> loadClass(String className) throws ClassNotFoundException {
-               ClassLoader loader;
-               Class<?> loadedClass;
-
-               try {
-                       /* Try to load class using the current thread's context class loader */
-                       loader = Thread.currentThread().getContextClassLoader();
-                       loadedClass = loader.loadClass(className);
-               } catch (ClassNotFoundException e) {
-                       /* Loading failed, try using the system class loader */
-                       loader = ClassLoader.getSystemClassLoader();
-                       loadedClass = loader.loadClass(className);
+       /**
+        * 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;
                }
 
-               return loadedClass;
-       }
-
-       private void initAgentJULClasses() {
                try {
-                       Class<?> lttngJUL = loadClass("org.lttng.ust.agent.jul.LTTngJUL");
-                       julUser = (LogFramework) lttngJUL.getDeclaredConstructor(new Class[] { Boolean.class }).newInstance(false);
-                       julRoot = (LogFramework) lttngJUL.getDeclaredConstructor(new Class[] { Boolean.class }).newInstance(true);
-                       this.useJUL = true;
+                       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) {
-                       /* LTTng JUL classes not found, no need to create the relevant objects */
-                       this.useJUL = false;
-               } catch (InstantiationException e) {
-                       this.useJUL = false;
+                       return;
                } catch (NoSuchMethodException e) {
-                       this.useJUL = false;
+                       return;
+               } catch (IllegalArgumentException e) {
+                       return;
+               } catch (InstantiationException e) {
+                       return;
                } catch (IllegalAccessException e) {
-                       this.useJUL = false;
+                       return;
                } catch (InvocationTargetException e) {
-                       this.useJUL = false;
+                       return;
                }
-       }
 
-       private void initAgentLog4jClasses() {
+               /*
+                * 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<?> lttngLog4j = loadClass("org.lttng.ust.agent.log4j.LTTngLog4j");
-                       log4jUser = (LogFramework)lttngLog4j.getDeclaredConstructor(new Class[] {Boolean.class}).newInstance(false);
-                       log4jRoot = (LogFramework)lttngLog4j.getDeclaredConstructor(new Class[] {Boolean.class}).newInstance(true);
-                       this.useLog4j = true;
+                       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) {
-                       /* LTTng Log4j classes not found, no need to create the relevant objects */
-                       this.useLog4j = false;
-               } catch (InstantiationException e) {
-                       this.useLog4j = false;
+                       throw new IllegalStateException(e);
                } catch (NoSuchMethodException e) {
-                       this.useLog4j = false;
+                       throw new IllegalStateException(e);
+               } catch (IllegalArgumentException e) {
+                       throw new IllegalStateException(e);
                } catch (IllegalAccessException e) {
-                       this.useLog4j = false;
+                       throw new IllegalStateException(e);
                } catch (InvocationTargetException e) {
-                       this.useLog4j = false;
+                       throw new IllegalStateException(e);
                }
        }
 
        /**
-        * Public getter to acquire a reference to this singleton object.
-        *
-        * @return The agent instance
-        * @throws IOException
+        * Check if log4j >= 1.2.15 library is present.
         */
-       public static synchronized LTTngAgent getLTTngAgent() throws IOException {
-               if (curAgent == null) {
-                       curAgent = new LTTngAgent();
-                       curAgent.init();
-               }
-
-               return curAgent;
-       }
-
-       private synchronized void init() throws SecurityException {
-               if (initialized) {
-                       return;
-               }
-
-               Integer numJULThreads = 0;
-               Integer numLog4jThreads = 0;
+       private static boolean testLog4jClasses() {
+               Class<?> loggingEventClass;
 
-               if (this.useJUL) {
-                       numJULThreads = initJULClientThreads();
-               }
-
-               if (this.useLog4j) {
-                       numLog4jThreads = initLog4jClientThreads();
+               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;
                }
 
-               Integer numThreads = numJULThreads + numLog4jThreads;
-
-               /* Wait for each registration to end. */
+               /*
+                * 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 {
-                       registerSem.tryAcquire(numThreads,
-                                                   SEM_TIMEOUT,
-                                                   TimeUnit.SECONDS);
-               } catch (InterruptedException e) {
-                       e.printStackTrace();
+                       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;
                }
 
-               initialized = true;
-       }
-
-       private synchronized static Integer initJULClientThreads() {
-               Integer numThreads = 2;
-
-               /* Handle user session daemon if any. */
-               julUserClient = new LTTngTCPSessiondClient(Domain.JUL,
-                                                               julUser,
-                                                               registerSem);
-
-               String userThreadName = "LTTng UST agent JUL user thread";
-               sessiondThreadJULUser = new Thread(julUserClient, userThreadName);
-               sessiondThreadJULUser.setDaemon(true);
-               sessiondThreadJULUser.start();
-
-               /* Handle root session daemon. */
-               julRootClient = new LTTngTCPSessiondClient(Domain.JUL,
-                                                               julRoot,
-                                                               registerSem);
-
-               String rootThreadName = "LTTng UST agent JUL root thread";
-               sessiondThreadJULRoot = new Thread(julRootClient, rootThreadName);
-               sessiondThreadJULRoot.setDaemon(true);
-               sessiondThreadJULRoot.start();
-
-               return numThreads;
-       }
-
-       private synchronized static Integer initLog4jClientThreads() {
-               Integer numThreads = 2;
-
-               log4jUserClient = new LTTngTCPSessiondClient(Domain.LOG4J,
-                                                                 log4jUser,
-                                                                 registerSem);
-
-               String userThreadName = "LTTng UST agent Log4j user thread";
-               sessiondThreadLog4jUser = new Thread(log4jUserClient, userThreadName);
-               sessiondThreadLog4jUser.setDaemon(true);
-               sessiondThreadLog4jUser.start();
-
-               log4jRootClient = new LTTngTCPSessiondClient(Domain.LOG4J,
-                                                                 log4jRoot,
-                                                                 registerSem);
-
-               String rootThreadName = "LTTng UST agent Log4j root thread";
-               sessiondThreadLog4jRoot = new Thread(log4jRootClient,rootThreadName);
-               sessiondThreadLog4jRoot.setDaemon(true);
-               sessiondThreadLog4jRoot.start();
-
-               return numThreads;
+               return true;
        }
 
        /**
-        * Dispose the agent. Applications should call this once they are done
-        * logging.
+        * Detach the JUL handler from its logger and close it.
         */
-       public void dispose() {
-               if (this.useJUL) {
-                       julUserClient.destroy();
-                       julRootClient.destroy();
-                       julUser.reset();
-                       julRoot.reset();
+       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;
+       }
 
-               if (this.useLog4j) {
-                       log4jUserClient.destroy();
-                       log4jRootClient.destroy();
-                       log4jUser.reset();
-                       log4jRoot.reset();
+       /**
+        * 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 {
-                       if (this.useJUL) {
-                               sessiondThreadJULUser.join();
-                               sessiondThreadJULRoot.join();
-                       }
+                       Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
+                       Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
 
-                       if (this.useLog4j) {
-                               sessiondThreadLog4jUser.join();
-                               sessiondThreadLog4jRoot.join();
-                       }
+                       Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
+                       Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass);
 
-               } catch (InterruptedException e) {
-                       e.printStackTrace();
+                       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;
        }
+
 }
This page took 0.028533 seconds and 4 git commands to generate.