Add jackson layout, rename stackdriver, include no logback.xml.

This commit is contained in:
Stephan Schnabel 2021-10-28 13:12:14 +02:00
parent 45c5a0a4c5
commit 5680a281ea
17 changed files with 246 additions and 65 deletions

View file

@ -0,0 +1,33 @@
package io.kokuwa.micronaut.logging.configurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.spi.ContextAwareBase;
/**
* Use logback-default.xml if no configuration is provided by user.
*
* @author Stephan Schnabel
*/
public class DefaultConfigurator extends ContextAwareBase implements Configurator {
@Override
public void configure(LoggerContext loggerContext) {
var base = DefaultConfigurator.class.getResource("/io/kokuwa/logback/logback-default.xml");
if (base == null) {
addError("Failed to find logback.xml from io.kokuwa:micronaut-logging");
return;
}
try {
addInfo("Use logback.xml from io.kokuwa:micronaut-logging");
var configurator = new MicronautJoranConfigurator();
configurator.setContext(loggerContext);
configurator.doConfigure(base);
} catch (JoranException e) {
addError("Failed to load logback.xml from io.kokuwa:micronaut-logging", e);
}
}
}

View file

@ -0,0 +1,19 @@
package io.kokuwa.micronaut.logging.configurator;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.joran.spi.RuleStore;
/**
* Add custom actions.
*
* @author Stephan Schnabel
*/
public class MicronautJoranConfigurator extends JoranConfigurator {
@Override
public void addInstanceRules(RuleStore rs) {
super.addInstanceRules(rs);
rs.addRule(new ElementSelector("configuration/root/autoAppender"), new RootAutoSelectAppenderAction());
}
}

View file

@ -0,0 +1,73 @@
package io.kokuwa.micronaut.logging.configurator;
import java.util.Map;
import org.xml.sax.Attributes;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.ActionConst;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import io.micronaut.core.util.StringUtils;
/**
* Auto select appender by environment.
*
* @author Stephan Schnabel
*/
public class RootAutoSelectAppenderAction extends Action {
private static final boolean IS_KUBERNETES = StringUtils.isNotEmpty(System.getenv("KUBERNETES_SERVICE_HOST"));
private static final boolean IS_GCP = StringUtils.isNotEmpty(System.getenv("GOOGLE_CLOUD_PROJECT"));
private static final String APPENDER_CONSOLE = "CONSOLE";
private static final String APPENDER_JSON = "JSON";
private static final String APPENDER_GCP = "GCP";
private static final String LOGBACK_APPENDER = "LOGBACK_APPENDER";
@Override
public void begin(InterpretationContext ic, String name, Attributes attributes) {
var rootLogger = LoggerContext.class.cast(context).getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
var rootLoggerAppenders = rootLogger.iteratorForAppenders();
if (rootLoggerAppenders.hasNext()) {
addWarn("Skip because appender already found: " + rootLoggerAppenders.next().getName());
return;
}
var envAppender = System.getenv(LOGBACK_APPENDER);
if (envAppender != null && setAppender(ic, rootLogger, envAppender)) {
return;
}
if (IS_KUBERNETES && setAppender(ic, rootLogger, APPENDER_JSON)) {
return;
}
if (IS_GCP && setAppender(ic, rootLogger, APPENDER_GCP)) {
return;
}
setAppender(ic, rootLogger, APPENDER_CONSOLE);
}
@Override
public void end(InterpretationContext ic, String name) {}
private boolean setAppender(InterpretationContext ic, Logger rootLogger, String appenderName) {
@SuppressWarnings("unchecked")
var appenderBag = (Map<String, Appender<ILoggingEvent>>) ic.getObjectMap().get(ActionConst.APPENDER_BAG);
var appender = appenderBag.get(appenderName);
if (appender == null) {
addError("Could not find an appender named [" + appenderName
+ "]. Did you define it below instead of above in the configuration file?");
return false;
}
addInfo("Use appender: " + appenderName);
rootLogger.addAppender(appender);
return true;
}
}

View file

