blob: 27d8cf80e4953091cb11ac8fcb75139a7cea73f2 [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>
51 <version>4.0.0</version>
52 </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 Benkard20210242022-01-15 10:39:30 +010062 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:4.0.0")
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +020063}
64----
65
66
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010067== Activation (Other Frameworks)
68
69If you are not using Quarkus, you can still make use of the `-core`
70module and wire it into your application in a custom way. Read this
71section for hints on how to do so.
72
73
74=== Installation
75
76If you are using Maven:
77
78[source,xml]
79----
80<dependencies>
81
82 <dependency>
83 <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
84 <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
85 <version>4.0.0</version>
86 </dependency>
87
88</dependencies>
89----
90
91If you are using Gradle:
92
93[source,groovy]
94----
95dependencies {
96 implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:4.0.0")
97}
98----
99
100
101=== Wiring (Spring Boot)
102
103If you are using Spring Boot, the easiest way to integrate the log
104formatter is by relying on `spring-boot-starter-logging` (which is
105pulled in by `spring-boot-starter`), excluding Logback, and pulling in
106JBoss Log Manager as the back end for SLF4J:
107
108[source,xml]
109----
110
111<dependencies>
112
113 <dependency>
114 <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
115 <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
116 <version>4.0.0</version>
117 </dependency>
118
119 <dependency>
120 <groupId>org.jboss.slf4j</groupId>
121 <artifactId>slf4j-jboss-logmanager</artifactId>
122 <version>1.1.0.Final</version>
123 </dependency>
124
125 <dependency>
126 <groupId>org.springframework.boot</groupId>
127 <artifactId>spring-boot-starter</artifactId>
128 <exclusions>
129 <exclusion>
130 <groupId>ch.qos.logback</groupId>
131 <artifactId>logback-classic</artifactId>
132 </exclusion>
133 </exclusions>
134 </dependency>
135
136</dependencies>
137----
138
139Create a text file called
140`META-INF/services/org.jboss.logmanager.EmbeddedConfigurator` in your
141`resources` directory and put the fully qualified name of
142`DefaultEmbeddedConfigurator` into it:
143
144[source]
145----
146eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.DefaultEmbeddedConfigurator
147----
148
149To configure `java.util.logging`, which is used by Tomcat by default,
150create an entry in `application.properties` that points to
151`logging.properties`:
152
153[source,properties]
154----
155logging.config = classpath:logging.properties
156----
157
158Create the `logging.properties` file in your `resources` directory and
159name `DefaultConsoleHandler` as a handler:
160
161[source,properties]
162----
163handlers = eu.mulk.quarkus.googlecloud.jsonlogging.logmanager.ConsoleHandler
164----
165
166
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200167== Usage
168
169Logging unstructured data requires no code changes. All logs are
170automatically converted to Google-Cloud-Logging-compatible JSON.
171
172Structured data can be logged in one of 3 different ways: by passing
173https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/Label.html[Label]s
174and
175https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.html[StructuredParameter]s
176as parameters to individual log entries, by supplying
177https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.html[LabelProvider]s
178and
179https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.html[StructuredParameterProvider]s,
180or by using the Mapped Diagnostic Context.
181
182
183=== Using Label and StructuredParameter
184
185Instances of
186https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/Label.html[Label]
187and
188https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.html[StructuredParameter]
189can be passed as log parameters to the `*f` family of logging
190functions on JBoss Logging's
191https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html[Logger].
192
193Simple key–value pairs are represented by
194https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.html[KeyValueParameter].
195
196**Example:**
197
198[source,java]
199----
200logger.logf(
201 "Request rejected: unauthorized.",
202 Label.of("requestId", "123"),
203 KeyValueParameter.of("resource", "/users/mulk"),
204 KeyValueParameter.of("method", "PATCH"),
205 KeyValueParameter.of("reason", "invalid token"));
206----
207
208Result:
209
210[source,json]
211----
212{
213 "jsonPayload": {
214 "message": "Request rejected: unauthorized.",
215 "resource": "/users/mulk",
216 "method": "PATCH",
217 "reason": "invalid token"
218 },
219 "labels": {
220 "requestId": "123"
221 }
222}
223----
224
225
226=== Using LabelProvider and StructuredParameterProvider
227
228Any CDI beans that implement
229https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.html[LabelProvider]
230and
231https://javadocs.dev/eu.mulk.quarkus-googlecloud-jsonlogging/quarkus-googlecloud-jsonlogging/3.1.0/eu.mulk.quarkus.googlecloud.jsonlogging/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.html[StructuredParameterProvider]
232are discovered at build time and consulted to provide labels and
233parameters for each message that is logged. This can be used to
234provide contextual information such as tracing and request IDs stored
235in thread-local storage.
236
Matthias Andreas Benkard93ecfd12022-01-15 14:03:41 +0100237Alternatively, you can also register providers through the Java
238https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html[ServiceLoader]
239mechanism.
240
Matthias Andreas Benkard37e804b2021-09-04 22:38:08 +0200241**Example:**
242
243[source,java]
244----
245@Singleton
246@Unremovable
247public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider {
248
249 @Override
250 public StructuredParameter getParameter() {
251 var b = Json.createObjectBuilder();
252 b.add("traceId", Span.current().getSpanContext().getTraceId());
253 b.add("spanId", Span.current().getSpanContext().getSpanId());
254 return () -> b;
255 }
256
257 @Override
258 public Collection<Label> getLabels() {
259 return List.of(Label.of("requestId", "123"));
260 }
261}
262----
263
264Result:
265
266[source,json]
267----
268{
269 "jsonPayload": {
270 "message": "Request rejected: unauthorized.",
271 "traceId": "39f9a49a9567a8bd7087b708f8932550",
272 "spanId": "c7431b14630b633d"
273 },
274 "labels": {
275 "requestId": "123"
276 }
277}
278----
279
280
281=== Using the Mapped Diagnostic Context
282
283Any key–value pairs in JBoss Logging's thread-local
284https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/MDC.html[MDC]
285are added to the resulting JSON.
286
287**Example:**
288
289[source,java]
290----
291MDC.put("resource", "/users/mulk");
292MDC.put("method", "PATCH");
293logger.logf("Request rejected: unauthorized.");
294----
295
296Result:
297
298[source,json]
299----
300{
301 "jsonPayload": {
302 "message": "Request rejected: unauthorized.",
303 "resource": "/users/mulk",
304 "method": "PATCH"
305 }
306}
307----