blob: 6b479cae97427624c033030fa30d112100f611b4 [file] [log] [blame]
Matthias Andreas Benkard80909242022-02-03 20:47:47 +01001// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
2//
3// SPDX-License-Identifier: GFDL-1.3-or-later
4
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +02005= Quarkus Google Cloud JSON Logging
6Matthias Andreas Benkard
7// Meta
8:experimental:
9:data-uri:
10:sectnums:
11:toc:
12:stem:
13:toclevels: 2
14:description: Quarkus Google Cloud JSON Logging Manual
15:keywords: mulk
16// Settings
17:icons: font
18:source-highlighter: rouge
19
20
21Structured logging to standard output according to the Google Cloud
22Logging specification.
23
24
25== Summary
26
27This package contains a log formatter for JBoss Logging in the form of
28a Quarkus plugin that implements the
29https://cloud.google.com/logging/docs/structured-logging[Google Cloud
30Logging JSON format] on standard output.
31
32It is possible to log unstructured text, structured data, or a mixture
33of both depending on the situation.
34
35
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010036== Activation (Quarkus)
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020037
38Add the runtime POM to your dependency list. As long as the JAR is on
39the classpath at both build time and runtime, the log formatter
40automatically registers itself on startup.
41
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010042If you are using Maven:
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020043
44[source,xml]
45----
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010046<dependencies>
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020047
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010048 <dependency>
49 <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
50 <artifactId>quarkus-googlecloud-jsonlogging</artifactId>
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +020051 <version>6.1.0</version>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010052 </dependency>
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020053
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010054</dependencies>
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020055----
56
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010057If you are using Gradle:
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020058
59[source,groovy]
60----
61dependencies {
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +020062 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:6.1.0")
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020063}
64----
65
Matthias Andreas Benkard090b8852022-09-03 18:23:23 +020066By default the extension is turned on. You can turn the extension on
67or off explicitly by configuring the `quarkus.log.console.google` key
68in `application.properties`:
69
70[source,properties]
71----
72quarkus.log.console.google = true
73----
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020074
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +020075
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010076== Activation (Other Frameworks)
77
78If you are not using Quarkus, you can still make use of the `-core`
79module and wire it into your application in a custom way. Read this
80section for hints on how to do so.
81
82
83=== Installation
84
85If you are using Maven:
86
87[source,xml]
88----
89<dependencies>
90
91 <dependency>
92 <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
93 <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +020094 <version>6.1.0</version>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010095 </dependency>
96
97</dependencies>
98----
99
100If you are using Gradle:
101
102[source,groovy]
103----
104dependencies {
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200105 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0")
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100106}
107----
108
109
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200110=== Wiring (java.util.logging)
111
112This section explains how to use JBoss Log Manager as the log manager
113for `java.util.logging` and how to configure it to use the log
114formatter provided by this library.
115
116Add a dependency on JBoss Log Manager and the `-core` module to your
117project.
118
119If you are using Maven:
120
121[source,xml]
122----
123<dependencies>
124
125 <dependency>
126 <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
127 <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
128 <version>6.1.0</version>
129 </dependency>
130
131 <dependency>
132 <groupId>org.jboss.logmanager</groupId>
133 <artifactId>jboss-logmanager</artifactId>
134 <version>3.0.2.Final</version>
135 </dependency>
136
137</dependencies>
138----
139
140If you are using Gradle:
141
142[source,groovy]
143----
144dependencies {
145 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0")
146 implementation("org.jboss.logmanager:jboss-logmanager:3.0.2.Final")
147}
148----
149
150Create a text file called `org.jboss.logmanager.ConfiguratorFactory`
151in your `resources/META-INF/services/` directory and put the fully
152qualified name of this library's `DefaultConfiguratorFactory` into it:
153
154[source]
155----
156eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConfiguratorFactory
157----
158
159Finally, set the `java.util.logging.manager` system property to
160`org.jboss.logmanager.LogManager` when running your application:
161
162[source,shell]
163----
164$ java -Djava.util.logging.manager=org.jboss.logmanager.LogManager ...
165----
166
167
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100168=== Wiring (Spring Boot)
169
170If you are using Spring Boot, the easiest way to integrate the log
171formatter is by relying on `spring-boot-starter-logging` (which is
172pulled in by `spring-boot-starter`), excluding Logback, and pulling in
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200173JBoss Log Manager as the back end for SLF4J. In addition, configure
174JBoss Log Manager as the log manager for `java.util.logging` by
175setting the `java.util.logging.manager` system property to
176`org.jboss.logmanager.LogManager`.
177
178If you are using Maven:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100179
180[source,xml]
181----
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200182<project>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100183
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200184 <dependencies>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100185
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200186 <dependency>
187 <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
188 <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
189 <version>6.1.0</version>
190 </dependency>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100191
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200192 <dependency>
193 <groupId>org.jboss.slf4j</groupId>
194 <artifactId>slf4j-jboss-logmanager</artifactId>
195 <version>2.0.1.Final</version>
196 </dependency>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100197
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200198 <dependency>
199 <groupId>org.jboss.logmanager</groupId>
200 <artifactId>jboss-logmanager</artifactId>
201 <version>3.0.2.Final</version>
202 </dependency>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100203
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200204 <dependency>
Matthias Andreas Benkardbe476482023-09-27 18:06:56 +0200205 <groupId>org.jboss.logmanager</groupId>
206 <artifactId>jboss-logmanager-embedded</artifactId>
207 <version>1.2.0.Final</version>
208 </dependency>
209
210 <dependency>
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200211 <groupId>org.springframework.boot</groupId>
212 <artifactId>spring-boot-starter</artifactId>
213 <exclusions>
214 <exclusion>
215 <groupId>ch.qos.logback</groupId>
216 <artifactId>logback-classic</artifactId>
217 </exclusion>
218 </exclusions>
219 </dependency>
220
221 </dependencies>
222
223 <build>
224 <plugins>
225
226 <plugin>
227 <groupId>org.springframework.boot</groupId>
228 <artifactId>spring-boot-maven-plugin</artifactId>
229 <version>${spring-boot.version}</version>
230 <configuration>
231 <systemPropertyVariables>
232 <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
233 </systemPropertyVariables>
234 </configuration>
235 </plugin>
236
237 </plugins>
238 </build>
239
240</project>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100241----
242
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200243If you are using Gradle:
244
245[source,groovy]
246----
247configurations {
248 all*.exclude(group: "ch.qos.logback", module: "logback-classic")
249}
250
251dependencies {
252 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0")
253 implementation("org.jboss.logmanager:jboss-logmanager:3.0.2.Final")
Matthias Andreas Benkardbe476482023-09-27 18:06:56 +0200254 implementation("org.jboss.logmanager:jboss-logmanager-embedded:1.2.0.Final")
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200255 implementation("org.jboss.slf4j:slf4j-jboss-logmanager:2.0.1.Final")
256}
257
258tasks.named("bootRun") {
259 systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
260}
261----
262
263Create a text file called `org.jboss.logmanager.ConfiguratorFactory`
264in your `resources/META-INF/services/` directory and put the fully
265qualified name of this library's `DefaultConfiguratorFactory` into it:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100266
267[source]
268----
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200269eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConfiguratorFactory
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100270----
271
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200272Because Spring Boot configures the system logger with a minimum log
273level of `SEVERE` by default, you may also want to configure the
274logger using a `logging.properties` file. To do so, first add an
275entry to `application.properties` that points to the file:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100276
277[source,properties]
278----
279logging.config = classpath:logging.properties
280----
281
Matthias Andreas Benkardbe476482023-09-27 18:06:56 +0200282Create the `logging.properties` file in your `resources` directory and
283set the root logger level to something other than `SEVERE`:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100284
285[source,properties]
286----
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200287logger.level = INFO
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100288----
289
Matthias Andreas Benkardbe476482023-09-27 18:06:56 +0200290Finally, add a `static` block to your Spring Boot application class
291that disables the Tomcat URL stream handler factory, which conflicts
292with the URL stream handler factory registered by the JBoss Modules
293library:
294
295[source,java]
296----
297@SpringBootApplication
298public class Application {
299
300 static {
301 TomcatURLStreamHandlerFactory.disable();
302 }
303
304 // ...
305}
306----
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100307
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200308== Usage
309
310Logging unstructured data requires no code changes. All logs are
311automatically converted to Google-Cloud-Logging-compatible JSON.
312
313Structured data can be logged in one of 3 different ways: by passing
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200314https://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
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200315and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200316https://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
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200317as parameters to individual log entries, by supplying
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200318https://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
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200319and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200320https://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,
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200321or by using the Mapped Diagnostic Context.
322
323
324=== Using Label and StructuredParameter
325
326Instances of
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200327https://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]
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200328and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200329https://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]
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200330can be passed as log parameters to the `*f` family of logging
331functions on JBoss Logging's
332https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html[Logger].
333
334Simple key–value pairs are represented by
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200335https://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].
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200336
337**Example:**
338
339[source,java]
340----
341logger.logf(
342 "Request rejected: unauthorized.",
343 Label.of("requestId", "123"),
344 KeyValueParameter.of("resource", "/users/mulk"),
345 KeyValueParameter.of("method", "PATCH"),
346 KeyValueParameter.of("reason", "invalid token"));
347----
348
349Result:
350
351[source,json]
352----
353{
354 "jsonPayload": {
355 "message": "Request rejected: unauthorized.",
356 "resource": "/users/mulk",
357 "method": "PATCH",
358 "reason": "invalid token"
359 },
360 "labels": {
361 "requestId": "123"
362 }
363}
364----
365
366
367=== Using LabelProvider and StructuredParameterProvider
368
369Any CDI beans that implement
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200370https://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]
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200371and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200372https://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]
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200373are discovered at build time and consulted to provide labels and
374parameters for each message that is logged. This can be used to
375provide contextual information such as tracing and request IDs stored
376in thread-local storage.
377
Matthias Andreas Benkard93ecfd12022-01-15 14:03:41 +0100378Alternatively, you can also register providers through the Java
379https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html[ServiceLoader]
380mechanism.
381
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200382**Example:**
383
384[source,java]
385----
386@Singleton
387@Unremovable
388public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider {
389
390 @Override
391 public StructuredParameter getParameter() {
392 var b = Json.createObjectBuilder();
393 b.add("traceId", Span.current().getSpanContext().getTraceId());
394 b.add("spanId", Span.current().getSpanContext().getSpanId());
395 return () -> b;
396 }
397
398 @Override
399 public Collection<Label> getLabels() {
400 return List.of(Label.of("requestId", "123"));
401 }
402}
403----
404
405Result:
406
407[source,json]
408----
409{
410 "jsonPayload": {
411 "message": "Request rejected: unauthorized.",
412 "traceId": "39f9a49a9567a8bd7087b708f8932550",
413 "spanId": "c7431b14630b633d"
414 },
415 "labels": {
416 "requestId": "123"
417 }
418}
419----
420
421
422=== Using the Mapped Diagnostic Context
423
424Any key–value pairs in JBoss Logging's thread-local
425https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/MDC.html[MDC]
426are added to the resulting JSON.
427
428**Example:**
429
430[source,java]
431----
432MDC.put("resource", "/users/mulk");
433MDC.put("method", "PATCH");
434logger.logf("Request rejected: unauthorized.");
435----
436
437Result:
438
439[source,json]
440----
441{
442 "jsonPayload": {
443 "message": "Request rejected: unauthorized.",
444 "resource": "/users/mulk",
445 "method": "PATCH"
446 }
447}
448----