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
/*
+ * 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.
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...");
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();
}
}
/*
+ * 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();
}
}
+======================
+ Using the Java agent
+======================
+
The agent can be built in three different configurations:
1) Java agent with JUL support:
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.
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)
uninstall-hook:
cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
-CLEANFILES = $(jarfile) $(pkgpath)/*.class
+CLEANFILES = $(jarfile) $(pkgpath)/*.class $(pkgpath)/client/*.class
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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();
+}
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;
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;
}
+
}
+++ /dev/null
-/*
- * 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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();
- }
-}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
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)
$(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)
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
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+}
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)
$(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)
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
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+}
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
* 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,
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);
(*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);
-}
#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,
)
)
-/*
- * 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
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
* 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
/*
* 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,
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);
(*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);
-}
#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,
)
)
-/*
- * 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