diff --git a/bridged/src/main/java/dev/ltms/bridged/auth/AuditLog.java b/bridged/src/main/java/dev/ltms/bridged/auth/AuditLog.java index e564466..1554dbd 100644 --- a/bridged/src/main/java/dev/ltms/bridged/auth/AuditLog.java +++ b/bridged/src/main/java/dev/ltms/bridged/auth/AuditLog.java @@ -3,6 +3,10 @@ package dev.ltms.bridged.auth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + /** * Append-only record of privileged actions (CB-505). * @@ -18,6 +22,8 @@ import org.slf4j.LoggerFactory; public final class AuditLog { private static final Logger AUDIT = LoggerFactory.getLogger("audit"); + private static final DateTimeFormatter TS = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneOffset.UTC); private AuditLog() { } @@ -40,8 +46,11 @@ public final class AuditLog { private static void write(Principal caller, Authz.Action action, String target, String outcome, String reason) { Principal c = caller != null ? caller : Principal.anonymous(); - StringBuilder sb = new StringBuilder(160); - sb.append("{\"role\":\"").append(c.role()).append('"') + StringBuilder sb = new StringBuilder(200); + // The timestamp is built here rather than by the appender pattern: a pattern that wrapped + // literal braces around the message collides with logback's own variable substitution. + sb.append("{\"ts\":\"").append(TS.format(Instant.now())).append('"') + .append(",\"role\":\"").append(c.role()).append('"') .append(",\"actor\":\"").append(esc(c.describe())).append('"') .append(",\"pid\":").append(c.pid()) .append(",\"action\":\"").append(action).append('"') diff --git a/bridged/src/main/resources/logback.xml b/bridged/src/main/resources/logback.xml index 1407ceb..afa8ab5 100644 --- a/bridged/src/main/resources/logback.xml +++ b/bridged/src/main/resources/logback.xml @@ -8,9 +8,9 @@