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