summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2022-01-15 16:13:01 +0100
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2022-01-15 21:14:23 +0100
commit348f2055bfb000633bb80e89bcf62ea037a5af0b (patch)
tree9ad5b0436a7fc24af466508a552366a1f867c7cb
parent93ecfd1e0c1d1e40fb17e580a7a11de35383ef79 (diff)
Add a Spring Boot example and integration code.
Change-Id: Ia11dea607c74d9b4cc9a698e9ec92e930bd03f37
-rw-r--r--README.adoc128
-rw-r--r--core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java24
-rw-r--r--core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultConsoleHandler.java68
-rw-r--r--core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultEmbeddedConfigurator.java46
-rw-r--r--core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/package-info.java18
-rw-r--r--core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java4
-rw-r--r--deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/GoogleCloudLoggingProcessor.java22
-rw-r--r--deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/package-info.java2
-rw-r--r--examples/quarkus/pom.xml (renamed from example/pom.xml)4
-rw-r--r--examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java (renamed from example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java)0
-rw-r--r--examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java (renamed from example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java)1
-rw-r--r--examples/quarkus/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider (renamed from example/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider)0
-rw-r--r--examples/quarkus/src/main/resources/application.properties (renamed from example/src/main/resources/application.properties)0
-rw-r--r--examples/spring-boot/pom.xml93
-rw-r--r--examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/Application.java12
-rw-r--r--examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/ApplicationLoggingSystem.java22
-rw-r--r--examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java14
-rw-r--r--examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java33
-rw-r--r--examples/spring-boot/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider1
-rw-r--r--examples/spring-boot/src/main/resources/META-INF/services/org.jboss.logmanager.EmbeddedConfigurator1
-rw-r--r--examples/spring-boot/src/main/resources/application.properties1
-rw-r--r--examples/spring-boot/src/main/resources/logging.properties2
22 files changed, 463 insertions, 33 deletions
diff --git a/README.adoc b/README.adoc
index 494f44c..506ba63 100644
--- a/README.adoc
+++ b/README.adoc
@@ -29,48 +29,134 @@ It is possible to log unstructured text, structured data, or a mixture
of both depending on the situation.
-== Activation
+== Activation (Quarkus)
Add the runtime POM to your dependency list. As long as the JAR is on
the classpath at both build time and runtime, the log formatter
automatically registers itself on startup.
-
-=== Activation with Maven
+If you are using Maven:
[source,xml]
----
-<project>
- ...
+<dependencies>
+
+ <dependency>
+ <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
+ <artifactId>quarkus-googlecloud-jsonlogging</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+
+</dependencies>
+----
+
+If you are using Gradle:
+
+[source,groovy]
+----
+dependencies {
+ implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:4.0.0")
+}
+----
+
+
+== Activation (Other Frameworks)
+
+If you are not using Quarkus, you can still make use of the `-core`
+module and wire it into your application in a custom way. Read this
+section for hints on how to do so.
- <dependencies>
- ...
- <dependency>
- <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
- <artifactId>quarkus-googlecloud-jsonlogging</artifactId>
- <version>4.0.0</version>
- </dependency>
+=== Installation
- ...
- </dependencies>
+If you are using Maven:
- ...
-</project>
+[source,xml]
----
+<dependencies>
+
+ <dependency>
+ <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
+ <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+</dependencies>
+----
-=== Activation with Gradle
+If you are using Gradle:
[source,groovy]
----
dependencies {
- ...
+ implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:4.0.0")
+}
+----
- implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:4.0.0")
- ...
-}
+=== Wiring (Spring Boot)
+
+If you are using Spring Boot, the easiest way to integrate the log
+formatter is by relying on `spring-boot-starter-logging` (which is
+pulled in by `spring-boot-starter`), excluding Logback, and pulling in
+JBoss Log Manager as the back end for SLF4J:
+
+[source,xml]
+----
+
+<dependencies>
+
+ <dependency>
+ <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
+ <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.slf4j</groupId>
+ <artifactId>slf4j-jboss-logmanager</artifactId>
+ <version>1.1.0.Final</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+</dependencies>
+----
+
+Create a text file called
+`META-INF/services/org.jboss.logmanager.EmbeddedConfigurator` in your
+`resources` directory and put the fully qualified name of
+`DefaultEmbeddedConfigurator` into it:
+
+[source]
+----
+eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultEmbeddedConfigurator
+----
+
+To configure `java.util.logging`, which is used by Tomcat by default,
+create an entry in `application.properties` that points to
+`logging.properties`:
+
+[source,properties]
+----
+logging.config = classpath:logging.properties
+----
+
+Create the `logging.properties` file in your `resources` directory and
+name `DefaultConsoleHandler` as a handler:
+
+[source,properties]
+----
+handlers = eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.ConsoleHandler
----
diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java
index 61a2dea..e759ff0 100644
--- a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java
+++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java
@@ -8,7 +8,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
+import java.util.ServiceLoader.Provider;
import java.util.logging.Level;
+import java.util.stream.Collectors;
import org.jboss.logmanager.ExtFormatter;
import org.jboss.logmanager.ExtLogRecord;
@@ -34,7 +36,7 @@ public class Formatter extends ExtFormatter {
private final List<LabelProvider> labelProviders;
/**
- * Constructs a {@link Formatter}.
+ * Constructs a {@link Formatter} with custom configuration.
*
* <p><strong>Note:</strong> This constructor does not automatically discover providers using the
* {@link ServiceLoader} mechanism. See {@link #load} for this case use.
@@ -59,21 +61,33 @@ public class Formatter extends ExtFormatter {
*
* @param parameterProviders the {@link StructuredParameterProvider}s to apply to each log entry.
* @param labelProviders the {@link LabelProvider}s to apply to each log entry.
+ * @return a new formatter.
*/
public static Formatter load(
Collection<StructuredParameterProvider> parameterProviders,
Collection<LabelProvider> labelProviders) {
parameterProviders = new ArrayList<>(parameterProviders);
- ServiceLoader.load(StructuredParameterProvider.class, Formatter.class.getClassLoader())
- .forEach(parameterProviders::add);
+ parameterProviders.addAll(loadStructuredParameterProviders());
labelProviders = new ArrayList<>(labelProviders);
- ServiceLoader.load(LabelProvider.class, Formatter.class.getClassLoader())
- .forEach(labelProviders::add);
+ labelProviders.addAll(loadLabelProviders());
return new Formatter(parameterProviders, labelProviders);
}
+ private static List<StructuredParameterProvider> loadStructuredParameterProviders() {
+ return ServiceLoader.load(StructuredParameterProvider.class, Formatter.class.getClassLoader())
+ .stream()
+ .map(Provider::get)
+ .collect(Collectors.toList());
+ }
+
+ private static List<LabelProvider> loadLabelProviders() {
+ return ServiceLoader.load(LabelProvider.class, Formatter.class.getClassLoader()).stream()
+ .map(Provider::get)
+ .collect(Collectors.toList());
+ }
+
@Override
public String format(ExtLogRecord logRecord) {
var message = formatMessageWithStackTrace(logRecord);
diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultConsoleHandler.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultConsoleHandler.java
new file mode 100644
index 0000000..a1dbde7
--- /dev/null
+++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultConsoleHandler.java
@@ -0,0 +1,68 @@
+package eu.mulk.quarkus.googlecloud.jsonlogging.logmanager;
+
+import eu.mulk.quarkus.googlecloud.jsonlogging.Formatter;
+import java.io.InputStream;
+import java.util.Collections;
+import org.jboss.logmanager.handlers.ConsoleHandler;
+
+/**
+ * A {@link ConsoleHandler} preconfigured with {@link Formatter}.
+ *
+ * <p>Useful as a handler for {@link java.util.logging}.
+ *
+ * <p>If you have a {@code logging.properties} file (see {@link
+ * java.util.logging.LogManager#readConfiguration(InputStream)}), you can use this handler by
+ * setting the following properties:
+ *
+ * <pre>{@code
+ * handlers = eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.ConsoleHandler
+ * }</pre>
+ *
+ * <p><strong>Note:</strong> You can use {@code org.slf4j.bridge.SLF4JBridgeHandler} from {@code
+ * org.slf4j:jul-to-slf4j} instead if you also have {@code org.jboss.slf4j:slf4j-jboss-logmanager}
+ * on the class path. In comparison to this class, which relies on the relatively efficient {@link
+ * org.jboss.logmanager.ExtLogRecord#wrap}, routing through SLF4J incurs additional overhead because
+ * of the necessary conversions between SLF4J's log entry structure and {@link
+ * java.util.logging.LogRecord}.
+ *
+ * <h2>Usage with Spring Boot</h2>
+ *
+ * <p>In case you are using Spring Boot, note that in addition to ensuring that {@code
+ * org.springframework.boot.logging.java.JavaLoggingSystem} is the logging system in use (see
+ * below), you need to accompany this with an entry in {@code application.properties} that points to
+ * your {@code logging.properties} file:
+ *
+ * <pre>{@code
+ * logging.config = classpath:logging.properties
+ * }</pre>
+ *
+ * <p>In order to ensure that Spring Boot chooses {@code JavaLoggingSystem} over other
+ * implementations, make sure that no other logging backends are present on the class path. A simple
+ * way of doing this is by relying on {@code spring-boot-starter-logging} while excluding Logback:
+ *
+ * <pre>{@code
+ * <dependency>
+ * <groupId>org.springframework.boot</groupId>
+ * <artifactId>spring-boot-starter</artifactId>
+ * <exclusions>
+ * <exclusion>
+ * <groupId>ch.qos.logback</groupId>
+ * <artifactId>logback-classic</artifactId>
+ * </exclusion>
+ * </exclusions>
+ * </dependency>
+ * }</pre>
+ *
+ * <p>You will probably want to include at least {@code org.jboss.slf4j:slf4j-jboss-logmanager} as
+ * well. In addition, {@code org.slf4j:jcl-over-slf4j}, {@code
+ * org.jboss.logmanager:log4j-jboss-logmanager}, and {@code
+ * org.jboss.logmanager:log4j2-jboss-logmanager} may be useful, but are not required.
+ */
+@SuppressWarnings("java:S110")
+public final class DefaultConsoleHandler extends ConsoleHandler {
+
+ /** Constructs console handler with a formatter created by {@link Formatter#load}. */
+ public DefaultConsoleHandler() {
+ super(Formatter.load(Collections.emptyList(), Collections.emptyList()));
+ }
+}
diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultEmbeddedConfigurator.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultEmbeddedConfigurator.java
new file mode 100644
index 0000000..8d9d4d8
--- /dev/null
+++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultEmbeddedConfigurator.java
@@ -0,0 +1,46 @@
+package eu.mulk.quarkus.googlecloud.jsonlogging.logmanager;
+
+import eu.mulk.quarkus.googlecloud.jsonlogging.Formatter;
+import java.util.logging.Handler;
+import org.jboss.logmanager.EmbeddedConfigurator;
+import org.jboss.logmanager.handlers.ConsoleHandler;
+
+/**
+ * A convenient {@link EmbeddedConfigurator} for JBoss Log Manager.
+ *
+ * <p>You can register this class through the {@link java.util.ServiceLoader} mechanism as a
+ * provider of the {@link EmbeddedConfigurator} interface (under the name of {@code
+ * org.jboss.logmanager.EmbeddedConfigurator}) to automatically register a {@link ConsoleHandler}
+ * using {@link Formatter} as the default log output method for the application.
+ */
+public final class DefaultEmbeddedConfigurator implements EmbeddedConfigurator {
+
+ private final Handler[] rootHandlers;
+
+ /**
+ * Constructs a JBoss Log Manager configuration that uses {@link Formatter} and {@link
+ * ConsoleHandler} for log output.
+ */
+ @SuppressWarnings("java:S2095")
+ public DefaultEmbeddedConfigurator() {
+ rootHandlers = new Handler[] {createConsoleHandler()};
+ }
+
+ /**
+ * Creates a {@link ConsoleHandler} that uses {@link Formatter} for formatting.
+ *
+ * @return a preconfigured {@link ConsoleHandler}.
+ */
+ public static ConsoleHandler createConsoleHandler() {
+ return new DefaultConsoleHandler();
+ }
+
+ @Override
+ public Handler[] getHandlersOf(String loggerName) {
+ if (loggerName.isEmpty()) {
+ return rootHandlers;
+ } else {
+ return EmbeddedConfigurator.NO_HANDLERS;
+ }
+ }
+}
diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/package-info.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/package-info.java
new file mode 100644
index 0000000..87eb71e
--- /dev/null
+++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/package-info.java
@@ -0,0 +1,18 @@
+/**
+ * Integration with JBoss Log Manager and {@link java.util.logging}.
+ *
+ * <p>Provides classes that can be used to conveniently configure the JBoss Log Manager ({@link
+ * org.jboss.logmanager.LogManager}) as well as {@link java.util.logging} to use the Google Cloud
+ * JSON Logging formatter ({@link eu.mulk.quarkus.googlecloud.jsonlogging.Formatter}).
+ *
+ * <p>{@link eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultEmbeddedConfigurator} can be
+ * set as a provided implementation of {@link org.jboss.logmanager.EmbeddedConfigurator} via the
+ * standard {@link java.util.ServiceLoader} mechanism.
+ *
+ * <p>{@link eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConsoleHandler} can be used
+ * as the target of the {@code handlers} property key in {@link
+ * java.util.logging.LogManager#readConfiguration(java.io.InputStream)}. This is particularly useful
+ * when used in conjunction with frameworks other than Quarkus (such as Spring Boot). See the class
+ * documentation for details.
+ */
+package eu.mulk.quarkus.googlecloud.jsonlogging.logmanager;
diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java
index 3617b8c..e684bfd 100644
--- a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java
+++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java
@@ -4,7 +4,7 @@
*
* <ul>
* <li><a href="#sect-summary">Summary</a>
- * <li><a href="#sect-activation">Activation</a>
+ * <li><a href="#sect-installation">Installation</a>
* <li><a href="#sect-usage">Usage</a>
* </ul>
*
@@ -17,7 +17,7 @@
* <p>It is possible to log unstructured text, structured data, or a mixture of both depending on
* the situation.
*
- * <h2 id="sect-activation">Installation</h2>
+ * <h2 id="sect-installation">Installation</h2>
*
* <ul>
* <li><a href="#sect-installation-maven">Installation with Maven</a>
diff --git a/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/GoogleCloudLoggingProcessor.java b/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/GoogleCloudLoggingProcessor.java
index 668128f..fe012ee 100644
--- a/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/GoogleCloudLoggingProcessor.java
+++ b/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/GoogleCloudLoggingProcessor.java
@@ -7,18 +7,34 @@ import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.LogConsoleFormatBuildItem;
-class GoogleCloudLoggingProcessor {
+/**
+ * Registers {@link eu.mulk.quarkus.googlecloud.jsonlogging.Formatter} as the formatter for the
+ * embedded JBoss Log Manager.
+ */
+public class GoogleCloudLoggingProcessor {
private static final String FEATURE = "googlecloud-jsonlogging";
+ /**
+ * Returns the feature name of {@code "googlecloud-jsonlogging"}.
+ *
+ * @return the feature {@code "googlecloud-jsonlogging"}
+ */
@BuildStep
- FeatureBuildItem feature() {
+ public FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}
+ /**
+ * Constructs a {@link eu.mulk.quarkus.googlecloud.jsonlogging.Formatter} at runtime and returns
+ * it.
+ *
+ * @param recorder the recorder that implements the construction process at runtime.
+ * @return an instance of {@link eu.mulk.quarkus.googlecloud.jsonlogging.Formatter}.
+ */
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
- LogConsoleFormatBuildItem setUpFormatter(GoogleCloudJsonLoggingRecorder recorder) {
+ public LogConsoleFormatBuildItem setUpFormatter(GoogleCloudJsonLoggingRecorder recorder) {
return new LogConsoleFormatBuildItem(recorder.initialize());
}
}
diff --git a/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/package-info.java b/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/package-info.java
new file mode 100644
index 0000000..eba03ab
--- /dev/null
+++ b/deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/package-info.java
@@ -0,0 +1,2 @@
+/** Quarkus deployment integration for the Google Cloud JSON Logging extension. */
+package eu.mulk.quarkus.googlecloud.jsonlogging.deployment;
diff --git a/example/pom.xml b/examples/quarkus/pom.xml
index 546a47c..a26f782 100644
--- a/example/pom.xml
+++ b/examples/quarkus/pom.xml
@@ -10,8 +10,8 @@
<version>3.1.4-SNAPSHOT</version>
</parent>
- <artifactId>quarkus-googlecloud-jsonlogging-example</artifactId>
- <name>Quarkus Google Cloud JSON Logging Extension - Example</name>
+ <artifactId>quarkus-googlecloud-jsonlogging-quarkus-example</artifactId>
+ <name>Quarkus Google Cloud JSON Logging Extension - Quarkus Example</name>
<dependencies>
<dependency>
diff --git a/example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java b/examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java
index 7e4158c..7e4158c 100644
--- a/example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java
+++ b/examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java
diff --git a/example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java b/examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java
index 6fa42cd..90cd587 100644
--- a/example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java
+++ b/examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java
@@ -32,5 +32,6 @@ public class RootResource {
Label.of("app", "foo"),
KeyValueParameter.of("version", 10));
throw new IllegalStateException();
+ // return "ok";
}
}
diff --git a/example/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider b/examples/quarkus/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider
index c0017e6..c0017e6 100644
--- a/example/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider
+++ b/examples/quarkus/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider
diff --git a/example/src/main/resources/application.properties b/examples/quarkus/src/main/resources/application.properties
index e69de29..e69de29 100644
--- a/example/src/main/resources/application.properties
+++ b/examples/quarkus/src/main/resources/application.properties
diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml
new file mode 100644
index 0000000..29e17d3
--- /dev/null
+++ b/examples/spring-boot/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
+ <artifactId>quarkus-googlecloud-jsonlogging-parent</artifactId>
+ <version>3.1.4-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>quarkus-googlecloud-jsonlogging-spring-boot-example</artifactId>
+ <name>Quarkus Google Cloud JSON Logging Extension - Spring Boot Example</name>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>2.6.2</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.slf4j</groupId>
+ <artifactId>slf4j-jboss-logmanager</artifactId>
+ <version>1.1.0.Final</version>
+ </dependency>
+ <!-- *** optional ***
+ <dependency>
+ <groupId>org.jboss.logmanager</groupId>
+ <artifactId>log4j2-jboss-logmanager</artifactId>
+ <version>1.0.0.Final</version>
+ </dependency>
+ -->
+ <!-- *** optional ***
+ <dependency>
+ <groupId>org.jboss.logmanager</groupId>
+ <artifactId>log4j-jboss-logmanager</artifactId>
+ <version>1.2.2.Final</version>
+ </dependency>
+ -->
+ <!-- *** optional ***
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </dependency>
+ -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </exclusion>
+ <!--
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-logging</artifactId>
+ </exclusion>
+ -->
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
+ <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>2.6.2</version>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/Application.java b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/Application.java
new file mode 100644
index 0000000..d003b3f
--- /dev/null
+++ b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/Application.java
@@ -0,0 +1,12 @@
+package eu.mulk.quarkus.googlecloud.jsonlogging.example;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/ApplicationLoggingSystem.java b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/ApplicationLoggingSystem.java
new file mode 100644
index 0000000..4050b99
--- /dev/null
+++ b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/ApplicationLoggingSystem.java
@@ -0,0 +1,22 @@
+package eu.mulk.quarkus.googlecloud.jsonlogging.example;
+
+import org.springframework.boot.logging.LogFile;
+import org.springframework.boot.logging.LoggingInitializationContext;
+import org.springframework.boot.logging.Slf4JLoggingSystem;
+
+public class ApplicationLoggingSystem extends Slf4JLoggingSystem {
+
+ public ApplicationLoggingSystem(ClassLoader classLoader) {
+ super(classLoader);
+ }
+
+ @Override
+ protected String[] getStandardConfigLocations() {
+ return new String[0];
+ }
+
+ @Override
+ protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
+ /* no configuration necessary */
+ }
+}
diff --git a/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java
new file mode 100644
index 0000000..7e4158c
--- /dev/null
+++ b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java
@@ -0,0 +1,14 @@
+package eu.mulk.quarkus.googlecloud.jsonlogging.example;
+
+import eu.mulk.quarkus.googlecloud.jsonlogging.KeyValueParameter;
+import eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter;
+import eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class RandomNumberParameterProvider implements StructuredParameterProvider {
+
+ @Override
+ public StructuredParameter getParameter() {
+ return KeyValueParameter.of("randomNumber", ThreadLocalRandom.current().nextInt());
+ }
+}
diff --git a/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java
new file mode 100644
index 0000000..b3197fd
--- /dev/null
+++ b/examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java
@@ -0,0 +1,33 @@
+package eu.mulk.quarkus.googlecloud.jsonlogging.example;
+
+import eu.mulk.quarkus.googlecloud.jsonlogging.KeyValueParameter;
+import eu.mulk.quarkus.googlecloud.jsonlogging.Label;
+import javax.annotation.PostConstruct;
+import org.jboss.logging.Logger;
+import org.jboss.logging.MDC;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController("/")
+public class RootResource {
+
+ static final Logger log = Logger.getLogger(RootResource.class);
+
+ @PostConstruct
+ public void init() {
+ log.warn("Hey!");
+ }
+
+ @GetMapping(produces = "text/plain")
+ public String hello() {
+ MDC.put("requestMethod", "GET");
+ log.infof(
+ "Hello %s.",
+ "Mulkiatsch",
+ KeyValueParameter.of("a", "b"),
+ Label.of("app", "foo"),
+ KeyValueParameter.of("version", 10));
+ throw new IllegalStateException();
+ // return "ok";
+ }
+}
diff --git a/examples/spring-boot/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider b/examples/spring-boot/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider
new file mode 100644
index 0000000..c0017e6
--- /dev/null
+++ b/examples/spring-boot/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider
@@ -0,0 +1 @@
+eu.mulk.quarkus.googlecloud.jsonlogging.example.RandomNumberParameterProvider
diff --git a/examples/spring-boot/src/main/resources/META-INF/services/org.jboss.logmanager.EmbeddedConfigurator b/examples/spring-boot/src/main/resources/META-INF/services/org.jboss.logmanager.EmbeddedConfigurator
new file mode 100644
index 0000000..6d937e5
--- /dev/null
+++ b/examples/spring-boot/src/main/resources/META-INF/services/org.jboss.logmanager.EmbeddedConfigurator
@@ -0,0 +1 @@
+eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultEmbeddedConfigurator
diff --git a/examples/spring-boot/src/main/resources/application.properties b/examples/spring-boot/src/main/resources/application.properties
new file mode 100644
index 0000000..c87d7c3
--- /dev/null
+++ b/examples/spring-boot/src/main/resources/application.properties
@@ -0,0 +1 @@
+logging.config = classpath:logging.properties
diff --git a/examples/spring-boot/src/main/resources/logging.properties b/examples/spring-boot/src/main/resources/logging.properties
new file mode 100644
index 0000000..e80e0c6
--- /dev/null
+++ b/examples/spring-boot/src/main/resources/logging.properties
@@ -0,0 +1,2 @@
+handlers = eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConsoleHandler
+.level = INFO