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