View Javadoc
1   package org.djutils.logger;
2   
3   import java.util.Arrays;
4   import java.util.HashMap;
5   import java.util.HashSet;
6   import java.util.Map;
7   import java.util.Set;
8   import java.util.function.BooleanSupplier;
9   
10  import org.pmw.tinylog.Configurator;
11  import org.pmw.tinylog.Level;
12  import org.pmw.tinylog.LogEntryForwarder;
13  import org.pmw.tinylog.writers.ConsoleWriter;
14  import org.pmw.tinylog.writers.Writer;
15  
16  /**
17   * The CategoryLogger can log for specific Categories. The way to call the logger for messages that always need to be logged,
18   * such as an error with an exception is:
19   * 
20   * <pre>
21   * CategoryLogger.always().error(exception, "Parameter {} did not initialize correctly", param1.toString());
22   * </pre>
23   * 
24   * It is also possible to indicate the category / categories for the message, which will only be logged if at least one of the
25   * indicated categories is turned on with addLogCategory() or setLogCategories(), or if one of the added or set LogCategories is
26   * LogCategory.ALL:
27   * 
28   * <pre>
29   * CategoryLogger.filter(Cat.BASE).debug("Parameter {} initialized correctly", param1.toString());
30   * </pre>
31   * <p>
32   * Copyright (c) 2018-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
33   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
34   * distributed under a three-clause BSD-style license, which can be found at
35   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
36   * </p>
37   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
38   */
39  @SuppressWarnings({"checkstyle:visibilitymodifier", "checkstyle:finalclass", "checkstyle:needbraces"})
40  public class CategoryLogger
41  {
42      /** The default message format. */
43      public static final String DEFAULT_MESSAGE_FORMAT = "{class_name}.{method}:{line} {message|indent=4}";
44  
45      /** The current message format. */
46      private static String defaultMessageFormat = DEFAULT_MESSAGE_FORMAT;
47  
48      /** The current logging level. */
49      private static Level defaultLevel = Level.INFO;
50  
51      /** The writers registered with this CategoryLogger. */
52      private static Set<Writer> writers = new HashSet<>();
53  
54      /** The log level per Writer. */
55      private static Map<Writer, Level> writerLevels = new HashMap<>();
56  
57      /** The message format per Writer. */
58      private static Map<Writer, String> writerFormats = new HashMap<>();
59  
60      /** The categories to log. */
61      protected static Set<LogCategory> categories = new HashSet<>(256);
62  
63      /** The console writer, replacing the default one. */
64      private static Writer consoleWriter;
65  
66      /** The conditional logger works on the basis of a true condition. */
67      protected static ConditionalLogger conditionalLogger = new ConditionalLogger(true);
68  
69      /** The conditional logger works on the basis of a condition, failing the condition. */
70      protected static ConditionalLogger conditionalNoLogger = new ConditionalLogger(false);
71  
72      /** The delegate logger instance that does the actual logging work, after a positive filter outcome. */
73      protected static DelegateLogger delegateLogger = new DelegateLogger(true);
74  
75      /** The delegate logger that returns immediately after a negative filter outcome. */
76      protected static DelegateLogger noLogger = new DelegateLogger(false);
77  
78      /** */
79      protected CategoryLogger()
80      {
81          // Utility class.
82      }
83  
84      static
85      {
86          create();
87      }
88  
89      /**
90       * Create a new logger for the system console. Note that this REPLACES current writers. Note that the initial LogCategory is
91       * LogCategory.ALL, so all categories will be logged. This category has to be explicitly removed (or new categories have to
92       * be set) to log a limited set of categories.
93       */
94      protected static void create()
95      {
96          consoleWriter = new ConsoleWriter();
97          writers.add(consoleWriter);
98          Configurator.currentConfig().writer(consoleWriter, defaultLevel, defaultMessageFormat).activate();
99          categories.add(LogCategory.ALL);
100     }
101 
102     /**
103      * Set a new logging format for the message lines of all writers. The default message format is:<br>
104      * {class_name}.{method}:{line} {message|indent=4}<br>
105      * <br>
106      * A few popular placeholders that can be used:<br>
107      * - {class} Fully-qualified class name where the logging request is issued<br>
108      * - {class_name} Class name (without package) where the logging request is issued<br>
109      * - {date} Date and time of the logging request, e.g. {date:yyyy-MM-dd HH:mm:ss} [SimpleDateFormat]<br>
110      * - {level} Logging level of the created log entry<br>
111      * - {line} Line number from where the logging request is issued<br>
112      * - {message} Associated message of the created log entry<br>
113      * - {method} Method name from where the logging request is issued<br>
114      * - {package} Package where the logging request is issued<br>
115      * @see <a href="https://tinylog.org/configuration#format">https://tinylog.org/configuration</a>
116      * @param newMessageFormat String; the new formatting pattern to use for all registered writers
117      */
118     public static void setAllLogMessageFormat(final String newMessageFormat)
119     {
120         for (Writer writer : writers)
121         {
122             Configurator.currentConfig().removeWriter(writer).activate();
123             defaultMessageFormat = newMessageFormat;
124             writerFormats.put(writer, newMessageFormat);
125             Configurator.currentConfig().addWriter(writer, defaultLevel, defaultMessageFormat).activate();
126         }
127     }
128 
129     /**
130      * Set a new logging level for all registered writers.
131      * @param newLevel Level; the new log level for all registered writers
132      */
133     public static void setAllLogLevel(final Level newLevel)
134     {
135         for (Writer writer : writers)
136         {
137             Configurator.currentConfig().removeWriter(writer).activate();
138             defaultLevel = newLevel;
139             writerLevels.put(writer, newLevel);
140             Configurator.currentConfig().addWriter(writer, defaultLevel, defaultMessageFormat).activate();
141         }
142     }
143 
144     /**
145      * Set a new logging format for the message lines of a writer. The default message format is:<br>
146      * {class_name}.{method}:{line} {message|indent=4}<br>
147      * <br>
148      * A few popular placeholders that can be used:<br>
149      * - {class} Fully-qualified class name where the logging request is issued<br>
150      * - {class_name} Class name (without package) where the logging request is issued<br>
151      * - {date} Date and time of the logging request, e.g. {date:yyyy-MM-dd HH:mm:ss} [SimpleDateFormat]<br>
152      * - {level} Logging level of the created log entry<br>
153      * - {line} Line number from where the logging request is issued<br>
154      * - {message} Associated message of the created log entry<br>
155      * - {method} Method name from where the logging request is issued<br>
156      * - {package} Package where the logging request is issued<br>
157      * @see <a href="https://tinylog.org/configuration#format">https://tinylog.org/configuration</a>
158      * @param writer Writer; the writer to change the message format for
159      * @param newMessageFormat String; the new formatting pattern to use for all registered writers
160      */
161     public static void setLogMessageFormat(final Writer writer, final String newMessageFormat)
162     {
163         Configurator.currentConfig().removeWriter(writer).activate();
164         writerFormats.put(writer, newMessageFormat);
165         Configurator.currentConfig().addWriter(writer, writerLevels.get(writer), newMessageFormat).activate();
166     }
167 
168     /**
169      * Set a new logging level for one of the registered writers.
170      * @param writer Writer; the writer to change the log level for
171      * @param newLevel Level; the new log level for the writer
172      */
173     public static void setLogLevel(final Writer writer, final Level newLevel)
174     {
175         Configurator.currentConfig().removeWriter(writer).activate();
176         writerLevels.put(writer, newLevel);
177         Configurator.currentConfig().addWriter(writer, newLevel, writerFormats.get(writer)).activate();
178     }
179 
180     /**
181      * Add a category to be logged to the Writers.
182      * @param logCategory LogCategory; the LogCategory to add
183      */
184     public static void addLogCategory(final LogCategory logCategory)
185     {
186         categories.add(logCategory);
187     }
188 
189     /**
190      * Remove a category to be logged to the Writers.
191      * @param logCategory LogCategory; the LogCategory to remove
192      */
193     public static void removeLogCategory(final LogCategory logCategory)
194     {
195         categories.remove(logCategory);
196     }
197 
198     /**
199      * Set the categories to be logged to the Writers.
200      * @param newLogCategories LogCategory...; the LogCategories to set, replacing the previous ones
201      */
202     public static void setLogCategories(final LogCategory... newLogCategories)
203     {
204         categories.clear();
205         categories.addAll(Arrays.asList(newLogCategories));
206     }
207 
208     /* ****************************************** FILTER ******************************************/
209 
210     /**
211      * The "pass" filter that will result in always trying to log.
212      * @return the logger that tries to execute logging (delegateLogger)
213      */
214     public static DelegateLogger always()
215     {
216         return delegateLogger;
217     }
218 
219     /**
220      * Check whether the provided category needs to be logged. Note that when LogCategory.ALL is contained in the categories,
221      * filter will return true.
222      * @param logCategory LogCategory; the category to check for.
223      * @return the logger that either tries to log (delegateLogger), or returns without logging (noLogger)
224      */
225     public static DelegateLogger filter(final LogCategory logCategory)
226     {
227         if (categories.contains(LogCategory.ALL))
228             return delegateLogger;
229         if (categories.contains(logCategory))
230             return delegateLogger;
231         return noLogger;
232     }
233 
234     /**
235      * Check whether the provided categories contain one or more categories that need to be logged. Note that when
236      * LogCategory.ALL is contained in the categories, filter will return true.
237      * @param logCategories LogCategory...; elements or array with the categories to check for
238      * @return the logger that either tries to log (delegateLogger), or returns without logging (noLogger)
239      */
240     public static DelegateLogger filter(final LogCategory... logCategories)
241     {
242         if (categories.contains(LogCategory.ALL))
243             return delegateLogger;
244         for (LogCategory logCategory : logCategories)
245         {
246             if (categories.contains(logCategory))
247                 return delegateLogger;
248         }
249         return noLogger;
250     }
251 
252     /**
253      * Check whether the provided categories contain one or more categories that need to be logged. Note that when
254      * LogCategory.ALL is contained in the categories, filter will return true.
255      * @param logCategories Set&lt;LogCategory&gt;; the categories to check for
256      * @return the logger that either tries to log (delegateLogger), or returns without logging (noLogger)
257      */
258     public static DelegateLogger filter(final Set<LogCategory> logCategories)
259     {
260         if (categories.contains(LogCategory.ALL))
261             return delegateLogger;
262         for (LogCategory logCategory : logCategories)
263         {
264             if (categories.contains(logCategory))
265                 return delegateLogger;
266         }
267         return noLogger;
268     }
269 
270     /**
271      * The conditional filter that will result in the usage of a ConditionalLogger.
272      * @param condition boolean; the condition that should be evaluated
273      * @return the logger that further processes logging (ConditionalLogger)
274      */
275     public static ConditionalLogger when(final boolean condition)
276     {
277         if (condition)
278             return conditionalLogger;
279         return conditionalNoLogger;
280     }
281 
282     /**
283      * The conditional filter that will result in the usage of a ConditionalLogger.
284      * @param supplier BooleanSupplier; the function evaluating the condition
285      * @return the logger that further processes logging (ConditionalLogger)
286      */
287     public static ConditionalLogger when(final BooleanSupplier supplier)
288     {
289         if (supplier.getAsBoolean())
290             return conditionalLogger;
291         return conditionalNoLogger;
292     }
293 
294     /* *********************************** CONDITIONAL LOGGER *************************************/
295 
296     /**
297      * ConditionalLogger class that takes care of filtering based on a condition. <br>
298      * <br>
299      * Copyright (c) 2003-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
300      * See for project information <a href="https://www.simulation.tudelft.nl/" target="_blank"> www.simulation.tudelft.nl</a>.
301      * The source code and binary code of this software is proprietary information of Delft University of Technology.
302      * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
303      */
304     public static class ConditionalLogger
305     {
306         /** Should we try to log or not? */
307         private final boolean log;
308 
309         /**
310          * @param log boolean; indicate whether we should log or not.
311          */
312         public ConditionalLogger(final boolean log)
313         {
314             super();
315             this.log = log;
316         }
317 
318         /**
319          * The "pass" filter that will result in always trying to log.
320          * @return the logger that tries to execute logging (delegateLogger)
321          */
322         public DelegateLogger always()
323         {
324             if (this.log)
325                 return delegateLogger;
326             return noLogger;
327         }
328 
329         /**
330          * Check whether the provided category needs to be logged. Note that when LogCategory.ALL is contained in the
331          * categories, filter will return true.
332          * @param logCategory LogCategory; the category to check for.
333          * @return the logger that either tries to log (delegateLogger), or returns without logging (noLogger)
334          */
335         public DelegateLogger filter(final LogCategory logCategory)
336         {
337             if (this.log)
338             {
339                 if (categories.contains(LogCategory.ALL))
340                     return delegateLogger;
341                 if (categories.contains(logCategory))
342                     return delegateLogger;
343             }
344             return noLogger;
345         }
346 
347         /**
348          * Check whether the provided categories contain one or more categories that need to be logged. Note that when
349          * LogCategory.ALL is contained in the categories, filter will return true.
350          * @param logCategories LogCategory...; elements or array with the categories to check for
351          * @return the logger that either tries to log (delegateLogger), or returns without logging (noLogger)
352          */
353         public DelegateLogger filter(final LogCategory... logCategories)
354         {
355             if (this.log)
356             {
357                 if (categories.contains(LogCategory.ALL))
358                     return delegateLogger;
359                 for (LogCategory logCategory : logCategories)
360                 {
361                     if (categories.contains(logCategory))
362                         return delegateLogger;
363                 }
364             }
365             return noLogger;
366         }
367     }
368 
369     /* ************************************ DELEGATE LOGGER ***************************************/
370 
371     /**
372      * DelegateLogger class that takes care of actually logging the message and/or exception. <br>
373      * <br>
374      * Copyright (c) 2003-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
375      * See for project information <a href="https://www.simulation.tudelft.nl/" target="_blank"> www.simulation.tudelft.nl</a>.
376      * The source code and binary code of this software is proprietary information of Delft University of Technology.
377      * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
378      */
379     public static class DelegateLogger
380     {
381         /** Should we try to log or not? */
382         private final boolean log;
383 
384         /**
385          * @param log boolean; indicate whether we should log or not.
386          */
387         public DelegateLogger(final boolean log)
388         {
389             super();
390             this.log = log;
391         }
392 
393         /* ****************************************** TRACE ******************************************/
394 
395         /**
396          * Create a trace log entry that will always be output, independent of LogCategory settings.
397          * @param object Object; the result of the <code>toString()</code> method of <code>object</code> will be logged
398          */
399         public void trace(final Object object)
400         {
401             if (this.log)
402                 LogEntryForwarder.forward(1, Level.TRACE, object);
403         }
404 
405         /**
406          * Create a trace log entry that will always be output, independent of LogCategory settings.
407          * @param message String; the message to log
408          */
409         public void trace(final String message)
410         {
411             if (this.log)
412                 LogEntryForwarder.forward(1, Level.TRACE, message);
413         }
414 
415         /**
416          * Create a trace log entry that will always be output, independent of LogCategory settings.
417          * @param message String; the message to be logged, where {} entries will be replaced by arguments
418          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
419          */
420         public void trace(final String message, final Object... arguments)
421         {
422             if (this.log)
423                 LogEntryForwarder.forward(1, Level.TRACE, message, arguments);
424         }
425 
426         /**
427          * Create a trace log entry that will always be output, independent of LogCategory settings.
428          * @param exception Throwable; the exception to log
429          */
430         public void trace(final Throwable exception)
431         {
432             if (this.log)
433                 LogEntryForwarder.forward(1, Level.TRACE, exception);
434         }
435 
436         /**
437          * Create a trace log entry that will always be output, independent of LogCategory settings.
438          * @param exception Throwable; the exception to log
439          * @param message String; the message to log
440          */
441         public void trace(final Throwable exception, final String message)
442         {
443             if (this.log)
444                 LogEntryForwarder.forward(1, Level.TRACE, exception, message);
445         }
446 
447         /**
448          * Create a trace log entry that will always be output, independent of LogCategory settings.
449          * @param exception Throwable; the exception to log
450          * @param message String; the message to log, where {} entries will be replaced by arguments
451          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
452          */
453         public void trace(final Throwable exception, final String message, final Object... arguments)
454         {
455             if (this.log)
456                 LogEntryForwarder.forward(1, Level.TRACE, exception, message, arguments);
457         }
458 
459         /* ****************************************** DEBUG ******************************************/
460 
461         /**
462          * Create a debug log entry that will always be output, independent of LogCategory settings.
463          * @param object Object; the result of the <code>toString()</code> method of <code>object</code> will be logged
464          */
465         public void debug(final Object object)
466         {
467             if (this.log)
468                 LogEntryForwarder.forward(1, Level.DEBUG, object);
469         }
470 
471         /**
472          * Create a debug log entry that will always be output, independent of LogCategory settings.
473          * @param message String; the message to log
474          */
475         public void debug(final String message)
476         {
477             if (this.log)
478                 LogEntryForwarder.forward(1, Level.DEBUG, message);
479         }
480 
481         /**
482          * Create a debug log entry that will always be output, independent of LogCategory settings.
483          * @param message String; the message to be logged, where {} entries will be replaced by arguments
484          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
485          */
486         public void debug(final String message, final Object... arguments)
487         {
488             if (this.log)
489                 LogEntryForwarder.forward(1, Level.DEBUG, message, arguments);
490         }
491 
492         /**
493          * Create a debug log entry that will always be output, independent of LogCategory settings.
494          * @param exception Throwable; the exception to log
495          */
496         public void debug(final Throwable exception)
497         {
498             if (this.log)
499                 LogEntryForwarder.forward(1, Level.DEBUG, exception);
500         }
501 
502         /**
503          * Create a debug log entry that will always be output, independent of LogCategory settings.
504          * @param exception Throwable; the exception to log
505          * @param message String; the message to log
506          */
507         public void debug(final Throwable exception, final String message)
508         {
509             if (this.log)
510                 LogEntryForwarder.forward(1, Level.DEBUG, exception, message);
511         }
512 
513         /**
514          * Create a debug log entry that will always be output, independent of LogCategory settings.
515          * @param exception Throwable; the exception to log
516          * @param message String; the message to log, where {} entries will be replaced by arguments
517          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
518          */
519         public void debug(final Throwable exception, final String message, final Object... arguments)
520         {
521             if (this.log)
522                 LogEntryForwarder.forward(1, Level.DEBUG, exception, message, arguments);
523         }
524 
525         /* ****************************************** INFO ******************************************/
526 
527         /**
528          * Create a info log entry that will always be output, independent of LogCategory settings.
529          * @param object Object; the result of the <code>toString()</code> method of <code>object</code> will be logged
530          */
531         public void info(final Object object)
532         {
533             if (this.log)
534                 LogEntryForwarder.forward(1, Level.INFO, object);
535         }
536 
537         /**
538          * Create a info log entry that will always be output, independent of LogCategory settings.
539          * @param message String; the message to log
540          */
541         public void info(final String message)
542         {
543             if (this.log)
544                 LogEntryForwarder.forward(1, Level.INFO, message);
545         }
546 
547         /**
548          * Create a info log entry that will always be output, independent of LogCategory settings.
549          * @param message String; the message to be logged, where {} entries will be replaced by arguments
550          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
551          */
552         public void info(final String message, final Object... arguments)
553         {
554             if (this.log)
555                 LogEntryForwarder.forward(1, Level.INFO, message, arguments);
556         }
557 
558         /**
559          * Create a info log entry that will always be output, independent of LogCategory settings.
560          * @param exception Throwable; the exception to log
561          */
562         public void info(final Throwable exception)
563         {
564             if (this.log)
565                 LogEntryForwarder.forward(1, Level.INFO, exception);
566         }
567 
568         /**
569          * Create a info log entry that will always be output, independent of LogCategory settings.
570          * @param exception Throwable; the exception to log
571          * @param message String; the message to log
572          */
573         public void info(final Throwable exception, final String message)
574         {
575             if (this.log)
576                 LogEntryForwarder.forward(1, Level.INFO, exception, message);
577         }
578 
579         /**
580          * Create a info log entry that will always be output, independent of LogCategory settings.
581          * @param exception Throwable; the exception to log
582          * @param message String; the message to log, where {} entries will be replaced by arguments
583          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
584          */
585         public void info(final Throwable exception, final String message, final Object... arguments)
586         {
587             if (this.log)
588                 LogEntryForwarder.forward(1, Level.INFO, exception, message, arguments);
589         }
590 
591         /* ****************************************** WARN ******************************************/
592 
593         /**
594          * Create a warn log entry that will always be output, independent of LogCategory settings.
595          * @param object Object; the result of the <code>toString()</code> method of <code>object</code> will be logged
596          */
597         public void warn(final Object object)
598         {
599             if (this.log)
600                 LogEntryForwarder.forward(1, Level.WARNING, object);
601         }
602 
603         /**
604          * Create a warn log entry that will always be output, independent of LogCategory settings.
605          * @param message String; the message to log
606          */
607         public void warn(final String message)
608         {
609             if (this.log)
610                 LogEntryForwarder.forward(1, Level.WARNING, message);
611         }
612 
613         /**
614          * Create a warn log entry that will always be output, independent of LogCategory settings.
615          * @param message String; the message to be logged, where {} entries will be replaced by arguments
616          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
617          */
618         public void warn(final String message, final Object... arguments)
619         {
620             if (this.log)
621                 LogEntryForwarder.forward(1, Level.WARNING, message, arguments);
622         }
623 
624         /**
625          * Create a warn log entry that will always be output, independent of LogCategory settings.
626          * @param exception Throwable; the exception to log
627          */
628         public void warn(final Throwable exception)
629         {
630             if (this.log)
631                 LogEntryForwarder.forward(1, Level.WARNING, exception);
632         }
633 
634         /**
635          * Create a warn log entry that will always be output, independent of LogCategory settings.
636          * @param exception Throwable; the exception to log
637          * @param message String; the message to log
638          */
639         public void warn(final Throwable exception, final String message)
640         {
641             if (this.log)
642                 LogEntryForwarder.forward(1, Level.WARNING, exception, message);
643         }
644 
645         /**
646          * Create a warn log entry that will always be output, independent of LogCategory settings.
647          * @param exception Throwable; the exception to log
648          * @param message String; the message to log, where {} entries will be replaced by arguments
649          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
650          */
651         public void warn(final Throwable exception, final String message, final Object... arguments)
652         {
653             if (this.log)
654                 LogEntryForwarder.forward(1, Level.WARNING, exception, message, arguments);
655         }
656 
657         /* ****************************************** ERROR ******************************************/
658 
659         /**
660          * Create a error log entry that will always be output, independent of LogCategory settings.
661          * @param object Object; the result of the <code>toString()</code> method of <code>object</code> will be logged
662          */
663         public void error(final Object object)
664         {
665             if (this.log)
666                 LogEntryForwarder.forward(1, Level.ERROR, object);
667         }
668 
669         /**
670          * Create a error log entry that will always be output, independent of LogCategory settings.
671          * @param message String; the message to log
672          */
673         public void error(final String message)
674         {
675             if (this.log)
676                 LogEntryForwarder.forward(1, Level.ERROR, message);
677         }
678 
679         /**
680          * Create a error log entry that will always be output, independent of LogCategory settings.
681          * @param message String; the message to be logged, where {} entries will be replaced by arguments
682          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
683          */
684         public void error(final String message, final Object... arguments)
685         {
686             if (this.log)
687                 LogEntryForwarder.forward(1, Level.ERROR, message, arguments);
688         }
689 
690         /**
691          * Create a error log entry that will always be output, independent of LogCategory settings.
692          * @param exception Throwable; the exception to log
693          */
694         public void error(final Throwable exception)
695         {
696             if (this.log)
697                 LogEntryForwarder.forward(1, Level.ERROR, exception);
698         }
699 
700         /**
701          * Create a error log entry that will always be output, independent of LogCategory settings.
702          * @param exception Throwable; the exception to log
703          * @param message String; the message to log
704          */
705         public void error(final Throwable exception, final String message)
706         {
707             if (this.log)
708                 LogEntryForwarder.forward(1, Level.ERROR, exception, message);
709         }
710 
711         /**
712          * Create a error log entry that will always be output, independent of LogCategory settings.
713          * @param exception Throwable; the exception to log
714          * @param message String; the message to log, where {} entries will be replaced by arguments
715          * @param arguments Object...; the arguments to substitute for the {} entries in the message string
716          */
717         public void error(final Throwable exception, final String message, final Object... arguments)
718         {
719             if (this.log)
720                 LogEntryForwarder.forward(1, Level.ERROR, exception, message, arguments);
721         }
722     }
723 }