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.
*
*
Roughly (but not quite) corresponds to Google Cloud Logging's LogEntry
* structure.
*
*
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.
*/
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 labels;
private final List parameters;
private final Map 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 labels,
List parameters,
Map 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 stringMap) {
return stringMap.entrySet().stream()
.reduce(
Json.createObjectBuilder(),
(acc, x) -> acc.add(x.getKey(), x.getValue()),
JsonObjectBuilder::addAll);
}
private static JsonObjectBuilder jsonOfParameterMap(List parameters) {
return parameters.stream()
.reduce(
Json.createObjectBuilder(),
(acc, p) -> acc.addAll(p.json()),
JsonObjectBuilder::addAll);
}
}