summaryrefslogtreecommitdiff
path: root/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java')
-rw-r--r--core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java157
1 files changed, 157 insertions, 0 deletions
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
new file mode 100644
index 0000000..d108c81
--- /dev/null
+++ b/core/src/main/java/eu/mulk/quarkus/googlecloud/jsonlogging/LogEntry.java
@@ -0,0 +1,157 @@
+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);
+ }
+}