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 | */ |
6b4fc51e AM |
55 | public void dispose() { |
56 | synchronized (LTTngAgent.class) { | |
57 | if (instance != null) { | |
58 | instance.disposeInstance(); | |
59 | instance = null; | |
60 | } | |
501f6777 | 61 | } |
d60dfbe4 | 62 | return; |
501f6777 CB |
63 | } |
64 | ||
d60dfbe4 AM |
65 | private ILttngHandler julHandler = null; |
66 | private ILttngHandler log4jAppender = null; | |
08284556 | 67 | |
d60dfbe4 AM |
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 | } | |
501f6777 | 76 | |
d60dfbe4 AM |
77 | /** |
78 | * "Destructor" method. | |
79 | */ | |
80 | private void disposeInstance() { | |
81 | disposeJulHandler(); | |
82 | disposeLog4jAppender(); | |
83 | } | |
501f6777 | 84 | |
d60dfbe4 AM |
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); | |
01a00a70 | 102 | |
d60dfbe4 | 103 | /* |
01a00a70 AM |
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. | |
d60dfbe4 | 107 | */ |
01a00a70 AM |
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) { | |
d60dfbe4 AM |
115 | } |
116 | } | |
501f6777 | 117 | |
d60dfbe4 AM |
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 | } | |
501f6777 | 129 | |
d60dfbe4 AM |
130 | try { |
131 | Class<?> log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender"); | |
132 | Constructor<?> log4jAppendCtor = log4jAppenderClass.getConstructor(); | |
133 | log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance(); | |
01a00a70 | 134 | |
d60dfbe4 | 135 | /* |
01a00a70 AM |
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. | |
d60dfbe4 | 139 | */ |
01a00a70 AM |
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) { | |
d60dfbe4 AM |
153 | return; |
154 | } | |
501f6777 | 155 | |
d60dfbe4 AM |
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"); | |
501f6777 | 165 | |
d60dfbe4 AM |
166 | Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null); |
167 | Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass); | |
501f6777 | 168 | |
d60dfbe4 AM |
169 | Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null); |
170 | addAppenderMethod.invoke(rootLogger, log4jAppender); | |
501f6777 | 171 | |
d60dfbe4 | 172 | /* |
01a00a70 AM |
173 | * We have checked for the log4j library version previously, none of |
174 | * the following exceptions should happen. | |
d60dfbe4 | 175 | */ |
01a00a70 AM |
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); | |
501f6777 | 188 | } |
501f6777 CB |
189 | } |
190 | ||
d60dfbe4 AM |
191 | /** |
192 | * Check if log4j >= 1.2.15 library is present. | |
193 | */ | |
194 | private static boolean testLog4jClasses() { | |
195 | Class<?> loggingEventClass; | |
17be0b58 | 196 | |
501f6777 | 197 | try { |
d60dfbe4 | 198 | loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent"); |
501f6777 | 199 | } catch (ClassNotFoundException e) { |
d60dfbe4 AM |
200 | /* |
201 | * Log4j classes not found, no need to create the relevant objects | |
202 | */ | |
17be0b58 CB |
203 | return false; |
204 | } | |
205 | ||
206 | /* | |
d60dfbe4 AM |
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. | |
17be0b58 | 210 | * |
d60dfbe4 AM |
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. | |
17be0b58 | 215 | * |
d60dfbe4 | 216 | * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370 |
17be0b58 | 217 | */ |
17be0b58 | 218 | try { |
d60dfbe4 | 219 | loggingEventClass.getDeclaredMethod("getTimeStamp"); |
17be0b58 | 220 | } catch (NoSuchMethodException e) { |
d60dfbe4 AM |
221 | System.err.println( |
222 | "Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled."); | |
17be0b58 CB |
223 | return false; |
224 | } catch (SecurityException e) { | |
225 | return false; | |
501f6777 CB |
226 | } |
227 | ||
17be0b58 | 228 | return true; |
501f6777 CB |
229 | } |
230 | ||
5bfeaeca | 231 | /** |
d60dfbe4 | 232 | * Detach the JUL handler from its logger and close it. |
501f6777 | 233 | */ |
d60dfbe4 AM |
234 | private void disposeJulHandler() { |
235 | if (julHandler == null) { | |
236 | /* The JUL handler was not activated, we have nothing to do */ | |
501f6777 CB |
237 | return; |
238 | } | |
d60dfbe4 AM |
239 | Logger.getLogger("").removeHandler((Handler) julHandler); |
240 | julHandler.close(); | |
241 | julHandler = null; | |
501f6777 CB |
242 | } |
243 | ||
5bfeaeca | 244 | /** |
d60dfbe4 | 245 | * Detach the log4j appender from its logger and close it. |
5bfeaeca | 246 | */ |
d60dfbe4 AM |
247 | private void disposeLog4jAppender() { |
248 | if (log4jAppender == null) { | |
249 | /* The log4j appender was not active, we have nothing to do */ | |
250 | return; | |
501f6777 CB |
251 | } |
252 | ||
d60dfbe4 AM |
253 | /* |
254 | * Detach the appender from the log4j root logger. Again, we have to do | |
255 | * this via reflection. | |
256 | */ | |
501f6777 | 257 | try { |
d60dfbe4 AM |
258 | Class<?> loggerClass = Class.forName("org.apache.log4j.Logger"); |
259 | Class<?> appenderClass = Class.forName("org.apache.log4j.Appender"); | |
501f6777 | 260 | |
d60dfbe4 AM |
261 | Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null); |
262 | Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass); | |
501f6777 | 263 | |
d60dfbe4 AM |
264 | Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null); |
265 | removeAppenderMethod.invoke(rootLogger, log4jAppender); | |
266 | ||
d60dfbe4 | 267 | /* |
01a00a70 AM |
268 | * We were able to attach the appender previously, we should not |
269 | * have problems here either! | |
d60dfbe4 | 270 | */ |
01a00a70 AM |
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); | |
501f6777 | 283 | } |
d60dfbe4 AM |
284 | |
285 | /* Close the appender */ | |
286 | log4jAppender.close(); | |
287 | log4jAppender = null; | |
501f6777 | 288 | } |
d60dfbe4 | 289 | |
501f6777 | 290 | } |