If I put a newline before the exception stack trace in the logging pattern:
<Pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} %msg %n %rEx{short} %n</Pattern>
then an extra newline is printed, so normal logging statements end up with blank lines in between them.
If I remove the extra newline:
<Pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} %msg %rEx{short} %n</Pattern>
then stack traces do not start on a new line, making them hard to read.
Can anyone think of a way to get logback to only print the extra newline when there is a stack trace, and not otherwise?
I have a working solution, but I dislike it because it's overengineered and should be possible more elegantly.
It's a specific case of a more generic problem: output a pattern only if a certain evaluator (one that checks the presence of an exception) matches. I got the implementation from somewhere here on SO long ago.
Requires janino. If anyone more logback-experienced than me can trim this down to a more reasonable amount of code, please do.
IfPresentConverter.java
public class IfPresentConverter extends CompositeConverter<ILoggingEvent> {
private final List<EventEvaluator<ILoggingEvent>> evaluatorList = new ArrayList<>();
private int errorCount = 0;
@Override
@SuppressWarnings("unchecked")
public void start() {
final List<String> optionList = getOptionList();
final Map<?, ?> evaluatorMap = (Map<?, ?>) getContext().getObject(CoreConstants.EVALUATOR_MAP);
for (final String evaluatorStr : optionList) {
final EventEvaluator<ILoggingEvent> ee = (EventEvaluator<ILoggingEvent>) evaluatorMap.get(evaluatorStr);
if (ee != null) {
evaluatorList.add(ee);
}
}
if (evaluatorList.isEmpty()) {
addError("At least one evaluator is expected, but you have declared none.");
return;
}
super.start();
}
@Override
public String convert(final ILoggingEvent event) {
boolean evalResult = true;
for (final EventEvaluator<ILoggingEvent> ee : evaluatorList) {
try {
if (!ee.evaluate(event)) {
evalResult = false;
break;
}
} catch (final EvaluationException eex) {
evalResult = false;
errorCount++;
if (errorCount < CoreConstants.MAX_ERROR_COUNT) {
addError("Exception thrown for evaluator named [" + ee.getName() + "].", eex);
} else {
final ErrorStatus errorStatus = new ErrorStatus("Exception thrown for evaluator named [" + ee.getName() + "].", this, eex);
errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors. " + "We don't want the StatusManager to get flooded.", this));
addStatus(errorStatus);
}
}
}
if (evalResult) {
return super.convert(event);
} else {
return CoreConstants.EMPTY_STRING;
}
}
@Override
protected String transform(final ILoggingEvent event, final String in) {
return in;
}
}
logback.xml
<configuration>
<conversionRule conversionWord="ifPresent" converterClass="org.example.IfPresentConverter"/>
<evaluator name="has_ex">
<expression>return throwableProxy != null;</expression>
</evaluator>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>lorem ipsum %msg{}%ifPresent(\n%ex{full}){has_ex}%n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With