Refactor Java agent to let applications manage the log handlers
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Wed, 22 Jul 2015 02:56:15 +0000 (22:56 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 29 Jul 2015 01:32:17 +0000 (21:32 -0400)
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 <alexmonthy@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
36 files changed:
.gitignore
doc/examples/java-jul/Hello.java
doc/examples/java-log4j/Hello.java
doc/java-agent.txt
liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngHandler.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngAgent.java
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngSessiondCmd2_6.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngTCPSessiondClient.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFramework.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LogFrameworkSkeleton.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondCommand.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ISessiondResponse.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableHandler.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableHandler.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondHeaderCommand.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondListLoggersResponse.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile.am
liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngJUL.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LTTngLogHandler.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulAgent.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile.am
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLog4j.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LTTngLogAppender.java [deleted file]
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jAgent.java [new file with mode: 0644]
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java [new file with mode: 0644]
liblttng-ust-java-agent/jni/jul/Makefile.am
liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c
liblttng-ust-java-agent/jni/jul/lttng_ust_jul.h
liblttng-ust-java-agent/jni/log4j/Makefile.am
liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c
liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.h

index bcd0ecb912b87e6f365f361de0b3bc99a4228966..4454fa3631a549cf68a540a7b783d53013c0ab42 100644 (file)
@@ -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
index 28360c721a809bc2eae8d2c4ae8d382b3e102787..7016cf4956bc90736d3eeeafc2a817bfa281b879 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
  * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * 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.
  *
+ * <p>
+ * 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.
+ * <p>
+ * </p>
+ * {@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.
+ * </p>
+ *
+ * @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();
        }
 }
index c0393392e96bea7a9d402d088b441391fdd3bdb3..c51d4d4013b1d7d7979de3ec65ea6487cf1dcc14 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
  * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * 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();
        }
 }
index 3fc6c4931e495fa2195fc7480c78af4d35ecea8b..b5f722c7d6a8e06ea1f2a54dde2453545e57934a 100644 (file)
@@ -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.
index 46ac7cdee8191cc38da1b06d9ae47e37b175d17a..287956ffe431e78c1ec15a356ca7d768dabf23e1 100644 (file)
@@ -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 (file)
index 0000000..4e6d4af
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 <T>
+ *            The type of logging handler that should register to this agent
+ */
+public abstract class AbstractLttngAgent<T extends ILttngHandler> implements ILttngAgent<T> {
+
+       private static final String WILDCARD = "*";
+       private static final int INIT_TIMEOUT = 3; /* Seconds */
+
+       /** The handlers registered to this agent */
+       private final Set<T> registeredHandlers = new HashSet<T>();
+
+       /**
+        * 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<String, Integer> enabledEvents = new ConcurrentHashMap<String, Integer>();
+
+       /**
+        * 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<String, Integer> enabledEventPrefixes =
+                       new ConcurrentSkipListMap<String, Integer>();
+
+       /** 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<String> listEnabledEvents() {
+               List<String> events = new LinkedList<String>();
+
+               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<String, Integer> 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<String, Integer> 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 (file)
index 0000000..044bdf0
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * 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 <T>
+ *            The type of logging handler that should register to this agent
+ */
+public interface ILttngAgent<T extends ILttngHandler> {
+
+       // ------------------------------------------------------------------------
+       // 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<String> listEnabledEvents();
+}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngHandler.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngHandler.java
new file mode 100644 (file)
index 0000000..f3ffab8
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * 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;
+
+/**
+ * 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();
+
+       /**
+        * Close the log handler. Should be called once the application is done
+        * logging through it.
+        */
+       void close();
+}
index e83504d94d8f3f26072cca3b0765da8182597850..3c9a7997d558950d40064130c18f2a21596356bd 100644 (file)
 
 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 (file)
