From 181def918d5d11a86b81c39123653148f5b6fa42 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Sep 2025 00:49:33 +0000
Subject: [PATCH 1/4] Initial plan
From b64dfd2c774826c10748fd46e6a406fa2af8a0dd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Sep 2025 00:59:40 +0000
Subject: [PATCH 2/4] Implement
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
support
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../ChatCompletion/OpenTelemetryChatClient.cs | 19 ++++-
.../OpenTelemetryEmbeddingGenerator.cs | 19 ++++-
.../OpenTelemetryConsts.cs | 3 +
.../OpenTelemetryChatClientTests.cs | 72 +++++++++++++++++++
.../OpenTelemetryEmbeddingGeneratorTests.cs | 72 +++++++++++++++++++
5 files changed, 183 insertions(+), 2 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
index f9215060dea..3ed41ef28ca 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
@@ -83,6 +83,20 @@ public OpenTelemetryChatClient(IChatClient innerClient, ILogger? logger = null,
);
_jsonSerializerOptions = AIJsonUtilities.DefaultOptions;
+
+ // Set the default value of EnableSensitiveData based on the environment variable
+ EnableSensitiveData = ShouldEnableSensitiveDataByDefault();
+ }
+
+ ///
+ /// Checks the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
+ /// to determine if sensitive data should be enabled by default.
+ ///
+ /// True if the environment variable is set to "true" (case-insensitive), otherwise false.
+ private static bool ShouldEnableSensitiveDataByDefault()
+ {
+ string? envVar = Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar);
+ return string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
}
/// Gets or sets JSON serialization options to use when formatting chat data into telemetry strings.
@@ -110,11 +124,14 @@ protected override void Dispose(bool disposing)
///
/// if potentially sensitive information should be included in telemetry;
/// if telemetry shouldn't include raw inputs and outputs.
- /// The default value is .
+ /// The default value is , unless the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
+ /// environment variable is set to "true" (case-insensitive).
///
///
/// By default, telemetry includes metadata, such as token counts, but not raw inputs
/// and outputs, such as message content, function call arguments, and function call results.
+ /// The default value can be overridden by setting the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
+ /// environment variable to "true". Explicitly setting this property will override the environment variable.
///
public bool EnableSensitiveData { get; set; }
diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
index 402222feaa7..f2892f4777c 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
@@ -81,6 +81,20 @@ public OpenTelemetryEmbeddingGenerator(IEmbeddingGenerator i
, advice: new() { HistogramBucketBoundaries = OpenTelemetryConsts.GenAI.Client.OperationDuration.ExplicitBucketBoundaries }
#endif
);
+
+ // Set the default value of EnableSensitiveData based on the environment variable
+ EnableSensitiveData = ShouldEnableSensitiveDataByDefault();
+ }
+
+ ///
+ /// Checks the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
+ /// to determine if sensitive data should be enabled by default.
+ ///
+ /// True if the environment variable is set to "true" (case-insensitive), otherwise false.
+ private static bool ShouldEnableSensitiveDataByDefault()
+ {
+ string? envVar = Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar);
+ return string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
}
///
@@ -89,11 +103,14 @@ public OpenTelemetryEmbeddingGenerator(IEmbeddingGenerator i
///
/// if potentially sensitive information should be included in telemetry;
/// if telemetry shouldn't include raw inputs and outputs.
- /// The default value is .
+ /// The default value is , unless the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
+ /// environment variable is set to "true" (case-insensitive).
///
///
/// By default, telemetry includes metadata, such as token counts, but not raw inputs
/// and outputs or additional options data.
+ /// The default value can be overridden by setting the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
+ /// environment variable to "true". Explicitly setting this property will override the environment variable.
///
public bool EnableSensitiveData { get; set; }
diff --git a/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs b/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
index faf7c32e4cb..72ac3f82fcb 100644
--- a/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
@@ -14,6 +14,9 @@ internal static class OpenTelemetryConsts
public const string SecondsUnit = "s";
public const string TokensUnit = "token";
+ /// Environment variable name for controlling whether sensitive content should be captured in telemetry.
+ public const string GenAICaptureMessageContentEnvVar = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT";
+
public const string ToolTypeFunction = "function";
public const string TypeText = "text";
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
index 4640b78f75d..6508b647a6d 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
@@ -273,4 +273,76 @@ async static IAsyncEnumerable CallbackAsync(
static string ReplaceWhitespace(string? input) => Regex.Replace(input ?? "", @"\s+", " ").Trim();
}
+
+ [Theory]
+ [InlineData(null, false)]
+ [InlineData("", false)]
+ [InlineData("false", false)]
+ [InlineData("FALSE", false)]
+ [InlineData("True", true)]
+ [InlineData("true", true)]
+ [InlineData("TRUE", true)]
+ [InlineData("yes", false)] // Should only respond to "true"
+ [InlineData("1", false)] // Should only respond to "true"
+ public void EnableSensitiveData_DefaultsBasedOnEnvironmentVariable(string? envVarValue, bool expectedDefault)
+ {
+ // Arrange: Set up environment variable
+ string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
+
+ try
+ {
+ if (envVarValue is null)
+ {
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", null);
+ }
+ else
+ {
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", envVarValue);
+ }
+
+ // Act: Create a new instance
+ using var innerClient = new TestChatClient();
+ using var client = new OpenTelemetryChatClient(innerClient);
+
+ // Assert: Check the default value
+ Assert.Equal(expectedDefault, client.EnableSensitiveData);
+ }
+ finally
+ {
+ // Cleanup: Restore original environment variable
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
+ string.IsNullOrEmpty(originalValue) ? null : originalValue);
+ }
+ }
+
+ [Fact]
+ public void EnableSensitiveData_ExplicitSettingOverridesEnvironmentVariable()
+ {
+ // Arrange: Set environment variable to true
+ string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
+
+ try
+ {
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "true");
+
+ // Act: Create instance and explicitly set to false
+ using var innerClient = new TestChatClient();
+ using var client = new OpenTelemetryChatClient(innerClient);
+
+ // Verify it defaults to true from environment variable
+ Assert.True(client.EnableSensitiveData);
+
+ // Explicitly set to false
+ client.EnableSensitiveData = false;
+
+ // Assert: Explicit setting should override environment variable
+ Assert.False(client.EnableSensitiveData);
+ }
+ finally
+ {
+ // Cleanup: Restore original environment variable
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
+ string.IsNullOrEmpty(originalValue) ? null : originalValue);
+ }
+ }
}
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs
index c8419338e44..28d7274d188 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs
@@ -95,4 +95,76 @@ public async Task ExpectedInformationLogged_Async(string? perRequestModelId, boo
Assert.True(activity.Duration.TotalMilliseconds > 0);
}
+
+ [Theory]
+ [InlineData(null, false)]
+ [InlineData("", false)]
+ [InlineData("false", false)]
+ [InlineData("FALSE", false)]
+ [InlineData("True", true)]
+ [InlineData("true", true)]
+ [InlineData("TRUE", true)]
+ [InlineData("yes", false)] // Should only respond to "true"
+ [InlineData("1", false)] // Should only respond to "true"
+ public void EnableSensitiveData_DefaultsBasedOnEnvironmentVariable(string? envVarValue, bool expectedDefault)
+ {
+ // Arrange: Set up environment variable
+ string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
+
+ try
+ {
+ if (envVarValue is null)
+ {
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", null);
+ }
+ else
+ {
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", envVarValue);
+ }
+
+ // Act: Create a new instance
+ using var innerGenerator = new TestEmbeddingGenerator>();
+ using var generator = new OpenTelemetryEmbeddingGenerator>(innerGenerator);
+
+ // Assert: Check the default value
+ Assert.Equal(expectedDefault, generator.EnableSensitiveData);
+ }
+ finally
+ {
+ // Cleanup: Restore original environment variable
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
+ string.IsNullOrEmpty(originalValue) ? null : originalValue);
+ }
+ }
+
+ [Fact]
+ public void EnableSensitiveData_ExplicitSettingOverridesEnvironmentVariable()
+ {
+ // Arrange: Set environment variable to true
+ string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
+
+ try
+ {
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "true");
+
+ // Act: Create instance and explicitly set to false
+ using var innerGenerator = new TestEmbeddingGenerator>();
+ using var generator = new OpenTelemetryEmbeddingGenerator>(innerGenerator);
+
+ // Verify it defaults to true from environment variable
+ Assert.True(generator.EnableSensitiveData);
+
+ // Explicitly set to false
+ generator.EnableSensitiveData = false;
+
+ // Assert: Explicit setting should override environment variable
+ Assert.False(generator.EnableSensitiveData);
+ }
+ finally
+ {
+ // Cleanup: Restore original environment variable
+ Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
+ string.IsNullOrEmpty(originalValue) ? null : originalValue);
+ }
+ }
}
From 0f933764a38f345139fe98c3329d3dcb703299f8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Sep 2025 01:21:34 +0000
Subject: [PATCH 3/4] Move ShouldEnableSensitiveDataByDefault to shared
TelemetryHelpers class
Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>
---
.../ChatCompletion/FunctionInvokingChatClient.cs | 4 ++--
.../ChatCompletion/LoggingChatClient.cs | 2 +-
.../ChatCompletion/OpenTelemetryChatClient.cs | 13 +------------
.../OpenTelemetryEmbeddingGenerator.cs | 13 +------------
.../Image/LoggingImageGenerator.cs | 2 +-
.../SpeechToText/LoggingSpeechToTextClient.cs | 2 +-
.../{LoggingHelpers.cs => TelemetryHelpers.cs} | 16 ++++++++++++++--
7 files changed, 21 insertions(+), 31 deletions(-)
rename src/Libraries/Microsoft.Extensions.AI/{LoggingHelpers.cs => TelemetryHelpers.cs} (62%)
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs
index e907078d535..e4a347ded70 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs
@@ -1128,7 +1128,7 @@ FunctionResultContent CreateFunctionResultContent(FunctionInvocationResult resul
startingTimestamp = Stopwatch.GetTimestamp();
if (_logger.IsEnabled(LogLevel.Trace))
{
- LogInvokingSensitive(context.Function.Name, LoggingHelpers.AsJson(context.Arguments, context.Function.JsonSerializerOptions));
+ LogInvokingSensitive(context.Function.Name, TelemetryHelpers.AsJson(context.Arguments, context.Function.JsonSerializerOptions));
}
else
{
@@ -1169,7 +1169,7 @@ FunctionResultContent CreateFunctionResultContent(FunctionInvocationResult resul
if (result is not null && _logger.IsEnabled(LogLevel.Trace))
{
- LogInvocationCompletedSensitive(context.Function.Name, elapsed, LoggingHelpers.AsJson(result, context.Function.JsonSerializerOptions));
+ LogInvocationCompletedSensitive(context.Function.Name, elapsed, TelemetryHelpers.AsJson(result, context.Function.JsonSerializerOptions));
}
else
{
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs
index aec72eddcdc..c39fa2c0eb6 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs
@@ -169,7 +169,7 @@ public override async IAsyncEnumerable GetStreamingResponseA
}
}
- private string AsJson(T value) => LoggingHelpers.AsJson(value, _jsonSerializerOptions);
+ private string AsJson(T value) => TelemetryHelpers.AsJson(value, _jsonSerializerOptions);
[LoggerMessage(LogLevel.Debug, "{MethodName} invoked.")]
private partial void LogInvoked(string methodName);
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
index 3ed41ef28ca..cdd934b5833 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
@@ -85,18 +85,7 @@ public OpenTelemetryChatClient(IChatClient innerClient, ILogger? logger = null,
_jsonSerializerOptions = AIJsonUtilities.DefaultOptions;
// Set the default value of EnableSensitiveData based on the environment variable
- EnableSensitiveData = ShouldEnableSensitiveDataByDefault();
- }
-
- ///
- /// Checks the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
- /// to determine if sensitive data should be enabled by default.
- ///
- /// True if the environment variable is set to "true" (case-insensitive), otherwise false.
- private static bool ShouldEnableSensitiveDataByDefault()
- {
- string? envVar = Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar);
- return string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
+ EnableSensitiveData = TelemetryHelpers.ShouldEnableSensitiveDataByDefault();
}
/// Gets or sets JSON serialization options to use when formatting chat data into telemetry strings.
diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
index f2892f4777c..f92aa6b54b2 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
@@ -83,18 +83,7 @@ public OpenTelemetryEmbeddingGenerator(IEmbeddingGenerator i
);
// Set the default value of EnableSensitiveData based on the environment variable
- EnableSensitiveData = ShouldEnableSensitiveDataByDefault();
- }
-
- ///
- /// Checks the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
- /// to determine if sensitive data should be enabled by default.
- ///
- /// True if the environment variable is set to "true" (case-insensitive), otherwise false.
- private static bool ShouldEnableSensitiveDataByDefault()
- {
- string? envVar = Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar);
- return string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
+ EnableSensitiveData = TelemetryHelpers.ShouldEnableSensitiveDataByDefault();
}
///
diff --git a/src/Libraries/Microsoft.Extensions.AI/Image/LoggingImageGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Image/LoggingImageGenerator.cs
index 2eee33456c1..9ca0b8d06d2 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Image/LoggingImageGenerator.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Image/LoggingImageGenerator.cs
@@ -102,7 +102,7 @@ public override async Task GenerateAsync(
}
}
- private string AsJson(T value) => LoggingHelpers.AsJson(value, _jsonSerializerOptions);
+ private string AsJson(T value) => TelemetryHelpers.AsJson(value, _jsonSerializerOptions);
[LoggerMessage(LogLevel.Debug, "{MethodName} invoked.")]
private partial void LogInvoked(string methodName);
diff --git a/src/Libraries/Microsoft.Extensions.AI/SpeechToText/LoggingSpeechToTextClient.cs b/src/Libraries/Microsoft.Extensions.AI/SpeechToText/LoggingSpeechToTextClient.cs
index e7bf7850a94..10e499a7e57 100644
--- a/src/Libraries/Microsoft.Extensions.AI/SpeechToText/LoggingSpeechToTextClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/SpeechToText/LoggingSpeechToTextClient.cs
@@ -179,7 +179,7 @@ public override async IAsyncEnumerable GetStreamingT
}
}
- private string AsJson(T value) => LoggingHelpers.AsJson(value, _jsonSerializerOptions);
+ private string AsJson(T value) => TelemetryHelpers.AsJson(value, _jsonSerializerOptions);
[LoggerMessage(LogLevel.Debug, "{MethodName} invoked.")]
private partial void LogInvoked(string methodName);
diff --git a/src/Libraries/Microsoft.Extensions.AI/LoggingHelpers.cs b/src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs
similarity index 62%
rename from src/Libraries/Microsoft.Extensions.AI/LoggingHelpers.cs
rename to src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs
index 72a7e283988..de439af8809 100644
--- a/src/Libraries/Microsoft.Extensions.AI/LoggingHelpers.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs
@@ -5,13 +5,25 @@
#pragma warning disable S108 // Nested blocks of code should not be left empty
#pragma warning disable S2486 // Generic exceptions should not be ignored
+using System;
using System.Text.Json;
namespace Microsoft.Extensions.AI;
-/// Provides internal helpers for implementing logging.
-internal static class LoggingHelpers
+/// Provides internal helpers for implementing telemetry.
+internal static class TelemetryHelpers
{
+ ///
+ /// Checks the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
+ /// to determine if sensitive data should be enabled by default.
+ ///
+ /// True if the environment variable is set to "true" (case-insensitive), otherwise false.
+ public static bool ShouldEnableSensitiveDataByDefault()
+ {
+ string? envVar = Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar);
+ return string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
+ }
+
/// Serializes as JSON for logging purposes.
public static string AsJson(T value, JsonSerializerOptions? options)
{
From 320b366130386b58206e4315e15759e436f4ad9d Mon Sep 17 00:00:00 2001
From: Stephen Toub
Date: Wed, 10 Sep 2025 13:07:10 -0400
Subject: [PATCH 4/4] Address feedback
---
.../ChatCompletion/OpenTelemetryChatClient.cs | 5 +-
.../OpenTelemetryEmbeddingGenerator.cs | 5 +-
.../OpenTelemetryConsts.cs | 2 +-
.../TelemetryHelpers.cs | 16 ++---
.../OpenTelemetryChatClientTests.cs | 72 -------------------
.../OpenTelemetryEmbeddingGeneratorTests.cs | 72 -------------------
6 files changed, 9 insertions(+), 163 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
index cdd934b5833..d06ec3d3b5e 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
@@ -83,9 +83,6 @@ public OpenTelemetryChatClient(IChatClient innerClient, ILogger? logger = null,
);
_jsonSerializerOptions = AIJsonUtilities.DefaultOptions;
-
- // Set the default value of EnableSensitiveData based on the environment variable
- EnableSensitiveData = TelemetryHelpers.ShouldEnableSensitiveDataByDefault();
}
/// Gets or sets JSON serialization options to use when formatting chat data into telemetry strings.
@@ -122,7 +119,7 @@ protected override void Dispose(bool disposing)
/// The default value can be overridden by setting the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
/// environment variable to "true". Explicitly setting this property will override the environment variable.
///
- public bool EnableSensitiveData { get; set; }
+ public bool EnableSensitiveData { get; set; } = TelemetryHelpers.EnableSensitiveDataDefault;
///
public override object? GetService(Type serviceType, object? serviceKey = null) =>
diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
index f92aa6b54b2..4b8f58ed7fb 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
@@ -81,9 +81,6 @@ public OpenTelemetryEmbeddingGenerator(IEmbeddingGenerator i
, advice: new() { HistogramBucketBoundaries = OpenTelemetryConsts.GenAI.Client.OperationDuration.ExplicitBucketBoundaries }
#endif
);
-
- // Set the default value of EnableSensitiveData based on the environment variable
- EnableSensitiveData = TelemetryHelpers.ShouldEnableSensitiveDataByDefault();
}
///
@@ -101,7 +98,7 @@ public OpenTelemetryEmbeddingGenerator(IEmbeddingGenerator i
/// The default value can be overridden by setting the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
/// environment variable to "true". Explicitly setting this property will override the environment variable.
///
- public bool EnableSensitiveData { get; set; }
+ public bool EnableSensitiveData { get; set; } = TelemetryHelpers.EnableSensitiveDataDefault;
///
public override object? GetService(Type serviceType, object? serviceKey = null) =>
diff --git a/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs b/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
index 72ac3f82fcb..096b2091908 100644
--- a/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
@@ -14,7 +14,7 @@ internal static class OpenTelemetryConsts
public const string SecondsUnit = "s";
public const string TokensUnit = "token";
- /// Environment variable name for controlling whether sensitive content should be captured in telemetry.
+ /// Environment variable name for controlling whether sensitive content should be captured in telemetry by default.
public const string GenAICaptureMessageContentEnvVar = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT";
public const string ToolTypeFunction = "function";
diff --git a/src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs b/src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs
index de439af8809..0468a93b26f 100644
--- a/src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/TelemetryHelpers.cs
@@ -4,6 +4,7 @@
#pragma warning disable CA1031 // Do not catch general exception types
#pragma warning disable S108 // Nested blocks of code should not be left empty
#pragma warning disable S2486 // Generic exceptions should not be ignored
+#pragma warning disable SA1623 // Property summary documentation should match accessors
using System;
using System.Text.Json;
@@ -13,16 +14,11 @@ namespace Microsoft.Extensions.AI;
/// Provides internal helpers for implementing telemetry.
internal static class TelemetryHelpers
{
- ///
- /// Checks the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable
- /// to determine if sensitive data should be enabled by default.
- ///
- /// True if the environment variable is set to "true" (case-insensitive), otherwise false.
- public static bool ShouldEnableSensitiveDataByDefault()
- {
- string? envVar = Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar);
- return string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
- }
+ /// Gets a value the OpenTelemetry clients should use for their EnableSensitiveData property's default value.
+ /// Defaults to false. May be overridden by setting the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT environment variable to "true".
+ public static bool EnableSensitiveDataDefault { get; } =
+ Environment.GetEnvironmentVariable(OpenTelemetryConsts.GenAICaptureMessageContentEnvVar) is string envVar &&
+ string.Equals(envVar, "true", StringComparison.OrdinalIgnoreCase);
/// Serializes as JSON for logging purposes.
public static string AsJson(T value, JsonSerializerOptions? options)
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
index 6508b647a6d..4640b78f75d 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
@@ -273,76 +273,4 @@ async static IAsyncEnumerable CallbackAsync(
static string ReplaceWhitespace(string? input) => Regex.Replace(input ?? "", @"\s+", " ").Trim();
}
-
- [Theory]
- [InlineData(null, false)]
- [InlineData("", false)]
- [InlineData("false", false)]
- [InlineData("FALSE", false)]
- [InlineData("True", true)]
- [InlineData("true", true)]
- [InlineData("TRUE", true)]
- [InlineData("yes", false)] // Should only respond to "true"
- [InlineData("1", false)] // Should only respond to "true"
- public void EnableSensitiveData_DefaultsBasedOnEnvironmentVariable(string? envVarValue, bool expectedDefault)
- {
- // Arrange: Set up environment variable
- string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
-
- try
- {
- if (envVarValue is null)
- {
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", null);
- }
- else
- {
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", envVarValue);
- }
-
- // Act: Create a new instance
- using var innerClient = new TestChatClient();
- using var client = new OpenTelemetryChatClient(innerClient);
-
- // Assert: Check the default value
- Assert.Equal(expectedDefault, client.EnableSensitiveData);
- }
- finally
- {
- // Cleanup: Restore original environment variable
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
- string.IsNullOrEmpty(originalValue) ? null : originalValue);
- }
- }
-
- [Fact]
- public void EnableSensitiveData_ExplicitSettingOverridesEnvironmentVariable()
- {
- // Arrange: Set environment variable to true
- string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
-
- try
- {
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "true");
-
- // Act: Create instance and explicitly set to false
- using var innerClient = new TestChatClient();
- using var client = new OpenTelemetryChatClient(innerClient);
-
- // Verify it defaults to true from environment variable
- Assert.True(client.EnableSensitiveData);
-
- // Explicitly set to false
- client.EnableSensitiveData = false;
-
- // Assert: Explicit setting should override environment variable
- Assert.False(client.EnableSensitiveData);
- }
- finally
- {
- // Cleanup: Restore original environment variable
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
- string.IsNullOrEmpty(originalValue) ? null : originalValue);
- }
- }
}
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs
index 28d7274d188..c8419338e44 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/Embeddings/OpenTelemetryEmbeddingGeneratorTests.cs
@@ -95,76 +95,4 @@ public async Task ExpectedInformationLogged_Async(string? perRequestModelId, boo
Assert.True(activity.Duration.TotalMilliseconds > 0);
}
-
- [Theory]
- [InlineData(null, false)]
- [InlineData("", false)]
- [InlineData("false", false)]
- [InlineData("FALSE", false)]
- [InlineData("True", true)]
- [InlineData("true", true)]
- [InlineData("TRUE", true)]
- [InlineData("yes", false)] // Should only respond to "true"
- [InlineData("1", false)] // Should only respond to "true"
- public void EnableSensitiveData_DefaultsBasedOnEnvironmentVariable(string? envVarValue, bool expectedDefault)
- {
- // Arrange: Set up environment variable
- string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
-
- try
- {
- if (envVarValue is null)
- {
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", null);
- }
- else
- {
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", envVarValue);
- }
-
- // Act: Create a new instance
- using var innerGenerator = new TestEmbeddingGenerator>();
- using var generator = new OpenTelemetryEmbeddingGenerator>(innerGenerator);
-
- // Assert: Check the default value
- Assert.Equal(expectedDefault, generator.EnableSensitiveData);
- }
- finally
- {
- // Cleanup: Restore original environment variable
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
- string.IsNullOrEmpty(originalValue) ? null : originalValue);
- }
- }
-
- [Fact]
- public void EnableSensitiveData_ExplicitSettingOverridesEnvironmentVariable()
- {
- // Arrange: Set environment variable to true
- string originalValue = Environment.GetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT") ?? "";
-
- try
- {
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "true");
-
- // Act: Create instance and explicitly set to false
- using var innerGenerator = new TestEmbeddingGenerator>();
- using var generator = new OpenTelemetryEmbeddingGenerator>(innerGenerator);
-
- // Verify it defaults to true from environment variable
- Assert.True(generator.EnableSensitiveData);
-
- // Explicitly set to false
- generator.EnableSensitiveData = false;
-
- // Assert: Explicit setting should override environment variable
- Assert.False(generator.EnableSensitiveData);
- }
- finally
- {
- // Cleanup: Restore original environment variable
- Environment.SetEnvironmentVariable("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
- string.IsNullOrEmpty(originalValue) ? null : originalValue);
- }
- }
}