Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #1090 from daniel-beck/JENKINS-21386
[FIXED JENKINS-21386] Allow more specific loggers to reduce log level
  • Loading branch information
kohsuke committed Jan 19, 2014
2 parents f082765 + b9426e4 commit 0fd39c4
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 9 deletions.
52 changes: 48 additions & 4 deletions core/src/main/java/hudson/logging/LogRecorder.java
Expand Up @@ -84,14 +84,38 @@ public class LogRecorder extends AbstractModelObject implements Saveable {

public final CopyOnWriteList<Target> targets = new CopyOnWriteList<Target>();

private transient /*almost final*/ RingBufferLogHandler handler = new RingBufferLogHandler() {
@Restricted(NoExternalUse.class)
Target[] orderedTargets() {
// will contain targets ordered by reverse name length (place specific targets at the beginning)
Target[] ts = targets.toArray(new Target[]{});

Arrays.sort(ts, new Comparator<Target>() {
public int compare(Target left, Target right) {
return right.getName().length() - left.getName().length();
}
});

return ts;
}

@Restricted(NoExternalUse.class)
transient /*almost final*/ RingBufferLogHandler handler = new RingBufferLogHandler() {
@Override
public void publish(LogRecord record) {
for (Target t : targets) {
if(t.includes(record)) {
for (Target t : orderedTargets()) {
Boolean match = t.matches(record);
if (match == null) {
// domain does not match, so continue looking
continue;
}

if (match.booleanValue()) {
// most specific logger matches, so publish
super.publish(record);
return;
}
// most specific logger does not match, so don't publish
// allows reducing log level for more specific loggers
return;
}
}
};
Expand Down Expand Up @@ -123,6 +147,11 @@ public Level getLevel() {
return Level.parse(String.valueOf(level));
}

public String getName() {
return name;
}

@Deprecated
public boolean includes(LogRecord r) {
if(r.getLevel().intValue() < level)
return false; // below the threshold
Expand All @@ -136,6 +165,21 @@ public boolean includes(LogRecord r) {
return rest.startsWith(".") || rest.length()==0;
}

public Boolean matches(LogRecord r) {
boolean levelSufficient = r.getLevel().intValue() >= level;
if (name.length() == 0) {
return Boolean.valueOf(levelSufficient); // include if level matches
}
String logName = r.getLoggerName();
if(logName==null || !logName.startsWith(name))
return null; // not in the domain of this logger
String rest = logName.substring(name.length());
if (rest.startsWith(".") || rest.length()==0) {
return Boolean.valueOf(levelSufficient); // include if level matches
}
return null;
}

public Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger(name);
Expand Down
66 changes: 64 additions & 2 deletions core/src/test/java/hudson/logging/LogRecorderTest.java
Expand Up @@ -42,10 +42,72 @@ public class LogRecorderTest {
assertTrue(includes("", "hudson.model.Hudson"));
}

private static boolean includes(String target, String logger) {
LogRecord r = new LogRecord(Level.INFO, "whatever");
@Test public void targetMatches() {
assertTrue(matches("hudson", "hudson"));
assertFalse(matches("hudson", "hudson", Level.FINE));
assertNull(matches("hudson", "hudsone"));
assertNull(matches("hudson", "hudso"));
assertTrue(matches("hudson", "hudson.model.Hudson"));
assertFalse(matches("hudson", "hudson.model.Hudson", Level.FINE));
assertNull(matches("hudson", "jenkins.model.Jenkins"));
assertTrue(matches("", "hudson.model.Hudson"));
assertFalse(matches("", "hudson.model.Hudson", Level.FINE));
}

@Test public void testSpecificExclusion() {
LogRecorder lr = new LogRecorder("foo");

LogRecorder.Target targetLevel0 = new LogRecorder.Target("", Level.FINE);
LogRecorder.Target targetLevel1 = new LogRecorder.Target("foo", Level.INFO);
LogRecorder.Target targetLevel2 = new LogRecorder.Target("foo.bar", Level.SEVERE);

lr.targets.add(targetLevel1);
lr.targets.add(targetLevel2);
lr.targets.add(targetLevel0);

assertEquals(lr.orderedTargets()[0], targetLevel2);
assertEquals(lr.orderedTargets()[1], targetLevel1);
assertEquals(lr.orderedTargets()[2], targetLevel0);

LogRecord r1 = createLogRecord("baz", Level.INFO, "visible");
LogRecord r2 = createLogRecord("foo", Level.FINE, "hidden");
LogRecord r3 = createLogRecord("foo.bar", Level.INFO, "hidden");
LogRecord r4 = createLogRecord("foo.bar.baz", Level.INFO, "hidden");
LogRecord r5 = createLogRecord("foo.bar.baz", Level.SEVERE, "visible");
LogRecord r6 = createLogRecord("foo", Level.INFO, "visible");
lr.handler.publish(r1);
lr.handler.publish(r2);
lr.handler.publish(r3);
lr.handler.publish(r4);
lr.handler.publish(r5);
lr.handler.publish(r6);

assertTrue(lr.handler.getView().contains(r1));
assertFalse(lr.handler.getView().contains(r2));
assertFalse(lr.handler.getView().contains(r3));
assertFalse(lr.handler.getView().contains(r4));
assertTrue(lr.handler.getView().contains(r5));
assertTrue(lr.handler.getView().contains(r6));
}

private static LogRecord createLogRecord(String logger, Level level, String message) {
LogRecord r = new LogRecord(level, message);
r.setLoggerName(logger);
return r;
}

private static boolean includes(String target, String logger) {
LogRecord r = createLogRecord(logger, Level.INFO, "whatever");
return new LogRecorder.Target(target, Level.INFO).includes(r);
}

private static Boolean matches(String target, String logger) {
return matches(target, logger, Level.INFO);
}

private static Boolean matches(String target, String logger, Level loggerLevel) {
LogRecord r = createLogRecord(logger, loggerLevel, "whatever");
return new LogRecorder.Target(target, Level.INFO).matches(r);
}

}
6 changes: 3 additions & 3 deletions war/src/main/webapp/help/LogRecorder/logger.html
Expand Up @@ -3,11 +3,11 @@
this log recorder will collect. For example, if you specify
<tt>hudson.model.Hudson</tt> and <tt>info</tt>, any logs recorded
for <tt>hudson.model.Hudson</tt> or its descendants with the log level
info or above will show up in this log recorder.
info or above will show up in this log recorder, unless a more specific
logger with higher log level is also specific.

<p>
If you have multiple entries, a log entry that matches any of them
will be collected.
If you have multiple entries, a log entry will the most specific one will be collected.

<p>
Also see <a href="http://wiki.jenkins-ci.org/display/JENKINS/Logger+Configuration">
Expand Down

0 comments on commit 0fd39c4

Please sign in to comment.