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