index 03635c2..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
- *
- *
- * 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<String> loggerList = new ArrayList<String>();
-
-               /** 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<String> 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/LTTngTCPSessiondClient.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/LTTngTCPSessiondClient.java
deleted file mode 100644 (file)
index d376f67..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
- *
- * 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.io.BufferedReader;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.concurrent.Semaphore;
-
-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;
-
-       /* Command header from the session deamon. */
-       private LTTngSessiondCmd2_6.sessiond_hdr headerCmd =
-               new LTTngSessiondCmd2_6.sessiond_hdr();
-
-       private Socket sessiondSock;
-       private volatile boolean quit = false;
-
-       private DataInputStream inFromSessiond;
-       private DataOutputStream outToSessiond;
-
-       private LogFramework log;
-
-       private Semaphore registerSem;
-
-
-       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;
-       }
-
-       /*
-        * Try to release the registerSem if it's not already done.
-        */
-       private void tryReleaseSem() {
-               /* Release semaphore so we unblock the agent. */
-               if (!this.semPosted) {
-                       this.registerSem.release();
-                       this.semPosted = true;
-               }
-       }
-
-       @Override
-       public void run() {
-               for (;;) {
-                       if (this.quit) {
-                               break;
-                       }
-
-                       /* Cleanup Agent state before trying to connect or reconnect. */
-                       this.log.reset();
-
-                       try {
-
-                               /*
-                                * Connect to the session daemon before anything else.
-                                */
-                               connectToSessiond();
-
-                               /*
-                                * Register to the session daemon as the Java component of the
-                                * UST application.
-                                */
-                               registerToSessiond();
-
-                               /*
-                                * Block on socket receive and wait for command from the
-                                * session daemon. This will return if and only if there is a
-                                * fatal error or the socket closes.
-                                */
-                               handleSessiondCmd();
-                       } catch (UnknownHostException uhe) {
-                               tryReleaseSem();
-                               System.out.println(uhe);
-                       } catch (IOException ioe) {
-                               tryReleaseSem();
-                               try {
-                                       Thread.sleep(3000);
-                               } catch (InterruptedException e) {
-                                       e.printStackTrace();
-                               }
-                       } catch (Exception e) {
-                               tryReleaseSem();
-                               e.printStackTrace();
-                       }
-               }
-       }
-
-       public void destroy() {
-               this.quit = true;
-
-               try {
-                       if (this.sessiondSock != null) {
-                               this.sessiondSock.close();
-                       }
-               } catch (Exception 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];
-
-               int readLen = this.inFromSessiond.read(data, 0, data.length);
-               if (readLen != data.length) {
-                       throw new IOException();
-               }
-               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];
-
-               /* Failsafe check so we don't waste our time reading 0 bytes. */
-               if (payload.length == 0) {
-                       return null;
-               }
-
-               this.inFromSessiond.read(payload, 0, payload.length);
-               return payload;
-       }
-
-       /*
-        * Handle session command from the session daemon.
-        */
-       private void handleSessiondCmd() throws Exception {
-               byte data[] = null;
-
-               while (true) {
-                       /* Get header from session daemon. */
-                       recvHeader();
-
-                       if (headerCmd.dataSize > 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();
-                                       break;
-                               }
-                               default:
-                               {
-                                       data = new byte[4];
-                                       ByteBuffer buf = ByteBuffer.wrap(data);
-                                       buf.order(ByteOrder.BIG_ENDIAN);
-                                       break;
-                               }
-                       }
-
-                       /* Send payload to session daemon. */
-                       this.outToSessiond.write(data, 0, data.length);
-                       this.outToSessiond.flush();
-               }
-       }
-
-       private static String getHomePath() {
-               return System.getProperty("user.home");
-       }
-
-       /**
-        * Read port number from file created by the session daemon.
-        *
-        * @return port value if found else 0.
-        */
-       private static int getPortFromFile(String path) throws IOException {
-               int port;
-               BufferedReader br;
-
-               try {
-                       br = new BufferedReader(new FileReader(path));
-                       String line = br.readLine();
-                       port = Integer.parseInt(line, 10);
-                       if (port < 0 || port > 65535) {
-                               /* Invalid value. Ignore. */
-                               port = 0;
-                       }
-                       br.close();
-               } catch (FileNotFoundException e) {
-                       /* No port available. */
-                       port = 0;
-               }
-
-               return port;
-       }
-
-       private void connectToSessiond() throws Exception {
-               int port;
-
-               if (this.log.isRoot()) {
-                       port = getPortFromFile(ROOT_PORT_FILE);
-                       if (port == 0) {
-                               /* No session daemon available. Stop and retry later. */
-                               throw new IOException();
-                       }
-               } else {
-                       port = getPortFromFile(getHomePath() + USER_PORT_FILE);
-                       if (port == 0) {
-                               /* No session daemon available. Stop and retry later. */
-                               throw new IOException();
-                       }
-               }
-
-               this.sessiondSock = new Socket(SESSION_HOST, port);
-               this.inFromSessiond = new DataInputStream(sessiondSock.getInputStream());
-               this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream());
-       }
-
-       private void registerToSessiond() throws Exception {
-               byte data[] = new byte[16];
-               ByteBuffer buf = ByteBuffer.wrap(data);
-               String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
-
-               buf.putInt(this.agentDomain.value());
-               buf.putInt(Integer.parseInt(pid));
-               buf.putInt(protocolMajorVersion);
-               buf.putInt(protocolMinorVersion);
-               this.outToSessiond.write(data, 0, data.length);
-               this.outToSessiond.flush();
-       }
-}
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/LogFramework.java
deleted file mode 100644 (file)
index 0dd7c98..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
- *
- *
- * 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.Iterator;
-
-interface LogFramework {
-       Boolean enableLogger(String name);
-       Boolean disableLogger(String name);
-       Iterator<String> listLoggers();
-       Boolean isRoot();
-       void reset();
-}
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 (file)
index 4a58d2e..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
- *
- *
- * 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<String, Integer> enabledLoggers;
-
-       /**
-        * Constructor
-        */
-       public LogFrameworkSkeleton() {
-               this.enabledLoggers = new HashMap<String, Integer>();
-       }
-
-       @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<String> 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 (file)
index 0000000..855ea9d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 (file)
index 0000000..2cb6614
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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/client/LttngTcpSessiondClient.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java
new file mode 100644 (file)
index 0000000..d7ed6da
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+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 int protocolMajorVersion = 1;
+       private static int protocolMinorVersion = 0;
+
+       /** 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;
+
+       private DataInputStream inFromSessiond;
+       private DataOutputStream outToSessiond;
+
+       private final AbstractLttngAgent<?> logAgent;
+       private final boolean isRoot;
+
+
+       /**
+        * 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;
+       }
+
+       /**
+        * 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.
+        */
+       public boolean waitForConnection(int seconds) {
+               try {
+                       return registrationLatch.await(seconds, TimeUnit.SECONDS);
+               } catch (InterruptedException e) {
+                       return false;
+               }
+       }
+
+       @Override
+       public void run() {
+               for (;;) {
+                       if (this.quit) {
+                               break;
+                       }
+
+                       try {
+
+                               /*
+                                * Connect to the session daemon before anything else.
+                                */
+                               connectToSessiond();
+
+                               /*
+                                * Register to the session daemon as the Java component of the
+                                * UST application.
+                                */
+                               registerToSessiond();
+
+                               /*
+                                * Block on socket receive and wait for command from the
+                                * session daemon. This will return if and only if there is a
+                                * fatal error or the socket closes.
+                                */
+                               handleSessiondCmd();
+                       } catch (UnknownHostException uhe) {
+                               uhe.printStackTrace();
+                       } catch (IOException ioe) {
+                               try {
+                                       Thread.sleep(3000);
+                               } catch (InterruptedException e) {
+                                       e.printStackTrace();
+                               }
+                       }
+               }
+       }
+
+       /**
+        * 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 (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 IOException {
+               byte data[] = new byte[SessiondHeaderCommand.HEADER_SIZE];
+
+               int readLen = this.inFromSessiond.read(data, 0, data.length);
+               if (readLen != data.length) {
+                       throw new IOException();
+               }
+               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 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) {
+                       return null;
+               }
+
+               this.inFromSessiond.read(payload, 0, payload.length);
+               return payload;
+       }
+
+       /**
+        * Handle session command from the session daemon.
+        */
+       private void handleSessiondCmd() throws IOException {
+               byte data[] = null;
+
+               while (true) {
+                       /* Get header from session daemon. */
+                       recvHeader();
+
+                       if (headerCmd.getDataSize() > 0) {
+                               data = recvPayload();
+                       }
+
+                       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;
+                               }
+                               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();
+               }
+       }
+
+       private static String getHomePath() {
+               return System.getProperty("user.home");
+       }
+
+       /**
+        * Read port number from file created by the session daemon.
+        *
+        * @return port value if found else 0.
+        */
+       private static int getPortFromFile(String path) throws IOException {
+               int port;
+               BufferedReader br = null;
+
+               try {
+                       br = new BufferedReader(new FileReader(path));
+                       String line = br.readLine();
+                       port = Integer.parseInt(line, 10);
+                       if (port < 0 || port > 65535) {
+                               /* Invalid value. Ignore. */
+                               port = 0;
+                       }
+               } catch (FileNotFoundException e) {
+                       /* No port available. */
+                       port = 0;
+               } finally {
+                       if (br != null) {
+                               br.close();
+                       }
+               }
+
+               return port;
+       }
+
+       private void connectToSessiond() throws IOException {
+               int port;
+
+               if (this.isRoot) {
+                       port = getPortFromFile(ROOT_PORT_FILE);
+                       if (port == 0) {
+                               /* No session daemon available. Stop and retry later. */
+                               throw new IOException();
+                       }
+               } else {
+                       port = getPortFromFile(getHomePath() + USER_PORT_FILE);
+                       if (port == 0) {
+                               /* No session daemon available. Stop and retry later. */
+                               throw new IOException();
+                       }
+               }
+
+               this.sessiondSock = new Socket(SESSION_HOST, port);
+               this.inFromSessiond = new DataInputStream(sessiondSock.getInputStream());
+               this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream());
+       }
+
+       private void registerToSessiond() throws IOException {
+               byte data[] = new byte[16];
+               ByteBuffer buf = ByteBuffer.wrap(data);
+               String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
+
+               buf.putInt(logAgent.getDomain().value());
+               buf.putInt(Integer.parseInt(pid));
+               buf.putInt(protocolMajorVersion);
+               buf.putInt(protocolMinorVersion);
+               this.outToSessiond.write(data, 0, data.length);
+               this.outToSessiond.flush();
+       }
+}
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 (file)
index 0000000..9dad7f6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 (file)
index 0000000..ec49cb2
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 (file)
index 0000000..a269727
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 (file)
index 0000000..a43c488
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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<String> loggerList = new ArrayList<String>();
+
+       /** 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;
+       }
+}
index b2aed8b467d0d91e8f764597a8a96340e8485963..d32e1d69ffd9dad593def6ee5497edf82ac5daac 100644 (file)
@@ -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 (file)
index 2f21d62..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
- *
- *
- * 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<String> listLoggers() {
-               Vector<String> logs = new Vector<String>();
-               for (Enumeration<String> 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 (file)
index b844d2f..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
- *
- * 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 (file)
index 0000000..fa92f64
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * 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<LttngLogHandler> {
+
+       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 (file)
index 0000000..d8e0cb8
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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<LttngLogHandler> 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);
+}
index 9113f85e3b0ffe5589aa75fd3000efa30c1ef920..9ae532330b74093a18e8d60691f122352a69328f 100644 (file)
@@ -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 (file)
index 534cc94..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
- *
- *
- * 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<String> listLoggers() {
-               Vector<String> logs = new Vector<String>();
-               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 (file)
index 30fac23..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
- *
- * 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 (file)
index 0000000..5dd8468
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * 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<LttngLogAppender> {
+
+       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 (file)
index 0000000..98cc2c8
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2014 - Christian Babeux <christian.babeux@efficios.com>
+ *
+ * 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<LttngLogAppender> 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);
+}
index d0d6d78055a72d0ac3ce1fe8d692888e0c985722..40906e10ca0b191712a7bcf8da9c612acccfa31d 100644 (file)
@@ -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
index b089fe3b3ce179610ceaafefb7c2d10269ac4537..7363f65c0b5adf640674ea5684ec3d8982cae0f1 100644 (file)
  * 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);
-}
index a45af396e6c2aff69cf953ce8be6694dfbc7c3b6..1482d90f571552b9c80dd08669ec7a2ffce34e3f 100644 (file)
@@ -25,9 +25,9 @@
 #include <lttng/tracepoint.h>
 
 /*
- * 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
index 7acbccdb7f1081dca446783f83ec4cfafda870d0..07384f8d6a6759b197985bf0e21b35a2dff588b8 100644 (file)
@@ -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
index c887f7f414e5602f0e72be0f543a261aeb4c838b..d5df70e2a18f5493fbcd2ec5167856e7415eecc2 100644 (file)
@@ -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);
-}
index cc403578b5df3af7e37998889fd10d52a5d2343f..7e20b6985f061d4ed6039ebc27c267d3566853c5 100644 (file)
@@ -25,9 +25,9 @@
 #include <lttng/tracepoint.h>
 
 /*
- * 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
This page took 0.087288 seconds and 5 git commands to generate.