001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors.
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.common.util;
023    
024    import java.util.Locale;
025    import java.util.concurrent.atomic.AtomicReference;
026    import net.jcip.annotations.ThreadSafe;
027    import org.jboss.dna.common.i18n.I18n;
028    import org.slf4j.ILoggerFactory;
029    import org.slf4j.LoggerFactory;
030    
031    /**
032     * A simple logging interface that is fully compatible with multiple logging implementations. This interface does take advantage
033     * of the variable arguments and autoboxing features in Java 5, reducing the number of methods that are necessary and allowing
034     * callers to supply primitive values as parameters.
035     */
036    @ThreadSafe
037    public final class Logger {
038    
039        public enum Level {
040            OFF,
041            ERROR,
042            WARNING,
043            INFO,
044            DEBUG,
045            TRACE;
046        }
047    
048        private static final AtomicReference<Locale> LOGGING_LOCALE = new AtomicReference<Locale>(null);
049    
050        /**
051         * Get the locale used for the logs. If null, the {@link Locale#getDefault() default locale} is used.
052         * 
053         * @return the current locale used for logging, or null if the system locale is used
054         * @see #setLoggingLocale(Locale)
055         */
056        public static Locale getLoggingLocale() {
057            return LOGGING_LOCALE.get();
058        }
059    
060        /**
061         * Set the locale used for the logs. This should be used when the logs are to be written is a specific locale, independent of
062         * the {@link Locale#getDefault() default locale}. To use the default locale, call this method with a null value.
063         * 
064         * @param locale the desired locale to use for the logs, or null if the system locale should be used
065         * @return the previous locale
066         * @see #getLoggingLocale()
067         */
068        public static Locale setLoggingLocale( Locale locale ) {
069            return LOGGING_LOCALE.getAndSet(locale != null ? locale : Locale.getDefault());
070        }
071    
072        /**
073         * Return a logger named corresponding to the class passed as parameter, using the statically bound {@link ILoggerFactory}
074         * instance.
075         * 
076         * @param clazz the returned logger will be named after clazz
077         * @return logger
078         */
079        public static Logger getLogger( Class<?> clazz ) {
080            return new Logger(LoggerFactory.getLogger(clazz));
081        }
082    
083        /**
084         * Return a logger named according to the name parameter using the statically bound {@link ILoggerFactory} instance.
085         * 
086         * @param name The name of the logger.
087         * @return logger
088         */
089        public static Logger getLogger( String name ) {
090            return new Logger(LoggerFactory.getLogger(name));
091        }
092    
093        private final org.slf4j.Logger delegate;
094    
095        private Logger( org.slf4j.Logger delegate ) {
096            this.delegate = delegate;
097        }
098    
099        /**
100         * Return the name of this logger instance.
101         * 
102         * @return the logger's name
103         */
104        public String getName() {
105            return this.delegate.getName();
106        }
107    
108        /**
109         * Log a message at the suplied level according to the specified format and (optional) parameters. The message should contain
110         * a pair of empty curly braces for each of the parameter, which should be passed in the correct order. This method is
111         * efficient and avoids superfluous object creation when the logger is disabled for the desired level.
112         * 
113         * @param level the level at which to log
114         * @param message the (localized) message string
115         * @param params the parameter values that are to replace the variables in the format string
116         */
117        public void log( Level level,
118                         I18n message,
119                         Object... params ) {
120            if (message == null) return;
121            switch (level) {
122                case DEBUG:
123                    debug(message.text(LOGGING_LOCALE.get(), params));
124                    break;
125                case ERROR:
126                    error(message, params);
127                    break;
128                case INFO:
129                    info(message, params);
130                    break;
131                case TRACE:
132                    trace(message.text(LOGGING_LOCALE.get(), params));
133                    break;
134                case WARNING:
135                    warn(message, params);
136                    break;
137                case OFF:
138                    break;
139            }
140        }
141    
142        /**
143         * Log an exception (throwable) at the supplied level with an accompanying message. If the exception is null, then this method
144         * calls {@link #debug(String, Object...)}.
145         * 
146         * @param level the level at which to log
147         * @param t the exception (throwable) to log
148         * @param message the message accompanying the exception
149         * @param params the parameter values that are to replace the variables in the format string
150         */
151        public void log( Level level,
152                         Throwable t,
153                         I18n message,
154                         Object... params ) {
155            if (message == null) return;
156            switch (level) {
157                case DEBUG:
158                    debug(t, message.text(LOGGING_LOCALE.get(), params));
159                    break;
160                case ERROR:
161                    error(t, message, params);
162                    break;
163                case INFO:
164                    info(t, message, params);
165                    break;
166                case TRACE:
167                    trace(t, message.text(LOGGING_LOCALE.get(), params));
168                    break;
169                case WARNING:
170                    warn(t, message, params);
171                    break;
172                case OFF:
173                    break;
174            }
175        }
176    
177        /**
178         * Log a message at the DEBUG level according to the specified format and (optional) parameters. The message should contain a
179         * pair of empty curly braces for each of the parameter, which should be passed in the correct order. This method is efficient
180         * and avoids superfluous object creation when the logger is disabled for the DEBUG level.
181         * 
182         * @param message the message string
183         * @param params the parameter values that are to replace the variables in the format string
184         */
185        public void debug( String message,
186                           Object... params ) {
187            if (!isDebugEnabled()) return;
188            if (message == null) return;
189            this.delegate.debug(StringUtil.createString(message, params));
190        }
191    
192        /**
193         * Log an exception (throwable) at the DEBUG level with an accompanying message. If the exception is null, then this method
194         * calls {@link #debug(String, Object...)}.
195         * 
196         * @param t the exception (throwable) to log
197         * @param message the message accompanying the exception
198         * @param params the parameter values that are to replace the variables in the format string
199         */
200        public void debug( Throwable t,
201                           String message,
202                           Object... params ) {
203            if (!isDebugEnabled()) return;
204            if (t == null) {
205                debug(message, params);
206                return;
207            }
208            if (message == null) {
209                this.delegate.debug(null, t);
210                return;
211            }
212            this.delegate.debug(StringUtil.createString(message, params), t);
213        }
214    
215        /**
216         * Log a message at the ERROR level according to the specified format and (optional) parameters. The message should contain a
217         * pair of empty curly braces for each of the parameter, which should be passed in the correct order. This method is efficient
218         * and avoids superfluous object creation when the logger is disabled for the ERROR level.
219         * 
220         * @param message the message string
221         * @param params the parameter values that are to replace the variables in the format string
222         */
223        public void error( I18n message,
224                           Object... params ) {
225            if (!isErrorEnabled()) return;
226            if (message == null) return;
227            this.delegate.error(message.text(LOGGING_LOCALE.get(), params));
228        }
229    
230        /**
231         * Log an exception (throwable) at the ERROR level with an accompanying message. If the exception is null, then this method
232         * calls {@link #error(I18n, Object...)}.
233         * 
234         * @param t the exception (throwable) to log
235         * @param message the message accompanying the exception
236         * @param params the parameter values that are to replace the variables in the format string
237         */
238        public void error( Throwable t,
239                           I18n message,
240                           Object... params ) {
241            if (!isErrorEnabled()) return;
242            if (t == null) {
243                error(message, params);
244                return;
245            }
246            if (message == null) {
247                this.delegate.error(null, t);
248                return;
249            }
250            this.delegate.error(message.text(LOGGING_LOCALE.get(), params), t);
251        }
252    
253        /**
254         * Log a message at the INFO level according to the specified format and (optional) parameters. The message should contain a
255         * pair of empty curly braces for each of the parameter, which should be passed in the correct order. This method is efficient
256         * and avoids superfluous object creation when the logger is disabled for the INFO level.
257         * 
258         * @param message the message string
259         * @param params the parameter values that are to replace the variables in the format string
260         */
261        public void info( I18n message,
262                          Object... params ) {
263            if (!isInfoEnabled()) return;
264            if (message == null) return;
265            this.delegate.info(message.text(LOGGING_LOCALE.get(), params));
266        }
267    
268        /**
269         * Log an exception (throwable) at the INFO level with an accompanying message. If the exception is null, then this method
270         * calls {@link #info(I18n, Object...)}.
271         * 
272         * @param t the exception (throwable) to log
273         * @param message the message accompanying the exception
274         * @param params the parameter values that are to replace the variables in the format string
275         */
276        public void info( Throwable t,
277                          I18n message,
278                          Object... params ) {
279            if (!isInfoEnabled()) return;
280            if (t == null) {
281                info(message, params);
282                return;
283            }
284            if (message == null) {
285                this.delegate.info(null, t);
286                return;
287            }
288            this.delegate.info(message.text(LOGGING_LOCALE.get(), params), t);
289        }
290    
291        /**
292         * Log a message at the TRACE level according to the specified format and (optional) parameters. The message should contain a
293         * pair of empty curly braces for each of the parameter, which should be passed in the correct order. This method is efficient
294         * and avoids superfluous object creation when the logger is disabled for the TRACE level.
295         * 
296         * @param message the message string
297         * @param params the parameter values that are to replace the variables in the format string
298         */
299        public void trace( String message,
300                           Object... params ) {
301            if (!isTraceEnabled()) return;
302            if (message == null) return;
303            this.delegate.trace(StringUtil.createString(message, params));
304        }
305    
306        /**
307         * Log an exception (throwable) at the TRACE level with an accompanying message. If the exception is null, then this method
308         * calls {@link #trace(String, Object...)}.
309         * 
310         * @param t the exception (throwable) to log
311         * @param message the message accompanying the exception
312         * @param params the parameter values that are to replace the variables in the format string
313         */
314        public void trace( Throwable t,
315                           String message,
316                           Object... params ) {
317            if (!isTraceEnabled()) return;
318            if (t == null) {
319                this.trace(message, params);
320                return;
321            }
322            if (message == null) {
323                this.delegate.trace(null, t);
324                return;
325            }
326            this.delegate.trace(StringUtil.createString(message, params), t);
327        }
328    
329        /**
330         * Log a message at the WARNING level according to the specified format and (optional) parameters. The message should contain
331         * a pair of empty curly braces for each of the parameter, which should be passed in the correct order. This method is
332         * efficient and avoids superfluous object creation when the logger is disabled for the WARNING level.
333         * 
334         * @param message the message string
335         * @param params the parameter values that are to replace the variables in the format string
336         */
337        public void warn( I18n message,
338                          Object... params ) {
339            if (!isWarnEnabled()) return;
340            if (message == null) return;
341            this.delegate.warn(message.text(LOGGING_LOCALE.get(), params));
342        }
343    
344        /**
345         * Log an exception (throwable) at the WARNING level with an accompanying message. If the exception is null, then this method
346         * calls {@link #warn(I18n, Object...)}.
347         * 
348         * @param t the exception (throwable) to log
349         * @param message the message accompanying the exception
350         * @param params the parameter values that are to replace the variables in the format string
351         */
352        public void warn( Throwable t,
353                          I18n message,
354                          Object... params ) {
355            if (!isWarnEnabled()) return;
356            if (t == null) {
357                warn(message, params);
358                return;
359            }
360            if (message == null) {
361                this.delegate.warn(null, t);
362                return;
363            }
364            this.delegate.warn(message.text(LOGGING_LOCALE.get(), params), t);
365        }
366    
367        /**
368         * Return whether messages at the INFORMATION level are being logged.
369         * 
370         * @return true if INFORMATION log messages are currently being logged, or false otherwise.
371         */
372        protected boolean isInfoEnabled() {
373            return this.delegate.isInfoEnabled();
374        }
375    
376        /**
377         * Return whether messages at the WARNING level are being logged.
378         * 
379         * @return true if WARNING log messages are currently being logged, or false otherwise.
380         */
381        protected boolean isWarnEnabled() {
382            return this.delegate.isWarnEnabled();
383        }
384    
385        /**
386         * Return whether messages at the ERROR level are being logged.
387         * 
388         * @return true if ERROR log messages are currently being logged, or false otherwise.
389         */
390        protected boolean isErrorEnabled() {
391            return this.delegate.isErrorEnabled();
392        }
393    
394        /**
395         * Return whether messages at the DEBUG level are being logged.
396         * 
397         * @return true if DEBUG log messages are currently being logged, or false otherwise.
398         */
399        public boolean isDebugEnabled() {
400            return this.delegate.isDebugEnabled();
401        }
402    
403        /**
404         * Return whether messages at the TRACE level are being logged.
405         * 
406         * @return true if TRACE log messages are currently being logged, or false otherwise.
407         */
408        public boolean isTraceEnabled() {
409            return this.delegate.isTraceEnabled();
410        }
411    
412        /**
413         * Get the logging level at which this logger is current set.
414         * 
415         * @return the current logging level
416         */
417        public Level getLevel() {
418            if (this.isTraceEnabled()) return Level.TRACE;
419            if (this.isDebugEnabled()) return Level.DEBUG;
420            if (this.isInfoEnabled()) return Level.INFO;
421            if (this.isWarnEnabled()) return Level.WARNING;
422            if (this.isErrorEnabled()) return Level.ERROR;
423            return Level.OFF;
424        }
425    
426    }