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<LogCategory>; 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 }