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