diff options
Diffstat (limited to 'runtime/src/main/java/eu')
10 files changed, 191 insertions, 87 deletions
| diff --git a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLoggingFormatter.java b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java index 3ec1fcc..d6eeb0e 100644 --- a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLoggingFormatter.java +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Formatter.java @@ -1,11 +1,12 @@ -package eu.mulk.quarkus.observability.googlecloud.jsonlogging; +package eu.mulk.quarkus.googlecloud.jsonlogging;  import java.io.PrintWriter;  import java.io.StringWriter; +import java.util.ArrayList;  import java.util.HashMap; +import java.util.List; +import java.util.Map;  import java.util.logging.Level; -import javax.json.bind.Jsonb; -import javax.json.bind.JsonbException;  import org.jboss.logmanager.ExtFormatter;  import org.jboss.logmanager.ExtLogRecord; @@ -14,9 +15,9 @@ import org.jboss.logmanager.ExtLogRecord;   *   * <p>Meant to be used in containers running on Google Kubernetes Engine (GKE).   * - * @see GoogleCloudLogEntry + * @see LogEntry   */ -class GoogleCloudLoggingFormatter extends ExtFormatter { +class Formatter extends ExtFormatter {    private static final String TRACE_LEVEL = "TRACE";    private static final String DEBUG_LEVEL = "DEBUG"; @@ -27,22 +28,16 @@ class GoogleCloudLoggingFormatter extends ExtFormatter {    private static final String ERROR_EVENT_TYPE =        "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent"; -  private final Jsonb jsonb; - -  GoogleCloudLoggingFormatter(Jsonb jsonb) { -    this.jsonb = jsonb; -  } -    @Override    public String format(ExtLogRecord logRecord) {      var message = formatMessageWithStackTrace(logRecord); -    var parameters = new HashMap<String, Object>(); -    var labels = new HashMap<String, String>(); +    List<StructuredParameter> parameters = new ArrayList<>(); +    Map<String, String> labels = new HashMap<>();      if (logRecord.getParameters() != null) {        for (var parameter : logRecord.getParameters()) { -        if (parameter instanceof KeyValueParameter kvparam) { -          parameters.put(kvparam.key(), kvparam.value()); +        if (parameter instanceof StructuredParameter sparam) { +          parameters.add(sparam);          } else if (parameter instanceof Label label) {            labels.put(label.key(), label.value());          } @@ -53,32 +48,27 @@ class GoogleCloudLoggingFormatter extends ExtFormatter {      var ndc = logRecord.getNdc();      var sourceLocation = -        new GoogleCloudLogEntry.SourceLocation( +        new LogEntry.SourceLocation(              logRecord.getSourceFileName(),              String.valueOf(logRecord.getSourceLineNumber()),              String.format(                  "%s.%s", logRecord.getSourceClassName(), logRecord.getSourceMethodName()));      var entry = -        new GoogleCloudLogEntry( +        new LogEntry(              message,              severityOf(logRecord.getLevel()), -            new GoogleCloudLogEntry.Timestamp(logRecord.getInstant()), +            new LogEntry.Timestamp(logRecord.getInstant()),              null,              null,              sourceLocation, -            labels.isEmpty() ? null : labels, -            parameters.isEmpty() ? null : parameters, -            mdc.isEmpty() ? null : mdc, -            ndc.isEmpty() ? null : ndc, +            labels, +            parameters, +            mdc, +            ndc,              logRecord.getLevel().intValue() >= 1000 ? ERROR_EVENT_TYPE : null); -    try { -      return jsonb.toJson(entry) + "\n"; -    } catch (JsonbException e) { -      e.printStackTrace(); -      return message + "\n"; -    } +    return entry.json().build().toString() + "\n";    }    /** 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 new file mode 100644 index 0000000..db2c2e9 --- /dev/null +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/GoogleCloudJsonLoggingRecorder.java @@ -0,0 +1,12 @@ +package eu.mulk.quarkus.googlecloud.jsonlogging; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; +import java.util.Optional; + +@Recorder +public class GoogleCloudJsonLoggingRecorder { +  public RuntimeValue<Optional<java.util.logging.Formatter>> initialize() { +    return new RuntimeValue<>(Optional.of(new Formatter())); +  } +} 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 new file mode 100644 index 0000000..5f582c9 --- /dev/null +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/KeyValueParameter.java @@ -0,0 +1,43 @@ +package eu.mulk.quarkus.googlecloud.jsonlogging; + +import java.math.BigDecimal; +import java.math.BigInteger; +import javax.json.Json; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; + +public record KeyValueParameter(String key, JsonValue value) implements StructuredParameter { + +  public static KeyValueParameter of(String key, String value) { +    return new KeyValueParameter(key, Json.createValue(value)); +  } + +  public static KeyValueParameter of(String key, int value) { +    return new KeyValueParameter(key, Json.createValue(value)); +  } + +  public static KeyValueParameter of(String key, long value) { +    return new KeyValueParameter(key, Json.createValue(value)); +  } + +  public static KeyValueParameter of(String key, double value) { +    return new KeyValueParameter(key, Json.createValue(value)); +  } + +  public static KeyValueParameter of(String key, BigDecimal value) { +    return new KeyValueParameter(key, Json.createValue(value)); +  } + +  public static KeyValueParameter of(String key, BigInteger value) { +    return new KeyValueParameter(key, Json.createValue(value)); +  } + +  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); +  } +} 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 new file mode 100644 index 0000000..02f7034 --- /dev/null +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/Label.java @@ -0,0 +1,8 @@ +package eu.mulk.quarkus.googlecloud.jsonlogging; + +public record Label(String key, String value) { + +  public static Label of(String key, String value) { +    return new Label(key, value); +  } +} 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 new file mode 100644 index 0000000..4394033 --- /dev/null +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java @@ -0,0 +1,103 @@ +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 treated specially by the fluentd instance running in Google Kubernetes + * Engine. All other fields end up in the jsonPayload field on the Google Cloud Logging side. + */ +record 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) { + +  static record SourceLocation( +      @Nullable String file, @Nullable String line, @Nullable String function) { + +    JsonObject json() { +      return Json.createObjectBuilder() +          .add("file", file) +          .add("line", line) +          .add("function", function) +          .build(); +    } +  } + +  static record Timestamp(long seconds, int 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("trace", trace); +    } + +    if (spanId != null) { +      b.add("spanId", spanId); +    } + +    if (nestedDiagnosticContext != null && !nestedDiagnosticContext.isEmpty()) { +      b.add("nestedDiagnosticContext", nestedDiagnosticContext); +    } + +    if (!labels.isEmpty()) { +      b.add("labels", jsonOfStringMap(labels)); +    } + +    if (type != null) { +      b.add("@type", type); +    } + +    return b.add("message", message) +        .add("severity", severity) +        .add("timestamp", timestamp.json()) +        .add("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 new file mode 100644 index 0000000..0b4a36e --- /dev/null +++ b/runtime/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/StructuredParameter.java @@ -0,0 +1,7 @@ +package eu.mulk.quarkus.googlecloud.jsonlogging; + +import javax.json.JsonObjectBuilder; + +public interface StructuredParameter { +  JsonObjectBuilder json(); +} diff --git a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLogEntry.java b/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLogEntry.java deleted file mode 100644 index 0450d0c..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLogEntry.java +++ /dev/null @@ -1,37 +0,0 @@ -package eu.mulk.quarkus.observability.googlecloud.jsonlogging; - -import io.smallrye.common.constraint.Nullable; -import java.time.Instant; -import java.util.Map; -import javax.json.bind.annotation.JsonbProperty; - -/** - * 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. - */ -public record GoogleCloudLogEntry( -    String getMessage, -    String getSeverity, -    Timestamp getTimestamp, -    @Nullable String getTrace, -    @Nullable String getSpanId, -    @Nullable SourceLocation getSourceLocation, -    @Nullable Map<String, String> getLabels, -    @Nullable Map<String, Object> getParameters, -    @Nullable Map<String, String> getMappedDiagnosticContext, -    @Nullable String getNestedDiagnosticContext, -    @Nullable @JsonbProperty("@type") String getType) { - -  public static record SourceLocation( -      @Nullable String getFile, @Nullable String getLine, @Nullable String getFunction) {} - -  public static record Timestamp(long getSeconds, int getNanos) { - -    public Timestamp(Instant t) { -      this(t.getEpochSecond(), t.getNano()); -    } -  } -} diff --git a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLoggingRecorder.java b/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLoggingRecorder.java deleted file mode 100644 index 9ae3ae1..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/GoogleCloudLoggingRecorder.java +++ /dev/null @@ -1,16 +0,0 @@ -package eu.mulk.quarkus.observability.googlecloud.jsonlogging; - -import io.quarkus.runtime.RuntimeValue; -import io.quarkus.runtime.annotations.Recorder; -import java.util.Optional; -import java.util.logging.Formatter; -import javax.json.bind.spi.JsonbProvider; - -@Recorder -public class GoogleCloudLoggingRecorder { - -  public RuntimeValue<Optional<Formatter>> initialize() { -    var jsonb = JsonbProvider.provider().create().build(); -    return new RuntimeValue<>(Optional.of(new GoogleCloudLoggingFormatter(jsonb))); -  } -} diff --git a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/KeyValueParameter.java b/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/KeyValueParameter.java deleted file mode 100644 index 358e470..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/KeyValueParameter.java +++ /dev/null @@ -1,3 +0,0 @@ -package eu.mulk.quarkus.observability.googlecloud.jsonlogging; - -public record KeyValueParameter(String key, Object value) {} diff --git a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/Label.java b/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/Label.java deleted file mode 100644 index 0c13739..0000000 --- a/runtime/src/main/java/eu/mulk/quarkus/observability/googlecloud/jsonlogging/Label.java +++ /dev/null @@ -1,3 +0,0 @@ -package eu.mulk.quarkus.observability.googlecloud.jsonlogging; - -public record Label(String key, String value) {} | 
