diff options
Diffstat (limited to 'runtime')
11 files changed, 82 insertions, 893 deletions
diff --git a/runtime/pom.xml b/runtime/pom.xml index 01c2c2c..beb4bcc 100644 --- a/runtime/pom.xml +++ b/runtime/pom.xml @@ -15,6 +15,12 @@ <dependencies> <dependency> + <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId> + <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> @@ -63,11 +69,10 @@ <plugin> <artifactId>maven-jar-plugin</artifactId> - <version>${jar-plugin.version}</version> <configuration> <archive> <manifestEntries> - <Automatic-Module-Name>eu.mulk.quarkus.googlecloud.jsonlogging</Automatic-Module-Name> + <Automatic-Module-Name>eu.mulk.quarkus.googlecloud.jsonlogging.runtime</Automatic-Module-Name> </manifestEntries> </archive> </configuration> 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 deleted file mode 100644 index 066f709..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java +++ /dev/null @@ -1,141 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.jboss.logmanager.ExtFormatter; -import org.jboss.logmanager.ExtLogRecord; - -/** - * Formats log records as JSON for consumption by Google Cloud Logging. - * - * <p>Meant to be used in containers running on Google Kubernetes Engine (GKE). - * - * @see LogEntry - */ -public class Formatter extends ExtFormatter { - - private static final String TRACE_LEVEL = "TRACE"; - private static final String DEBUG_LEVEL = "DEBUG"; - private static final String INFO_LEVEL = "INFO"; - private static final String WARNING_LEVEL = "WARNING"; - private static final String ERROR_LEVEL = "ERROR"; - - private static final String ERROR_EVENT_TYPE = - "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent"; - - 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) { - this.parameterProviders = List.copyOf(parameterProviders); - this.labelProviders = List.copyOf(labelProviders); - } - - @Override - public String format(ExtLogRecord logRecord) { - var message = formatMessageWithStackTrace(logRecord); - - List<StructuredParameter> parameters = new ArrayList<>(); - Map<String, String> labels = new HashMap<>(); - - for (var parameterProvider : parameterProviders) { - var parameter = parameterProvider.getParameter(); - if (parameter != null) { - parameters.add(parameter); - } - } - - for (var labelProvider : labelProviders) { - var providedLabels = labelProvider.getLabels(); - if (providedLabels != null) { - for (var label : providedLabels) { - labels.put(label.key(), label.value()); - } - } - } - - if (logRecord.getParameters() != null) { - for (var parameter : logRecord.getParameters()) { - if (parameter instanceof StructuredParameter) { - parameters.add((StructuredParameter) parameter); - } else if (parameter instanceof Label) { - var label = (Label) parameter; - labels.put(label.key(), label.value()); - } - } - } - - var mdc = logRecord.getMdcCopy(); - var ndc = logRecord.getNdc(); - - var sourceLocation = - new LogEntry.SourceLocation( - logRecord.getSourceFileName(), - String.valueOf(logRecord.getSourceLineNumber()), - String.format( - "%s.%s", logRecord.getSourceClassName(), logRecord.getSourceMethodName())); - - var entry = - new LogEntry( - message, - severityOf(logRecord.getLevel()), - new LogEntry.Timestamp(logRecord.getInstant()), - null, - null, - sourceLocation, - labels, - parameters, - mdc, - ndc, - logRecord.getLevel().intValue() >= 1000 ? ERROR_EVENT_TYPE : null); - - return entry.json().build().toString() + "\n"; - } - - /** - * Formats the log message corresponding to {@code logRecord} including a stack trace of the - * {@link ExtLogRecord#getThrown()} exception if any. - */ - private String formatMessageWithStackTrace(ExtLogRecord logRecord) { - var messageStringWriter = new StringWriter(); - var messagePrintWriter = new PrintWriter(messageStringWriter); - messagePrintWriter.append(this.formatMessage(logRecord)); - - if (logRecord.getThrown() != null) { - messagePrintWriter.println(); - logRecord.getThrown().printStackTrace(messagePrintWriter); - } - - messagePrintWriter.close(); - return messageStringWriter.toString(); - } - - /** Computes the Google Cloud Logging severity corresponding to a given {@link Level}. */ - private static String severityOf(Level level) { - if (level.intValue() < 500) { - return TRACE_LEVEL; - } else if (level.intValue() < 700) { - return DEBUG_LEVEL; - } else if (level.intValue() < 900) { - return INFO_LEVEL; - } else if (level.intValue() < 1000) { - return WARNING_LEVEL; - } else { - return ERROR_LEVEL; - } - } -} 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 deleted file mode 100644 index a5924b4..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java +++ /dev/null @@ -1,181 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Objects; -import javax.json.Json; -import javax.json.JsonObjectBuilder; -import javax.json.JsonValue; - -/** - * 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 - * {@code *f} family of logging functions on {@link org.jboss.logging.Logger}. For advanced use - * cases, provide your own implementation of {@link StructuredParameter}. - * - * <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 - */ -public final class KeyValueParameter implements StructuredParameter { - - private final String key; - private final JsonValue value; - - private KeyValueParameter(String key, JsonValue value) { - this.key = key; - 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); - } - - @Override - public JsonObjectBuilder json() { - 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; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (KeyValueParameter) obj; - return Objects.equals(this.key, that.key) && Objects.equals(this.value, that.value); - } - - @Override - public int hashCode() { - return Objects.hash(key, value); - } - - @Override - public String toString() { - return "KeyValueParameter[" + "key=" + key + ", " + "value=" + 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 deleted file mode 100644 index 33664dd..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Label.java +++ /dev/null @@ -1,93 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -import java.util.Objects; - -/** - * A label usable to tag a log message. - * - * <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><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 - */ -public final class Label { - - private final String key; - private final String value; - - private Label(String key, String value) { - this.key = key; - 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; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (Label) obj; - return Objects.equals(this.key, that.key) && Objects.equals(this.value, that.value); - } - - @Override - public int hashCode() { - return Objects.hash(key, value); - } - - @Override - public String toString() { - return "Label[" + "key=" + key + ", " + "value=" + 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 deleted file mode 100644 index dbd035c..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LabelProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -import java.util.Collection; - -/** - * A user-supplied provider for {@link Label}s. - * - * <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. - * - * @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/LogEntry.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java deleted file mode 100644 index d108c81..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java +++ /dev/null @@ -1,157 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -import io.smallrye.common.constraint.Nullable; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonObjectBuilder; - -/** - * A JSON log entry compatible with Google Cloud Logging. - * - * <p>Roughly (but not quite) corresponds to Google Cloud Logging's <a - * href="https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry">LogEntry</a> - * structure. - * - * <p>A few of the fields are <a href="https://cloud.google.com/logging/docs/structured-logging"> - * treated specially</a> by the fluentd instance running in Google Kubernetes Engine. All other - * fields end up in the jsonPayload field on the Google Cloud Logging side. - */ -final class LogEntry { - - private final String message; - private final String severity; - private final Timestamp timestamp; - @Nullable private final String trace; - @Nullable private final String spanId; - private final SourceLocation sourceLocation; - private final Map<String, String> labels; - private final List<StructuredParameter> parameters; - private final Map<String, String> mappedDiagnosticContext; - @Nullable private final String nestedDiagnosticContext; - @Nullable private final String type; - - LogEntry( - String message, - String severity, - Timestamp timestamp, - @Nullable String trace, - @Nullable String spanId, - SourceLocation sourceLocation, - Map<String, String> labels, - List<StructuredParameter> parameters, - Map<String, String> mappedDiagnosticContext, - @Nullable String nestedDiagnosticContext, - @Nullable String type) { - this.message = message; - this.severity = severity; - this.timestamp = timestamp; - this.trace = trace; - this.spanId = spanId; - this.sourceLocation = sourceLocation; - this.labels = labels; - this.parameters = parameters; - this.mappedDiagnosticContext = mappedDiagnosticContext; - this.nestedDiagnosticContext = nestedDiagnosticContext; - this.type = type; - } - - static final class SourceLocation { - - @Nullable private final String file; - @Nullable private final String line; - @Nullable private final String function; - - SourceLocation(@Nullable String file, @Nullable String line, @Nullable String function) { - this.file = file; - this.line = line; - this.function = function; - } - - JsonObject json() { - var b = Json.createObjectBuilder(); - - if (file != null) { - b.add("file", file); - } - - if (line != null) { - b.add("line", line); - } - - if (function != null) { - b.add("function", function); - } - - return b.build(); - } - } - - static final class Timestamp { - - private final long seconds; - private final int nanos; - - Timestamp(long seconds, int nanos) { - this.seconds = seconds; - this.nanos = nanos; - } - - Timestamp(Instant t) { - this(t.getEpochSecond(), t.getNano()); - } - - JsonObject json() { - return Json.createObjectBuilder().add("seconds", seconds).add("nanos", nanos).build(); - } - } - - JsonObjectBuilder json() { - var b = Json.createObjectBuilder(); - - if (trace != null) { - b.add("logging.googleapis.com/trace", trace); - } - - if (spanId != null) { - b.add("logging.googleapis.com/spanId", spanId); - } - - if (nestedDiagnosticContext != null && !nestedDiagnosticContext.isEmpty()) { - b.add("nestedDiagnosticContext", nestedDiagnosticContext); - } - - if (!labels.isEmpty()) { - b.add("logging.googleapis.com/labels", jsonOfStringMap(labels)); - } - - if (type != null) { - b.add("@type", type); - } - - return b.add("message", message) - .add("severity", severity) - .add("timestamp", timestamp.json()) - .add("logging.googleapis.com/sourceLocation", sourceLocation.json()) - .addAll(jsonOfStringMap(mappedDiagnosticContext)) - .addAll(jsonOfParameterMap(parameters)); - } - - private static JsonObjectBuilder jsonOfStringMap(Map<String, String> stringMap) { - return stringMap.entrySet().stream() - .reduce( - Json.createObjectBuilder(), - (acc, x) -> acc.add(x.getKey(), x.getValue()), - JsonObjectBuilder::addAll); - } - - private static JsonObjectBuilder jsonOfParameterMap(List<StructuredParameter> parameters) { - return parameters.stream() - .reduce( - Json.createObjectBuilder(), - (acc, p) -> acc.addAll(p.json()), - JsonObjectBuilder::addAll); - } -} 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 deleted file mode 100644 index c718080..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.java +++ /dev/null @@ -1,34 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -import javax.json.JsonObjectBuilder; - -/** - * A structured parameter usable as logging payload. - * - * <p>Any instance of {@link StructuredParameter} can be passed as a log parameter to the {@code *f} - * family of logging functions on {@link org.jboss.logging.Logger}. - * - * <p>Example: - * - * <pre>{@code - * StructuredParameter p1 = ...; - * StructuredParameter p2 = ...; - * - * logger.logf("Something interesting happened.", p1, p2); - * }</pre> - * - * @see KeyValueParameter - * @see Label - */ -public interface StructuredParameter { - - /** - * 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 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 deleted file mode 100644 index cacfea6..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameterProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; - -/** - * A user-supplied provider for {@link StructuredParameter}s. - * - * <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. - * - * <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 deleted file mode 100644 index 110ab73..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java +++ /dev/null @@ -1,188 +0,0 @@ -/** - * 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; diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/runtime/GoogleCloudJsonLoggingRecorder.java index 7234a71..661b69f 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/runtime/GoogleCloudJsonLoggingRecorder.java @@ -1,5 +1,8 @@ -package eu.mulk.quarkus.googlecloud.jsonlogging; +package eu.mulk.quarkus.googlecloud.jsonlogging.runtime; +import eu.mulk.quarkus.googlecloud.jsonlogging.Formatter; +import eu.mulk.quarkus.googlecloud.jsonlogging.LabelProvider; +import eu.mulk.quarkus.googlecloud.jsonlogging.StructuredParameterProvider; import io.quarkus.arc.Arc; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; diff --git a/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/runtime/package-info.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/runtime/package-info.java new file mode 100644 index 0000000..2fa7499 --- /dev/null +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/runtime/package-info.java @@ -0,0 +1,71 @@ +/** + * 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>4.0.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:4.0.0") + * + * ... + * } + * }</pre> + * + * <h2 id="sect-usage">Usage</h2> + * + * <p>See the documentation of the {@link eu.mulk.quarkus.googlecloud.jsonlogging} package for usage + * instructions. + * + * @see eu.mulk.quarkus.googlecloud.jsonlogging + */ +package eu.mulk.quarkus.googlecloud.jsonlogging.runtime; |