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 --- .../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 +- 5 files changed, 153 insertions(+), 7 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 (limited to 'core') 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

* *