Fix: Fix synchronization of LTTngAgent#dispose
[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.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.logging.Handler;
24 import java.util.logging.Logger;
25
26 /**
27 * The central agent managing the JUL and Log4j handlers.
28 *
29 * @author David Goulet
30 * @deprecated Applications are now expected to manage their Logger and Handler
31 * objects.
32 */
33 @Deprecated
34 public class LTTngAgent {
35
36 private static LTTngAgent instance = null;
37
38 /**
39 * Public getter to acquire a reference to this singleton object.
40 *
41 * @return The agent instance
42 */
43 public static synchronized LTTngAgent getLTTngAgent() {
44 if (instance == null) {
45 instance = new LTTngAgent();
46 }
47 return instance;
48 }
49
50 /**
51 * Dispose the agent. Applications should call this once they are done
52 * logging. This dispose function is non-static for backwards
53 * compatibility purposes.
54 */
55 public void dispose() {
56 synchronized (LTTngAgent.class) {
57 if (instance != null) {
58 instance.disposeInstance();
59 instance = null;
60 }
61 }
62 return;
63 }
64
65 private ILttngHandler julHandler = null;
66 private ILttngHandler log4jAppender = null;
67
68 /**
69 * Private constructor. This is a singleton and a reference should be
70 * acquired using {@link #getLTTngAgent()}.
71 */
72 private LTTngAgent() {
73 initJulHandler();
74 initLog4jAppender();
75 }
76
77 /**
78 * "Destructor" method.
79 */
80 private void disposeInstance() {
81 disposeJulHandler();
82 disposeLog4jAppender();
83 }
84
85 /**
86 * Create a LTTng-JUL handler, and attach it to the JUL root logger.
87 */
88 private void initJulHandler() {
89 try {
90 Class<?> julHandlerClass = Class.forName("org.lttng.ust.agent.jul.LttngLogHandler");
91 /*
92 * It is safer to use Constructor.newInstance() rather than
93 * Class.newInstance(), because it will catch the exceptions thrown
94 * by the constructor below (which happens if the Java library is
95 * present, but the matching JNI one is not).
96 */
97 Constructor<?> julHandlerCtor = julHandlerClass.getConstructor();
98 julHandler = (ILttngHandler) julHandlerCtor.newInstance();
99
100 /* Attach the handler to the root JUL logger */
101 Logger.getLogger("").addHandler((Handler) julHandler);
102
103 /*
104 * If any of the following exceptions happen, it means we could not
105 * find or initialize LTTng JUL classes. We will not setup LTTng JUL
106 * tracing in this case.
107 */
108 } catch (SecurityException e) {
109 } catch (IllegalAccessException e) {
110 } catch (IllegalArgumentException e) {
111 } catch (ClassNotFoundException e) {
112 } catch (NoSuchMethodException e) {
113 } catch (InstantiationException e) {
114 } catch (InvocationTargetException e) {
115 }
116 }
117
118 /**
119 * Create a LTTng-logj4 appender, and attach it to the log4j root logger.
120 */
121 private void initLog4jAppender() {
122 /*
123 * Since Log4j is a 3rd party library, we first need to check if we can
124 * load any of its classes.
125 */
126 if (!testLog4jClasses()) {
127 return;
128 }
129
130 try {
131 Class<?> log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender");
132 Constructor<?> log4jAppendCtor = log4jAppenderClass.getConstructor();
133 log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance();
134
135 /*
136 * If any of the following exceptions happen, it means we could not
137 * find or initialize LTTng log4j classes. We will not setup LTTng
138 * log4j tracing in this case.
139 */
140 } catch (SecurityException e) {
141 return;
142 } catch (ClassNotFoundException e) {
143 return;
144 } catch (NoSuchMethodException e) {
145 return;
146 } catch (IllegalArgumentException e) {
147 return;
148 } catch (InstantiationException e) {
149 return;
150 } catch (IllegalAccessException e) {
151 return;
152 } catch (InvocationTargetException e) {
153 return;
154 }
155
156 /*
157 * Attach the appender to the root Log4j logger. Slightly more tricky
158 * here, as log4j.Logger is not in the base Java library, and we do not
159 * want the "common" package to depend on log4j. So we have to obtain it
160 * through reflection too.
161 */
162 try {
163 Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
164 Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
165
166 Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
167 Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass);
168
169 Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
170 addAppenderMethod.invoke(rootLogger, log4jAppender);
171
172 /*
173 * We have checked for the log4j library version previously, none of
174 * the following exceptions should happen.
175 */
176 } catch (SecurityException e) {
177 throw new IllegalStateException(e);
178 } catch (ClassNotFoundException e) {
179 throw new IllegalStateException(e);
180 } catch (NoSuchMethodException e) {
181 throw new IllegalStateException(e);
182 } catch (IllegalArgumentException e) {
183 throw new IllegalStateException(e);
184 } catch (IllegalAccessException e) {
185 throw new IllegalStateException(e);
186 } catch (InvocationTargetException e) {
187 throw new IllegalStateException(e);
188 }
189 }
190
191 /**
192 * Check if log4j >= 1.2.15 library is present.
193 */
194 private static boolean testLog4jClasses() {
195 Class<?> loggingEventClass;
196
197 try {
198 loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent");
199 } catch (ClassNotFoundException e) {
200 /*
201 * Log4j classes not found, no need to create the relevant objects
202 */
203 return false;
204 }
205
206 /*
207 * Detect capabilities of the log4j library. We only support log4j >=
208 * 1.2.15. The getTimeStamp() method was introduced in log4j 1.2.15, so
209 * verify that it is available.
210 *
211 * We can't rely on the getPackage().getImplementationVersion() call
212 * that would retrieves information from the manifest file found in the
213 * JAR since the manifest file shipped from upstream is known to be
214 * broken in several versions of the library.
215 *
216 * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370
217 */
218 try {
219 loggingEventClass.getDeclaredMethod("getTimeStamp");
220 } catch (NoSuchMethodException e) {
221 System.err.println(
222 "Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled.");
223 return false;
224 } catch (SecurityException e) {
225 return false;
226 }
227
228 return true;
229 }
230
231 /**
232 * Detach the JUL handler from its logger and close it.
233 */
234 private void disposeJulHandler() {
235 if (julHandler == null) {
236 /* The JUL handler was not activated, we have nothing to do */
237 return;
238 }
239 Logger.getLogger("").removeHandler((Handler) julHandler);
240 julHandler.close();
241 julHandler = null;
242 }
243
244 /**
245 * Detach the log4j appender from its logger and close it.
246 */
247 private void disposeLog4jAppender() {
248 if (log4jAppender == null) {
249 /* The log4j appender was not active, we have nothing to do */
250 return;
251 }
252
253 /*
254 * Detach the appender from the log4j root logger. Again, we have to do
255 * this via reflection.
256 */
257 try {
258 Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
259 Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
260
261 Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
262 Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass);
263
264 Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
265 removeAppenderMethod.invoke(rootLogger, log4jAppender);
266
267 /*
268 * We were able to attach the appender previously, we should not
269 * have problems here either!
270 */
271 } catch (SecurityException e) {
272 throw new IllegalStateException(e);
273 } catch (ClassNotFoundException e) {
274 throw new IllegalStateException(e);
275 } catch (NoSuchMethodException e) {
276 throw new IllegalStateException(e);
277 } catch (IllegalArgumentException e) {
278 throw new IllegalStateException(e);
279 } catch (IllegalAccessException e) {
280 throw new IllegalStateException(e);
281 } catch (InvocationTargetException e) {
282 throw new IllegalStateException(e);
283 }
284
285 /* Close the appender */
286 log4jAppender.close();
287 log4jAppender = null;
288 }
289
290 }
This page took 0.036185 seconds and 5 git commands to generate.