blob: e684bfd8fd56f3562e0f8647e59a410c7fa865ed [file] [log] [blame]
Matthias Andreas Benkard692f48d2021-08-31 21:06:50 +02001/**
2 * Provides structured logging to standard output according to the Google Cloud Logging
3 * specification.
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +02004 *
5 * <ul>
6 * <li><a href="#sect-summary">Summary</a>
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +01007 * <li><a href="#sect-installation">Installation</a>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +02008 * <li><a href="#sect-usage">Usage</a>
9 * </ul>
10 *
11 * <h2 id="sect-summary">Summary</h2>
12 *
13 * <p>This package contains a log formatter for JBoss Logging in the form of a Quarkus plugin that
14 * implements the <a href="https://cloud.google.com/logging/docs/structured-logging">Google Cloud
15 * Logging JSON format</a> on standard output.
16 *
17 * <p>It is possible to log unstructured text, structured data, or a mixture of both depending on
18 * the situation.
19 *
Matthias Andreas Benkard348f2052022-01-15 16:13:01 +010020 * <h2 id="sect-installation">Installation</h2>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020021 *
22 * <ul>
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010023 * <li><a href="#sect-installation-maven">Installation with Maven</a>
24 * <li><a href="#sect-installation-gradle">Installation with Gradle</a>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020025 * </ul>
26 *
27 * <p>Add the runtime POM to your dependency list. As long as the JAR is on the classpath at both
28 * build time and runtime, the log formatter automatically registers itself on startup.
29 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010030 * <h3 id="sect-installation-maven">Installation with Maven</h3>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020031 *
32 * <pre>{@code
33 * <project>
34 * ...
35 *
36 * <dependencies>
37 * ...
38 *
39 * <dependency>
40 * <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId>
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010041 * <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId>
42 * <version>4.0.0</version>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020043 * </dependency>
44 *
45 * ...
46 * </dependencies>
47 *
48 * ...
49 * </project>
50 * }</pre>
51 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010052 * <h3 id="sect-installation-gradle">Installation with Gradle</h3>
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020053 *
54 * <pre>{@code
55 * dependencies {
56 * ...
57 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +010058 * implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:4.0.0")
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +020059 *
60 * ...
61 * }
62 * }</pre>
63 *
64 * <h2 id="sect-usage">Usage</h2>
65 *
66 * <ul>
67 * <li><a href="#sect-usage-parameter">Using Label and StructuredParameter</a>
68 * <li><a href="#sect-usage-provider">Using LabelProvider and StructuredParameterProvider</a>
69 * <li><a href="#sect-usage-mdc">Using the Mapped Diagnostic Context</a>
70 * </ul>
71 *
72 * <p>Logging unstructured data requires no code changes. All logs are automatically converted to
73 * Google-Cloud-Logging-compatible JSON.
74 *
75 * <p>Structured data can be logged in one of 3 different ways: by passing {@link
76 * eu.mulk.quarkus.googlecloud.jsonlogging.Label}s and {@link
77 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter}s as parameters to individual log
78 * entries, by supplying {@link eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider}s and {@link
79 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider}s, or by using the Mapped
80 * Diagnostic Context.
81 *
82 * <h3 id="sect-usage-parameter">Using Label and StructuredParameter</h3>
83 *
84 * <p>Instances of {@link eu.mulk.quarkus.googlecloud.jsonlogging.Label} and {@link
85 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter} can be passed as log parameters to
86 * the {@code *f} family of logging functions on JBoss Logging's {@link org.jboss.logging.Logger}.
87 *
88 * <p>Simple key–value pairs are represented by {@link
89 * eu.mulk.quarkus.googlecloud.jsonlogging.KeyValueParameter}.
90 *
91 * <p><strong>Example:</strong>
92 *
93 * <pre>{@code
94 * logger.logf(
95 * "Request rejected: unauthorized.",
96 * Label.of("requestId", "123"),
97 * KeyValueParameter.of("resource", "/users/mulk"),
98 * KeyValueParameter.of("method", "PATCH"),
99 * KeyValueParameter.of("reason", "invalid token"));
100 * }</pre>
101 *
102 * Result:
103 *
104 * <pre>{@code
105 * {
106 * "jsonPayload": {
107 * "message": "Request rejected: unauthorized.",
108 * "resource": "/users/mulk",
109 * "method": "PATCH",
110 * "reason": "invalid token"
111 * },
112 * "labels": {
113 * "requestId": "123"
114 * }
115 * }
116 * }</pre>
117 *
118 * <h3 id="sect-usage-provider">Using LabelProvider and StructuredParameterProvider</h3>
119 *
Matthias Andreas Benkard20210242022-01-15 10:39:30 +0100120 * <p>If you pass {@link eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider}s and {@link
121 * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider}s to {@link
122 * eu.mulk.quarkus.googlecloud.jsonlogging.Formatter}, then they are consulted to provide labels and
123 * parameters for each message that is logged. This can be used to provide contextual information
124 * such as tracing and request IDs stored in thread-local storage.
125 *
Matthias Andreas Benkard93ecfd12022-01-15 14:03:41 +0100126 * <p><strong>Service provider support:</strong> Providers can be registered using the {@link
127 * java.util.ServiceLoader} mechanism, in which case {@link
128 * eu.mulk.quarkus.googlecloud.jsonlogging.Formatter#load} picks them up automatically.
129 *
130 * <p><strong>CDI support:</strong> If you are using the Quarkus extension, CDI beans that implement
131 * one of the provider interfaces are automatically detected at build time and passed to the
132 * formatter on startup. In addition, providers using the {@link java.util.ServiceLoader} mechanism
133 * are detected and passed to the formatter as well.
Matthias Andreas Benkard42da9f12021-09-02 18:47:28 +0200134 *
135 * <p><strong>Example:</strong>
136 *
137 * <pre>{@code
138 * @Singleton
139 * @Unremovable
140 * public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider {
141 *
142 * @Override
143 * public StructuredParameter getParameter() {
144 * var b = Json.createObjectBuilder();
145 * b.add("traceId", Span.current().getSpanContext().getTraceId());
146 * b.add("spanId", Span.current().getSpanContext().getSpanId());
147 * return () -> b;
148 * }
149 *
150 * @Override
151 * public Collection<Label> getLabels() {
152 * return List.of(Label.of("requestId", "123"));
153 * }
154 * }
155 * }</pre>
156 *
157 * Result:
158 *
159 * <pre>{@code
160 * {
161 * "jsonPayload": {
162 * "message": "Request rejected: unauthorized.",
163 * "traceId": "39f9a49a9567a8bd7087b708f8932550",
164 * "spanId": "c7431b14630b633d"
165 * },
166 * "labels": {
167 * "requestId": "123"
168 * }
169 * }
170 * }</pre>
171 *
172 * <h3 id="sect-usage-mdc">Using the Mapped Diagnostic Context</h3>
173 *
174 * <p>Any key–value pairs in JBoss Logging's thread-local {@link org.jboss.logging.MDC} are added to
175 * the resulting JSON.
176 *
177 * <p><strong>Example:</strong>
178 *
179 * <pre>{@code
180 * MDC.put("resource", "/users/mulk");
181 * MDC.put("method", "PATCH");
182 * logger.logf("Request rejected: unauthorized.");
183 * }</pre>
184 *
185 * Result:
186 *
187 * <pre>{@code
188 * {
189 * "jsonPayload": {
190 * "message": "Request rejected: unauthorized.",
191 * "resource": "/users/mulk",
192 * "method": "PATCH"
193 * }
194 * }
195 * }</pre>
Matthias Andreas Benkard692f48d2021-08-31 21:06:50 +0200196 */
197package eu.mulk.quarkus.googlecloud.jsonlogging;