X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust-java-agent%2Fjava%2Flttng-ust-agent-common%2Forg%2Flttng%2Fust%2Fagent%2FLTTngAgent.java;h=f6aae3513ac42acae5834fd833ea1fe3552b0038;hb=c0c0989ab70574e09b2f7e8b48c2da6af664a849;hp=e83504d94d8f3f26072cca3b0765da8182597850;hpb=5bfeaeca7fd73ebdc5aa9f555213055272daab29;p=lttng-ust.git diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngAgent.java index e83504d9..f6aae351 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngAgent.java +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngAgent.java @@ -1,318 +1,280 @@ /* - * Copyright (C) 2013 - David Goulet + * 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 */ 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; } + }