@ -1,4 +1,4 @@
package io.kokuwa.micronaut.logging;
package io.kokuwa.micronaut.logging.layout;
import java.time.Instant;
import java.util.HashMap;
@ -6,43 +6,33 @@ import java.util.LinkedHashMap;
import java.util.Map;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.contrib.jackson.JacksonJsonFormatter;
import ch.qos.logback.contrib.json.classic.JsonLayout;
import io.micronaut.core.util.StringUtils;
import lombok.Getter;
import lombok.Setter;
/**
* Stackdriver layout.
* GCP logging layout.
*
* @author Stephan Schnabel
* @see "https://cloud.google.com/logging/docs/agent/configuration#process-payload"
* @see "https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext"
*/
@Getter
@Setter
public class StackdriverJsonLayout extends JsonLayout {
public class GcpJsonLayout extends JsonLayout {
private static final String TIMESTAMP_ATTR_NAME = "time";
private static final String UNDEFINED = "_IS_UNDEFINED";
private static final String TIME_ATTR_NAME = "time";
private static final String SEVERITY_ATTR_NAME = "severity";
private Map<String, String> serviceContext;
@Setter
private String serviceName;
@Setter
private String serviceVersion;
private boolean includeExceptionInMessage;
public StackdriverJsonLayout() {
appendLineSeparator = true;
includeContextName = false;
includeMessage = true;
includeExceptionInMessage = true;
setJsonFormatter(new JacksonJsonFormatter());
}
@Override
protected Map<String, Object> toJsonMap(ILoggingEvent event) {
var map = new LinkedHashMap<String, Object>();
add(TIMESTAMP_ATTR_NAME, includeTimestamp, Instant.ofEpochMilli(event.getTimeStamp()).toString(), map);
add(TIME_ATTR_NAME, includeTimestamp, Instant.ofEpochMilli(event.getTimeStamp()).toString(), map);
add(SEVERITY_ATTR_NAME, includeLevel, String.valueOf(event.getLevel()), map);
add(THREAD_ATTR_NAME, includeThreadName, event.getThreadName(), map);
add(CONTEXT_ATTR_NAME, includeContextName, event.getLoggerContextVO().getName(), map);
@ -50,7 +40,7 @@ public class StackdriverJsonLayout extends JsonLayout {
addMap(MDC_ATTR_NAME, includeMDC, event.getMDCPropertyMap(), map);
add(FORMATTED_MESSAGE_ATTR_NAME, includeFormattedMessage, event.getFormattedMessage(), map);
add(MESSAGE_ATTR_NAME, includeMessage, event.getMessage(), map);
addThrowableInfo(JsonLayout.EXCEPTION_ATTR_NAME, includeException, event, map);
addThrowableInfo(EXCEPTION_ATTR_NAME, includeException, event, map);
addServiceContext(map);
return map;
}
@ -58,10 +48,10 @@ public class StackdriverJsonLayout extends JsonLayout {
private void addServiceContext(Map<String, Object> map) {
if (serviceContext == null) {
serviceContext = new HashMap<>(2);
if (StringUtils.isNotEmpty(serviceName)) {
if (StringUtils.isNotEmpty(serviceName) && !serviceName.endsWith(UNDEFINED)) {
serviceContext.put("service", serviceName);
}
if (StringUtils.isNotEmpty(serviceVersion)) {
if (StringUtils.isNotEmpty(serviceVersion) && !serviceVersion.endsWith(UNDEFINED)) {
serviceContext.put("version", serviceVersion);
}
}

View file

@ -0,0 +1 @@
io.kokuwa.micronaut.logging.configurator.DefaultConfigurator

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>${CONSOLE_LOG_JANSI:-true}</withJansi>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN:-%cyan(%d{HH:mm:ss.SSS}) %gray(%-6.6thread) %highlight(%-5level) %magenta(%32logger{32}) %mdc %msg%n}</pattern>
<charset>${CONSOLE_LOG_CHARSET:-default}</charset>
</encoder>
</appender>
</included>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<included>
<appender name="GCP" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="io.kokuwa.micronaut.logging.layout.GcpJsonLayout">
<serviceName>${serviceName}</serviceName>
<serviceVersion>${serviceVersion}</serviceVersion>
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter" />
<appendLineSeparator>true</appendLineSeparator>
<includeMessage>true</includeMessage>
<includeContextName>false</includeContextName>
</layout>
</encoder>
</appender>
</included>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<included>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter" />
<appendLineSeparator>true</appendLineSeparator>
<includeMessage>true</includeMessage>
<includeContextName>false</includeContextName>
</layout>
</encoder>
</appender>
</included>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<included>
<include resource="io/kokuwa/logback/appender-console.xml" />
<include resource="io/kokuwa/logback/appender-json.xml" />
<include resource="io/kokuwa/logback/appender-gcp.xml" />
</included>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<include resource="io/kokuwa/logback/base.xml" />
<logger name="io.micronaut.logging.PropertiesLoggingLevelsConfigurer" levels="WARN" />
<root level="INFO">
<autoAppender />
</root>
</configuration>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="false">
<include resource="io/kokuwa/logback/base.xml" />
<logger name="io.micronaut.logging.PropertiesLoggingLevelsConfigurer" levels="WARN" />
<root level="INFO">
<appender-ref ref="${LOGBACK_APPENDER:-CONSOLE}" />
</root>
</configuration>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property resource="META-INF/build-info.properties" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<pattern>%cyan(%d{HH:mm:ss.SSS}) %gray(%-6.6thread) %highlight(%-5level) %magenta(%32logger{32}) %mdc %msg%n</pattern>
</encoder>
</appender>
<appender name="STACKDRIVER" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="io.kokuwa.micronaut.logging.StackdriverJsonLayout">
<serviceName>${serviceName}</serviceName>
<serviceVersion>${serviceVersion}</serviceVersion>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="${LOGBACK_APPENDER:-CONSOLE}" />
</root>
</configuration>

View file

@ -39,6 +39,8 @@ public class TestController {
level = Level.ERROR;
}
log.info("Test log for MDC inclusion, expected: {}", principal);
return new TestResponse(level.toString(), principal);
}

View file

@ -1,2 +0,0 @@
serviceName: ${project.artifactId}
serviceVersion: ${project.version}

View file

@ -10,7 +10,3 @@ micronaut:
http:
client:
logger-name: io.kokuwa.Test
logger:
levels:
io.kokuwa.Test: TRACE