blob: 6c4f51beb40ae826924453cb93bce0240713ae96 [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: LGPL-3.0-or-later
4
Matthias Andreas Benkard692f48d2021-08-31 21:06:50 +02005/**
6 * Provides structured logging to standard output according to the Google Cloud Logging
7 * specification.
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +02008 *
9 * <ul>
10 * <li><a href="#sect-summary">Summary</a>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010011 * <li><a href="#sect-installation">Installation</a>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020012 * <li><a href="#sect-usage">Usage</a>
13 * </ul>
14 *
15 * <h2 id="sect-summary">Summary</h2>
16 *
17 * <p>This package contains a log formatter for JBoss Logging in the form of a Quarkus plugin that
18 * implements the <a href="https://cloud.google.com/logging/docs/structured-logging">Google Cloud
19 * Logging JSON format</a> on standard output.
20 *
21 * <p>It is possible to log unstructured text, structured data, or a mixture of both depending on
22 * the situation.
23 *
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010024 * <h2 id="sect-installation">Installation</h2>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020025 *
26 * <ul>
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010027 * <li><a href="#sect-installation-maven">Installation with Maven</a>
28 * <li><a href="#sect-installation-gradle">Installation with Gradle</a>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020029 * </ul>
30 *
31 * <p>Add the runtime POM to your dependency list. As long as the JAR is on the classpath at both
32 * build time and runtime, the log formatter automatically registers itself on startup.
33 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010034 * <h3 id="sect-installation-maven">Installation with Maven</h3>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020035 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020036 * {@snippet lang="xml" :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020037 * <project>
38 * ...
39 *
40 * <dependencies>
41 * ...
42 *
43 * <dependency>
44 * <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010045 * <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
Matthias Andreas Benkard62ee2f52022-10-05 07:57:02 +020046 * <version>5.0.0</version>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020047 * </dependency>
48 *
49 * ...
50 * </dependencies>
51 *
52 * ...
53 * </project>
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020054 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020055 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010056 * <h3 id="sect-installation-gradle">Installation with Gradle</h3>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020057 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020058 * {@snippet lang="groovy" :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020059 * dependencies {
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020060 * // ...
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020061 *
Matthias Andreas Benkard62ee2f52022-10-05 07:57:02 +020062 * implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:5.0.0")
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020063 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020064 * // ...
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020065 * }
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020066 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020067 *
68 * <h2 id="sect-usage">Usage</h2>
69 *
70 * <ul>
71 * <li><a href="#sect-usage-parameter">Using Label and StructuredParameter</a>
72 * <li><a href="#sect-usage-provider">Using LabelProvider and StructuredParameterProvider</a>
73 * <li><a href="#sect-usage-mdc">Using the Mapped Diagnostic Context</a>
74 * </ul>
75 *
76 * <p>Logging unstructured data requires no code changes. All logs are automatically converted to
77 * Google-Cloud-Logging-compatible JSON.
78 *
79 * <p>Structured data can be logged in one of 3 different ways: by passing {@link
80 * eu.mulk.quarkus.googlecloud.jsonlogging.Label}s and {@link
81 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter}s as parameters to individual log
82 * entries, by supplying {@link eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider}s and {@link
83 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider}s, or by using the Mapped
84 * Diagnostic Context.
85 *
86 * <h3 id="sect-usage-parameter">Using Label and StructuredParameter</h3>
87 *
88 * <p>Instances of {@link eu.mulk.quarkus.googlecloud.jsonlogging.Label} and {@link
89 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter} can be passed as log parameters to
90 * the {@code *f} family of logging functions on JBoss Logging's {@link org.jboss.logging.Logger}.
91 *
92 * <p>Simple key–value pairs are represented by {@link
93 * eu.mulk.quarkus.googlecloud.jsonlogging.KeyValueParameter}.
94 *
95 * <p><strong>Example:</strong>
96 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +020097 * {@snippet :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020098 * logger.logf(
99 * "Request rejected: unauthorized.",
100 * Label.of("requestId", "123"),
101 * KeyValueParameter.of("resource", "/users/mulk"),
102 * KeyValueParameter.of("method", "PATCH"),
103 * KeyValueParameter.of("reason", "invalid token"));
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200104 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200105 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200106 * <p>Result:
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200107 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200108 * {@snippet lang="json" :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200109 * {
110 * "jsonPayload": {
111 * "message": "Request rejected: unauthorized.",
112 * "resource": "/users/mulk",
113 * "method": "PATCH",
114 * "reason": "invalid token"
115 * },
116 * "labels": {
117 * "requestId": "123"
118 * }
119 * }
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200120 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200121 *
122 * <h3 id="sect-usage-provider">Using LabelProvider and StructuredParameterProvider</h3>
123 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +0100124 * <p>If you pass {@link eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider}s and {@link
125 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider}s to {@link
126 * eu.mulk.quarkus.googlecloud.jsonlogging.Formatter}, then they are consulted to provide labels and
127 * parameters for each message that is logged. This can be used to provide contextual information
128 * such as tracing and request IDs stored in thread-local storage.
129 *
Matthias Andreas Benkard93ecfd12022-01-15 14:03:41 +0100130 * <p><strong>Service provider support:</strong> Providers can be registered using the {@link
131 * java.util.ServiceLoader} mechanism, in which case {@link
132 * eu.mulk.quarkus.googlecloud.jsonlogging.Formatter#load} picks them up automatically.
133 *
134 * <p><strong>CDI support:</strong> If you are using the Quarkus extension, CDI beans that implement
135 * one of the provider interfaces are automatically detected at build time and passed to the
136 * formatter on startup. In addition, providers using the {@link java.util.ServiceLoader} mechanism
137 * are detected and passed to the formatter as well.
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200138 *
139 * <p><strong>Example:</strong>
140 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200141 * {@snippet :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200142 * @Singleton
143 * @Unremovable
144 * public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider {
145 *
146 * @Override
147 * public StructuredParameter getParameter() {
148 * var b = Json.createObjectBuilder();
149 * b.add("traceId", Span.current().getSpanContext().getTraceId());
150 * b.add("spanId", Span.current().getSpanContext().getSpanId());
151 * return () -> b;
152 * }
153 *
154 * @Override
155 * public Collection<Label> getLabels() {
156 * return List.of(Label.of("requestId", "123"));
157 * }
158 * }
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200159 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200160 *
161 * Result:
162 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200163 * {@snippet lang="json" :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200164 * {
165 * "jsonPayload": {
166 * "message": "Request rejected: unauthorized.",
167 * "traceId": "39f9a49a9567a8bd7087b708f8932550",
168 * "spanId": "c7431b14630b633d"
169 * },
170 * "labels": {
171 * "requestId": "123"
172 * }
173 * }
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200174 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200175 *
176 * <h3 id="sect-usage-mdc">Using the Mapped Diagnostic Context</h3>
177 *
178 * <p>Any key–value pairs in JBoss Logging's thread-local {@link org.jboss.logging.MDC} are added to
179 * the resulting JSON.
180 *
181 * <p><strong>Example:</strong>
182 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200183 * {@snippet :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200184 * MDC.put("resource", "/users/mulk");
185 * MDC.put("method", "PATCH");
186 * logger.logf("Request rejected: unauthorized.");
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200187 * }
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200188 *
189 * Result:
190 *
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200191 * {@snippet lang="json" :
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200192 * {
193 * "jsonPayload": {
194 * "message": "Request rejected: unauthorized.",
195 * "resource": "/users/mulk",
196 * "method": "PATCH"
197 * }
198 * }
Matthias Andreas Benkarde369c512022-04-15 20:54:52 +0200199 * }
Matthias Andreas Benkard692f48d2021-08-31 21:06:50 +0200200 */
201package eu.mulk.quarkus.googlecloud.jsonlogging;