diff options
Diffstat (limited to 'runtime/src/main')
8 files changed, 396 insertions, 8 deletions
| diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java index c8bb310..066f709 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java @@ -32,6 +32,12 @@ public class Formatter extends ExtFormatter {    private final List<StructuredParameterProvider> parameterProviders;    private final List<LabelProvider> labelProviders; +  /** +   * Constructs a {@link Formatter}. +   * +   * @param parameterProviders the {@link StructuredParameterProvider}s to apply to each log entry. +   * @param labelProviders the {@link LabelProvider}s to apply to each log entry. +   */    public Formatter(        Collection<StructuredParameterProvider> parameterProviders,        Collection<LabelProvider> labelProviders) { diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java index a26f4da..7234a71 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java @@ -3,12 +3,22 @@ package eu.mulk.quarkus.googlecloud.jsonlogging;  import io.quarkus.arc.Arc;  import io.quarkus.runtime.RuntimeValue;  import io.quarkus.runtime.annotations.Recorder; +import java.util.Collection;  import java.util.Optional;  import java.util.stream.Collectors;  /** A Quarkus recorder that registers {@link Formatter} as a log formatter for the application. */  @Recorder  public class GoogleCloudJsonLoggingRecorder { + +  /** +   * Registers {@link Formatter} as a log formatter for the application. +   * +   * <p>Collects all discoverable {@link StructuredParameterProvider}s and {@link LabelProvider}s +   * and passes them to {@link Formatter#Formatter(Collection, Collection)}. +   * +   * @return the registered {@link Formatter}. +   */    public RuntimeValue<Optional<java.util.logging.Formatter>> initialize() {      var parameterProviders = diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java index 173d9e2..a5924b4 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java @@ -8,18 +8,29 @@ import javax.json.JsonObjectBuilder;  import javax.json.JsonValue;  /** - * A simple single key--value pair forming a {@link StructuredParameter}. + * A simple single key–value pair forming a {@link StructuredParameter}.   * - * <p>This class is suitable for the common case of logging a key—value pair as parameter to the + * <p>This class is suitable for the common case of logging a key–value pair as parameter to the   * {@code *f} family of logging functions on {@link org.jboss.logging.Logger}. For advanced use   * cases, provide your own implementation of {@link StructuredParameter}.   * - * <p>Example: + * <p><strong>Example:</strong>   *   * <pre>{@code   * logger.infof("Application starting.", StructuredParameter.of("version", "1.0"));   * }</pre>   * + * Result: + * + * <pre>{@code + * { + *   "jsonPayload": { + *     "message": "Application starting.", + *     "version": "1.0" + *   } + * } + * }</pre> + *   * @see Label   * @see StructuredParameter   */ @@ -33,30 +44,93 @@ public final class KeyValueParameter implements StructuredParameter {      this.value = value;    } +  /** +   * Creates a {@link KeyValueParameter} from a {@link String} value. +   * +   * <p>The resulting JSON value is of type {@code string}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, String value) {      return new KeyValueParameter(key, Json.createValue(value));    } +  /** +   * Creates a {@link KeyValueParameter} from an {@code int} value. +   * +   * <p>The resulting JSON value is of type {@code number}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, int value) {      return new KeyValueParameter(key, Json.createValue(value));    } +  /** +   * Creates a {@link KeyValueParameter} from a {@code long} value. +   * +   * <p>The resulting JSON value is of type {@code number}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, long value) {      return new KeyValueParameter(key, Json.createValue(value));    } +  /** +   * Creates a {@link KeyValueParameter} from a {@code double} value. +   * +   * <p>The resulting JSON value is of type {@code number}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, double value) {      return new KeyValueParameter(key, Json.createValue(value));    } +  /** +   * Creates a {@link KeyValueParameter} from a {@link BigDecimal} value. +   * +   * <p>The resulting JSON value is of type {@code number}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, BigDecimal value) {      return new KeyValueParameter(key, Json.createValue(value));    } +  /** +   * Creates a {@link KeyValueParameter} from a {@link BigInteger} value. +   * +   * <p>The resulting JSON value is of type {@code number}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, BigInteger value) {      return new KeyValueParameter(key, Json.createValue(value));    } +  /** +   * Creates a {@link KeyValueParameter} from a {@code boolean} value. +   * +   * <p>The resulting JSON value is of type {@code boolean}. +   * +   * @param key the key part of the key–value pair. +   * @param value the value part of the key–value pair. +   * @return the newly constructed parameter, ready to be passed to a logging function. +   */    public static KeyValueParameter of(String key, boolean value) {      return new KeyValueParameter(key, value ? JsonValue.TRUE : JsonValue.FALSE);    } @@ -66,10 +140,23 @@ public final class KeyValueParameter implements StructuredParameter {      return Json.createObjectBuilder().add(key, value);    } +  /** +   * The key part of the key–value pair. +   * +   * @return the key part of the key–value pair. +   */    public String key() {      return key;    } +  /** +   * The value part of the key–value pair. +   * +   * <p>Can be of any non-composite JSON type (i.e. {@code string}, {@code number}, or {@code +   * boolean}). +   * +   * @return the value pairt of the key–value pair. +   */    public JsonValue value() {      return value;    } diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Label.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Label.java index 72929f1..33664dd 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Label.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Label.java @@ -8,12 +8,23 @@ import java.util.Objects;   * <p>Instances of {@link Label} can be passed as log parameters to the {@code *f} family of logging   * functions on {@link org.jboss.logging.Logger}.   * - * <p>Example: + * <p><strong>Example:</strong>   *   * <pre>{@code   * logger.logf("Request rejected: unauthorized.", Label.of("requestId", "123"));   * }</pre>   * + * Result: + * + * <pre>{@code + * { + *   "textPayload": "Request rejected: unauthorized.", + *   "labels": { + *     "requestId": "123" + *   } + * } + * }</pre> + *   * @see KeyValueParameter   * @see StructuredParameter   */ @@ -27,14 +38,37 @@ public final class Label {      this.value = value;    } +  /** +   * Constructs a {@link Label} from a key (i.e. name) and a value. +   * +   * <p>It is often useful for the key to be a {@link String} constant that is shared by multiple +   * parts of the program. +   * +   * @param key the key (name) of the label. +   * @param value the value of the label. +   * @return the newly constructed {@link Label}, ready to be passed to a logging function. +   */    public static Label of(String key, String value) {      return new Label(key, value);    } +  /** +   * The name of the label. +   * +   * <p>It is often useful for this to be a {@link String} constant that is shared by multiple parts +   * of the program. +   * +   * @return the name of the label. +   */    public String key() {      return key;    } +  /** +   * The value of the label. +   * +   * @return the value of the label. +   */    public String value() {      return value;    } diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.java index ef31bcc..dbd035c 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.java @@ -7,10 +7,39 @@ import java.util.Collection;   *   * <p>Any CDI beans registered under this class are applied to each log entry that is logged.   * + * <p><strong>Example:</strong> + * + * <pre>{@code + * @Singleton + * @Unremovable + * public final class RequestIdLabelProvider implements LabelProvider { + * + *   @Override + *   public Collection<Label> getLabels() { + *     return List.of(Label.of("requestId", RequestContext.current().getRequestId())); + *   } + * } + * }</pre> + * + * Result: + * + * <pre>{@code + * { + *   "textPayload": "Request rejected: unauthorized.", + *   "labels": { + *     "requestId": "123" + *   } + * } + * }</pre> + *   * @see StructuredParameterProvider   */  public interface LabelProvider { -  /** Provides a collection of {@link Label}s to add to each log entry that is logged. */ +  /** +   * Provides a collection of {@link Label}s to add to each log entry that is logged. +   * +   * @return a collection of {@link Label}s to add to each log entry that is logged. +   */    Collection<Label> getLabels();  } diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.java index fa326d5..c718080 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.java @@ -23,10 +23,12 @@ import javax.json.JsonObjectBuilder;  public interface StructuredParameter {    /** -   * The JSON to be embedded in the log entry. +   * The JSON to be embedded in the payload of the log entry.     *     * <p>May contain multiple keys and values as well as nested objects. Each top-level entry of the -   * returned object is embedded as a top-level entry in the log entry. +   * returned object is embedded as a top-level entry in the payload of the log entry. +   * +   * @return A {@link JsonObjectBuilder} holding a set of key–value pairs.     */    JsonObjectBuilder json();  } diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.java index d8ab39b..cacfea6 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.java @@ -5,10 +5,47 @@ package eu.mulk.quarkus.googlecloud.jsonlogging;   *   * <p>Any CDI beans registered under this class are applied to each log entry that is logged.   * + * <p><strong>Example:</strong> + * + * <pre>{@code + * @Singleton + * @Unremovable + * public final class TraceLogParameterProvider implements StructuredParameterProvider { + * + *   @Override + *   public StructuredParameter getParameter() { + *     var b = Json.createObjectBuilder(); + *     b.add("traceId", Span.current().getSpanContext().getTraceId()); + *     b.add("spanId", Span.current().getSpanContext().getSpanId()); + *     return () -> b; + *   } + * } + * }</pre> + * + * Result: + * + * <pre>{@code + * { + *   "jsonPayload": { + *     "message": "Request rejected: unauthorized.", + *     "traceId": "39f9a49a9567a8bd7087b708f8932550", + *     "spanId": "c7431b14630b633d" + *   } + * } + * }</pre> + *   * @see LabelProvider   */  public interface StructuredParameterProvider { -  /** Provides a {@link StructuredParameter} to add to each log entry that is logged. */ +  /** +   * Provides a {@link StructuredParameter} to add to each log entry that is logged. +   * +   * <p>It is often useful to return a custom {@link StructuredParameter} rather than a {@link +   * KeyValueParameter} from this method. This way multiple key–value pairs can be generated by a +   * single invocation. +   * +   * @return a {@link StructuredParameter} to add to each log entry that is logged. +   */    StructuredParameter getParameter();  } diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java index cbb6e16..110ab73 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java @@ -1,5 +1,188 @@  /**   * Provides structured logging to standard output according to the Google Cloud Logging   * specification. + * + * <ul> + *   <li><a href="#sect-summary">Summary</a> + *   <li><a href="#sect-activation">Activation</a> + *   <li><a href="#sect-usage">Usage</a> + * </ul> + * + * <h2 id="sect-summary">Summary</h2> + * + * <p>This package contains a log formatter for JBoss Logging in the form of a Quarkus plugin that + * implements the <a href="https://cloud.google.com/logging/docs/structured-logging">Google Cloud + * Logging JSON format</a> on standard output. + * + * <p>It is possible to log unstructured text, structured data, or a mixture of both depending on + * the situation. + * + * <h2 id="sect-activation">Activation</h2> + * + * <ul> + *   <li><a href="#sect-activation-maven">Activation with Maven</a> + *   <li><a href="#sect-activation-gradle">Activation with Gradle</a> + * </ul> + * + * <p>Add the runtime POM to your dependency list. As long as the JAR is on the classpath at both + * build time and runtime, the log formatter automatically registers itself on startup. + * + * <h3 id="sect-activation-maven">Activation with Maven</h3> + * + * <pre>{@code + * <project> + *   ... + * + *   <dependencies> + *     ... + * + *     <dependency> + *       <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId> + *       <artifactId>quarkus-googlecloud-jsonlogging</artifactId> + *       <version>3.1.0</version> + *     </dependency> + * + *     ... + *   </dependencies> + * + *   ... + * </project> + * }</pre> + * + * <h3 id="sect-activation-gradle">Activation with Gradle</h3> + * + * <pre>{@code + * dependencies { + *   ... + * + *   implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging:3.1.0") + * + *   ... + * } + * }</pre> + * + * <h2 id="sect-usage">Usage</h2> + * + * <ul> + *   <li><a href="#sect-usage-parameter">Using Label and StructuredParameter</a> + *   <li><a href="#sect-usage-provider">Using LabelProvider and StructuredParameterProvider</a> + *   <li><a href="#sect-usage-mdc">Using the Mapped Diagnostic Context</a> + * </ul> + * + * <p>Logging unstructured data requires no code changes. All logs are automatically converted to + * Google-Cloud-Logging-compatible JSON. + * + * <p>Structured data can be logged in one of 3 different ways: by passing {@link + * eu.mulk.quarkus.googlecloud.jsonlogging.Label}s and {@link + * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter}s as parameters to individual log + * entries, by supplying {@link eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider}s and {@link + * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider}s, or by using the Mapped + * Diagnostic Context. + * + * <h3 id="sect-usage-parameter">Using Label and StructuredParameter</h3> + * + * <p>Instances of {@link eu.mulk.quarkus.googlecloud.jsonlogging.Label} and {@link + * eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameter} can be passed as log parameters to + * the {@code *f} family of logging functions on JBoss Logging's {@link org.jboss.logging.Logger}. + * + * <p>Simple key–value pairs are represented by {@link + * eu.mulk.quarkus.googlecloud.jsonlogging.KeyValueParameter}. + * + * <p><strong>Example:</strong> + * + * <pre>{@code + * logger.logf( + *   "Request rejected: unauthorized.", + *   Label.of("requestId", "123"), + *   KeyValueParameter.of("resource", "/users/mulk"), + *   KeyValueParameter.of("method", "PATCH"), + *   KeyValueParameter.of("reason", "invalid token")); + * }</pre> + * + * Result: + * + * <pre>{@code + * { + *   "jsonPayload": { + *     "message": "Request rejected: unauthorized.", + *     "resource": "/users/mulk", + *     "method": "PATCH", + *     "reason": "invalid token" + *   }, + *   "labels": { + *     "requestId": "123" + *   } + * } + * }</pre> + * + * <h3 id="sect-usage-provider">Using LabelProvider and StructuredParameterProvider</h3> + * + * <p>Any CDI beans that implement {@link eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider}s + * and {@link eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider}s are discovered + * at build time and consulted to provide labels and parameters for each message that is logged. + * This can be used to provide contextual information such as tracing and request IDs stored in + * thread-local storage. + * + * <p><strong>Example:</strong> + * + * <pre>{@code + * @Singleton + * @Unremovable + * public final class TraceLogParameterProvider implements StructuredParameterProvider, LabelProvider { + * + *   @Override + *   public StructuredParameter getParameter() { + *     var b = Json.createObjectBuilder(); + *     b.add("traceId", Span.current().getSpanContext().getTraceId()); + *     b.add("spanId", Span.current().getSpanContext().getSpanId()); + *     return () -> b; + *   } + * + *   @Override + *   public Collection<Label> getLabels() { + *     return List.of(Label.of("requestId", "123")); + *   } + * } + * }</pre> + * + * Result: + * + * <pre>{@code + * { + *   "jsonPayload": { + *     "message": "Request rejected: unauthorized.", + *     "traceId": "39f9a49a9567a8bd7087b708f8932550", + *     "spanId": "c7431b14630b633d" + *   }, + *   "labels": { + *     "requestId": "123" + *   } + * } + * }</pre> + * + * <h3 id="sect-usage-mdc">Using the Mapped Diagnostic Context</h3> + * + * <p>Any key–value pairs in JBoss Logging's thread-local {@link org.jboss.logging.MDC} are added to + * the resulting JSON. + * + * <p><strong>Example:</strong> + * + * <pre>{@code + * MDC.put("resource", "/users/mulk"); + * MDC.put("method", "PATCH"); + * logger.logf("Request rejected: unauthorized."); + * }</pre> + * + * Result: + * + * <pre>{@code + * { + *   "jsonPayload": { + *     "message": "Request rejected: unauthorized.", + *     "resource": "/users/mulk", + *     "method": "PATCH" + *   } + * } + * }</pre>   */  package eu.mulk.quarkus.googlecloud.jsonlogging; | 
