Browse Source

Allow DeferredLogger to replay and switch loggers

Add additional `switchTo` methods to allow a `DeferredLogger` to behave
like a regular logger once it has been replayed.

This commit also improves thread thread safety within the implementation.

Closes gh-14452
pull/14471/head
Phillip Webb 7 years ago
parent
commit
bc92becfd8
  1. 2
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java
  2. 144
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/DeferredLog.java
  3. 11
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/DeferredLogTests.java

2
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java

@ -189,7 +189,7 @@ public class ConfigFileApplicationListener @@ -189,7 +189,7 @@ public class ConfigFileApplicationListener
}
private void onApplicationPreparedEvent(ApplicationEvent event) {
this.logger.replayTo(ConfigFileApplicationListener.class);
this.logger.switchTo(ConfigFileApplicationListener.class);
addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
}

144
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/DeferredLog.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,36 +31,50 @@ import org.apache.commons.logging.LogFactory; @@ -31,36 +31,50 @@ import org.apache.commons.logging.LogFactory;
*/
public class DeferredLog implements Log {
private List<Line> lines = new ArrayList<>();
private volatile Log destination;
private final List<Line> lines = new ArrayList<>();
@Override
public boolean isTraceEnabled() {
return true;
synchronized (this.lines) {
return (this.destination != null) ? this.destination.isTraceEnabled() : true;
}
}
@Override
public boolean isDebugEnabled() {
return true;
synchronized (this.lines) {
return (this.destination != null) ? this.destination.isDebugEnabled() : true;
}
}
@Override
public boolean isInfoEnabled() {
return true;
synchronized (this.lines) {
return (this.destination != null) ? this.destination.isInfoEnabled() : true;
}
}
@Override
public boolean isWarnEnabled() {
return true;
synchronized (this.lines) {
return (this.destination != null) ? this.destination.isWarnEnabled() : true;
}
}
@Override
public boolean isErrorEnabled() {
return true;
synchronized (this.lines) {
return (this.destination != null) ? this.destination.isErrorEnabled() : true;
}
}
@Override
public boolean isFatalEnabled() {
return true;
synchronized (this.lines) {
return (this.destination != null) ? this.destination.isFatalEnabled() : true;
}
}
@Override
@ -124,24 +138,75 @@ public class DeferredLog implements Log { @@ -124,24 +138,75 @@ public class DeferredLog implements Log {
}
private void log(LogLevel level, Object message, Throwable t) {
this.lines.add(new Line(level, message, t));
synchronized (this.lines) {
if (this.destination != null) {
logTo(this.destination, level, message, t);
}
this.lines.add(new Line(level, message, t));
}
}
/**
* Switch from deferred logging to immediate logging to the specified destination.
* @param destination the new log destination
*/
public void switchTo(Class<?> destination) {
switchTo(LogFactory.getLog(destination));
}
/**
* Switch from deferred logging to immediate logging to the specified destination.
* @param destination the new log destination
*/
public void switchTo(Log destination) {
synchronized (this.lines) {
replayTo(destination);
this.destination = destination;
}
}
/**
* Replay deferred logging to the specified destination.
* @param destination the destination for the deferred log messages
*/
public void replayTo(Class<?> destination) {
replayTo(LogFactory.getLog(destination));
}
/**
* Replay deferred logging to the specified destination.
* @param destination the destination for the deferred log messages
*/
public void replayTo(Log destination) {
for (Line line : this.lines) {
line.replayTo(destination);
synchronized (this.lines) {
for (Line line : this.lines) {
logTo(destination, line.getLevel(), line.getMessage(),
line.getThrowable());
}
this.lines.clear();
}
this.lines.clear();
}
/**
* Replay from a source log to a destination log when the source is deferred.
* @param source the source logger
* @param destination the destination logger class
* @return the destination
* @deprecated since 2.1.0 in favor of {@link #switchTo(Class)}
*/
@Deprecated
public static Log replay(Log source, Class<?> destination) {
return replay(source, LogFactory.getLog(destination));
}
/**
* Replay from a source log to a destination log when the source is deferred.
* @param source the source logger
* @param destination the destination logger
* @return the destination
* @deprecated since 2.1.0 in favor of {@link #switchTo(Log)}
*/
@Deprecated
public static Log replay(Log source, Log destination) {
if (source instanceof DeferredLog) {
((DeferredLog) source).replayTo(destination);
@ -149,6 +214,30 @@ public class DeferredLog implements Log { @@ -149,6 +214,30 @@ public class DeferredLog implements Log {
return destination;
}
private static void logTo(Log log, LogLevel level, Object message,
Throwable throwable) {
switch (level) {
case TRACE:
log.trace(message, throwable);
return;
case DEBUG:
log.debug(message, throwable);
return;
case INFO:
log.info(message, throwable);
return;
case WARN:
log.warn(message, throwable);
return;
case ERROR:
log.error(message, throwable);
return;
case FATAL:
log.fatal(message, throwable);
return;
}
}
private static class Line {
private final LogLevel level;
@ -163,27 +252,16 @@ public class DeferredLog implements Log { @@ -163,27 +252,16 @@ public class DeferredLog implements Log {
this.throwable = throwable;
}
public void replayTo(Log log) {
switch (this.level) {
case TRACE:
log.trace(this.message, this.throwable);
return;
case DEBUG:
log.debug(this.message, this.throwable);
return;
case INFO:
log.info(this.message, this.throwable);
return;
case WARN:
log.warn(this.message, this.throwable);
return;
case ERROR:
log.error(this.message, this.throwable);
return;
case FATAL:
log.fatal(this.message, this.throwable);
return;
}
public LogLevel getLevel() {
return this.level;
}
public Object getMessage() {
return this.message;
}
public Throwable getThrowable() {
return this.throwable;
}
}

11
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/DeferredLogTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -167,4 +167,13 @@ public class DeferredLogTests { @@ -167,4 +167,13 @@ public class DeferredLogTests {
verifyZeroInteractions(log2);
}
@Test
public void switchTo() {
this.deferredLog.error(this.message, this.throwable);
this.deferredLog.switchTo(this.log);
this.deferredLog.info("Message2");
verify(this.log).error(this.message, this.throwable);
verify(this.log).info("Message2", null);
}
}

Loading…
Cancel
Save