Skip to content
Permalink
Browse files

add warnOnce

  • Loading branch information...
bobjacobsen committed Mar 19, 2018
1 parent 299df88 commit e98afe327625fa259b27d7bffd4ae550f08f5a76
Showing with 70 additions and 36 deletions.
  1. +26 −34 help/en/html/doc/Technical/Logging.shtml
  2. +22 −2 java/src/jmri/util/Log4JUtil.java
  3. +22 −0 java/test/jmri/util/Log4JUtilTest.java
@@ -133,10 +133,11 @@
As

<p>The line:</p>
<pre>
<pre style="font-family: monospace;">
log4j.rootCategory= INFO, A1, T, R
</pre>controls where logging output goes. Later in the file, there
are "appenders" A1, T and R defined that write log messages to:
</pre>
controls where logging output goes. Later in the file, there
are "appenders" A1, T and R defined that write log messages to:

<ul>
<li>A1 - the console on the local computer, really
@@ -158,18 +159,15 @@ are "appenders" A1, T and R defined that write log messages to:

<p>The "default.lcf" file also determines the format of the
output by setting the "layout" parameter(s).</p>
<pre>
<code>
<pre style="font-family: monospace;">
log4j.appender.R.layout=org.apache.log4j.PatternLayout<br>log4j.appender.R.layout.ConversionPattern=%d{ISO8601} %-37.37c{2} %-5p - %m [%t]%n
</code>
</pre>

<p>An example of the default format:</p>
<pre>
<code>
<pre style="font-family: monospace;">
2015-10-28 20:31:52,307 jmri.jmrit.powerpanel.PowerPane WARN - No power manager instance found, panel not active [AWT-EventQueue-0]
</code>
</pre>The columns are:
</pre>
The columns are:

<ul>
<li><code>2015-10-28 20:31:52,307</code> - local time the
@@ -190,43 +188,33 @@ log4j.appender.R.layout=org.apache.log4j.PatternLayout<br>log4j.appender.R.layou

<h3>Coding</h3>To log messages from a class named MyClass,
add this to the bottom of the class's .java file:
<pre>
<code>
<pre style="font-family: monospace;">
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
</code>
</pre>

<p>and add imports for org.slf4j.Logger and
org.slf4j.LoggerFactory in your imports section:</p>
<pre>
<code>
<pre style="font-family: monospace;">
import org.slf4j.Logger;<br>
import org.slf4j.LoggerFactory;
</code>
</pre>It's also OK to combine those into the fully-qualified form:
<pre>
<code>
<pre style="font-family: monospace;">
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MyClass.class);
</code>
</pre>

<p>(If logging is removed (e.g. commented out), it's OK to
comment out the log definition so that it can be easily added
back later on.)</p>

<p>Then for each message to log insert a line like:</p>
<pre>
<code>
<pre style="font-family: monospace;">
log.debug("message");
</code>
</pre>

<p>Messages that are not just an explicit string should use
this form instead for variables:</p>
<pre>
<code>
<pre style="font-family: monospace;">
log.debug("Found {}", numberEntries);
</code>
</pre>
<p>The string operations to build the actual error message (in
this case, combining "Found" and the numberEntries argument) are
@@ -242,21 +230,25 @@ log4j.appender.R.layout=org.apache.log4j.PatternLayout<br>log4j.appender.R.layou
does not waste time calculating parameters (in this case,
calling numberEntries() to get a value to pass to the
function call):</p>
<pre>
<code>
<pre style="font-family: monospace;">
if (log.isDebugEnabled()) {
log.debug("Found {}", numberEntries());
}
</code>
</pre>

<p>If you want to emit a warning only the first time
it happens, there's a service method that will
handle that for you:
<pre style="font-family: monospace;">
Log4JUtil.warnOnce(log, "The warning with arguments {} {}", "A", "B");
</pre>

<p>Exceptions should be logged as:</p>
<pre>
<code>
<pre style="font-family: monospace;">
log.error("my local text"+exception.getLocalizedMessage(), exception);
</code>
</pre>to include the user readable description from the exception
itself, plus all its traceback information.

</pre>
to include the user readable description from the exception
itself, plus all its traceback information.
<!--#include virtual="/Footer" -->
</div><!-- closes #mainContent-->
</div><!-- closes #mBody-->
@@ -5,8 +5,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Enumeration;
import java.util.Properties;
import java.util.*;
import jmri.util.exceptionhandler.UncaughtExceptionHandler;
import org.apache.log4j.Appender;
import org.apache.log4j.BasicConfigurator;
@@ -43,6 +42,27 @@
private static final String jmriLog = "****** JMRI log *******";
private static final Logger log = LoggerFactory.getLogger(Log4JUtil.class);

/**
* Emit a particular WARNING-level message just once.
* @return true if the log was emitted this time
*/
// Goal is to be lightweight and fast; this will only be used in a few places,
// and only those should appear in data structure.
static public boolean warnOnce(Logger log, String msg, Object... args) {
// the Map<String, Boolean> is just being checked for existence; it's never False
Map<String, Boolean> loggerMap = warnedOnce.get(log);
if (loggerMap == null) {
loggerMap = new HashMap<>();
warnedOnce.put(log, loggerMap);
} else {
if (loggerMap.get(msg) == Boolean.TRUE) return false;
}
loggerMap.put(msg, Boolean.TRUE);
log.warn(msg, args);
return true;
}
static private Map<Logger, Map<String, Boolean>> warnedOnce = new HashMap<>();

/**
* Initialize logging from a default control file.
* <p>
@@ -20,9 +20,31 @@ public void testLog4JWarnMessage() {

log.debug("DEBUG message"); // should be suppressed see tests.lcf

Assert.assertTrue(jmri.util.JUnitAppender.verifyNoBacklog());
}

public void testWarnOnceCounts() {
Assert.assertTrue(Log4JUtil.warnOnce(log, "WARN message")); // string has to be same until further notice
Assert.assertFalse(Log4JUtil.warnOnce(log, "WARN message"));
jmri.util.JUnitAppender.assertWarnMessage("WARN message");
Assert.assertTrue(jmri.util.JUnitAppender.verifyNoBacklog());

Logger log2 = LoggerFactory.getLogger("Log4JUtilTest-extra-logger");
Assert.assertTrue(Log4JUtil.warnOnce(log2, "WARN message"));
Assert.assertFalse(Log4JUtil.warnOnce(log2, "WARN message"));
jmri.util.JUnitAppender.assertWarnMessage("WARN message");
Assert.assertTrue(jmri.util.JUnitAppender.verifyNoBacklog());

Assert.assertTrue(Log4JUtil.warnOnce(log, "WARN message 2")); // different string
jmri.util.JUnitAppender.assertWarnMessage("WARN message 2");
}

public void testWarnOnceArguments() {
Assert.assertTrue(Log4JUtil.warnOnce(log, "Test {} {}", "A", "B"));
jmri.util.JUnitAppender.assertWarnMessage("Test A B");
Assert.assertTrue(jmri.util.JUnitAppender.verifyNoBacklog());
}

public void testSendJavaUtilLogInfoMessage() {
// test that java.util.logging is getting to Log4J
java.util.logging.Logger logger =

0 comments on commit e98afe3

Please sign in to comment.
You can’t perform that action at this time.