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