diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilder.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilder.java index ac6ad701ac27..c0e7e7400fa1 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilder.java +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/main/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilder.java @@ -13,13 +13,23 @@ import java.time.OffsetDateTime; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; + +import static java.util.Arrays.asList; public abstract class AbstractTelemetryBuilder { private static final int MAX_PROPERTY_KEY_LENGTH = 150; private static final int MAX_PROPERTY_VALUE_LENGTH = 8192; + // gen_ai properties can contain large payloads (e.g. full conversation messages, + // tool definitions) that should not be truncated + private static final Set TRUNCATION_EXEMPT_PROPERTY_KEYS = new HashSet<>(asList("gen_ai.input.messages", + "gen_ai.output.messages", "gen_ai.system_instructions", "gen_ai.tool.definitions", "gen_ai.tool.call.arguments", + "gen_ai.tool.call.result", "gen_ai.evaluation.explanation")); + protected static final int MAX_MEASUREMENT_KEY_LENGTH = 150; protected static final int MAX_NAME_LENGTH = 1024; @@ -79,7 +89,11 @@ public void addProperty(@Nullable String key, @Nullable String value) { // TODO (trask) log return; } - getProperties().put(key, TelemetryTruncation.truncatePropertyValue(value, MAX_PROPERTY_VALUE_LENGTH, key)); + if (TRUNCATION_EXEMPT_PROPERTY_KEYS.contains(key)) { + getProperties().put(key, value); + } else { + getProperties().put(key, TelemetryTruncation.truncatePropertyValue(value, MAX_PROPERTY_VALUE_LENGTH, key)); + } } public TelemetryItem build() { diff --git a/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilderTest.java b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilderTest.java new file mode 100644 index 000000000000..63bbd6c45595 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-autoconfigure/src/test/java/com/azure/monitor/opentelemetry/autoconfigure/implementation/builders/AbstractTelemetryBuilderTest.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.monitor.opentelemetry.autoconfigure.implementation.builders; + +import com.azure.monitor.opentelemetry.autoconfigure.implementation.models.TelemetryItem; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AbstractTelemetryBuilderTest { + + @Test + public void regularPropertyIsTruncatedAt8192() { + MessageTelemetryBuilder builder = MessageTelemetryBuilder.create(); + String longValue = repeat("a", 10000); + builder.addProperty("some.regular.property", longValue); + + TelemetryItem item = builder.build(); + Map properties = getProperties(item); + assertEquals(8192, properties.get("some.regular.property").length()); + } + + @ParameterizedTest + @ValueSource( + strings = { + "gen_ai.input.messages", + "gen_ai.output.messages", + "gen_ai.system_instructions", + "gen_ai.tool.definitions", + "gen_ai.tool.call.arguments", + "gen_ai.tool.call.result", + "gen_ai.evaluation.explanation" }) + public void genAiPropertyIsNotTruncated(String key) { + MessageTelemetryBuilder builder = MessageTelemetryBuilder.create(); + String longValue = repeat("a", 50000); + builder.addProperty(key, longValue); + + TelemetryItem item = builder.build(); + Map properties = getProperties(item); + assertEquals(50000, properties.get(key).length()); + assertEquals(longValue, properties.get(key)); + } + + @SuppressWarnings("unchecked") + private static Map getProperties(TelemetryItem item) { + if (item.getData() == null || item.getData().getBaseData() == null) { + return null; + } + Object baseData = item.getData().getBaseData(); + try { + return (Map) baseData.getClass().getMethod("getProperties").invoke(baseData); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static String repeat(String s, int count) { + StringBuilder sb = new StringBuilder(s.length() * count); + for (int i = 0; i < count; i++) { + sb.append(s); + } + return sb.toString(); + } +}