// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard // // SPDX-License-Identifier: GFDL-1.3-or-later = Quarkus Google Cloud JSON Logging Matthias Andreas Benkard // Meta :experimental: :data-uri: :sectnums: :toc: :stem: :toclevels: 2 :description: Quarkus Google Cloud JSON Logging Manual :keywords: mulk // Settings :icons: font :source-highlighter: rouge Structured logging to standard output according to the Google Cloud Logging specification. == Summary This package contains a log formatter for JBoss Logging in the form of a Quarkus plugin that implements the https://cloud.google.com/logging/docs/structured-logging[Google Cloud Logging JSON format] on standard output. It is possible to log unstructured text, structured data, or a mixture of both depending on the situation. == 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. If you are using Maven: [source,xml] ---- eu.mulk.quarkus-googlecloud-jsonlogging quarkus-googlecloud-jsonlogging 6.1.0 ---- If you are using Gradle: [source,groovy] ---- dependencies { implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:6.1.0") } ---- By default the extension is turned on. You can turn the extension on or off explicitly by configuring the `quarkus.log.console.google` key in `application.properties`: [source,properties] ---- quarkus.log.console.google = true ---- == 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. === Installation If you are using Maven: [source,xml] ---- eu.mulk.quarkus-googlecloud-jsonlogging quarkus-googlecloud-jsonlogging-core 6.1.0 ---- If you are using Gradle: [source,groovy] ---- dependencies { implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0") } ---- === Wiring (java.util.logging) This section explains how to use JBoss Log Manager as the log manager for `java.util.logging` and how to configure it to use the log formatter provided by this library. Add a dependency on JBoss Log Manager and the `-core` module to your project. If you are using Maven: [source,xml] ---- eu.mulk.quarkus-googlecloud-jsonlogging quarkus-googlecloud-jsonlogging-core 6.1.0 org.jboss.logmanager jboss-logmanager 3.0.2.Final ---- If you are using Gradle: [source,groovy] ---- dependencies { implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0") implementation("org.jboss.logmanager:jboss-logmanager:3.0.2.Final") } ---- Create a text file called `org.jboss.logmanager.ConfiguratorFactory` in your `resources/META-INF/services/` directory and put the fully qualified name of this library's `DefaultConfiguratorFactory` into it: [source] ---- eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConfiguratorFactory ---- Finally, set the `java.util.logging.manager` system property to `org.jboss.logmanager.LogManager` when running your application: [source,shell] ---- $ java -Djava.util.logging.manager=org.jboss.logmanager.LogManager ... ---- === 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. In addition, configure JBoss Log Manager as the log manager for `java.util.logging` by setting the `java.util.logging.manager` system property to `org.jboss.logmanager.LogManager`. If you are using Maven: [source,xml] ---- eu.mulk.quarkus-googlecloud-jsonlogging quarkus-googlecloud-jsonlogging-core 6.1.0 org.jboss.slf4j slf4j-jboss-logmanager 2.0.1.Final org.jboss.logmanager jboss-logmanager 3.0.2.Final org.jboss.logmanager jboss-logmanager-embedded 1.2.0.Final org.springframework.boot spring-boot-starter ch.qos.logback logback-classic org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} org.jboss.logmanager.LogManager ---- If you are using Gradle: [source,groovy] ---- configurations { all*.exclude(group: "ch.qos.logback", module: "logback-classic") } dependencies { implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0") implementation("org.jboss.logmanager:jboss-logmanager:3.0.2.Final") implementation("org.jboss.logmanager:jboss-logmanager-embedded:1.2.0.Final") implementation("org.jboss.slf4j:slf4j-jboss-logmanager:2.0.1.Final") } tasks.named("bootRun") { systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager") } ---- Create a text file called `org.jboss.logmanager.ConfiguratorFactory` in your `resources/META-INF/services/` directory and put the fully qualified name of this library's `DefaultConfiguratorFactory` into it: [source] ---- eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConfiguratorFactory ---- Because Spring Boot configures the system logger with a minimum log level of `SEVERE` by default, you may also want to configure the logger using a `logging.properties` file. To do so, first add an entry to `application.properties` that points to the file: [source,properties] ---- logging.config = classpath:logging.properties ---- Create the `logging.properties` file in your `resources` directory and set the root logger level to something other than `SEVERE`: [source,properties] ---- logger.level = INFO ---- Finally, add a `static` block to your Spring Boot application class that disables the Tomcat URL stream handler factory, which conflicts with the URL stream handler factory registered by the JBoss Modules library: [source,java] ---- @SpringBootApplication public class Application { static { TomcatURLStreamHandlerFactory.disable(); } // ... } ---- == Usage Logging unstructured data requires no code changes. All logs are automatically converted to Google-Cloud-Logging-compatible JSON. Structured data can be logged in one of 3 different ways: by passing https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/Label.html[Label]s and https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.html[StructuredParameter]s as parameters to individual log entries, by supplying https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.html[LabelProvider]s and https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.html[StructuredParameterProvider]s, or by using the Mapped Diagnostic Context. === Using Label and StructuredParameter Instances of https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/Label.html[Label] and https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.html[StructuredParameter] can be passed as log parameters to the `*f` family of logging functions on JBoss Logging's https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html[Logger]. Simple key–value pairs are represented by https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.html[KeyValueParameter]. **Example:** [source,java] ---- logger.logf( "Request rejected: unauthorized.", Label.of("requestId", "123"), KeyValueParameter.of("resource", "/users/mulk"), KeyValueParameter.of("method", "PATCH"), KeyValueParameter.of("reason", "invalid token")); ---- Result: [source,json] ---- { "jsonPayload": { "message": "Request rejected: unauthorized.", "resource": "/users/mulk", "method": "PATCH", "reason": "invalid token" }, "labels": { "requestId": "123" } } ---- === Using LabelProvider and StructuredParameterProvider Any CDI beans that implement https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.html[LabelProvider] and https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging-core/6.1.0/eu.mulk.quarkus.googlecloud.jsonlogging.core/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.html[StructuredParameterProvider] are discovered at build time and consulted to provide labels and parameters for each message that is logged. This can be used to provide contextual information such as tracing and request IDs stored in thread-local storage. Alternatively, you can also register providers through the Java https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html[ServiceLoader] mechanism. **Example:** [source,java] ---- @Singleton @Unremovable public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider { @Override public StructuredParameter getParameter() { var b = Json.createObjectBuilder(); b.add("traceId", Span.current().getSpanContext().getTraceId()); b.add("spanId", Span.current().getSpanContext().getSpanId()); return () -> b; } @Override public Collection