diff options
Diffstat (limited to 'core/src')
7 files changed, 140 insertions, 31 deletions
diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java index 4aa8f9f..0b2003d 100644 --- a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java +++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java @@ -123,6 +123,8 @@ public class Formatter extends ExtFormatter { } } + String insertId = null; + if (logRecord.getParameters() != null) { for (var parameter : logRecord.getParameters()) { if (parameter instanceof StructuredParameter) { @@ -130,6 +132,8 @@ public class Formatter extends ExtFormatter { } else if (parameter instanceof Label) { var label = (Label) parameter; labels.put(label.key(), label.value()); + } else if (parameter instanceof InsertId) { + insertId = ((InsertId) parameter).value(); } } } @@ -151,7 +155,8 @@ public class Formatter extends ExtFormatter { parameters, mdc, ndc, - logRecord.getLevel().intValue() >= 1000 ? ERROR_EVENT_TYPE : null); + logRecord.getLevel().intValue() >= 1000 ? ERROR_EVENT_TYPE : null, + insertId); var b = stringBuilder.get(); b.delete(0, b.length()); diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/InsertId.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/InsertId.java new file mode 100644 index 0000000..b55cf78 --- /dev/null +++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/InsertId.java @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: © 2024 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +package eu.mulk.quarkus.googlecloud.jsonlogging; + +import java.util.Objects; + +/** + * A unique identifier for a log entry. + * + * <p>Prevents the duplicate insertion of log entries. Also serves as a discriminator to order log entries that carry + * the same time stamp. + * + * <p>Will be generated by Google Cloud Logging if not provided. + * + * <p>Instances of {@link InsertId} can be passed as log parameters to the {@code *f} family of logging + * functions on {@link org.jboss.logging.Logger}. + * + * <p><strong>Example:</strong> + * + * {@snippet : + * logger.logf("Request rejected: unauthorized.", InsertId.of("123")); + * } + * + * <p>Result: + * + * {@snippet lang="json" : + * { + * "textPayload": "Request rejected: unauthorized.", + * "logging.googleapis.com/insertId": "123" + * } + * } + * + * @see Label + * @see StructuredParameter + */ +public final class InsertId { + + private final String value; + + private InsertId(String value) { + this.value = value; + } + + /** + * Constructs an {@link InsertId} from a string. + * + * @param value the value of the insertion ID. + * @return the newly constructed {@link InsertId}, ready to be passed to a logging function. + */ + public static InsertId of(String value) { + return new InsertId(value); + } + + /** + * 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 = (InsertId) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "InsertId[value=" + value + ']'; + } +} diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java index 2ea4521..a2c468b 100644 --- a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java +++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java @@ -4,9 +4,9 @@ package eu.mulk.quarkus.googlecloud.jsonlogging; -import jakarta.json.Json; import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonValue; +import jakarta.json.spi.JsonProvider; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Objects; @@ -40,6 +40,8 @@ import java.util.Objects; */ public final class KeyValueParameter implements StructuredParameter { + private static final JsonProvider JSON = JsonProvider.provider(); + private final String key; private final JsonValue value; @@ -58,7 +60,7 @@ public final class KeyValueParameter implements StructuredParameter { * @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)); + return new KeyValueParameter(key, JSON.createValue(value)); } /** @@ -71,7 +73,7 @@ public final class KeyValueParameter implements StructuredParameter { * @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)); + return new KeyValueParameter(key, JSON.createValue(value)); } /** @@ -84,7 +86,7 @@ public final class KeyValueParameter implements StructuredParameter { * @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)); + return new KeyValueParameter(key, JSON.createValue(value)); } /** @@ -97,7 +99,7 @@ public final class KeyValueParameter implements StructuredParameter { * @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)); + return new KeyValueParameter(key, JSON.createValue(value)); } /** @@ -110,7 +112,7 @@ public final class KeyValueParameter implements StructuredParameter { * @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)); + return new KeyValueParameter(key, JSON.createValue(value)); } /** @@ -123,7 +125,7 @@ public final class KeyValueParameter implements StructuredParameter { * @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)); + return new KeyValueParameter(key, JSON.createValue(value)); } /** @@ -141,7 +143,7 @@ public final class KeyValueParameter implements StructuredParameter { @Override public JsonObjectBuilder json() { - return Json.createObjectBuilder().add(key, value); + return JSON.createObjectBuilder().add(key, value); } /** diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java index 19e6a3f..2d08c29 100644 --- a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java +++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java @@ -35,6 +35,7 @@ final class LogEntry { private final Map<String, String> mappedDiagnosticContext; @Nullable private final String nestedDiagnosticContext; @Nullable private final String type; + @Nullable private final String insertId; LogEntry( String message, @@ -47,7 +48,8 @@ final class LogEntry { List<StructuredParameter> parameters, Map<String, String> mappedDiagnosticContext, @Nullable String nestedDiagnosticContext, - @Nullable String type) { + @Nullable String type, + @Nullable String insertId) { this.message = message; this.severity = severity; this.timestamp = timestamp; @@ -59,6 +61,7 @@ final class LogEntry { this.mappedDiagnosticContext = mappedDiagnosticContext; this.nestedDiagnosticContext = nestedDiagnosticContext; this.type = type; + this.insertId = insertId; } static final class SourceLocation { @@ -78,7 +81,7 @@ final class LogEntry { if (file != null) { b.append("\"file\":"); - appendEscapedString(b, file); + appendJsonString(b, file); commaNeeded = true; } @@ -87,7 +90,7 @@ final class LogEntry { b.append(","); } b.append("\"line\":"); - appendEscapedString(b, line); + appendJsonString(b, line); commaNeeded = true; } @@ -96,7 +99,7 @@ final class LogEntry { b.append(","); } b.append("\"function\":"); - appendEscapedString(b, function); + appendJsonString(b, function); } } } @@ -124,21 +127,28 @@ final class LogEntry { } void json(StringBuilder b) { + + if (insertId != null) { + b.append("\"logging.googleapis.com/insertId\":"); + appendJsonString(b, insertId); + b.append(","); + } + if (trace != null) { b.append("\"logging.googleapis.com/trace\":"); - appendEscapedString(b, trace); + appendJsonString(b, trace); b.append(","); } if (spanId != null) { b.append("\"logging.googleapis.com/spanId\":"); - appendEscapedString(b, spanId); + appendJsonString(b, spanId); b.append(","); } if (nestedDiagnosticContext != null && !nestedDiagnosticContext.isEmpty()) { b.append("\"nestedDiagnosticContext\":"); - appendEscapedString(b, nestedDiagnosticContext); + appendJsonString(b, nestedDiagnosticContext); b.append(","); } @@ -153,18 +163,18 @@ final class LogEntry { first = false; } - appendEscapedString(b, entry.getKey()); + appendJsonString(b, entry.getKey()); b.append(":"); - appendEscapedString(b, entry.getValue()); + appendJsonString(b, entry.getValue()); } b.append("},"); } for (var entry : mappedDiagnosticContext.entrySet()) { - appendEscapedString(b, entry.getKey()); + appendJsonString(b, entry.getKey()); b.append(":"); - appendEscapedString(b, entry.getValue()); + appendJsonString(b, entry.getValue()); b.append(","); } @@ -172,7 +182,7 @@ final class LogEntry { var jsonObject = parameter.json().build(); jsonObject.forEach( (key, value) -> { - appendEscapedString(b, key); + appendJsonString(b, key); b.append(":"); appendJsonObject(b, value); b.append(","); @@ -181,7 +191,7 @@ final class LogEntry { if (type != null) { b.append("\"@type\":"); - appendEscapedString(b, type); + appendJsonString(b, type); b.append(","); } @@ -192,17 +202,17 @@ final class LogEntry { } b.append("\"message\":"); - appendEscapedString(b, message); + appendJsonString(b, message); b.append(",\"severity\":"); - appendEscapedString(b, severity); + appendJsonString(b, severity); b.append(",\"timestamp\":{"); timestamp.json(b); b.append("}"); } - private void appendJsonObject(StringBuilder b, JsonValue value) { + private static void appendJsonObject(StringBuilder b, JsonValue value) { switch (value.getValueType()) { case ARRAY: b.append("["); @@ -226,7 +236,7 @@ final class LogEntry { } else { first = false; } - appendEscapedString(b, entry.getKey()); + appendJsonString(b, entry.getKey()); b.append(":"); appendJsonObject(b, entry.getValue()); } @@ -234,7 +244,7 @@ final class LogEntry { break; case STRING: - appendEscapedString(b, ((JsonString) value).getString()); + appendJsonString(b, ((JsonString) value).getString()); break; case NUMBER: @@ -255,7 +265,7 @@ final class LogEntry { } } - private static void appendEscapedString(StringBuilder b, String s) { + private static void appendJsonString(StringBuilder b, String s) { b.append('"'); for (var i = 0; i < s.length(); i++) { diff --git a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java index 61967a0..a84f1fc 100644 --- a/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java +++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/package-info.java @@ -43,7 +43,7 @@ * <dependency> * <groupId>eu.mulk.quarkus-googlecloud-jsonlogging</groupId> * <artifactId>quarkus-googlecloud-jsonlogging-core</artifactId> - * <version>6.4.0</version> + * <version>6.5.0</version> * </dependency> * * ... @@ -59,7 +59,7 @@ * dependencies { * // ... * - * implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.4.0") + * implementation("eu.mulk.quarkus-googlecloud-jsonlogging:quarkus-googlecloud-jsonlogging-core:6.5.0") * * // ... * } diff --git a/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterBenchmark.java b/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterBenchmark.java index 78291c1..c330dfe 100644 --- a/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterBenchmark.java +++ b/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterBenchmark.java @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: © 2024 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: LGPL-3.0-or-later + package eu.mulk.quarkus.googlecloud.jsonlogging; import java.util.List; diff --git a/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterTest.java b/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterTest.java index 16fc537..33df302 100644 --- a/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterTest.java +++ b/core/src/test/java/eu/mulk/quarkus/googlecloud/jsonlogging/FormatterTest.java @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: © 2024 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: LGPL-3.0-or-later + package eu.mulk.quarkus.googlecloud.jsonlogging; import static org.junit.jupiter.api.Assertions.assertLinesMatch; @@ -67,6 +71,7 @@ class FormatterTest { assertLinesMatch( List.of( "\\{" + + "\"logging.googleapis.com/insertId\":\"123-456-789\"," + "\"logging.googleapis.com/labels\":\\{\"a\":\"b\",\"requestId\":\"123\"\\}," + "\"traceId\":\"39f9a49a9567a8bd7087b708f8932550\"," + "\"spanId\":\"c7431b14630b633d\"," @@ -91,7 +96,8 @@ class FormatterTest { new Object[] { (StructuredParameter) () -> JSON.createObjectBuilder().add("one", 1).add("two", 2.0).add("yes", true), - Label.of("a", "b") + Label.of("a", "b"), + InsertId.of("123-456-789"), }); return logRecord; } |