Commit bc92becf authored by Phillip Webb's avatar Phillip Webb

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
parent b32887b8
...@@ -189,7 +189,7 @@ public class ConfigFileApplicationListener ...@@ -189,7 +189,7 @@ public class ConfigFileApplicationListener
} }
private void onApplicationPreparedEvent(ApplicationEvent event) { private void onApplicationPreparedEvent(ApplicationEvent event) {
this.logger.replayTo(ConfigFileApplicationListener.class); this.logger.switchTo(ConfigFileApplicationListener.class);
addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext()); addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
} }
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { public class DeferredLog implements Log {
private List<Line> lines = new ArrayList<>(); private volatile Log destination;
private final List<Line> lines = new ArrayList<>();
@Override @Override
public boolean isTraceEnabled() { public boolean isTraceEnabled() {
return true; synchronized (this.lines) {
return (this.destination != null) ? this.destination.isTraceEnabled() : true;
}
} }
@Override @Override
public boolean isDebugEnabled() { public boolean isDebugEnabled() {
return true; synchronized (this.lines) {
return (this.destination != null) ? this.destination.isDebugEnabled() : true;
}
} }
@Override @Override
public boolean isInfoEnabled() { public boolean isInfoEnabled() {
return true; synchronized (this.lines) {
return (this.destination != null) ? this.destination.isInfoEnabled() : true;
}
} }
@Override @Override
public boolean isWarnEnabled() { public boolean isWarnEnabled() {
return true; synchronized (this.lines) {
return (this.destination != null) ? this.destination.isWarnEnabled() : true;
}
} }
@Override @Override
public boolean isErrorEnabled() { public boolean isErrorEnabled() {
return true; synchronized (this.lines) {
return (this.destination != null) ? this.destination.isErrorEnabled() : true;
}
} }
@Override @Override
public boolean isFatalEnabled() { public boolean isFatalEnabled() {
return true; synchronized (this.lines) {
return (this.destination != null) ? this.destination.isFatalEnabled() : true;
}
} }
@Override @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) { 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) { public void replayTo(Class<?> destination) {
replayTo(LogFactory.getLog(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) { public void replayTo(Log destination) {
for (Line line : this.lines) { synchronized (this.lines) {
line.replayTo(destination); 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) { public static Log replay(Log source, Class<?> destination) {
return replay(source, LogFactory.getLog(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) { public static Log replay(Log source, Log destination) {
if (source instanceof DeferredLog) { if (source instanceof DeferredLog) {
((DeferredLog) source).replayTo(destination); ((DeferredLog) source).replayTo(destination);
...@@ -149,6 +214,30 @@ public class DeferredLog implements Log { ...@@ -149,6 +214,30 @@ public class DeferredLog implements Log {
return destination; 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 static class Line {
private final LogLevel level; 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; this.throwable = throwable;
} }
public void replayTo(Log log) { public LogLevel getLevel() {
switch (this.level) { return this.level;
case TRACE: }
log.trace(this.message, this.throwable);
return; public Object getMessage() {
case DEBUG: return this.message;
log.debug(this.message, this.throwable); }
return;
case INFO: public Throwable getThrowable() {
log.info(this.message, this.throwable); return 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;
}
} }
} }
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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); 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);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment