Refactor Java agent to let applications manage the log handlers
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / LTTngAgent.java
1 /*
2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 package org.lttng.ust.agent;
19
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Method;
22 import java.util.logging.Handler;
23 import java.util.logging.Logger;
24
25 /**
26 * The central agent managing the JUL and Log4j handlers.
27 *
28 * @author David Goulet
29 * @deprecated Applications are now expected to manage their Logger and Handler
30 * objects.
31 */
32 @Deprecated
33 public class LTTngAgent {
34
35 private static LTTngAgent instance = null;
36
37 /**
38 * Public getter to acquire a reference to this singleton object.
39 *
40 * @return The agent instance
41 */
42 public static synchronized LTTngAgent getLTTngAgent() {
43 if (instance == null) {
44 instance = new LTTngAgent();
45 }
46 return instance;
47 }
48
49 /**
50 * Dispose the agent. Applications should call this once they are done
51 * logging.
52 */
53 public static synchronized void dispose() {
54 if (instance != null) {
55 instance.disposeInstance();
56 instance = null;
57 }
58 return;
59 }
60
61 private ILttngHandler julHandler = null;
62 private ILttngHandler log4jAppender = null;
63
64 /**
65 * Private constructor. This is a singleton and a reference should be
66 * acquired using {@link #getLTTngAgent()}.
67 */
68 private LTTngAgent() {
69 initJulHandler();
70 initLog4jAppender();
71 }
72
73 /**
74 * "Destructor" method.
75 */
76 private void disposeInstance() {
77 disposeJulHandler();
78 disposeLog4jAppender();
79 }
80
81 /**
82 * Create a LTTng-JUL handler, and attach it to the JUL root logger.
83 */
84 private void initJulHandler() {
85 try {
86 Class<?> julHandlerClass = Class.forName("org.lttng.ust.agent.jul.LttngLogHandler");
87 /*
88 * It is safer to use Constructor.newInstance() rather than
89 * Class.newInstance(), because it will catch the exceptions thrown
90 * by the constructor below (which happens if the Java library is
91 * present, but the matching JNI one is not).
92 */
93 Constructor<?> julHandlerCtor = julHandlerClass.getConstructor();
94 julHandler = (ILttngHandler) julHandlerCtor.newInstance();
95
96 /* Attach the handler to the root JUL logger */
97 Logger.getLogger("").addHandler((Handler) julHandler);
98 } catch (ReflectiveOperationException e) {
99 /*
100 * LTTng JUL classes not found, no need to create the relevant
101 * objects
102 */
103 }
104 }
105
106 /**
107 * Create a LTTng-logj4 appender, and attach it to the log4j root logger.
108 */
109 private void initLog4jAppender() {
110 /*
111 * Since Log4j is a 3rd party library, we first need to check if we can
112 * load any of its classes.
113 */
114 if (!testLog4jClasses()) {
115 return;
116 }
117
118 try {
119 Class<?> log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender");
120 Constructor<?> log4jAppendCtor = log4jAppenderClass.getConstructor();
121 log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance();
122 } catch (ReflectiveOperationException e) {
123 /*
124 * LTTng Log4j classes not found, no need to create the relevant
125 * objects.
126 */
127 return;
128 }
129
130 /*
131 * Attach the appender to the root Log4j logger. Slightly more tricky
132 * here, as log4j.Logger is not in the base Java library, and we do not
133 * want the "common" package to depend on log4j. So we have to obtain it
134 * through reflection too.
135 */
136 try {
137 Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
138 Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
139
140 Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
141 Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass);
142
143 Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
144 addAppenderMethod.invoke(rootLogger, log4jAppender);
145
146 } catch (ReflectiveOperationException e) {
147 /*
148 * We have checked for the log4j library version previously, these
149 * classes should exist.
150 */
151 throw new IllegalStateException();
152 }
153 }
154
155 /**
156 * Check if log4j >= 1.2.15 library is present.
157 */
158 private static boolean testLog4jClasses() {
159 Class<?> loggingEventClass;
160
161 try {
162 loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent");
163 } catch (ClassNotFoundException e) {
164 /*
165 * Log4j classes not found, no need to create the relevant objects
166 */
167 return false;
168 }
169
170 /*
171 * Detect capabilities of the log4j library. We only support log4j >=
172 * 1.2.15. The getTimeStamp() method was introduced in log4j 1.2.15, so
173 * verify that it is available.
174 *
175 * We can't rely on the getPackage().getImplementationVersion() call
176 * that would retrieves information from the manifest file found in the
177 * JAR since the manifest file shipped from upstream is known to be
178 * broken in several versions of the library.
179 *
180 * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370
181 */
182 try {
183 loggingEventClass.getDeclaredMethod("getTimeStamp");
184 } catch (NoSuchMethodException e) {
185 System.err.println(
186 "Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled.");
187 return false;
188 } catch (SecurityException e) {
189 return false;
190 }
191
192 return true;
193 }
194
195 /**
196 * Detach the JUL handler from its logger and close it.
197 */
198 private void disposeJulHandler() {
199 if (julHandler == null) {
200 /* The JUL handler was not activated, we have nothing to do */
201 return;
202 }
203 Logger.getLogger("").removeHandler((Handler) julHandler);
204 julHandler.close();
205 julHandler = null;
206 }
207
208 /**
209 * Detach the log4j appender from its logger and close it.
210 */
211 private void disposeLog4jAppender() {
212 if (log4jAppender == null) {
213 /* The log4j appender was not active, we have nothing to do */
214 return;
215 }
216
217 /*
218 * Detach the appender from the log4j root logger. Again, we have to do
219 * this via reflection.
220 */
221 try {
222 Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
223 Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
224
225 Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
226 Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass);
227
228 Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
229 removeAppenderMethod.invoke(rootLogger, log4jAppender);
230
231 } catch (ReflectiveOperationException e) {
232 /*
233 * We were able to attach the appender, we should not have problems
234 * here either!
235 */
236 throw new IllegalStateException();
237 }
238
239 /* Close the appender */
240 log4jAppender.close();
241 log4jAppender = null;
242 }
243
244 }
This page took 0.035044 seconds and 5 git commands to generate.