Structured logging to standard output according to the Google Cloud Logging specification.
This package contains a log formatter for JBoss Logging in the form of a Quarkus plugin that implements the 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.
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:
<dependencies>
<dependency>
<groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
<artifactId>quarkus-googlecloud-jsonlogging</artifactId>
<version>6.6.0</version>
</dependency>
</dependencies>
If you are using Gradle:
dependencies {
implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:6.6.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
:
quarkus.log.console.google = true
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.
If you are using Maven:
<dependencies>
<dependency>
<groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
<artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
<version>6.6.0</version>
</dependency>
</dependencies>
If you are using Gradle:
dependencies {
implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.6.0")
}
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:
<dependencies>
<dependency>
<groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
<artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<version>3.0.2.Final</version>
</dependency>
</dependencies>
If you are using Gradle:
dependencies {
implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.6.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:
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:
$ java -Djava.util.logging.manager=org.jboss.logmanager.LogManager ...
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:
<project>
<dependencies>
<dependency>
<groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
<artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>org.jboss.slf4j</groupId>
<artifactId>slf4j-jboss-logmanager</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<version>3.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager-embedded</artifactId>
<version>1.2.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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>
If you are using Gradle:
configurations {
all*.exclude(group: "ch.qos.logback", module: "logback-classic")
}
dependencies {
implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.6.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:
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:
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
:
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:
@SpringBootApplication
public class Application {
static {
TomcatURLStreamHandlerFactory.disable();
}
// ...
}
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 Labels and StructuredParameters as parameters to individual log entries, by supplying LabelProviders and StructuredParameterProviders, or by using the Mapped Diagnostic Context.
Instances of
Label
and
StructuredParameter
can be passed as log parameters to the *f
family of logging
functions on JBoss Logging’s
Logger.
Simple key–value pairs are represented by KeyValueParameter.
Example:
logger.logf(
"Request rejected: unauthorized.",
Label.of("requestId", "123"),
KeyValueParameter.of("resource", "/users/mulk"),
KeyValueParameter.of("method", "PATCH"),
KeyValueParameter.of("reason", "invalid token"));
Result:
{
"jsonPayload": {
"message": "Request rejected: unauthorized.",
"resource": "/users/mulk",
"method": "PATCH",
"reason": "invalid token"
},
"labels": {
"requestId": "123"
}
}
Any CDI beans that implement LabelProvider and 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 ServiceLoader mechanism.
Example:
@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<Label> getLabels() {
return List.of(Label.of("requestId", "123"));
}
}
Result:
{
"jsonPayload": {
"message": "Request rejected: unauthorized.",
"traceId": "39f9a49a9567a8bd7087b708f8932550",
"spanId": "c7431b14630b633d"
},
"labels": {
"requestId": "123"
}
}
Any key–value pairs in JBoss Logging’s thread-local MDC are added to the resulting JSON.
Example:
MDC.put("resource", "/users/mulk");
MDC.put("method", "PATCH");
logger.logf("Request rejected: unauthorized.");
Result:
{
"jsonPayload": {
"message": "Request rejected: unauthorized.",
"resource": "/users/mulk",
"method": "PATCH"
}
}