From d60dfbe48a0ceff16852f46419bcbc405502c61d Mon Sep 17 00:00:00 2001 From: Alexandre Montplaisir Date: Tue, 21 Jul 2015 22:56:15 -0400 Subject: [PATCH] Refactor Java agent to let applications manage the log handlers Major refactoring of the UST Java agent and its JUL and log4j logging facilities. Split up the big LTTngAgent class into two separate agent singletons, one for JUL and one for log4j. The large parts of common code remain in the -common package in AbstractLttngAgent, with the API-specific implementations in their respective -jul or -log4j package. This removes the need for circular-dependencies (which were worked around using reflection) between the common and the other packages. The applications are now expected to instantiate their JUL Handler or log4j LogAppender themselves, and to attach them to corresponding Logger objects. The LttngLogHandler and LttngLogAppender classes are now made public to allow this. Agents are now spawned automatically on demand, as Handler objects are created. Once the last handler/appender is close()'d, the agent is disposed as well. This reduces the number of threads and socket connections to the minimum that is necessary. The two separate tracepoints in the JNI libraries, "tracepointS" and "tracepointU", are now merged into one. Events sent to either the root or user session daemon will use the same call. The LTTngAgent.getLTTngAgent() API is kept for compatibility with applications using it, but now marked @Deprecated. The functionality underneath was re-implemented using the new classes, but the behaviour should be kept the same. Signed-off-by: Alexandre Montplaisir Signed-off-by: Mathieu Desnoyers --- .gitignore | 4 +- doc/examples/java-jul/Hello.java | 96 +++-- doc/examples/java-log4j/Hello.java | 44 +- doc/java-agent.txt | 66 ++- .../java/lttng-ust-agent-common/Makefile.am | 20 +- .../lttng/ust/agent/AbstractLttngAgent.java | 321 ++++++++++++++ .../org/lttng/ust/agent/ILttngAgent.java | 104 +++++ .../{LogFramework.java => ILttngHandler.java} | 28 +- .../org/lttng/ust/agent/LTTngAgent.java | 400 +++++++----------- .../lttng/ust/agent/LTTngSessiondCmd2_6.java | 249 ----------- .../lttng/ust/agent/LogFrameworkSkeleton.java | 107 ----- .../ust/agent/client/ISessiondCommand.java | 52 +++ .../ust/agent/client/ISessiondResponse.java | 44 ++ .../LttngTcpSessiondClient.java} | 231 +++++----- .../agent/client/SessiondDisableHandler.java | 73 ++++ .../agent/client/SessiondEnableHandler.java | 73 ++++ .../agent/client/SessiondHeaderCommand.java | 50 +++ .../client/SessiondListLoggersResponse.java | 67 +++ .../java/lttng-ust-agent-jul/Makefile.am | 8 +- .../org/lttng/ust/agent/jul/LTTngJUL.java | 125 ------ .../lttng/ust/agent/jul/LTTngLogHandler.java | 92 ---- .../lttng/ust/agent/jul/LttngJulAgent.java | 42 ++ .../lttng/ust/agent/jul/LttngLogHandler.java | 129 ++++++ .../java/lttng-ust-agent-log4j/Makefile.am | 8 +- .../org/lttng/ust/agent/log4j/LTTngLog4j.java | 121 ------ .../ust/agent/log4j/LTTngLogAppender.java | 115 ----- .../ust/agent/log4j/LttngLog4jAgent.java | 43 ++ .../ust/agent/log4j/LttngLogAppender.java | 142 +++++++ liblttng-ust-java-agent/jni/jul/Makefile.am | 2 +- .../jni/jul/lttng_ust_jul.c | 35 +- .../jni/jul/lttng_ust_jul.h | 28 +- liblttng-ust-java-agent/jni/log4j/Makefile.am | 2 +- .../jni/log4j/lttng_ust_log4j.c | 39 +- .../jni/log4j/lttng_ust_log4j.h | 32 +- 34 files changed, 1621 insertions(+), 1371 deletions(-) create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java rename liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/{LogFramework.java => ILttngHandler.java} (58%) delete mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngSessiondCmd2_6.java delete mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFrameworkSkeleton.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondCommand.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondResponse.java rename liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/{LTTngTCPSessiondClient.java => client/LttngTcpSessiondClient.java} (54%) create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableHandler.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableHandler.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondHeaderCommand.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondListLoggersResponse.java delete mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngJUL.java delete mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngLogHandler.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulAgent.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java delete mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLog4j.java delete mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLogAppender.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jAgent.java create mode 100644 liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java diff --git a/.gitignore b/.gitignore index bcd0ecb9..4454fa36 100644 --- a/.gitignore +++ b/.gitignore @@ -59,8 +59,8 @@ classnoinst.stamp jni-header.stamp jul-jni-header.stamp log4j-jni-header.stamp -org_lttng_ust_agent_jul_LTTngLogHandler.h -org_lttng_ust_agent_log4j_LTTngLogAppender.h +org_lttng_ust_agent_jul_LttngLogHandler.h +org_lttng_ust_agent_log4j_LttngLogAppender.h # Python agent liblttng-ust-python-agent/__init__.py diff --git a/doc/examples/java-jul/Hello.java b/doc/examples/java-jul/Hello.java index 28360c72..7016cf49 100644 --- a/doc/examples/java-jul/Hello.java +++ b/doc/examples/java-jul/Hello.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir * Copyright (C) 2013 - David Goulet * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -20,54 +21,49 @@ * IN THE SOFTWARE. */ +import java.io.IOException; +import java.util.logging.Handler; import java.util.logging.Logger; -/* - * That's the import you need being the path in the liblttng-ust-jul Jar file. - */ -import org.lttng.ust.agent.LTTngAgent; +import org.lttng.ust.agent.jul.LttngLogHandler; /** * Example application using the LTTng-UST Java JUL agent. * + *

+ * Basically all that is required is to instantiate a {@link LttngLogHandler} + * and to attach it to a JUL {@link Logger}. Then use the Logger normally to log + * messages, which will be sent to UST as trace events. + *

+ *

+ * {@link Logger} names are used as event names on the UST side. This means that + * by enabling/disabling certain events in the tracing session, you are + * effectively enabling and disabling Loggers on the Java side. Note that this + * applies only to {@link LttngLogHandler}'s. If other handlers are attached to + * the Logger, those will continue logging events normally. + *

+ * + * @author Alexandre Montplaisir * @author David Goulet */ public class Hello { - /* Of course :) */ - private static final int answer = 42; - - /* - * Static reference to the LTTngAgent. Used to dispose of it at the end - * which is recommended but not mandatory to do. - */ - private static LTTngAgent lttngAgent; + /** Class-wide JUL logger object */ + private static final Logger LOGGER = Logger.getLogger(Hello.class.getName()); /** * Application start * * @param args * Command-line arguments - * @throws Exception + * @throws IOException + * @throws InterruptedException */ - public static void main(String args[]) throws Exception { - /* - * For this example, a custom "hello" logger is created. Note that JUL - * has a default "global" that can also be used. - */ - Logger helloLog = Logger.getLogger("hello"); + public static void main(String args[]) throws IOException, InterruptedException { - /* - * Get the LTTngAgent singleton reference. This will also initialize - * the Agent and make it register to the session daemon if available. - * When this returns, the Agent is registered and fully ready. If no - * session daemon is found, it will return and retry every 3 seconds in - * the background. TCP is used for communication. - * - * Note that the LTTngAgent once registered is a separate thread in - * your Java application. - */ - lttngAgent = LTTngAgent.getLTTngAgent(); + /* Instantiate a LTTngLogHandler object, and attach it to our logger */ + Handler lttngHandler = new LttngLogHandler(); + LOGGER.addHandler(lttngHandler); /* * Gives you time to do some lttng commands before any event is hit. @@ -75,27 +71,40 @@ public class Hello { Thread.sleep(5000); /* Trigger a tracing event using the JUL Logger created before. */ - helloLog.info("Hello World, the answer is " + answer); + LOGGER.info("Hello World, the answer is " + 42); /* * From this point on, the above message will be collected in the trace - * if the event "hello" is enabled for the JUL domain using the lttng + * if the event "Hello" is enabled for the JUL domain using the lttng * command line or the lttng-ctl API. For instance: * - * $ lttng enable-event -j hello + * $ lttng enable-event -j Hello + */ + + /* + * A new logger is created here and fired after. Typically with JUL, you + * use one static Logger per class. This example here can represent a + * class being lazy-loaded later in the execution of the application. * - * A new logger is created here and fired after. The Agent has an - * internal timer that is fired every 5 seconds in order to enable - * events that were not found at first but might need to be enabled - * when new Logger appears. Unfortunately, there is no way right now to - * get notify of that so we have to actively poll. + * The agent has an internal timer that is fired every 5 seconds in + * order to enable events that were not found at first but might need to + * be enabled when a new Logger appears. Unfortunately, there is no way + * right now to get notified of that so we have to actively poll. * * Using the --all command for instance, it will make this Logger - * available in a LTTng trace after the internal Agent's timer is - * fired. (lttng enable-event -a -j). + * available in a LTTng trace after the internal agent's timer is fired. + * (lttng enable-event -j -a). */ Logger helloLogDelayed = Logger.getLogger("hello_delay"); + /* + * Attach a handler to this new logger. + * + * Using the same handler as before would also work. + */ + Handler lttngHandler2 = new LttngLogHandler(); + helloLogDelayed.addHandler(lttngHandler2); + System.out.println("Firing hello delay in 10 seconds..."); Thread.sleep(10000); helloLogDelayed.info("Hello World delayed..."); @@ -103,9 +112,10 @@ public class Hello { System.out.println("Cleaning Hello"); /* - * Again, this is highly recommended so the session daemon socket gets - * cleaned up explicitly but it is not mandatory to do this step. + * Do not forget to close() all handlers so that the agent can shutdown + * and the session daemon socket gets cleaned up explicitly. */ - lttngAgent.dispose(); + lttngHandler.close(); + lttngHandler2.close(); } } diff --git a/doc/examples/java-log4j/Hello.java b/doc/examples/java-log4j/Hello.java index c0393392..c51d4d40 100644 --- a/doc/examples/java-log4j/Hello.java +++ b/doc/examples/java-log4j/Hello.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir * Copyright (C) 2014 - Christian Babeux * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -20,47 +21,60 @@ * IN THE SOFTWARE. */ +import java.io.IOException; + +import org.apache.log4j.Appender; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; -import org.lttng.ust.agent.LTTngAgent; +import org.lttng.ust.agent.log4j.LttngLogAppender; /** * Example application using the LTTng-UST Java JUL agent. * + * @author Alexandre Montplaisir * @author Christian Babeux */ public class Hello { - /* Of course :) */ - private static final int answer = 42; - - static Logger helloLog = Logger.getLogger(Hello.class); - - private static LTTngAgent lttngAgent; + private static final Logger HELLO_LOG = Logger.getLogger(Hello.class); /** * Application start * * @param args * Command-line arguments - * @throws Exception + * @throws IOException + * @throws InterruptedException */ - public static void main(String args[]) throws Exception { + public static void main(String args[]) throws IOException, InterruptedException { + /* Start with the default Log4j configuration, which logs to console */ BasicConfigurator.configure(); - lttngAgent = LTTngAgent.getLTTngAgent(); + + /* + * Add a LTTng log appender to the logger, which will also send the + * logged events to UST. + */ + Appender lttngAppender = new LttngLogAppender(); + HELLO_LOG.addAppender(lttngAppender); + + /* + * Here we've set up the appender programmatically, but it could also be + * defined at runtime, by reading a configuration file for example: + */ + // PropertyConfigurator.configure(fileName); /* * Gives you time to do some lttng commands before any event is hit. */ Thread.sleep(5000); - /* Trigger a tracing event using the JUL Logger created before. */ - helloLog.info("Hello World, the answer is " + answer); + /* Trigger a tracing event using the Log4j Logger created before. */ + HELLO_LOG.info("Hello World, the answer is " + 42); - System.out.println("Firing hello delay in 5 seconds..."); + System.out.println("Firing second event in 5 seconds..."); Thread.sleep(5000); - helloLog.info("Hello World delayed..."); + HELLO_LOG.info("Hello World delayed..."); - lttngAgent.dispose(); + lttngAppender.close(); } } diff --git a/doc/java-agent.txt b/doc/java-agent.txt index 3fc6c493..b5f722c7 100644 --- a/doc/java-agent.txt +++ b/doc/java-agent.txt @@ -1,3 +1,7 @@ +====================== + Using the Java agent +====================== + The agent can be built in three different configurations: 1) Java agent with JUL support: @@ -32,18 +36,56 @@ application's classpath. Both logging libraries also require an architecture-specific shared object (e.g: "liblttng-ust-jul-jni.so"), which is installed by the build system when doing "make install". Make sure that your Java application can find this shared -object with the "java.library.path" property. +object, by using the "java.library.path" property if necessary. + +In order to use UST tracing in your Java application, you simply need to +instantiate a LttngLogHandler or a LttngLogAppender (for JUL or Log4j, +respectively), then attach it to a JUL or Log4j Logger class. + +Refer to the code examples in examples/java-jul/ and examples/java-log4j/. + +LTTng session daemon agents will be initialized as needed. If no session daemon +is available, the execution will continue and the agents will retry connecting +every 3 seconds. + + +============== + Object model +============== + +The object model of the Java agent implementation is as follows: + +--------- +Ownership +--------- +Log Handlers: LttngLogHandler, LttngLogAppender + n handlers/appenders, managed by the application. + Can be created programmatically, or via a configuration file, + Each one registers to a specific agent singleton (one per logging API) that is loaded on-demand + +Agent singletons: LttngJulAgent, LttngLog4jAgent + Keep track of all handlers/appenders registered to them. + Are disposed when last handler deregisters. + Each agent instantiates 2 TCP clients, one for the root session daemon, one for the user one. + One type of TCP client class for now. TCP client may become a singleton in the future. + +------- +Control +------- +Messages come from the session daemon through the socket connection. +Agent passes back-reference to itself to the TCP clients. +Clients use this reference to invoke callbacks, which modify the state of the agent (enabling/disabling events, etc.) + +--------- +Data path +--------- +Log messages are generated by the application and sent to the Logger objects, +which then send them to the Handlers. -In order to enable the agent in your Java application, you simply have to add -this as early as you can in the runtime process. +When a log event is received by a Handler (publish(LogRecord)), the handler +checks with the agent if it should log it or not, via +ILttngAgent#isEventEnabled() for example. -import org.lttng.ust.agent.LTTngAgent; -[...] - private static LTTngAgent lttngAgent; - [...] - lttngAgent = LTTngAgent.getLTTngAgent(); +Events that are logged call the native tracepoint through JNI, which generates +a UST event. There is one type of tracepoint per domain (Jul or Logj4). -This will initialize automatically the singleton LTTngAgent, and will -return when the session daemon registration is done. If no session daemon is -available, the execution will continue and the agent will retry every -3 seconds. diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am b/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am index 46ac7cde..287956ff 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am @@ -10,17 +10,23 @@ jarfile = lttng-ust-agent-common-$(jarfile_version).jar jardir = $(datadir)/java -dist_noinst_JAVA = $(pkgpath)/LTTngAgent.java \ - $(pkgpath)/LTTngSessiondCmd2_6.java \ - $(pkgpath)/LTTngTCPSessiondClient.java \ - $(pkgpath)/LogFramework.java \ - $(pkgpath)/LogFrameworkSkeleton.java +dist_noinst_JAVA = $(pkgpath)/AbstractLttngAgent.java \ + $(pkgpath)/ILttngAgent.java \ + $(pkgpath)/ILttngHandler.java \ + $(pkgpath)/LTTngAgent.java \ + $(pkgpath)/client/ISessiondCommand.java \ + $(pkgpath)/client/ISessiondResponse.java \ + $(pkgpath)/client/LttngTcpSessiondClient.java \ + $(pkgpath)/client/SessiondDisableHandler.java \ + $(pkgpath)/client/SessiondEnableHandler.java \ + $(pkgpath)/client/SessiondHeaderCommand.java \ + $(pkgpath)/client/SessiondListLoggersResponse.java dist_noinst_DATA = $(jarfile_manifest) jar_DATA = $(jarfile) -classes = $(pkgpath)/*.class +classes = $(pkgpath)/*.class $(pkgpath)/client/*.class $(jarfile): $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink) @@ -33,4 +39,4 @@ install-data-hook: uninstall-hook: cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) -CLEANFILES = $(jarfile) $(pkgpath)/*.class +CLEANFILES = $(jarfile) $(pkgpath)/*.class $(pkgpath)/client/*.class diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java new file mode 100644 index 00000000..4e6d4af0 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.lttng.ust.agent.client.LttngTcpSessiondClient; + +/** + * Base implementation of a {@link ILttngAgent}. + * + * @author Alexandre Montplaisir + * @param + * The type of logging handler that should register to this agent + */ +public abstract class AbstractLttngAgent implements ILttngAgent { + + private static final String WILDCARD = "*"; + private static final int INIT_TIMEOUT = 3; /* Seconds */ + + /** The handlers registered to this agent */ + private final Set registeredHandlers = new HashSet(); + + /** + * The trace events currently enabled in the sessions. + * + * The key represents the event name, the value is the ref count (how many + * different sessions currently have this event enabled). Once the ref count + * falls to 0, this means we can avoid sending log events through JNI + * because nobody wants them. + * + * It uses a concurrent hash set", so that the {@link #isEventEnabled} and + * read methods do not need to take a synchronization lock. + */ + private final Map enabledEvents = new ConcurrentHashMap(); + + /** + * The trace events prefixes currently enabled in the sessions, which means + * the event names finishing in *, like "abcd*". We track them separately + * from the standard event names, so that we can use {@link String#equals} + * and {@link String#startsWith} appropriately. + * + * We track the lone wildcard "*" separately, in {@link #enabledWildcards}. + */ + private final NavigableMap enabledEventPrefixes = + new ConcurrentSkipListMap(); + + /** Number of sessions currently enabling the wildcard "*" event */ + private final AtomicInteger enabledWildcards = new AtomicInteger(0); + + /** Tracing domain. Defined by the sub-classes via the constructor. */ + private final Domain domain; + + /* Lazy-loaded sessiond clients and their thread objects */ + private LttngTcpSessiondClient rootSessiondClient = null; + private LttngTcpSessiondClient userSessiondClient = null; + private Thread rootSessiondClientThread = null; + private Thread userSessiondClientThread = null; + + /** Indicates if this agent has been initialized. */ + private boolean initialized = false; + + /** + * Constructor. Should only be called by sub-classes via super(...); + * + * @param domain + * The tracing domain of this agent. + */ + protected AbstractLttngAgent(Domain domain) { + this.domain = domain; + } + + @Override + public Domain getDomain() { + return domain; + } + + @Override + public void registerHandler(T handler) { + synchronized (registeredHandlers) { + if (registeredHandlers.isEmpty()) { + /* + * This is the first handler that registers, we will initialize + * the agent. + */ + init(); + } + registeredHandlers.add(handler); + } + } + + @Override + public void unregisterHandler(T handler) { + synchronized (registeredHandlers) { + registeredHandlers.remove(handler); + if (registeredHandlers.isEmpty()) { + /* There are no more registered handlers, close the connection. */ + dispose(); + } + } + } + + private void init() { + /* + * Only called from a synchronized (registeredHandlers) block, should + * not need additional synchronization. + */ + if (initialized) { + return; + } + String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName(); + + rootSessiondClient = new LttngTcpSessiondClient(this, true); + rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName); + rootSessiondClientThread.setDaemon(true); + rootSessiondClientThread.start(); + + String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName(); + + userSessiondClient = new LttngTcpSessiondClient(this, false); + userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName); + userSessiondClientThread.setDaemon(true); + userSessiondClientThread.start(); + + /* Give the threads' registration a chance to end. */ + if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) { + userSessiondClient.waitForConnection(INIT_TIMEOUT); + } + + initialized = true; + } + + /** + * Dispose the agent + */ + private void dispose() { + /* + * Only called from a synchronized (registeredHandlers) block, should + * not need additional synchronization. + */ + rootSessiondClient.close(); + userSessiondClient.close(); + + try { + rootSessiondClientThread.join(); + userSessiondClientThread.join(); + + } catch (InterruptedException e) { + e.printStackTrace(); + } + rootSessiondClient = null; + rootSessiondClientThread = null; + userSessiondClient = null; + userSessiondClientThread = null; + + /* Reset all enabled event counts to 0 */ + enabledEvents.clear(); + enabledEventPrefixes.clear(); + enabledWildcards.set(0); + + initialized = false; + + } + + /** + * Callback for the TCP clients to notify the agent that a request for + * enabling an event was sent from the session daemon. + * + * @param eventName + * The name of the event that was requested to be enabled. + * @return Since we do not track individual sessions, right now this command + * cannot fail. It will always return true. + */ + public boolean eventEnabled(String eventName) { + if (eventName.equals(WILDCARD)) { + enabledWildcards.incrementAndGet(); + return true; + } + + if (eventName.endsWith(WILDCARD)) { + /* Strip the "*" from the name. */ + String prefix = eventName.substring(0, eventName.length() - 1); + return incrementEventCount(prefix, enabledEventPrefixes); + } + + return incrementEventCount(eventName, enabledEvents); + } + + /** + * Callback for the TCP clients to notify the agent that a request for + * disabling an event was sent from the session daemon. + * + * @param eventName + * The name of the event that was requested to be disabled. + * @return True if the command completed successfully, false if we should + * report an error (event was not enabled, etc.) + */ + public boolean eventDisabled(String eventName) { + if (eventName.equals(WILDCARD)) { + int newCount = enabledWildcards.decrementAndGet(); + if (newCount < 0) { + /* Event was not enabled, bring the count back to 0 */ + enabledWildcards.incrementAndGet(); + return false; + } + } + + if (eventName.endsWith(WILDCARD)) { + /* Strip the "*" from the name. */ + String prefix = eventName.substring(0, eventName.length() - 1); + return decrementEventCount(prefix, enabledEventPrefixes); + } + + return decrementEventCount(eventName, enabledEvents); + } + + @Override + public boolean isEventEnabled(String eventName) { + /* If at least one session enabled the "*" wildcard, send the event */ + if (enabledWildcards.get() > 0) { + return true; + } + + /* Check if at least one session wants this exact event name */ + if (enabledEvents.containsKey(eventName)) { + return true; + } + + /* Look in the enabled prefixes if one of them matches the event */ + String potentialMatch = enabledEventPrefixes.floorKey(eventName); + if (potentialMatch != null && eventName.startsWith(potentialMatch)) { + return true; + } + + return false; + } + + @Override + public Iterable listEnabledEvents() { + List events = new LinkedList(); + + if (enabledWildcards.get() > 0) { + events.add(WILDCARD); + } + for (String prefix : enabledEventPrefixes.keySet()) { + events.add(new String(prefix + WILDCARD)); + } + events.addAll(enabledEvents.keySet()); + return events; + } + + private static boolean incrementEventCount(String eventName, Map eventMap) { + synchronized (eventMap) { + Integer count = eventMap.get(eventName); + if (count == null) { + /* This is the first instance of this event being enabled */ + eventMap.put(eventName, Integer.valueOf(1)); + return true; + } + if (count.intValue() <= 0) { + /* It should not have been in the map in the first place! */ + throw new IllegalStateException(); + } + /* The event was already enabled, increment its refcount */ + eventMap.put(eventName, Integer.valueOf(count.intValue() + 1)); + return true; + } + } + + private static boolean decrementEventCount(String eventName, Map eventMap) { + synchronized (eventMap) { + Integer count = eventMap.get(eventName); + if (count == null || count.intValue() <= 0) { + /* + * The sessiond asked us to disable an event that was not + * enabled previously. Command error? + */ + return false; + } + if (count.intValue() == 1) { + /* + * This is the last instance of this event being disabled, + * remove it from the map so that we stop sending it. + */ + eventMap.remove(eventName); + return true; + } + /* + * Other sessions are still looking for this event, simply decrement + * its refcount. + */ + eventMap.put(eventName, Integer.valueOf(count.intValue() - 1)); + return true; + } + } +} + diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java new file mode 100644 index 00000000..044bdf07 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * + * 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 + */ + +package org.lttng.ust.agent; + +/** + * Interface to define LTTng Java agents. + * + * An "agent" is a representative of an LTTng session daemon in the Java world. + * It tracks the settings of a tracing session as they defined in the session + * daemon. + * + * It also track the current logging handlers that are sending events to UST. + * + * @author Alexandre Montplaisir + * + * @param + * The type of logging handler that should register to this agent + */ +public interface ILttngAgent { + + // ------------------------------------------------------------------------ + // Agent configuration elements + // ------------------------------------------------------------------------ + + /** + * Tracing domains. Corresponds to domains defined by LTTng Tools. + */ + enum Domain { + JUL(3), LOG4J(4); + private int value; + + private Domain(int value) { + this.value = value; + } + + public int value() { + return value; + } + } + + /** + * The tracing domain of this agent. + * + * @return The tracing domain. + */ + Domain getDomain(); + + // ------------------------------------------------------------------------ + // Log handler registering + // ------------------------------------------------------------------------ + + /** + * Register a handler to this agent. + * + * @param handler + * The handler to register + */ + void registerHandler(T handler); + + /** + * Deregister a handler from this agent. + * + * @param handler + * The handler to deregister. + */ + void unregisterHandler(T handler); + + // ------------------------------------------------------------------------ + // Tracing session parameters + // ------------------------------------------------------------------------ + + /** + * Query if a given event is currently enabled in a current tracing session, + * meaning it should be sent to UST. May be quicker than listing all events + * via {@link #listEnabledEvents()}. + * + * @param eventName + * The name of the event to check. + * @return True if the event is currently enabled, false if it is not. + */ + boolean isEventEnabled(String eventName); + + /** + * List the all events currently enabled in the current tracing sessions. + * + * @return The list of enabled events + */ + Iterable listEnabledEvents(); +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFramework.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngHandler.java similarity index 58% rename from liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFramework.java rename to liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngHandler.java index 0dd7c982..f3ffab86 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFramework.java +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngHandler.java @@ -1,6 +1,5 @@ /* - * Copyright (C) 2014 - Christian Babeux - * + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir * * 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, @@ -18,12 +17,23 @@ package org.lttng.ust.agent; -import java.util.Iterator; +/** + * Simple interface to organize all LTTng log handlers under one type. + * + * @author Alexandre Montplaisir + */ +public interface ILttngHandler { + + /** + * Get the number of events logged by this handler since its inception. + * + * @return The number of logged events + */ + long getEventCount(); -interface LogFramework { - Boolean enableLogger(String name); - Boolean disableLogger(String name); - Iterator listLoggers(); - Boolean isRoot(); - void reset(); + /** + * Close the log handler. Should be called once the application is done + * logging through it. + */ + void close(); } 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..3c9a7997 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 @@ -17,110 +17,173 @@ package org.lttng.ust.agent; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; +import java.lang.reflect.Constructor; +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. + */ + public static synchronized void dispose() { + if (instance != null) { + instance.disposeInstance(); + instance = null; } + return; } - private static final int SEM_TIMEOUT = 3; /* Seconds */ + private ILttngHandler julHandler = null; + private ILttngHandler log4jAppender = null; - private static LogFramework julUser; - private static LogFramework julRoot; - private static LogFramework log4jUser; - private static LogFramework log4jRoot; + /** + * Private constructor. This is a singleton and a reference should be + * acquired using {@link #getLTTngAgent()}. + */ + private LTTngAgent() { + initJulHandler(); + initLog4jAppender(); + } - /* Sessiond clients */ - private static LTTngTCPSessiondClient julUserClient; - private static LTTngTCPSessiondClient julRootClient; - private static LTTngTCPSessiondClient log4jUserClient; - private static LTTngTCPSessiondClient log4jRootClient; + /** + * "Destructor" method. + */ + private void disposeInstance() { + disposeJulHandler(); + disposeLog4jAppender(); + } - private static Thread sessiondThreadJULUser; - private static Thread sessiondThreadJULRoot; - private static Thread sessiondThreadLog4jUser; - private static Thread sessiondThreadLog4jRoot; + /** + * 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); + } catch (ReflectiveOperationException e) { + /* + * LTTng JUL classes not found, no need to create the relevant + * objects + */ + } + } - private boolean useJUL = false; - private boolean useLog4j = false; + /** + * 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; + } - /* Singleton agent object */ - private static LTTngAgent curAgent = null; + try { + Class log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender"); + Constructor log4jAppendCtor = log4jAppenderClass.getConstructor(); + log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance(); + } catch (ReflectiveOperationException e) { + /* + * LTTng Log4j classes not found, no need to create the relevant + * objects. + */ + return; + } - /* Indicate if this object has been initialized. */ - private static boolean initialized = false; + /* + * 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"); - private static Semaphore registerSem; + Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class[]) null); + Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass); - /* - * Constructor is private. This is a singleton and a reference should be - * acquired using getLTTngAgent(). - */ - private LTTngAgent() { - initAgentJULClasses(); + Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null); + addAppenderMethod.invoke(rootLogger, log4jAppender); - /* 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(); + } catch (ReflectiveOperationException e) { + /* + * We have checked for the log4j library version previously, these + * classes should exist. + */ + throw new IllegalStateException(); } - - registerSem = new Semaphore(0, true); } - private static Boolean loadLog4jClasses() { - Class logging; + /** + * Check if log4j >= 1.2.15 library is present. + */ + private static boolean testLog4jClasses() { + Class loggingEventClass; try { - logging = loadClass("org.apache.log4j.spi.LoggingEvent"); + loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent"); } catch (ClassNotFoundException e) { - /* Log4j classes not found, no need to create the relevant objects */ + /* + * 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. + * 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. + * 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 + * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370 */ - try { - logging.getDeclaredMethod("getTimeStamp"); + 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 (NullPointerException e) { - /* Should never happen */ + 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; @@ -129,190 +192,53 @@ public class LTTngAgent { 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); - } - - 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; - } catch (ClassNotFoundException e) { - /* LTTng JUL classes not found, no need to create the relevant objects */ - this.useJUL = false; - } catch (InstantiationException e) { - this.useJUL = false; - } catch (NoSuchMethodException e) { - this.useJUL = false; - } catch (IllegalAccessException e) { - this.useJUL = false; - } catch (InvocationTargetException e) { - this.useJUL = false; - } - } - - private void initAgentLog4jClasses() { - 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; - } catch (ClassNotFoundException e) { - /* LTTng Log4j classes not found, no need to create the relevant objects */ - this.useLog4j = false; - } catch (InstantiationException e) { - this.useLog4j = false; - } catch (NoSuchMethodException e) { - this.useLog4j = false; - } catch (IllegalAccessException e) { - this.useLog4j = false; - } catch (InvocationTargetException e) { - this.useLog4j = false; - } - } - /** - * Public getter to acquire a reference to this singleton object. - * - * @return The agent instance - * @throws IOException + * Detach the JUL handler from its logger and close it. */ - 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) { + private void disposeJulHandler() { + if (julHandler == null) { + /* The JUL handler was not activated, we have nothing to do */ return; } - - Integer numJULThreads = 0; - Integer numLog4jThreads = 0; - - if (this.useJUL) { - numJULThreads = initJULClientThreads(); - } - - if (this.useLog4j) { - numLog4jThreads = initLog4jClientThreads(); - } - - Integer numThreads = numJULThreads + numLog4jThreads; - - /* Wait for each registration to end. */ - try { - registerSem.tryAcquire(numThreads, - SEM_TIMEOUT, - TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - 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; + Logger.getLogger("").removeHandler((Handler) julHandler); + julHandler.close(); + julHandler = null; } /** - * Dispose the agent. Applications should call this once they are done - * logging. + * Detach the log4j appender from its logger and close it. */ - public void dispose() { - if (this.useJUL) { - julUserClient.destroy(); - julRootClient.destroy(); - julUser.reset(); - julRoot.reset(); - } - - if (this.useLog4j) { - log4jUserClient.destroy(); - log4jRootClient.destroy(); - log4jUser.reset(); - log4jRoot.reset(); + 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); + + } catch (ReflectiveOperationException e) { + /* + * We were able to attach the appender, we should not have problems + * here either! + */ + throw new IllegalStateException(); } + + /* Close the appender */ + log4jAppender.close(); + log4jAppender = null; } + } diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngSessiondCmd2_6.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngSessiondCmd2_6.java deleted file mode 100644 index 03635c2e..00000000 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngSessiondCmd2_6.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2013 - David Goulet - * - * - * 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 - */ - -package org.lttng.ust.agent; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -interface LTTngSessiondCmd2_6 { - - /** - * Maximum name length for a logger name to be send to sessiond. - */ - int NAME_MAX = 255; - - /* - * Size of a primitive type int in byte. Because you know, Java can't - * provide that since it does not makes sense... - * - * - */ - int INT_SIZE = 4; - - interface SessiondResponse { - /** - * Gets a byte array of the command so that it may be streamed - * - * @return the byte array of the command - */ - public byte[] getBytes(); - } - - interface SessiondCommand { - /** - * Populate the class from a byte array - * - * @param data - * the byte array containing the streamed command - */ - public void populate(byte[] data); - } - - enum lttng_agent_command { - /** List logger(s). */ - CMD_LIST(1), - /** Enable logger by name. */ - CMD_ENABLE(2), - /** Disable logger by name. */ - CMD_DISABLE(3), - /** Registration done */ - CMD_REG_DONE(4); - - private int code; - - private lttng_agent_command(int c) { - code = c; - } - - public int getCommand() { - return code; - } - } - - enum lttng_agent_ret_code { - CODE_SUCCESS_CMD(1), - CODE_INVALID_CMD(2), - CODE_UNK_LOGGER_NAME(3); - private int code; - - private lttng_agent_ret_code(int c) { - code = c; - } - - public int getCode() { - return code; - } - } - - class sessiond_hdr implements SessiondCommand { - - /** ABI size of command header. */ - public final static int SIZE = 16; - /** Payload size in bytes following this header. */ - public long dataSize; - /** Command type. */ - public lttng_agent_command cmd; - /** Command version. */ - public int cmdVersion; - - @Override - public void populate(byte[] data) { - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.BIG_ENDIAN); - - dataSize = buf.getLong(); - cmd = lttng_agent_command.values()[buf.getInt() - 1]; - cmdVersion = buf.getInt(); - } - } - - class sessiond_enable_handler implements SessiondResponse, SessiondCommand { - - private static final int SIZE = 4; - public String name; - public int lttngLogLevel; - public int lttngLogLevelType; - - /** Return status code to the session daemon. */ - public lttng_agent_ret_code code; - - @Override - public void populate(byte[] data) { - int dataOffset = INT_SIZE * 2; - - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.LITTLE_ENDIAN); - lttngLogLevel = buf.getInt(); - lttngLogLevelType = buf.getInt(); - name = new String(data, dataOffset, data.length - dataOffset).trim(); - } - - @Override - public byte[] getBytes() { - byte data[] = new byte[SIZE]; - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.BIG_ENDIAN); - buf.putInt(code.getCode()); - return data; - } - - /** - * Execute enable handler action which is to enable the given handler - * to the received name. - * - * @param log - */ - public void execute(LogFramework log) { - if (log.enableLogger(this.name)) { - this.code = lttng_agent_ret_code.CODE_SUCCESS_CMD; - } else { - this.code = lttng_agent_ret_code.CODE_INVALID_CMD; - } - } - } - - class sessiond_disable_handler implements SessiondResponse, SessiondCommand { - - private final static int SIZE = 4; - public String name; - - - /** Return status code to the session daemon. */ - public lttng_agent_ret_code code; - - @Override - public void populate(byte[] data) { - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.LITTLE_ENDIAN); - name = new String(data).trim(); - } - - @Override - public byte[] getBytes() { - byte data[] = new byte[SIZE]; - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.BIG_ENDIAN); - buf.putInt(code.getCode()); - return data; - } - - /** - * Execute disable handler action which is to disable the given handler - * to the received name. - * - * @param log - */ - public void execute(LogFramework log) { - if (log.disableLogger(this.name)) { - this.code = lttng_agent_ret_code.CODE_SUCCESS_CMD; - } else { - this.code = lttng_agent_ret_code.CODE_INVALID_CMD; - } - } - } - - class sessiond_list_logger implements SessiondResponse { - - private final static int SIZE = 12; - - private int dataSize = 0; - private int nbLogger = 0; - - List loggerList = new ArrayList(); - - /** Return status code to the session daemon. */ - public lttng_agent_ret_code code; - - @Override - public byte[] getBytes() { - byte data[] = new byte[SIZE + dataSize]; - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.BIG_ENDIAN); - - /* Returned code */ - buf.putInt(code.getCode()); - buf.putInt(dataSize); - buf.putInt(nbLogger); - - for (String logger: loggerList) { - buf.put(logger.getBytes()); - /* NULL terminated byte after the logger name. */ - buf.put((byte) 0x0); - } - return data; - } - - public void execute(LogFramework log) { - String loggerName; - - Iterator loggers = log.listLoggers(); - while (loggers.hasNext()) { - loggerName = loggers.next(); - this.loggerList.add(loggerName); - this.nbLogger++; - this.dataSize += loggerName.length() + 1; - } - - this.code = lttng_agent_ret_code.CODE_SUCCESS_CMD; - } - } -} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFrameworkSkeleton.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFrameworkSkeleton.java deleted file mode 100644 index 4a58d2e2..00000000 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFrameworkSkeleton.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2014 - Christian Babeux - * - * - * 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 - */ - -package org.lttng.ust.agent; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * Basic implementation of LogFramework. - * - * @author Christian Babeux - */ -public abstract class LogFrameworkSkeleton implements LogFramework { - - /* A map of event name and reference count */ - private final Map enabledLoggers; - - /** - * Constructor - */ - public LogFrameworkSkeleton() { - this.enabledLoggers = new HashMap(); - } - - @Override - public Boolean enableLogger(String name) { - if (name == null) { - return false; - } - - if (enabledLoggers.containsKey(name)) { - /* Event is already enabled, simply increment its refcount */ - Integer refcount = enabledLoggers.get(name); - refcount++; - Integer oldval = enabledLoggers.put(name, refcount); - assert (oldval != null); - } else { - /* Event was not enabled, init refcount to 1 */ - Integer oldval = enabledLoggers.put(name, 1); - assert (oldval == null); - } - - return true; - } - - @Override - public Boolean disableLogger(String name) { - if (name == null) { - return false; - } - - if (!enabledLoggers.containsKey(name)) { - /* Event was never enabled, abort */ - return false; - } - - /* Event was previously enabled, simply decrement its refcount */ - Integer refcount = enabledLoggers.get(name); - refcount--; - assert (refcount >= 0); - - if (refcount == 0) { - /* Event is not used anymore, remove it from the map */ - Integer oldval = enabledLoggers.remove(name); - assert (oldval != null); - } - - return true; - } - - @Override - public abstract Iterator listLoggers(); - - @Override - public abstract Boolean isRoot(); - - @Override - public void reset() { - enabledLoggers.clear(); - } - - /** - * Get the number of enabled events. - * - * @return The number of enabled events - */ - protected Integer getEventCount() { - return enabledLoggers.size(); - } -} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondCommand.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondCommand.java new file mode 100644 index 00000000..855ea9da --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondCommand.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.client; + +interface ISessiondCommand { + + enum LttngAgentCommand { + + /** List logger(s). */ + CMD_LIST(1), + /** Enable logger by name. */ + CMD_ENABLE(2), + /** Disable logger by name. */ + CMD_DISABLE(3), + /** Registration done */ + CMD_REG_DONE(4); + + private int code; + + private LttngAgentCommand(int c) { + code = c; + } + + public int getCommand() { + return code; + } + } + + /** + * Populate the class from a byte array + * + * @param data + * the byte array containing the streamed command + */ + void populate(byte[] data); +} \ No newline at end of file diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondResponse.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondResponse.java new file mode 100644 index 00000000..2cb66149 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondResponse.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.client; + +interface ISessiondResponse { + + enum LttngAgentRetCode { + CODE_SUCCESS_CMD(1), + CODE_INVALID_CMD(2), + CODE_UNK_LOGGER_NAME(3); + private int code; + + private LttngAgentRetCode(int c) { + code = c; + } + + public int getCode() { + return code; + } + } + + /** + * Gets a byte array of the command so that it may be streamed + * + * @return the byte array of the command + */ + byte[] getBytes(); +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngTCPSessiondClient.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java similarity index 54% rename from liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngTCPSessiondClient.java rename to liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java index d376f672..d7ed6da4 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngTCPSessiondClient.java +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java @@ -15,7 +15,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package org.lttng.ust.agent; +package org.lttng.ust.agent.client; import java.io.BufferedReader; import java.io.DataInputStream; @@ -28,20 +28,28 @@ import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.concurrent.Semaphore; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; -class LTTngTCPSessiondClient implements Runnable { +import org.lttng.ust.agent.AbstractLttngAgent; + +/** + * Client for agents to connect to a local session daemon, using a TCP socket. + * + * @author David Goulet + */ +public class LttngTcpSessiondClient implements Runnable { private static final String SESSION_HOST = "127.0.0.1"; private static final String ROOT_PORT_FILE = "/var/run/lttng/agent.port"; private static final String USER_PORT_FILE = "/.lttng/agent.port"; - private static Integer protocolMajorVersion = 1; - private static Integer protocolMinorVersion = 0; + private static int protocolMajorVersion = 1; + private static int protocolMinorVersion = 0; - /* Command header from the session deamon. */ - private LTTngSessiondCmd2_6.sessiond_hdr headerCmd = - new LTTngSessiondCmd2_6.sessiond_hdr(); + /** Command header from the session deamon. */ + private final SessiondHeaderCommand headerCmd = new SessiondHeaderCommand(); + private final CountDownLatch registrationLatch = new CountDownLatch(1); private Socket sessiondSock; private volatile boolean quit = false; @@ -49,30 +57,40 @@ class LTTngTCPSessiondClient implements Runnable { private DataInputStream inFromSessiond; private DataOutputStream outToSessiond; - private LogFramework log; - - private Semaphore registerSem; - + private final AbstractLttngAgent logAgent; + private final boolean isRoot; - private LTTngAgent.Domain agentDomain; - /* Indicate if we've already released the semaphore. */ - private boolean semPosted = false; - - public LTTngTCPSessiondClient(LTTngAgent.Domain domain, LogFramework log, Semaphore sem) { - this.agentDomain = domain; - this.log = log; - this.registerSem = sem; + /** + * Constructor + * + * @param logAgent + * The logging agent this client will operate on. + * @param isRoot + * True if this client should connect to the root session daemon, + * false if it should connect to the user one. + */ + public LttngTcpSessiondClient(AbstractLttngAgent logAgent, boolean isRoot) { + this.logAgent = logAgent; + this.isRoot = isRoot; } - /* - * Try to release the registerSem if it's not already done. + /** + * Wait until this client has successfully established a connection to its + * target session daemon. + * + * @param seconds + * A timeout in seconds after which this method will return + * anyway. + * @return True if the the client actually established the connection, false + * if we returned because the timeout has elapsed or the thread was + * interrupted. */ - private void tryReleaseSem() { - /* Release semaphore so we unblock the agent. */ - if (!this.semPosted) { - this.registerSem.release(); - this.semPosted = true; + public boolean waitForConnection(int seconds) { + try { + return registrationLatch.await(seconds, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return false; } } @@ -83,9 +101,6 @@ class LTTngTCPSessiondClient implements Runnable { break; } - /* Cleanup Agent state before trying to connect or reconnect. */ - this.log.reset(); - try { /* @@ -106,40 +121,38 @@ class LTTngTCPSessiondClient implements Runnable { */ handleSessiondCmd(); } catch (UnknownHostException uhe) { - tryReleaseSem(); - System.out.println(uhe); + uhe.printStackTrace(); } catch (IOException ioe) { - tryReleaseSem(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } - } catch (Exception e) { - tryReleaseSem(); - e.printStackTrace(); } } } - public void destroy() { + /** + * Dispose this client and close any socket connection it may hold. + */ + public void close() { this.quit = true; try { if (this.sessiondSock != null) { this.sessiondSock.close(); } - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); } } - /* + /** * Receive header data from the session daemon using the LTTng command * static buffer of the right size. */ - private void recvHeader() throws Exception { - byte data[] = new byte[LTTngSessiondCmd2_6.sessiond_hdr.SIZE]; + private void recvHeader() throws IOException { + byte data[] = new byte[SessiondHeaderCommand.HEADER_SIZE]; int readLen = this.inFromSessiond.read(data, 0, data.length); if (readLen != data.length) { @@ -148,15 +161,15 @@ class LTTngTCPSessiondClient implements Runnable { this.headerCmd.populate(data); } - /* + /** * Receive payload from the session daemon. This MUST be done after a * recvHeader() so the header value of a command are known. * * The caller SHOULD use isPayload() before which returns true if a payload * is expected after the header. */ - private byte[] recvPayload() throws Exception { - byte payload[] = new byte[(int) this.headerCmd.dataSize]; + private byte[] recvPayload() throws IOException { + byte payload[] = new byte[(int) this.headerCmd.getDataSize()]; /* Failsafe check so we don't waste our time reading 0 bytes. */ if (payload.length == 0) { @@ -167,77 +180,84 @@ class LTTngTCPSessiondClient implements Runnable { return payload; } - /* + /** * Handle session command from the session daemon. */ - private void handleSessiondCmd() throws Exception { + private void handleSessiondCmd() throws IOException { byte data[] = null; while (true) { /* Get header from session daemon. */ recvHeader(); - if (headerCmd.dataSize > 0) { + if (headerCmd.getDataSize() > 0) { data = recvPayload(); } - switch (headerCmd.cmd) { - case CMD_REG_DONE: - { - /* - * Release semaphore so meaning registration is done and we - * can proceed to continue tracing. - */ - tryReleaseSem(); - /* - * We don't send any reply to the registration done command. - * This just marks the end of the initial session setup. - */ - continue; - } - case CMD_LIST: - { - LTTngSessiondCmd2_6.sessiond_list_logger listLoggerCmd = - new LTTngSessiondCmd2_6.sessiond_list_logger(); - listLoggerCmd.execute(this.log); - data = listLoggerCmd.getBytes(); - break; - } - case CMD_ENABLE: - { - LTTngSessiondCmd2_6.sessiond_enable_handler enableCmd = - new LTTngSessiondCmd2_6.sessiond_enable_handler(); - if (data == null) { - enableCmd.code = LTTngSessiondCmd2_6.lttng_agent_ret_code.CODE_INVALID_CMD; - break; - } - enableCmd.populate(data); - enableCmd.execute(this.log); - data = enableCmd.getBytes(); - break; - } - case CMD_DISABLE: - { - LTTngSessiondCmd2_6.sessiond_disable_handler disableCmd = - new LTTngSessiondCmd2_6.sessiond_disable_handler(); - if (data == null) { - disableCmd.code = LTTngSessiondCmd2_6.lttng_agent_ret_code.CODE_INVALID_CMD; - break; - } - disableCmd.populate(data); - disableCmd.execute(this.log); - data = disableCmd.getBytes(); + switch (headerCmd.getCommandType()) { + case CMD_REG_DONE: + { + /* + * Countdown the registration latch, meaning registration is + * done and we can proceed to continue tracing. + */ + registrationLatch.countDown(); + /* + * We don't send any reply to the registration done command. + * This just marks the end of the initial session setup. + */ + continue; + } + case CMD_LIST: + { + SessiondListLoggersResponse listLoggerCmd = new SessiondListLoggersResponse(); + listLoggerCmd.execute(logAgent); + data = listLoggerCmd.getBytes(); + break; + } + case CMD_ENABLE: + { + SessiondEnableHandler enableCmd = new SessiondEnableHandler(); + if (data == null) { + enableCmd.code = ISessiondResponse.LttngAgentRetCode.CODE_INVALID_CMD; break; } - default: - { - data = new byte[4]; - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.BIG_ENDIAN); + enableCmd.populate(data); + enableCmd.execute(logAgent); + data = enableCmd.getBytes(); + break; + } + case CMD_DISABLE: + { + SessiondDisableHandler disableCmd = new SessiondDisableHandler(); + if (data == null) { + disableCmd.setRetCode(ISessiondResponse.LttngAgentRetCode.CODE_INVALID_CMD); break; } + disableCmd.populate(data); + disableCmd.execute(logAgent); + data = disableCmd.getBytes(); + break; + } + default: + { + data = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + break; + } } + if (data == null) { + /* + * Simply used to silence a potential null access warning below. + * + * The flow analysis gets confused here and thinks "data" may be + * null at this point. It should not happen according to program + * logic, if it does we've done something wrong. + */ + throw new IllegalStateException(); + } /* Send payload to session daemon. */ this.outToSessiond.write(data, 0, data.length); this.outToSessiond.flush(); @@ -255,7 +275,7 @@ class LTTngTCPSessiondClient implements Runnable { */ private static int getPortFromFile(String path) throws IOException { int port; - BufferedReader br; + BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); @@ -265,19 +285,22 @@ class LTTngTCPSessiondClient implements Runnable { /* Invalid value. Ignore. */ port = 0; } - br.close(); } catch (FileNotFoundException e) { /* No port available. */ port = 0; + } finally { + if (br != null) { + br.close(); + } } return port; } - private void connectToSessiond() throws Exception { + private void connectToSessiond() throws IOException { int port; - if (this.log.isRoot()) { + if (this.isRoot) { port = getPortFromFile(ROOT_PORT_FILE); if (port == 0) { /* No session daemon available. Stop and retry later. */ @@ -296,12 +319,12 @@ class LTTngTCPSessiondClient implements Runnable { this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream()); } - private void registerToSessiond() throws Exception { + private void registerToSessiond() throws IOException { byte data[] = new byte[16]; ByteBuffer buf = ByteBuffer.wrap(data); String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; - buf.putInt(this.agentDomain.value()); + buf.putInt(logAgent.getDomain().value()); buf.putInt(Integer.parseInt(pid)); buf.putInt(protocolMajorVersion); buf.putInt(protocolMinorVersion); diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableHandler.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableHandler.java new file mode 100644 index 00000000..9dad7f6d --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableHandler.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.client; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.lttng.ust.agent.AbstractLttngAgent; + +class SessiondDisableHandler implements ISessiondResponse, ISessiondCommand { + + private static final int INT_SIZE = 4; + + /** Event name to disable from the tracing session */ + private String eventName; + + /** Return status code to the session daemon. */ + private LttngAgentRetCode code; + + @Override + public void populate(byte[] data) { + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.LITTLE_ENDIAN); + eventName = new String(data).trim(); + } + + @Override + public byte[] getBytes() { + byte data[] = new byte[INT_SIZE]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putInt(code.getCode()); + return data; + } + + public String getEventName() { + return eventName; + } + + public void setRetCode(LttngAgentRetCode code) { + this.code = code; + } + + /** + * Execute disable handler action which is to disable the given handler to + * the received name. + * + * @param agent + * The agent on which to execute the command + */ + public void execute(AbstractLttngAgent agent) { + if (agent.eventDisabled(this.eventName)) { + this.code = LttngAgentRetCode.CODE_SUCCESS_CMD; + } else { + this.code = LttngAgentRetCode.CODE_INVALID_CMD; + } + } +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableHandler.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableHandler.java new file mode 100644 index 00000000..ec49cb2f --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableHandler.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.client; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.lttng.ust.agent.AbstractLttngAgent; + +class SessiondEnableHandler implements ISessiondResponse, ISessiondCommand { + + private static final int INT_SIZE = 4; + + /** Event name to enable in the tracing session */ + private String eventName; + + /** Return status code to the session daemon. */ + public LttngAgentRetCode code; + + @Override + public void populate(byte[] data) { + int dataOffset = INT_SIZE * 2; + + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.getInt(); //logLevel, currently unused + buf.getInt(); //logLevelType, currently unused + eventName = new String(data, dataOffset, data.length - dataOffset).trim(); + } + + @Override + public byte[] getBytes() { + byte data[] = new byte[INT_SIZE]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putInt(code.getCode()); + return data; + } + + public String getEventName() { + return eventName; + } + + /** + * Execute enable handler action which is to enable the given handler to the + * received name. + * + * @param agent + * The agent on which to execute the command + */ + public void execute(AbstractLttngAgent agent) { + if (agent.eventEnabled(this.eventName)) { + this.code = LttngAgentRetCode.CODE_SUCCESS_CMD; + } else { + this.code = LttngAgentRetCode.CODE_INVALID_CMD; + } + } +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondHeaderCommand.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondHeaderCommand.java new file mode 100644 index 00000000..a2697270 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondHeaderCommand.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.client; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +class SessiondHeaderCommand implements ISessiondCommand { + + /** ABI size of command header. */ + public static final int HEADER_SIZE = 16; + + /** Payload size in bytes following this header. */ + private long dataSize; + /** Command type. */ + private LttngAgentCommand cmd; + + @Override + public void populate(byte[] data) { + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + + dataSize = buf.getLong(); + cmd = LttngAgentCommand.values()[buf.getInt() - 1]; + buf.getInt(); // command version, currently unused + } + + public long getDataSize() { + return dataSize; + } + + public LttngAgentCommand getCommandType() { + return cmd; + } +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondListLoggersResponse.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondListLoggersResponse.java new file mode 100644 index 00000000..a43c4887 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondListLoggersResponse.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.client; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +import org.lttng.ust.agent.ILttngAgent; + +class SessiondListLoggersResponse implements ISessiondResponse { + + private final static int SIZE = 12; + + private int dataSize = 0; + private int nbLogger = 0; + + private final List loggerList = new ArrayList(); + + /** Return status code to the session daemon. */ + public LttngAgentRetCode code; + + @Override + public byte[] getBytes() { + byte data[] = new byte[SIZE + dataSize]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + + /* Returned code */ + buf.putInt(code.getCode()); + buf.putInt(dataSize); + buf.putInt(nbLogger); + + for (String logger : loggerList) { + buf.put(logger.getBytes()); + /* NULL terminated byte after the logger name. */ + buf.put((byte) 0x0); + } + return data; + } + + public void execute(ILttngAgent agent) { + for (String event : agent.listEnabledEvents()) { + this.loggerList.add(event); + this.nbLogger++; + this.dataSize += event.length() + 1; + } + + this.code = LttngAgentRetCode.CODE_SUCCESS_CMD; + } +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile.am b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile.am index b2aed8b4..d32e1d69 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile.am +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile.am @@ -12,8 +12,8 @@ jardir = $(datadir)/java juljniout = ../../jni/jul -dist_noinst_JAVA = $(pkgpath)/LTTngJUL.java \ - $(pkgpath)/LTTngLogHandler.java +dist_noinst_JAVA = $(pkgpath)/LttngJulAgent.java \ + $(pkgpath)/LttngLogHandler.java dist_noinst_DATA = $(jarfile_manifest) @@ -26,7 +26,7 @@ $(jarfile): classnoinst.stamp $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink) jul-jni-header.stamp: $(dist_noinst_JAVA) - $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(juljniout) $(JAVAHFLAGS) org.lttng.ust.agent.jul.LTTngLogHandler && \ + $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(juljniout) $(JAVAHFLAGS) org.lttng.ust.agent.jul.LttngLogHandler && \ echo "JUL JNI header generated" > jul-jni-header.stamp all-local: $(stamp) @@ -38,4 +38,4 @@ uninstall-hook: cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) CLEANFILES = $(jarfile) $(pkgpath)/*.class jul-jni-header.stamp \ - $(juljniout)/org_lttng_ust_agent_jul_LTTngLogHandler.h + $(juljniout)/org_lttng_ust_agent_jul_LttngLogHandler.h diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngJUL.java b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngJUL.java deleted file mode 100644 index 2f21d62e..00000000 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngJUL.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2014 - Christian Babeux - * - * - * 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 - */ - -package org.lttng.ust.agent.jul; - -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Vector; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -import org.lttng.ust.agent.LogFrameworkSkeleton; - -/** - * JUL logging framework - * - * @author Christian Babeux - */ -public class LTTngJUL extends LogFrameworkSkeleton { - - private LTTngLogHandler handler; - private Boolean attached; - - /** - * Constructor - * - * @param isRoot - * If this logger is a root logger or not. - */ - public LTTngJUL(Boolean isRoot) { - super(); - this.handler = new LTTngLogHandler(isRoot); - this.attached = false; - } - - @Override - public Boolean enableLogger(String name) { - if(!super.enableLogger(name)) { - return false; - } - - /* The first enable of any event triggers the attachment to the root logger */ - if (getEventCount() == 1 && !this.attached) { - attachToRootLogger(); - } - - return true; - } - - @Override - public Boolean disableLogger(String name) { - if(!super.disableLogger(name)) { - return false; - } - - /* Detach from the root logger when the event count reach zero */ - if (getEventCount() == 0 && this.attached) { - detachFromRootLogger(); - } - - return true; - } - - @Override - public Iterator listLoggers() { - Vector logs = new Vector(); - for (Enumeration loggers = LogManager.getLogManager().getLoggerNames(); loggers.hasMoreElements(); ) { - String name = loggers.nextElement(); - /* Skip the root logger */ - if (name.equals("")) { - continue; - } - - logs.add(name); - } - - return logs.iterator(); - } - - @Override - public Boolean isRoot() { - return handler.isRoot(); - } - - @Override - public void reset() { - super.reset(); - detachFromRootLogger(); - } - - private void attachToRootLogger() { - if (this.attached) { - return; - } - - Logger rootLogger = LogManager.getLogManager().getLogger(""); - rootLogger.addHandler(this.handler); - this.attached = true; - } - - private void detachFromRootLogger() { - if (!this.attached) { - return; - } - - Logger rootLogger = LogManager.getLogManager().getLogger(""); - rootLogger.removeHandler(this.handler); - this.attached = false; - } -} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngLogHandler.java b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngLogHandler.java deleted file mode 100644 index b844d2f6..00000000 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngLogHandler.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2013 - David Goulet - * - * 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 - */ - -package org.lttng.ust.agent.jul; - -import java.lang.String; - -import java.util.logging.Handler; -import java.util.logging.LogRecord; - -class LTTngLogHandler extends Handler { - - private final Boolean isRoot; - - public LTTngLogHandler(Boolean isRoot) { - super(); - this.isRoot = isRoot; - /* Initialize LTTng UST tracer. */ - try { - System.loadLibrary("lttng-ust-jul-jni"); //$NON-NLS-1$ - } catch (SecurityException e) { - e.printStackTrace(); - } catch (UnsatisfiedLinkError e) { - e.printStackTrace(); - } catch (NullPointerException e) { - /* Should never happen */ - e.printStackTrace(); - } - } - - public Boolean isRoot() { - return this.isRoot; - } - - @Override - public void close() throws SecurityException {} - - @Override - public void flush() {} - - @Override - public void publish(LogRecord record) { - /* - * Specific tracepoint designed for JUL events. The source class of the - * caller is used for the event name, the raw message is taken, the - * loglevel of the record and the thread ID. - */ - if (this.isRoot) { - tracepointS(record.getMessage(), - record.getLoggerName(), record.getSourceClassName(), - record.getSourceMethodName(), record.getMillis(), - record.getLevel().intValue(), record.getThreadID()); - } else { - tracepointU(record.getMessage(), - record.getLoggerName(), record.getSourceClassName(), - record.getSourceMethodName(), record.getMillis(), - record.getLevel().intValue(), record.getThreadID()); - } - } - - /* Use for a user session daemon. */ - private native void tracepointU(String msg, - String logger_name, - String class_name, - String method_name, - long millis, - int log_level, - int thread_id); - - /* Use for a root session daemon. */ - private native void tracepointS(String msg, - String logger_name, - String class_name, - String method_name, - long millis, - int log_level, - int thread_id); -} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulAgent.java new file mode 100644 index 00000000..fa92f645 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulAgent.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * + * 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 + */ + +package org.lttng.ust.agent.jul; + +import org.lttng.ust.agent.AbstractLttngAgent; + +/** + * Agent implementation for tracing from JUL loggers. + * + * @author Alexandre Montplaisir + */ +class LttngJulAgent extends AbstractLttngAgent { + + private static LttngJulAgent instance = null; + + private LttngJulAgent() { + super(Domain.JUL); + } + + public static synchronized LttngJulAgent getInstance() { + if (instance == null) { + instance = new LttngJulAgent(); + } + return instance; + } + +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java new file mode 100644 index 00000000..d8e0cb82 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +package org.lttng.ust.agent.jul; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +import org.lttng.ust.agent.ILttngAgent; +import org.lttng.ust.agent.ILttngHandler; + +/** + * LTTng-UST JUL log handler. + * + * Applications can attach this handler to their + * {@link java.util.logging.Logger} to have it generate UST events from logging + * events received through the logger. + * + * It sends its events to UST via the JNI library "liblttng-ust-jul-jni.so". + * Make sure this library is available before using this handler. + * + * @author Alexandre Montplaisir + * @author David Goulet + */ +public class LttngLogHandler extends Handler implements ILttngHandler { + + private static final String SHARED_OBJECT_NAME = "lttng-ust-jul-jni"; + + private final ILttngAgent agent; + + /** Number of events logged (really sent through JNI) by this handler */ + private final AtomicLong eventCount = new AtomicLong(0); + + /** + * Constructor + * + * @throws IOException + * This handler requires the lttng-ust-jul-jni.so native + * library, through which it will send the trace events. This + * exception is throw is this library cannot be found. + * @throws SecurityException + * We will forward any SecurityExcepion that may be thrown when + * trying to load the JNI library. + */ + public LttngLogHandler() throws IOException, SecurityException { + super(); + /* Initialize LTTng UST tracer. */ + try { + System.loadLibrary(SHARED_OBJECT_NAME); //$NON-NLS-1$ + } catch (UnsatisfiedLinkError e) { + throw new IOException(e); + } + + /** Register to the relevant agent */ + agent = LttngJulAgent.getInstance(); + agent.registerHandler(this); + } + + @Override + public synchronized void close() { + agent.unregisterHandler(this); + } + + /** + * Get the number of events logged by this handler so far. This means the + * number of events actually sent through JNI to UST. + * + * @return The number of events logged so far + */ + @Override + public long getEventCount() { + return eventCount.get(); + } + + @Override + public void flush() { + } + + @Override + public void publish(LogRecord record) { + /* + * Check if the current message should be logged, according to the UST + * session settings. + */ + if (!agent.isEventEnabled(record.getLoggerName())) { + return; + } + + eventCount.incrementAndGet(); + /* + * Specific tracepoint designed for JUL events. The source class of the + * caller is used for the event name, the raw message is taken, the + * loglevel of the record and the thread ID. + */ + tracepoint(record.getMessage(), + record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + record.getMillis(), + record.getLevel().intValue(), + record.getThreadID()); + } + + /* Send tracepoint information to the JNI library */ + private native void tracepoint(String msg, + String logger_name, + String class_name, + String method_name, + long millis, + int log_level, + int thread_id); +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile.am b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile.am index 9113f85e..9ae53233 100644 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile.am +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile.am @@ -12,8 +12,8 @@ jardir = $(datadir)/java log4jjniout = ../../jni/log4j -dist_noinst_JAVA = $(pkgpath)/LTTngLog4j.java \ - $(pkgpath)/LTTngLogAppender.java +dist_noinst_JAVA = $(pkgpath)/LttngLog4jAgent.java \ + $(pkgpath)/LttngLogAppender.java dist_noinst_DATA = $(jarfile_manifest) @@ -26,7 +26,7 @@ $(jarfile): classnoinst.stamp $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink) log4j-jni-header.stamp: $(dist_noinst_JAVA) - $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(log4jjniout) $(JAVAHFLAGS) org.lttng.ust.agent.log4j.LTTngLogAppender && \ + $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(log4jjniout) $(JAVAHFLAGS) org.lttng.ust.agent.log4j.LttngLogAppender && \ echo "Log4j JNI header generated" > log4j-jni-header.stamp all-local: $(stamp) @@ -38,4 +38,4 @@ uninstall-hook: cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) CLEANFILES = $(jarfile) $(pkgpath)/*.class log4j-jni-header.stamp \ - $(log4jjniout)/org_lttng_ust_agent_log4j_LTTngLogAppender.h + $(log4jjniout)/org_lttng_ust_agent_log4j_LttngLogAppender.h diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLog4j.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLog4j.java deleted file mode 100644 index 534cc949..00000000 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLog4j.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2014 - Christian Babeux - * - * - * 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 - */ - -package org.lttng.ust.agent.log4j; - -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Vector; - -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.lttng.ust.agent.LogFrameworkSkeleton; - -/** - * log4j logging framework - * - * @author Christian Babeux - */ -public class LTTngLog4j extends LogFrameworkSkeleton { - - private LTTngLogAppender appender; - private Boolean attached; - - /** - * Constructor - * - * @param isRoot - * If this logger is a root logger or not. - */ - public LTTngLog4j(Boolean isRoot) { - super(); - this.appender = new LTTngLogAppender(isRoot); - this.attached = false; - } - - @Override - public Boolean enableLogger(String name) { - if(!super.enableLogger(name)) { - return false; - } - - /* The first enable of any event triggers the attachment to the root logger */ - if (getEventCount() == 1 && !this.attached) { - attachToRootLogger(); - } - - return true; - } - - @Override - public Boolean disableLogger(String name) { - if(!super.disableLogger(name)) { - return false; - } - - /* Detach from the root logger when the event counts reach zero */ - if (getEventCount() == 0 && this.attached) { - detachFromRootLogger(); - } - - return true; - } - - @Override - public Iterator listLoggers() { - Vector logs = new Vector(); - for (Enumeration loggers = LogManager.getCurrentLoggers(); loggers.hasMoreElements(); ) { - Logger logger = (Logger) loggers.nextElement(); - String name = logger.getName(); - logs.add(name); - } - - return logs.iterator(); - } - - @Override - public Boolean isRoot() { - return appender.isRoot(); - } - - @Override - public void reset() { - super.reset(); - detachFromRootLogger(); - } - - private void attachToRootLogger() { - if (this.attached) { - return; - } - - Logger logger = Logger.getRootLogger(); - logger.addAppender(this.appender); - this.attached = true; - } - - private void detachFromRootLogger() { - if (!this.attached) { - return; - } - - Logger logger = Logger.getRootLogger(); - logger.removeAppender(this.appender); - this.attached = false; - } -} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLogAppender.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLogAppender.java deleted file mode 100644 index 30fac23f..00000000 --- a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLogAppender.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2014 - Christian Babeux - * - * 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 - */ - -package org.lttng.ust.agent.log4j; - -import java.lang.String; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.spi.LoggingEvent; - -class LTTngLogAppender extends AppenderSkeleton { - - private Boolean isRoot; - - public LTTngLogAppender(Boolean isRoot) { - super(); - this.isRoot = isRoot; - try { - System.loadLibrary("lttng-ust-log4j-jni"); //$NON-NLS-1$ - } catch (SecurityException e) { - e.printStackTrace(); - } catch (UnsatisfiedLinkError e) { - e.printStackTrace(); - } catch (NullPointerException e) { - /* Should never happen */ - e.printStackTrace(); - } - } - - public Boolean isRoot() { - return this.isRoot; - } - - @Override - protected void append(LoggingEvent event) { - int line; - - /* - * The line number returned from LocationInformation is a - * string. At least try to convert to a proper int. - */ - try { - String lineString = event.getLocationInformation().getLineNumber(); - line = Integer.parseInt(lineString); - } catch (NumberFormatException n) { - line = -1; - } - - if (this.isRoot) { - tracepointS(event.getRenderedMessage(), - event.getLoggerName(), - event.getLocationInformation().getClassName(), - event.getLocationInformation().getMethodName(), - event.getLocationInformation().getFileName(), - line, - event.getTimeStamp(), - event.getLevel().toInt(), - event.getThreadName()); - } else { - tracepointU(event.getRenderedMessage(), - event.getLoggerName(), - event.getLocationInformation().getClassName(), - event.getLocationInformation().getMethodName(), - event.getLocationInformation().getFileName(), - line, - event.getTimeStamp(), - event.getLevel().toInt(), - event.getThreadName()); - } - } - - @Override - public void close() {} - - @Override - public boolean requiresLayout() { - return false; - } - - /* Use for a user session daemon. */ - private native void tracepointU(String msg, - String logger_name, - String class_name, - String method_name, - String file_name, - int line_number, - long timestamp, - int loglevel, - String thread_name); - - /* Use for a root session daemon. */ - private native void tracepointS(String msg, - String logger_name, - String class_name, - String method_name, - String file_name, - int line_number, - long timestamp, - int loglevel, - String thread_name); -} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jAgent.java new file mode 100644 index 00000000..5dd84687 --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jAgent.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * + * 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 + */ + +package org.lttng.ust.agent.log4j; + +import org.lttng.ust.agent.AbstractLttngAgent; + +/** + * Agent implementation for using the Log4j logger, connecting to a root session + * daemon. + * + * @author Alexandre Montplaisir + */ +class LttngLog4jAgent extends AbstractLttngAgent { + + private static LttngLog4jAgent instance = null; + + private LttngLog4jAgent() { + super(Domain.LOG4J); + } + + public static synchronized LttngLog4jAgent getInstance() { + if (instance == null) { + instance = new LttngLog4jAgent(); + } + return instance; + } + +} diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java new file mode 100644 index 00000000..98cc2c8d --- /dev/null +++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir + * Copyright (C) 2014 - Christian Babeux + * + * 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 + */ + +package org.lttng.ust.agent.log4j; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.spi.LoggingEvent; +import org.lttng.ust.agent.ILttngAgent; +import org.lttng.ust.agent.ILttngHandler; + +/** + * LTTng-UST Log4j 1.x log handler. + * + * Applications can attach this appender to their + * {@link org.apache.log4j.Logger} to have it generate UST events from logging + * events received through the logger. + * + * It sends its events to UST via the JNI library "liblttng-ust-log4j-jni.so". + * Make sure this library is available before using this appender. + * + * @author Alexandre Montplaisir + * @author Christian Babeux + */ +public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler { + + private static final String SHARED_OBJECT_NAME = "lttng-ust-log4j-jni"; + + private final AtomicLong eventCount = new AtomicLong(0); + + private final ILttngAgent agent; + + + /** + * Constructor + * + * @throws IOException + * This handler requires the lttng-ust-log4j-jni.so native + * library, through which it will send the trace events. This + * exception is throw is this library cannot be found. + * @throws SecurityException + * We will forward any SecurityExcepion that may be thrown when + * trying to load the JNI library. + */ + public LttngLogAppender() throws IOException, SecurityException { + super(); + /* Initialize LTTng UST tracer. */ + try { + System.loadLibrary(SHARED_OBJECT_NAME); // $NON-NLS-1$ + } catch (UnsatisfiedLinkError e) { + throw new IOException(e); + } + + /** Register to the relevant agent */ + agent = LttngLog4jAgent.getInstance(); + agent.registerHandler(this); + } + + @Override + public synchronized void close() { + agent.unregisterHandler(this); + } + + /** + * Get the number of events logged by this handler so far. This means the + * number of events actually sent through JNI to UST. + * + * @return The number of events logged so far + */ + @Override + public long getEventCount() { + return eventCount.get(); + } + + @Override + public boolean requiresLayout() { + return false; + } + + @Override + protected void append(LoggingEvent event) { + /* + * Check if the current message should be logged, according to the UST + * session settings. + */ + if (!agent.isEventEnabled(event.getLoggerName())) { + return; + } + + /* + * The line number returned from LocationInformation is a string. At + * least try to convert to a proper int. + */ + int line; + try { + String lineString = event.getLocationInformation().getLineNumber(); + line = Integer.parseInt(lineString); + } catch (NumberFormatException n) { + line = -1; + } + + eventCount.incrementAndGet(); + tracepoint(event.getRenderedMessage(), + event.getLoggerName(), + event.getLocationInformation().getClassName(), + event.getLocationInformation().getMethodName(), + event.getLocationInformation().getFileName(), + line, + event.getTimeStamp(), + event.getLevel().toInt(), + event.getThreadName()); + } + + + /* Use for a user session daemon. */ + private native void tracepoint(String msg, + String logger_name, + String class_name, + String method_name, + String file_name, + int line_number, + long timestamp, + int loglevel, + String thread_name); +} diff --git a/liblttng-ust-java-agent/jni/jul/Makefile.am b/liblttng-ust-java-agent/jni/jul/Makefile.am index d0d6d780..40906e10 100644 --- a/liblttng-ust-java-agent/jni/jul/Makefile.am +++ b/liblttng-ust-java-agent/jni/jul/Makefile.am @@ -4,6 +4,6 @@ lib_LTLIBRARIES = liblttng-ust-jul-jni.la liblttng_ust_jul_jni_la_SOURCES = lttng_ust_jul.c \ lttng_ust_jul.h -nodist_liblttng_ust_jul_jni_la_SOURCES = org_lttng_ust_agent_jul_LTTngLogHandler.h +nodist_liblttng_ust_jul_jni_la_SOURCES = org_lttng_ust_agent_jul_LttngLogHandler.h liblttng_ust_jul_jni_la_LIBADD = -lc -L$(top_builddir)/liblttng-ust/.libs -llttng-ust diff --git a/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c b/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c index b089fe3b..7363f65c 100644 --- a/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c +++ b/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c @@ -16,16 +16,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "org_lttng_ust_agent_jul_LTTngLogHandler.h" +#include "org_lttng_ust_agent_jul_LttngLogHandler.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_CREATE_PROBES #include "lttng_ust_jul.h" /* - * System tracepoint meaning only root agent will fire this. + * Tracepoint used by Java applications using the JUL handler. */ -JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LTTngLogHandler_tracepointS(JNIEnv *env, +JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngLogHandler_tracepoint(JNIEnv *env, jobject jobj, jstring msg, jstring logger_name, @@ -41,6 +41,7 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LTTngLogHandler_tracepointS( const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy); const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy); + // FIXME Change "sys_event" to "event" once lttng-tools is updated tracepoint(lttng_jul, sys_event, msg_cstr, logger_name_cstr, class_name_cstr, method_name_cstr, millis, log_level, thread_id); @@ -49,31 +50,3 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LTTngLogHandler_tracepointS( (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr); (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr); } - -/* - * User tracepoint meaning only a non root agent will fire this. - */ -JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LTTngLogHandler_tracepointU(JNIEnv *env, - jobject jobj, - jstring msg, - jstring logger_name, - jstring class_name, - jstring method_name, - jlong millis, - jint log_level, - jint thread_id) -{ - jboolean iscopy; - const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy); - const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy); - const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy); - const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy); - - tracepoint(lttng_jul, user_event, msg_cstr, logger_name_cstr, - class_name_cstr, method_name_cstr, millis, log_level, thread_id); - - (*env)->ReleaseStringUTFChars(env, msg, msg_cstr); - (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr); - (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr); - (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr); -} diff --git a/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.h b/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.h index a45af396..1482d90f 100644 --- a/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.h +++ b/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.h @@ -25,9 +25,9 @@ #include /* - * Privileged tracepoint meaning that this is only enable and fired by the root - * session daemon. + * Tracepoint used by Java applications using the JUL handler. */ +// FIXME Change "sys_event" to "event" once lttng-tools is updated TRACEPOINT_EVENT(lttng_jul, sys_event, TP_ARGS( const char *, msg, @@ -48,30 +48,6 @@ TRACEPOINT_EVENT(lttng_jul, sys_event, ) ) -/* - * User tracepoint meaning that this is only enable and fired by a non root - * session daemon. - */ -TRACEPOINT_EVENT(lttng_jul, user_event, - TP_ARGS( - const char *, msg, - const char *, logger_name, - const char *, class_name, - const char *, method_name, - long, millis, - int, log_level, - int, thread_id), - TP_FIELDS( - ctf_string(msg, msg) - ctf_string(logger_name, logger_name) - ctf_string(class_name, class_name) - ctf_string(method_name, method_name) - ctf_integer(long, long_millis, millis) - ctf_integer(int, int_loglevel, log_level) - ctf_integer(int, int_threadid, thread_id) - ) -) - #endif /* _TRACEPOINT_LTTNG_UST_JUL_H */ #undef TRACEPOINT_INCLUDE diff --git a/liblttng-ust-java-agent/jni/log4j/Makefile.am b/liblttng-ust-java-agent/jni/log4j/Makefile.am index 7acbccdb..07384f8d 100644 --- a/liblttng-ust-java-agent/jni/log4j/Makefile.am +++ b/liblttng-ust-java-agent/jni/log4j/Makefile.am @@ -3,6 +3,6 @@ lib_LTLIBRARIES = liblttng-ust-log4j-jni.la liblttng_ust_log4j_jni_la_SOURCES = lttng_ust_log4j.c \ lttng_ust_log4j.h -nodist_liblttng_ust_log4j_jni_la_SOURCES = org_lttng_ust_agent_log4j_LTTngLogAppender.h +nodist_liblttng_ust_log4j_jni_la_SOURCES = org_lttng_ust_agent_log4j_LttngLogAppender.h liblttng_ust_log4j_jni_la_LIBADD = -lc -L$(top_builddir)/liblttng-ust/.libs -llttng-ust diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c index c887f7f4..d5df70e2 100644 --- a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c +++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c @@ -16,7 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "org_lttng_ust_agent_log4j_LTTngLogAppender.h" +#include "org_lttng_ust_agent_log4j_LttngLogAppender.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_CREATE_PROBES @@ -25,7 +25,7 @@ /* * System tracepoint meaning only root agent will fire this. */ -JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LTTngLogAppender_tracepointS(JNIEnv *env, +JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLogAppender_tracepoint(JNIEnv *env, jobject jobj, jstring msg, jstring logger_name, @@ -45,6 +45,7 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LTTngLogAppender_tracepoin const char *file_name_cstr = (*env)->GetStringUTFChars(env, file_name, &iscopy); const char *thread_name_cstr = (*env)->GetStringUTFChars(env, thread_name, &iscopy); + // FIXME Change "sys_event" to "event" once lttng-tools is updated tracepoint(lttng_log4j, sys_event, msg_cstr, logger_name_cstr, class_name_cstr, method_name_cstr, file_name_cstr, line_number, timestamp, loglevel, thread_name_cstr); @@ -57,37 +58,3 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LTTngLogAppender_tracepoin (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr); } -/* - * User tracepoint meaning only a non root agent will fire this. - */ -JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LTTngLogAppender_tracepointU(JNIEnv *env, - jobject jobj, - jstring msg, - jstring logger_name, - jstring class_name, - jstring method_name, - jstring file_name, - jint line_number, - jlong timestamp, - jint loglevel, - jstring thread_name) -{ - jboolean iscopy; - const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy); - const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy); - const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy); - const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy); - const char *file_name_cstr = (*env)->GetStringUTFChars(env, file_name, &iscopy); - const char *thread_name_cstr = (*env)->GetStringUTFChars(env, thread_name, &iscopy); - - tracepoint(lttng_log4j, user_event, msg_cstr, logger_name_cstr, - class_name_cstr, method_name_cstr, file_name_cstr, - line_number, timestamp, loglevel, thread_name_cstr); - - (*env)->ReleaseStringUTFChars(env, msg, msg_cstr); - (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr); - (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr); - (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr); - (*env)->ReleaseStringUTFChars(env, file_name, file_name_cstr); - (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr); -} diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.h b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.h index cc403578..7e20b698 100644 --- a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.h +++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.h @@ -25,9 +25,9 @@ #include /* - * Privileged tracepoint meaning that this is only enable and fired by the root - * session daemon. + * Tracepoint used by Java applications using the log4j log appender. */ +// FIXME Change "sys_event" to "event" once lttng-tools is updated TRACEPOINT_EVENT(lttng_log4j, sys_event, TP_ARGS( const char *, msg, @@ -52,34 +52,6 @@ TRACEPOINT_EVENT(lttng_log4j, sys_event, ) ) -/* - * User tracepoint meaning that this is only enable and fired by a non root - * session daemon. - */ -TRACEPOINT_EVENT(lttng_log4j, user_event, - TP_ARGS( - const char *, msg, - const char *, logger_name, - const char *, class_name, - const char *, method_name, - const char *, file_name, - int, line_number, - long, timestamp, - int, log_level, - const char *, thread_name), - TP_FIELDS( - ctf_string(msg, msg) - ctf_string(logger_name, logger_name) - ctf_string(class_name, class_name) - ctf_string(method_name, method_name) - ctf_string(filename, file_name) - ctf_integer(int, line_number, line_number) - ctf_integer(long, timestamp, timestamp) - ctf_integer(int, int_loglevel, log_level) - ctf_string(thread_name, thread_name) - ) -) - #endif /* _TRACEPOINT_LTTNG_UST_LOG4J_H */ #undef TRACEPOINT_INCLUDE -- 2.34.1