Skip to content

use logfmt for audit logs #468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/main/java/com/uid2/shared/audit/Audit.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.uid2.shared.audit;

import com.uid2.shared.util.LogFmtLineBuilder;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's hold off on this. If we can get parsing and labeling through grafana agent, that can work across services, Java/nodeJS, and SAAS solutions we use.

import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.Json;
Expand Down Expand Up @@ -72,6 +73,29 @@ public String toString() {
return toJson().encode();
}

public String toLogFmtLine() {
LogFmtLineBuilder builder = new LogFmtLineBuilder();

builder.with("timestamp", timestamp.toString());
builder.with("log_type", logType);
builder.with("source", source);
builder.with("status", status);
builder.with("method", method);
builder.with("endpoint", endpoint);
builder.with("trace_id", traceId);
builder.with("actor", actor);
if (uidTraceId != null) {
builder.with("uid_trace_id", uidTraceId);
}
if (uidInstanceId != null) {
builder.with("uid_instance_id", uidInstanceId);
}
if (queryParams != null) builder.with("query_params", queryParams);
if (requestBody != null) builder.with("request_body", requestBody);

return builder.build();
}

public static class Builder {
private final int status;
private final String method;
Expand Down Expand Up @@ -288,7 +312,7 @@ public void log(RoutingContext ctx, AuditParams params) {
}

AuditRecord auditRecord = builder.build();
LOGGER.info(auditRecord.toString());
LOGGER.info(auditRecord.toLogFmtLine());
} catch (Exception e) {
LOGGER.warn("Failed to log audit record", e);
}
Expand Down
63 changes: 63 additions & 0 deletions src/main/java/com/uid2/shared/util/LogFmtLineBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.uid2.shared.util;

import io.vertx.core.json.JsonObject;

import java.util.Map;

public class LogFmtLineBuilder {
private final StringBuilder stringBuilder;

public LogFmtLineBuilder() {
this.stringBuilder = new StringBuilder();
}

private String escape(String value) {
if (value == null) {
return "null";
}

if (value.contains(" ") || value.contains("\"") || value.contains("=") || value.contains("\n") ||
value.contains("\r") || value.contains("\t") || value.contains("\\")) {
return "\"" + value.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t") + "\"";
}

return value;
}

public LogFmtLineBuilder with(String key, String value) {
if (!stringBuilder.isEmpty()) {
stringBuilder.append(" ");
}
stringBuilder.append(key).append("=").append(escape(value));
return this;
}

public LogFmtLineBuilder with(Map<String, String> map) {
if (map != null) {
for (Map.Entry<String, String> entry : map.entrySet()) {
with(entry.getKey(), entry.getValue());
}
}
return this;
}

public LogFmtLineBuilder with(String key, int value) {
return with(key, String.valueOf(value));
}

// Only supports one level of nesting
public LogFmtLineBuilder with(String key, JsonObject obj) {
for (String objKey : obj.fieldNames()) {
with(key+ "." + objKey, obj.getString(objKey));
}
return this;
}

public String build() {
return stringBuilder.toString();
}
}
19 changes: 7 additions & 12 deletions src/test/java/com/uid2/shared/audit/AuditTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,13 @@ public void testAudit() throws JsonProcessingException {
Optional<ILoggingEvent> maybeEvent = listAppender.list.stream()
.filter(event -> event.getFormattedMessage().contains("GET"))
.findFirst();
String jsonLog = maybeEvent.get().getFormattedMessage();
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(jsonLog);

assertThat(jsonNode.get("method").asText()).isEqualTo("GET");
assertThat(jsonNode.get("status").asInt()).isEqualTo(200);
assertThat(jsonNode.get("source").asText()).isEqualTo("admin");

JsonNode actor = jsonNode.get("actor");
assertThat(actor).isNotNull();
assertThat(actor.get("user_agent").asText()).isEqualTo("JUnit-Test-Agent");
assertThat(actor.get("ip").asText()).isEqualTo("127.0.0.1");
String auditLog = maybeEvent.get().getFormattedMessage();

assertThat(auditLog.contains("method=GET")).isTrue();
assertThat(auditLog.contains("status=200")).isTrue();
assertThat(auditLog.contains("source=admin")).isTrue();
assertThat(auditLog.contains("actor.user_agent=JUnit-Test-Agent")).isTrue();
assertThat(auditLog.contains("actor.ip=127.0.0.1")).isTrue();

}

Expand Down
Loading