Skip to content

Commit bc92bec

Browse files
committed
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
1 parent b32887b commit bc92bec

File tree

3 files changed

+122
-35
lines changed

3 files changed

+122
-35
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public void postProcessEnvironment(ConfigurableEnvironment environment,
189189
}
190190

191191
private void onApplicationPreparedEvent(ApplicationEvent event) {
192-
this.logger.replayTo(ConfigFileApplicationListener.class);
192+
this.logger.switchTo(ConfigFileApplicationListener.class);
193193
addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
194194
}
195195

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

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,36 +31,50 @@
3131
*/
3232
public class DeferredLog implements Log {
3333

34-
private List<Line> lines = new ArrayList<>();
34+
private volatile Log destination;
35+
36+
private final List<Line> lines = new ArrayList<>();
3537

3638
@Override
3739
public boolean isTraceEnabled() {
38-
return true;
40+
synchronized (this.lines) {
41+
return (this.destination != null) ? this.destination.isTraceEnabled() : true;
42+
}
3943
}
4044

4145
@Override
4246
public boolean isDebugEnabled() {
43-
return true;
47+
synchronized (this.lines) {
48+
return (this.destination != null) ? this.destination.isDebugEnabled() : true;
49+
}
4450
}
4551

4652
@Override
4753
public boolean isInfoEnabled() {
48-
return true;
54+
synchronized (this.lines) {
55+
return (this.destination != null) ? this.destination.isInfoEnabled() : true;
56+
}
4957
}
5058

5159
@Override
5260
public boolean isWarnEnabled() {
53-
return true;
61+
synchronized (this.lines) {
62+
return (this.destination != null) ? this.destination.isWarnEnabled() : true;
63+
}
5464
}
5565

5666
@Override
5767
public boolean isErrorEnabled() {
58-
return true;
68+
synchronized (this.lines) {
69+
return (this.destination != null) ? this.destination.isErrorEnabled() : true;
70+
}
5971
}
6072

6173
@Override
6274
public boolean isFatalEnabled() {
63-
return true;
75+
synchronized (this.lines) {
76+
return (this.destination != null) ? this.destination.isFatalEnabled() : true;
77+
}
6478
}
6579

6680
@Override
@@ -124,31 +138,106 @@ public void fatal(Object message, Throwable t) {
124138
}
125139

126140
private void log(LogLevel level, Object message, Throwable t) {
127-
this.lines.add(new Line(level, message, t));
141+
synchronized (this.lines) {
142+
if (this.destination != null) {
143+
logTo(this.destination, level, message, t);
144+
}
145+
this.lines.add(new Line(level, message, t));
146+
}
128147
}
129148

149+
/**
150+
* Switch from deferred logging to immediate logging to the specified destination.
151+
* @param destination the new log destination
152+
*/
153+
public void switchTo(Class<?> destination) {
154+
switchTo(LogFactory.getLog(destination));
155+
}
156+
157+
/**
158+
* Switch from deferred logging to immediate logging to the specified destination.
159+
* @param destination the new log destination
160+
*/
161+
public void switchTo(Log destination) {
162+
synchronized (this.lines) {
163+
replayTo(destination);
164+
this.destination = destination;
165+
}
166+
}
167+
168+
/**
169+
* Replay deferred logging to the specified destination.
170+
* @param destination the destination for the deferred log messages
171+
*/
130172
public void replayTo(Class<?> destination) {
131173
replayTo(LogFactory.getLog(destination));
132174
}
133175

176+
/**
177+
* Replay deferred logging to the specified destination.
178+
* @param destination the destination for the deferred log messages
179+
*/
134180
public void replayTo(Log destination) {
135-
for (Line line : this.lines) {
136-
line.replayTo(destination);
181+
synchronized (this.lines) {
182+
for (Line line : this.lines) {
183+
logTo(destination, line.getLevel(), line.getMessage(),
184+
line.getThrowable());
185+
}
186+
this.lines.clear();
137187
}
138-
this.lines.clear();
139188
}
140189

190+
/**
191+
* Replay from a source log to a destination log when the source is deferred.
192+
* @param source the source logger
193+
* @param destination the destination logger class
194+
* @return the destination
195+
* @deprecated since 2.1.0 in favor of {@link #switchTo(Class)}
196+
*/
197+
@Deprecated
141198
public static Log replay(Log source, Class<?> destination) {
142199
return replay(source, LogFactory.getLog(destination));
143200
}
144201

202+
/**
203+
* Replay from a source log to a destination log when the source is deferred.
204+
* @param source the source logger
205+
* @param destination the destination logger
206+
* @return the destination
207+
* @deprecated since 2.1.0 in favor of {@link #switchTo(Log)}
208+
*/
209+
@Deprecated
145210
public static Log replay(Log source, Log destination) {
146211
if (source instanceof DeferredLog) {
147212
((DeferredLog) source).replayTo(destination);
148213
}
149214
return destination;
150215
}
151216

217+
private static void logTo(Log log, LogLevel level, Object message,
218+
Throwable throwable) {
219+
switch (level) {
220+
case TRACE:
221+
log.trace(message, throwable);
222+
return;
223+
case DEBUG:
224+
log.debug(message, throwable);
225+
return;
226+
case INFO:
227+
log.info(message, throwable);
228+
return;
229+
case WARN:
230+
log.warn(message, throwable);
231+
return;
232+
case ERROR:
233+
log.error(message, throwable);
234+
return;
235+
case FATAL:
236+
log.fatal(message, throwable);
237+
return;
238+
}
239+
}
240+
152241
private static class Line {
153242

154243
private final LogLevel level;
@@ -163,27 +252,16 @@ private static class Line {
163252
this.throwable = throwable;
164253
}
165254

166-
public void replayTo(Log log) {
167-
switch (this.level) {
168-
case TRACE:
169-
log.trace(this.message, this.throwable);
170-
return;
171-
case DEBUG:
172-
log.debug(this.message, this.throwable);
173-
return;
174-
case INFO:
175-
log.info(this.message, this.throwable);
176-
return;
177-
case WARN:
178-
log.warn(this.message, this.throwable);
179-
return;
180-
case ERROR:
181-
log.error(this.message, this.throwable);
182-
return;
183-
case FATAL:
184-
log.fatal(this.message, this.throwable);
185-
return;
186-
}
255+
public LogLevel getLevel() {
256+
return this.level;
257+
}
258+
259+
public Object getMessage() {
260+
return this.message;
261+
}
262+
263+
public Throwable getThrowable() {
264+
return this.throwable;
187265
}
188266

189267
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -167,4 +167,13 @@ public void clearsOnReplayTo() {
167167
verifyZeroInteractions(log2);
168168
}
169169

170+
@Test
171+
public void switchTo() {
172+
this.deferredLog.error(this.message, this.throwable);
173+
this.deferredLog.switchTo(this.log);
174+
this.deferredLog.info("Message2");
175+
verify(this.log).error(this.message, this.throwable);
176+
verify(this.log).info("Message2", null);
177+
}
178+
170179
}

0 commit comments

Comments
 (0)