forked from spring-projects/spring-boot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Logback StructuredLogFormatter exceptions are not visible to the user
Before this commit, any exceptions thrown in Logback encoders were just swallowed. This commit adds the FilteringStatusListener that delegates to OnErrorConsoleStatusListener to print any errors that happened in logback encoders. See spring-projectsgh-43384
- Loading branch information
Showing
7 changed files
with
289 additions
and
0 deletions.
There are no files selected for viewing
80 changes: 80 additions & 0 deletions
80
...-boot/src/main/java/org/springframework/boot/logging/logback/FilteringStatusListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright 2012-2024 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.logging.logback; | ||
|
||
import ch.qos.logback.core.spi.ContextAwareBase; | ||
import ch.qos.logback.core.spi.LifeCycle; | ||
import ch.qos.logback.core.status.Status; | ||
import ch.qos.logback.core.status.StatusListener; | ||
|
||
/** | ||
* Logback {@link StatusListener} that filters {@link Status} by its logging level and | ||
* delegates to the underlying {@code StatusListener}. | ||
* | ||
* @author Dmytro Nosan | ||
*/ | ||
final class FilteringStatusListener extends ContextAwareBase implements StatusListener, LifeCycle { | ||
|
||
private final StatusListener delegate; | ||
|
||
private final int levelThreshold; | ||
|
||
/** | ||
* Creates a new {@link FilteringStatusListener}. | ||
* @param delegate the {@link StatusListener} delegate to | ||
* @param levelThreshold the minimum log level accepted for delegation | ||
*/ | ||
FilteringStatusListener(StatusListener delegate, int levelThreshold) { | ||
this.delegate = delegate; | ||
this.levelThreshold = levelThreshold; | ||
} | ||
|
||
@Override | ||
public void addStatusEvent(Status status) { | ||
if (status.getLevel() >= this.levelThreshold) { | ||
this.delegate.addStatusEvent(status); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean isResetResistant() { | ||
return this.delegate.isResetResistant(); | ||
} | ||
|
||
@Override | ||
public void start() { | ||
if (this.delegate instanceof LifeCycle lifeCycle) { | ||
lifeCycle.start(); | ||
} | ||
} | ||
|
||
@Override | ||
public void stop() { | ||
if (this.delegate instanceof LifeCycle lifeCycle) { | ||
lifeCycle.stop(); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean isStarted() { | ||
if (this.delegate instanceof LifeCycle lifeCycle) { | ||
return lifeCycle.isStarted(); | ||
} | ||
return true; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
.../src/test/java/org/springframework/boot/logging/logback/FilteringStatusListenerTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* | ||
* Copyright 2012-2024 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.logging.logback; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import ch.qos.logback.core.spi.LifeCycle; | ||
import ch.qos.logback.core.status.ErrorStatus; | ||
import ch.qos.logback.core.status.InfoStatus; | ||
import ch.qos.logback.core.status.Status; | ||
import ch.qos.logback.core.status.StatusListener; | ||
import ch.qos.logback.core.status.WarnStatus; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
/** | ||
* Tests for {@link FilteringStatusListener}. | ||
* | ||
* @author Dmytro Nosan | ||
*/ | ||
class FilteringStatusListenerTests { | ||
|
||
private final DelegateStatusListener delegate = new DelegateStatusListener(); | ||
|
||
@Test | ||
void shouldFilterOutInfoStatus() { | ||
FilteringStatusListener listener = createListener(Status.WARN); | ||
InfoStatus info = new InfoStatus("info", getClass()); | ||
WarnStatus warn = new WarnStatus("warn", getClass()); | ||
ErrorStatus error = new ErrorStatus("error", getClass()); | ||
listener.addStatusEvent(info); | ||
listener.addStatusEvent(warn); | ||
listener.addStatusEvent(error); | ||
assertThat(this.delegate.getStatuses()).containsExactly(warn, error); | ||
} | ||
|
||
@Test | ||
void shouldStartUnderlyingStatusListener() { | ||
FilteringStatusListener listener = createListener(Status.INFO); | ||
assertThat(this.delegate.isStarted()).isFalse(); | ||
listener.start(); | ||
assertThat(this.delegate.isStarted()).isTrue(); | ||
} | ||
|
||
@Test | ||
void shouldStopUnderlyingStatusListener() { | ||
FilteringStatusListener listener = createListener(); | ||
this.delegate.start(); | ||
assertThat(this.delegate.isStarted()).isTrue(); | ||
listener.stop(); | ||
assertThat(this.delegate.isStarted()).isFalse(); | ||
} | ||
|
||
@Test | ||
void shouldUseResetResistantValueFromUnderlyingStatusListener() { | ||
FilteringStatusListener listener = createListener(); | ||
assertThat(listener.isResetResistant()).isEqualTo(this.delegate.isResetResistant()); | ||
} | ||
|
||
private FilteringStatusListener createListener() { | ||
return new FilteringStatusListener(this.delegate, Status.INFO); | ||
} | ||
|
||
private FilteringStatusListener createListener(int levelThreshold) { | ||
return new FilteringStatusListener(this.delegate, levelThreshold); | ||
} | ||
|
||
private static final class DelegateStatusListener implements StatusListener, LifeCycle { | ||
|
||
private final List<Status> statuses = new ArrayList<>(); | ||
|
||
private boolean started = false; | ||
|
||
@Override | ||
public void addStatusEvent(Status status) { | ||
this.statuses.add(status); | ||
} | ||
|
||
List<Status> getStatuses() { | ||
return this.statuses; | ||
} | ||
|
||
@Override | ||
public boolean isResetResistant() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public void start() { | ||
this.started = true; | ||
} | ||
|
||
@Override | ||
public void stop() { | ||
this.started = false; | ||
} | ||
|
||
@Override | ||
public boolean isStarted() { | ||
return this.started; | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
...red-logging/src/main/java/smoketest/structuredlogging/DuplicateJsonMembersCustomizer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright 2012-2024 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package smoketest.structuredlogging; | ||
|
||
import java.util.Objects; | ||
|
||
import org.springframework.boot.json.JsonWriter.Members; | ||
import org.springframework.boot.logging.structured.StructuredLoggingJsonMembersCustomizer; | ||
|
||
public class DuplicateJsonMembersCustomizer implements StructuredLoggingJsonMembersCustomizer<Object> { | ||
|
||
@Override | ||
public void customize(Members<Object> members) { | ||
members.add("test").as(Objects::toString); | ||
members.add("test").as(Objects::toString); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters