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