Add a Spring Boot example and integration code.

Change-Id: Ia11dea607c74d9b4cc9a698e9ec92e930bd03f37
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.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 @@
   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 @@
    *
    * @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>