From 1fd875f29d0d7429e3a1f55534c44564996b14ff Mon Sep 17 00:00:00 2001 From: Santiago Mola Date: Tue, 3 Jun 2025 17:54:38 +0200 Subject: [PATCH] Support attaching tags to telemetry logs --- .../trace/api/telemetry/LogCollector.java | 24 ++++++++++++++++--- .../telemetry/log/LogPeriodicAction.java | 1 + .../log/LogPeriodicActionTest.groovy | 15 ++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/LogCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/LogCollector.java index 80412572dd4..2348843d6da 100644 --- a/internal-api/src/main/java/datadog/trace/api/telemetry/LogCollector.java +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/LogCollector.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; import org.slf4j.Marker; import org.slf4j.MarkerFactory; @@ -34,13 +35,27 @@ private LogCollector() { this.rawLogMessages = new ConcurrentHashMap<>(maxCapacity); } - public void addLogMessage(String logLevel, String message, Throwable throwable) { + public void addLogMessage(String logLevel, String message, @Nullable Throwable throwable) { + addLogMessage(logLevel, message, throwable, null); + } + + /** + * Queue a log message to be sent on next telemetry flush. + * + * @param logLevel Log level (ERROR, WARN, DEBUG). Unknown log levels will be ignored. + * @param message Log message. + * @param throwable Optional throwable to attach a stacktrace. + * @param tags Optional tags to attach to the log. These are a comma-separated list, e.g. + * tag1:value1,tag2:value2 + */ + public void addLogMessage( + String logLevel, String message, @Nullable Throwable throwable, @Nullable String tags) { if (rawLogMessages.size() >= maxCapacity) { // TODO: We could emit a metric for dropped logs. return; } RawLogMessage rawLogMessage = - new RawLogMessage(logLevel, message, throwable, System.currentTimeMillis() / 1000); + new RawLogMessage(logLevel, message, throwable, tags, System.currentTimeMillis() / 1000); AtomicInteger count = rawLogMessages.computeIfAbsent(rawLogMessage, k -> new AtomicInteger()); count.incrementAndGet(); } @@ -73,13 +88,16 @@ public static class RawLogMessage { public final String message; public final String logLevel; public final Throwable throwable; + public final String tags; public final long timestamp; public int count; - public RawLogMessage(String logLevel, String message, Throwable throwable, long timestamp) { + public RawLogMessage( + String logLevel, String message, Throwable throwable, String tags, long timestamp) { this.logLevel = logLevel; this.message = message; this.throwable = throwable; + this.tags = tags; this.timestamp = timestamp; } diff --git a/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java b/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java index c90b103f404..d099e8ba32e 100644 --- a/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java +++ b/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java @@ -36,6 +36,7 @@ public void doIteration(TelemetryService service) { new LogMessage() .message(rawLogMsg.message) .tracerTime(rawLogMsg.timestamp) + .tags(rawLogMsg.tags) .count(rawLogMsg.count); if (rawLogMsg.logLevel != null) { diff --git a/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy b/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy index aa29acdd6a3..1142594e0fa 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy @@ -21,6 +21,21 @@ class LogPeriodicActionTest extends DDSpecification { LogCollector.get().drain() } + void 'log with tags'() { + LogMessage logMessage + + when: + LogCollector.get().addLogMessage('ERROR', "test", null, 'tag1:value1,tag2:value2') + periodicAction.doIteration(telemetryService) + + then: + 1 * telemetryService.addLogMessage(_) >> { args -> logMessage = args[0] } + 0 * _ + logMessage.getLevel() == LogMessageLevel.ERROR + logMessage.getMessage() == 'test' + logMessage.getTags() == 'tag1:value1,tag2:value2' + } + void 'log with datadog throwable'() { LogMessage logMessage