blob: 76f9eae2f95d405f88c0497063f48b79c954c573 [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>
205 <groupId>org.springframework.boot</groupId>
206 <artifactId>spring-boot-starter</artifactId>
207 <exclusions>
208 <exclusion>
209 <groupId>ch.qos.logback</groupId>
210 <artifactId>logback-classic</artifactId>
211 </exclusion>
212 </exclusions>
213 </dependency>
214
215 </dependencies>
216
217 <build>
218 <plugins>
219
220 <plugin>
221 <groupId>org.springframework.boot</groupId>
222 <artifactId>spring-boot-maven-plugin</artifactId>
223 <version>${spring-boot.version}</version>
224 <configuration>
225 <systemPropertyVariables>
226 <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
227 </systemPropertyVariables>
228 </configuration>
229 </plugin>
230
231 </plugins>
232 </build>
233
234</project>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100235----
236
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200237If you are using Gradle:
238
239[source,groovy]
240----
241configurations {
242 all*.exclude(group: "ch.qos.logback", module: "logback-classic")
243}
244
245dependencies {
246 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.1.0")
247 implementation("org.jboss.logmanager:jboss-logmanager:3.0.2.Final")
248 implementation("org.jboss.slf4j:slf4j-jboss-logmanager:2.0.1.Final")
249}
250
251tasks.named("bootRun") {
252 systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
253}
254----
255
256Create a text file called `org.jboss.logmanager.ConfiguratorFactory`
257in your `resources/META-INF/services/` directory and put the fully
258qualified name of this library's `DefaultConfiguratorFactory` into it:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100259
260[source]
261----
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200262eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultConfiguratorFactory
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100263----
264
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200265Because Spring Boot configures the system logger with a minimum log
266level of `SEVERE` by default, you may also want to configure the
267logger using a `logging.properties` file. To do so, first add an
268entry to `application.properties` that points to the file:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100269
270[source,properties]
271----
272logging.config = classpath:logging.properties
273----
274
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200275Finally, create the `logging.properties` file in your `resources`
276directory and set the root logger level to something other than
277`SEVERE`:
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100278
279[source,properties]
280----
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200281logger.level = INFO
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +0100282----
283
284
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200285== Usage
286
287Logging unstructured data requires no code changes. All logs are
288automatically converted to Google-Cloud-Logging-compatible JSON.
289
290Structured data can be logged in one of 3 different ways: by passing
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200291https://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 +0200292and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200293https://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 +0200294as parameters to individual log entries, by supplying
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200295https://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 +0200296and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200297https://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 +0200298or by using the Mapped Diagnostic Context.
299
300
301=== Using Label and StructuredParameter
302
303Instances of
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200304https://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 +0200305and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200306https://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 +0200307can be passed as log parameters to the `*f` family of logging
308functions on JBoss Logging's
309https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html[Logger].
310
311Simple key–value pairs are represented by
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200312https://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 +0200313
314**Example:**
315
316[source,java]
317----
318logger.logf(
319 "Request rejected: unauthorized.",
320 Label.of("requestId", "123"),
321 KeyValueParameter.of("resource", "/users/mulk"),
322 KeyValueParameter.of("method", "PATCH"),
323 KeyValueParameter.of("reason", "invalid token"));
324----
325
326Result:
327
328[source,json]
329----
330{
331 "jsonPayload": {
332 "message": "Request rejected: unauthorized.",
333 "resource": "/users/mulk",
334 "method": "PATCH",
335 "reason": "invalid token"
336 },
337 "labels": {
338 "requestId": "123"
339 }
340}
341----
342
343
344=== Using LabelProvider and StructuredParameterProvider
345
346Any CDI beans that implement
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200347https://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 +0200348and
Matthias Andreas Benkard663163d2023-09-24 13:46:22 +0200349https://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 +0200350are discovered at build time and consulted to provide labels and
351parameters for each message that is logged. This can be used to
352provide contextual information such as tracing and request IDs stored
353in thread-local storage.
354
Matthias Andreas Benkard93ecfd12022-01-15 14:03:41 +0100355Alternatively, you can also register providers through the Java
356https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html[ServiceLoader]
357mechanism.
358
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200359**Example:**
360
361[source,java]
362----
363@Singleton
364@Unremovable
365public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider {
366
367 @Override
368 public StructuredParameter getParameter() {
369 var b = Json.createObjectBuilder();
370 b.add("traceId", Span.current().getSpanContext().getTraceId());
371 b.add("spanId", Span.current().getSpanContext().getSpanId());
372 return () -> b;
373 }
374
375 @Override
376 public Collection<Label> getLabels() {
377 return List.of(Label.of("requestId", "123"));
378 }
379}
380----
381
382Result:
383
384[source,json]
385----
386{
387 "jsonPayload": {
388 "message": "Request rejected: unauthorized.",
389 "traceId": "39f9a49a9567a8bd7087b708f8932550",
390 "spanId": "c7431b14630b633d"
391 },
392 "labels": {
393 "requestId": "123"
394 }
395}
396----
397
398
399=== Using the Mapped Diagnostic Context
400
401Any key–value pairs in JBoss Logging's thread-local
402https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/MDC.html[MDC]
403are added to the resulting JSON.
404
405**Example:**
406
407[source,java]
408----
409MDC.put("resource", "/users/mulk");
410MDC.put("method", "PATCH");
411logger.logf("Request rejected: unauthorized.");
412----
413
414Result:
415
416[source,json]
417----
418{
419 "jsonPayload": {
420 "message": "Request rejected: unauthorized.",
421 "resource": "/users/mulk",
422 "method": "PATCH"
423 }
424}
425----