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