From 5680a281ea5d1740f5eea4afe6f6bf90a5d2736f Mon Sep 17 00:00:00 2001 From: Stephan Schnabel Date: Thu, 28 Oct 2021 13:12:14 +0200 Subject: [PATCH] Add jackson layout, rename stackdriver, include no logback.xml. --- README.md | 38 ++++++++-- pom.xml | 6 -- .../configurator/DefaultConfigurator.java | 33 +++++++++ .../MicronautJoranConfigurator.java | 19 +++++ .../RootAutoSelectAppenderAction.java | 73 +++++++++++++++++++ .../GcpJsonLayout.java} | 32 +++----- .../ch.qos.logback.classic.spi.Configurator | 1 + .../io/kokuwa/logback/appender-console.xml | 12 +++ .../io/kokuwa/logback/appender-gcp.xml | 17 +++++ .../io/kokuwa/logback/appender-json.xml | 15 ++++ src/main/resources/io/kokuwa/logback/base.xml | 8 ++ .../io/kokuwa/logback/logback-default.xml | 12 +++ .../io/kokuwa/logback/logback-example.xml | 12 +++ src/main/resources/logback.xml | 25 ------- .../logging/request/TestController.java | 2 + .../resources/META-INF/build-info.properties | 2 - src/test/resources/application-test.yaml | 4 - 17 files changed, 246 insertions(+), 65 deletions(-) create mode 100644 src/main/java/io/kokuwa/micronaut/logging/configurator/DefaultConfigurator.java create mode 100644 src/main/java/io/kokuwa/micronaut/logging/configurator/MicronautJoranConfigurator.java create mode 100644 src/main/java/io/kokuwa/micronaut/logging/configurator/RootAutoSelectAppenderAction.java rename src/main/java/io/kokuwa/micronaut/logging/{StackdriverJsonLayout.java => layout/GcpJsonLayout.java} (67%) create mode 100644 src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator create mode 100644 src/main/resources/io/kokuwa/logback/appender-console.xml create mode 100644 src/main/resources/io/kokuwa/logback/appender-gcp.xml create mode 100644 src/main/resources/io/kokuwa/logback/appender-json.xml create mode 100644 src/main/resources/io/kokuwa/logback/base.xml create mode 100644 src/main/resources/io/kokuwa/logback/logback-default.xml create mode 100644 src/main/resources/io/kokuwa/logback/logback-example.xml delete mode 100644 src/main/resources/logback.xml delete mode 100644 src/test/resources/META-INF/build-info.properties diff --git a/README.md b/README.md index 05d3835..79d33d2 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,35 @@ ## Features +### Default logback.xml + +If no `logback.xml` by user is provided a default [logback.xml](src/main/resources/io/kokuwa/logback/logback-default.xml) is loaded. Otherwise use custom [logback.xml](src/main/resources/io/kokuwa/logback/logback-example.xml): + +```xml + + + + + + + + +``` + ### Available Appender -* console format -* Stackdriver format (with support for error reporting) +* console with jansi for developers +* gcp logging format (with support for error reporting) +* json + +### AutoSelect appender logback.xml + +1. if `LOGBACK_APPENDER` is set this appender will be used +2. if GCP is detected gcp appender will be used +3. if Kubernetes is detected json appender will be used +4. console appender else + +*IMPORTENT*: only works without custom `logback.xml` ### Set log level based on MDC values @@ -72,14 +97,14 @@ logger: Configuration for server filter (prefixed with *logger.request.filter*): * *enabled*: enable HTTP server filter (`true` is default) -* *order*: order for [Ordered](https://github.com/micronaut-projects/micronaut-core/blob/master/core/src/main/java/io/micronaut/core/order/Ordered.java) (highest is default) +* *order*: order for [Ordered](https://github.com/micronaut-projects/micronaut-core/blob/v2.5.13/core/src/main/java/io/micronaut/core/order/Ordered.java) (highest is default) * *path*: filter path (`/**` is default) * *header*: name of HTTP header (`x-log-level` is default) Configuration for client filter for propagation (prefixed with *logger.request.propagation*): * *enabled*: enable HTTP client filter (`true` is default) -* *order*: order for [Ordered](https://github.com/micronaut-projects/micronaut-core/blob/master/core/src/main/java/io/micronaut/core/order/Ordered.java) (tracing is default) +* *order*: order for [Ordered](https://github.com/micronaut-projects/micronaut-core/blob/v2.5.13/core/src/main/java/io/micronaut/core/order/Ordered.java) (tracing is default) * *path*: filter path (`/**` is default) * *header*: name of HTTP header (server header is default) @@ -105,7 +130,7 @@ logger: Configuration: * *enabled*: enable HTTP principal filter (`true` is default) -* *order*: order for [Ordered](https://github.com/micronaut-projects/micronaut-core/blob/master/core/src/main/java/io/micronaut/core/order/Ordered.java) ([ServerFilterPhase.SECURITY.after()](https://github.com/micronaut-projects/micronaut-core/blob/v2.0.1/http/src/main/java/io/micronaut/http/filter/ServerFilterPhase.java#L54) is default) +* *order*: order for [Ordered](https://github.com/micronaut-projects/micronaut-core/blob/v2.5.13/core/src/main/java/io/micronaut/core/order/Ordered.java) ([ServerFilterPhase.SECURITY.after()](https://github.com/micronaut-projects/micronaut-core/blob/v2.5.13/http/src/main/java/io/micronaut/http/filter/ServerFilterPhase.java#L54) is default) * *path*: filter path (`/**` is default) * *key*: name of MDC header (`principal` is default) @@ -148,6 +173,5 @@ mvn release:prepare release:perform release:clean -B -DreleaseProfiles=oss-relea ## Open Topics * configure mdc on refresh event -* add stackdriver per configuration -* add fluent per configuration * read **serviceName** and **serviceVersion** from yaml +* support auto select appender with custom `logback.xml` diff --git a/pom.xml b/pom.xml index f44fa16..6f17ef5 100644 --- a/pom.xml +++ b/pom.xml @@ -130,12 +130,6 @@ - - - src/test/resources - true - - diff --git a/src/main/java/io/kokuwa/micronaut/logging/configurator/DefaultConfigurator.java b/src/main/java/io/kokuwa/micronaut/logging/configurator/DefaultConfigurator.java new file mode 100644 index 0000000..1ebef70 --- /dev/null +++ b/src/main/java/io/kokuwa/micronaut/logging/configurator/DefaultConfigurator.java @@ -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); + } + } +} diff --git a/src/main/java/io/kokuwa/micronaut/logging/configurator/MicronautJoranConfigurator.java b/src/main/java/io/kokuwa/micronaut/logging/configurator/MicronautJoranConfigurator.java new file mode 100644 index 0000000..f89db16 --- /dev/null +++ b/src/main/java/io/kokuwa/micronaut/logging/configurator/MicronautJoranConfigurator.java @@ -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()); + } +} diff --git a/src/main/java/io/kokuwa/micronaut/logging/configurator/RootAutoSelectAppenderAction.java b/src/main/java/io/kokuwa/micronaut/logging/configurator/RootAutoSelectAppenderAction.java new file mode 100644 index 0000000..b490a35 --- /dev/null +++ b/src/main/java/io/kokuwa/micronaut/logging/configurator/RootAutoSelectAppenderAction.java @@ -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>) 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; + } +} diff --git a/src/main/java/io/kokuwa/micronaut/logging/StackdriverJsonLayout.java b/src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java similarity index 67% rename from src/main/java/io/kokuwa/micronaut/logging/StackdriverJsonLayout.java rename to src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java index a609ca1..7b0ce16 100644 --- a/src/main/java/io/kokuwa/micronaut/logging/StackdriverJsonLayout.java +++ b/src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java @@ -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 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 toJsonMap(ILoggingEvent event) { var map = new LinkedHashMap(); - 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 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); } } diff --git a/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator b/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator new file mode 100644 index 0000000..1f9f2da --- /dev/null +++ b/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator @@ -0,0 +1 @@ +io.kokuwa.micronaut.logging.configurator.DefaultConfigurator diff --git a/src/main/resources/io/kokuwa/logback/appender-console.xml b/src/main/resources/io/kokuwa/logback/appender-console.xml new file mode 100644 index 0000000..e4bfb1d --- /dev/null +++ b/src/main/resources/io/kokuwa/logback/appender-console.xml @@ -0,0 +1,12 @@ + + + + + ${CONSOLE_LOG_JANSI:-true} + + ${CONSOLE_LOG_PATTERN:-%cyan(%d{HH:mm:ss.SSS}) %gray(%-6.6thread) %highlight(%-5level) %magenta(%32logger{32}) %mdc %msg%n} + ${CONSOLE_LOG_CHARSET:-default} + + + + diff --git a/src/main/resources/io/kokuwa/logback/appender-gcp.xml b/src/main/resources/io/kokuwa/logback/appender-gcp.xml new file mode 100644 index 0000000..6089d9c --- /dev/null +++ b/src/main/resources/io/kokuwa/logback/appender-gcp.xml @@ -0,0 +1,17 @@ + + + + + + + ${serviceName} + ${serviceVersion} + + true + true + false + + + + + diff --git a/src/main/resources/io/kokuwa/logback/appender-json.xml b/src/main/resources/io/kokuwa/logback/appender-json.xml new file mode 100644 index 0000000..b850ac7 --- /dev/null +++ b/src/main/resources/io/kokuwa/logback/appender-json.xml @@ -0,0 +1,15 @@ + + + + + + + + true + true + false + + + + + diff --git a/src/main/resources/io/kokuwa/logback/base.xml b/src/main/resources/io/kokuwa/logback/base.xml new file mode 100644 index 0000000..24ae946 --- /dev/null +++ b/src/main/resources/io/kokuwa/logback/base.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/io/kokuwa/logback/logback-default.xml b/src/main/resources/io/kokuwa/logback/logback-default.xml new file mode 100644 index 0000000..1092ea9 --- /dev/null +++ b/src/main/resources/io/kokuwa/logback/logback-default.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/io/kokuwa/logback/logback-example.xml b/src/main/resources/io/kokuwa/logback/logback-example.xml new file mode 100644 index 0000000..47deab2 --- /dev/null +++ b/src/main/resources/io/kokuwa/logback/logback-example.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 42a34f7..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - true - - %cyan(%d{HH:mm:ss.SSS}) %gray(%-6.6thread) %highlight(%-5level) %magenta(%32logger{32}) %mdc %msg%n - - - - - - ${serviceName} - ${serviceVersion} - - - - - - - - - diff --git a/src/test/java/io/kokuwa/micronaut/logging/request/TestController.java b/src/test/java/io/kokuwa/micronaut/logging/request/TestController.java index 58bdf5b..d179f63 100644 --- a/src/test/java/io/kokuwa/micronaut/logging/request/TestController.java +++ b/src/test/java/io/kokuwa/micronaut/logging/request/TestController.java @@ -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); } diff --git a/src/test/resources/META-INF/build-info.properties b/src/test/resources/META-INF/build-info.properties deleted file mode 100644 index 5401a6c..0000000 --- a/src/test/resources/META-INF/build-info.properties +++ /dev/null @@ -1,2 +0,0 @@ -serviceName: ${project.artifactId} -serviceVersion: ${project.version} diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index 42aa67e..71beb2c 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -10,7 +10,3 @@ micronaut: http: client: logger-name: io.kokuwa.Test - -logger: - levels: - io.kokuwa.Test: TRACE