From 348f2055bfb000633bb80e89bcf62ea037a5af0b Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sat, 15 Jan 2022 16:13:01 +0100 Subject: Add a Spring Boot example and integration code. Change-Id: Ia11dea607c74d9b4cc9a698e9ec92e930bd03f37 --- README.adoc | 128 +++++++++++++++++---- .../quarkus/googlecloud/jsonlogging/Formatter.java | 24 +++- .../logmanager/DefaultConsoleHandler.java | 68 +++++++++++ .../logmanager/DefaultEmbeddedConfigurator.java | 46 ++++++++ .../jsonlogging/logmanager/package-info.java | 18 +++ .../googlecloud/jsonlogging/package-info.java | 4 +- .../deployment/GoogleCloudLoggingProcessor.java | 22 +++- .../jsonlogging/deployment/package-info.java | 2 + example/pom.xml | 44 ------- .../example/RandomNumberParameterProvider.java | 14 --- .../jsonlogging/example/RootResource.java | 36 ------ ...lecloud.jsonlogging.StructuredParameterProvider | 1 - example/src/main/resources/application.properties | 0 examples/quarkus/pom.xml | 44 +++++++ .../example/RandomNumberParameterProvider.java | 14 +++ .../jsonlogging/example/RootResource.java | 37 ++++++ ...lecloud.jsonlogging.StructuredParameterProvider | 1 + .../src/main/resources/application.properties | 0 examples/spring-boot/pom.xml | 93 +++++++++++++++ .../jsonlogging/example/Application.java | 12 ++ .../example/ApplicationLoggingSystem.java | 22 ++++ .../example/RandomNumberParameterProvider.java | 14 +++ .../jsonlogging/example/RootResource.java | 33 ++++++ ...lecloud.jsonlogging.StructuredParameterProvider | 1 + .../org.jboss.logmanager.EmbeddedConfigurator | 1 + .../src/main/resources/application.properties | 1 + .../src/main/resources/logging.properties | 2 + 27 files changed, 556 insertions(+), 126 deletions(-) create mode 100644 core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultConsoleHandler.java create mode 100644 core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/DefaultEmbeddedConfigurator.java create mode 100644 core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/logmanager/package-info.java create mode 100644 deployment/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/deployment/package-info.java delete mode 100644 example/pom.xml delete mode 100644 example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java delete mode 100644 example/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java delete mode 100644 example/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider delete mode 100644 example/src/main/resources/application.properties create mode 100644 examples/quarkus/pom.xml create mode 100644 examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java create mode 100644 examples/quarkus/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java create mode 100644 examples/quarkus/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider create mode 100644 examples/quarkus/src/main/resources/application.properties create mode 100644 examples/spring-boot/pom.xml create mode 100644 examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/Application.java create mode 100644 examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/ApplicationLoggingSystem.java create mode 100644 examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RandomNumberParameterProvider.java create mode 100644 examples/spring-boot/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/example/RootResource.java create mode 100644 examples/spring-boot/src/main/resources/META-INF/services/eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider create mode 100644 examples/spring-boot/src/main/resources/META-INF/services/org.jboss.logmanager.EmbeddedConfigurator create mode 100644 examples/spring-boot/src/main/resources/application.properties create mode 100644 examples/spring-boot/src/main/resources/logging.properties 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] ---- - - ... + + + + eu.mulk.quarkus-googlecloud-jsonlogging + quarkus-googlecloud-jsonlogging + 4.0.0 + + + +---- + +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. - - ... - - eu.mulk.quarkus-googlecloud-jsonlogging - quarkus-googlecloud-jsonlogging - 4.0.0 - +=== Installation - ... - +If you are using Maven: - ... - +[source,xml] ---- + + + + eu.mulk.quarkus-googlecloud-jsonlogging + quarkus-googlecloud-jsonlogging-core + 4.0.0 + + +---- -=== 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] +---- + + + + + eu.mulk.quarkus-googlecloud-jsonlogging + quarkus-googlecloud-jsonlogging-core + 4.0.0 + + + + org.jboss.slf4j + slf4j-jboss-logmanager + 1.1.0.Final + + + + org.springframework.boot + spring-boot-starter + + + ch.qos.logback + logback-classic + + + + + +---- + +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 labelProviders; /** - * Constructs a {@link Formatter}. + * Constructs a {@link Formatter} with custom configuration. * *

Note: 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 parameterProviders, Collection 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 loadStructuredParameterProviders() { + return ServiceLoader.load(StructuredParameterProvider.class, Formatter.class.getClassLoader()) + .stream() + .map(Provider::get) + .collect(Collectors.toList()); + } + + private static List 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}. + * + *

Useful as a handler for {@link java.util.logging}. + * + *

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: + * + *

{@code
+ * handlers = eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.ConsoleHandler
+ * }
+ * + *

Note: 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}. + * + *

Usage with Spring Boot

+ * + *

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: + * + *

{@code
+ * logging.config = classpath:logging.properties
+ * }
+ * + *

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: + * + *

{@code
+ * 
+ *   org.springframework.boot
+ *   spring-boot-starter
+ *   
+ *     
+ *       ch.qos.logback
+ *       logback-classic
+ *     
+ *   
+ * 
+ * }
+ * + *

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. + * + *

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}. + * + *

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}). + * + *

{@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. + * + *

{@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 @@ * *

* @@ -17,7 +17,7 @@ *

It is possible to log unstructured text, structured data, or a mixture of both depending on * the situation. * - *

Installation

+ *

Installation

* *