1 /*
2 * Copyright (c) 2004-2008 QOS.ch
3 *
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
21 * SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
22 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
23 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
24 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 *
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
30 */
31
32 package org.slf4j.bridge;
33
34 import java.text.MessageFormat;
35 import java.util.MissingResourceException;
36 import java.util.ResourceBundle;
37 import java.util.logging.Handler;
38 import java.util.logging.Level;
39 import java.util.logging.LogManager;
40 import java.util.logging.LogRecord;
41
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.slf4j.spi.LocationAwareLogger;
45
46 // Based on http://bugzilla.slf4j.org/show_bug.cgi?id=38
47
48 /**
49 * Bridge/route all JUL log records to the SLF4J API.
50 *
51 * <p>
52 * Essentially, the idea is to install on the root logger an instance of
53 * SLF4JBridgeHandler as the sole JUL handler in the system. Subsequently, the
54 * SLF4JBridgeHandler instance will redirect all JUL log records are redirected
55 * to the SLF4J API based on the following mapping of levels:
56 *
57 * <pre>
58 * FINEST -> TRACE
59 * FINER -> DEBUG
60 * FINE -> DEBUG
61 * INFO -> INFO
62 * WARNING -> WARN
63 * SEVER -> ERROR
64 * </pre>
65 *
66 * Usage:
67 *
68 * <pre>
69 * // call only once during initialization time of your application
70 * SLF4JBridgeHandler.install();
71 *
72 * // usual pattern: get a Logger and then log a message
73 * java.util.logging.Logger julLogger = java.util.logging.Logger
74 * .getLogger("org.wombat");
75 * julLogger.fine("hello world"); // this will get redirected to SLF4J
76 * </pre>
77 *
78 * @author Christian Stein
79 * @author Joern Huxhorn
80 * @author Ceki Gülcü
81 * @author Darryl Smith
82 *
83 * @since 1.5.1
84 */
85 public class SLF4JBridgeHandler extends Handler {
86
87 // The caller is java.util.logging.Logger
88 private static final String FQCN = java.util.logging.Logger.class.getName();
89 private static final String UNKNOWN_LOGGER_NAME = "unknown.jul.logger";
90
91 private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue();
92 private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue();
93 private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
94 private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();
95
96 /**
97 * Adds a SLF4JBridgeHandler instance to jul's root logger.
98 *
99 * <p>
100 * This handler will redirect jul logging to SLF4J. However, only logs enabled
101 * in j.u.l. will be redirected. For example, if a log statement invoking a
102 * j.u.l. logger disabled that statement, by definition, will <em>not</em> reach
103 * any SLF4JBridgeHandler instance and cannot be redirected.
104 */
105 public static void install() {
106 LogManager.getLogManager().getLogger("").addHandler(
107 new SLF4JBridgeHandler());
108 }
109
110 /**
111 * Removes previously installed SLF4JBridgeHandler instances. See also
112 * {@link #install()}.
113 *
114 * @throws SecurityException
115 * A <code>SecurityException</code> is thrown, if a security
116 * manager exists and if the caller does not have
117 * LoggingPermission("control").
118 */
119 public static void uninstall() throws SecurityException {
120 java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger(
121 "");
122 Handler[] handlers = rootLogger.getHandlers();
123 for (int i = 0; i < handlers.length; i++) {
124 if (handlers[i] instanceof SLF4JBridgeHandler) {
125 rootLogger.removeHandler(handlers[i]);
126 }
127 }
128 }
129
130 /**
131 * Initialize this handler.
132 *
133 */
134 public SLF4JBridgeHandler() {
135 }
136
137 /**
138 * No-op implementation.
139 */
140 public void close() {
141 // empty
142 }
143
144 /**
145 * No-op implementation.
146 */
147 public void flush() {
148 // empty
149 }
150
151 /**
152 * Return the Logger instance that will be used for logging.
153 */
154 protected Logger getSLF4JLogger(LogRecord record) {
155 String name = record.getLoggerName();
156 if (name == null) {
157 name = UNKNOWN_LOGGER_NAME;
158 }
159 return LoggerFactory.getLogger(name);
160 }
161
162 protected void callLocationAwareLogger(LocationAwareLogger lal,
163 LogRecord record) {
164 int julLevelValue = record.getLevel().intValue();
165 int slf4jLevel;
166
167 if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
168 slf4jLevel = LocationAwareLogger.TRACE_INT;
169 } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
170 slf4jLevel = LocationAwareLogger.DEBUG_INT;
171 } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
172 slf4jLevel = LocationAwareLogger.INFO_INT;
173 } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
174 slf4jLevel = LocationAwareLogger.WARN_INT;
175 } else {
176 slf4jLevel = LocationAwareLogger.ERROR_INT;
177 }
178 String i18nMessage = getMessageI18N(record);
179 lal.log(null, FQCN, slf4jLevel, i18nMessage, record.getThrown());
180 }
181
182 protected void callPlainSLF4JLogger(Logger slf4jLogger, LogRecord record) {
183 String i18nMessage = getMessageI18N(record);
184 int julLevelValue = record.getLevel().intValue();
185 if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
186 slf4jLogger.trace(i18nMessage, record.getThrown());
187 } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
188 slf4jLogger.debug(i18nMessage, record.getThrown());
189 } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
190 slf4jLogger.info(i18nMessage, record.getThrown());
191 } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
192 slf4jLogger.warn(i18nMessage, record.getThrown());
193 } else {
194 slf4jLogger.error(i18nMessage, record.getThrown());
195 }
196 }
197
198 /**
199 * Get the record's message, possibly via a resource bundle.
200 *
201 * @param record
202 * @return
203 */
204 private String getMessageI18N(LogRecord record) {
205 String message = record.getMessage();
206
207 if (message == null) {
208 return null;
209 }
210
211 ResourceBundle bundle = record.getResourceBundle();
212 if (bundle != null) {
213 try {
214 message = bundle.getString(message);
215 } catch (MissingResourceException e) {
216 }
217 }
218 Object[] params = record.getParameters();
219 if (params != null) {
220 message = MessageFormat.format(message, params);
221 }
222 return message;
223 }
224
225 /**
226 * Publish a LogRecord.
227 * <p>
228 * The logging request was made initially to a Logger object, which
229 * initialized the LogRecord and forwarded it here.
230 * <p>
231 * This handler ignores the Level attached to the LogRecord, as SLF4J cares
232 * about discarding log statements.
233 *
234 * @param record
235 * Description of the log event. A null record is silently
236 * ignored and is not published.
237 */
238 public void publish(LogRecord record) {
239 // Silently ignore null records.
240 if (record == null) {
241 return;
242 }
243
244 Logger slf4jLogger = getSLF4JLogger(record);
245 String message = record.getMessage(); // can be null!
246 // this is a check to avoid calling the underlying logging system
247 // with a null message
248 if (message == null) {
249 return;
250 }
251 if (slf4jLogger instanceof LocationAwareLogger) {
252 callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record);
253 } else {
254 callPlainSLF4JLogger(slf4jLogger, record);
255 }
256 }
257
258 }