From bdb24022cc85e6ca4f1f2fd571a08e8beaf6c999 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 09:55:28 -0700 Subject: [PATCH 1/9] Added a 256kb custom properties truncation limit on specific GenAI attributes --- .../CHANGELOG.md | 2 +- .../monitor/opentelemetry/exporter/_utils.py | 3 +- .../tests/test_utils.py | 30 +++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index 8494c6ad9a2f..ea9d14bdf808 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -3,7 +3,7 @@ ## 1.0.0b49 (Unreleased) ### Features Added - +- Added a 256kb custom properties truncation limit on specific GenAI attributes - Add Browser SDK loader SDK Stats feature bit ([#42904](https://github.com/Azure/azure-sdk-for-python/pull/44162)) ### Breaking Changes diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py index 5b8d016eaf76..8d2d95cdd7cf 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py @@ -365,7 +365,8 @@ def _filter_custom_properties(properties: Attributes, filter=None) -> Dict[str, if not key or len(key) > 150 or val is None: continue if key in _GEN_AI_ATTRIBUTES: - processed_properties[key] = str(val) + max_length_for_genai_attributes = 256 * 1024 + processed_properties[key] = str(val)[:max_length_for_genai_attributes] else: processed_properties[key] = str(val)[:max_length] return processed_properties diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py index 3101992e8f25..0de901d6e394 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py @@ -54,8 +54,8 @@ def test_filter_custom_properties_drops_invalid_entries(self): self.assertEqual(filtered["short"], "ok") self.assertNotIn("k" * 151, filtered) - def test_custom_properties_gen_ai_attributes_not_truncated(self): - # All values in _GEN_AI_ATTRIBUTES should not be truncated even when > 64KiB + def test_custom_properties_gen_ai_attributes_not_truncated_at_64kb(self): + # All values in _GEN_AI_ATTRIBUTES should not be truncated at > 64kb but at > 256kb large_value = "x" * (64 * 1024 + 1000) properties = {key: large_value for key in _GEN_AI_ATTRIBUTES} filtered = _utils._filter_custom_properties(properties) @@ -65,7 +65,7 @@ def test_custom_properties_gen_ai_attributes_not_truncated(self): self.assertEqual(len(filtered[key]), 64 * 1024 + 1000) def test_filter_custom_properties_non_gen_ai_truncated_at_64kb(self): - # Regular properties exceeding 64KiB should be truncated + # Regular properties exceeding 64kb should be truncated max_length = 64 * 1024 large_value = "y" * (max_length + 2000) properties = { @@ -81,9 +81,10 @@ def test_filter_custom_properties_non_gen_ai_truncated_at_64kb(self): self.assertEqual(len(filtered[key]), max_length) def test_filter_custom_properties_mixed_gen_ai_and_regular(self): - # Gen AI attributes keep full value, regular ones are truncated + # Gen AI attributes truncated at 256kb, regular ones are truncated at 64kb max_length = 64 * 1024 - large_value = "z" * (max_length + 3000) + max_length_for_genai_attributes = 256 * 1024 + large_value = "z" * (1024 * 1024 + 3000) properties = { "gen_ai.input.messages": large_value, "gen_ai.output.messages": large_value, @@ -92,14 +93,25 @@ def test_filter_custom_properties_mixed_gen_ai_and_regular(self): "db.statement": large_value, } filtered = _utils._filter_custom_properties(properties) - # Gen AI attributes — not truncated - self.assertEqual(len(filtered["gen_ai.input.messages"]), max_length + 3000) - self.assertEqual(len(filtered["gen_ai.output.messages"]), max_length + 3000) - # Regular attributes — truncated + + self.assertEqual(len(filtered["gen_ai.input.messages"]), max_length_for_genai_attributes) + self.assertEqual(len(filtered["gen_ai.output.messages"]), max_length_for_genai_attributes) + self.assertEqual(len(filtered["gen_ai.agent.version"]), max_length) self.assertEqual(len(filtered["span_kind"]), max_length) self.assertEqual(len(filtered["db.statement"]), max_length) + def test_custom_properties_gen_ai_attributes_truncated_at_256kb(self): + # All values in _GEN_AI_ATTRIBUTES should be truncated when > 256kb + max_length_for_genai_attributes = 256 * 1024 + large_value = "x" * (256 * 1024 + 1000) + properties = {key: large_value for key in _GEN_AI_ATTRIBUTES} + filtered = _utils._filter_custom_properties(properties) + for key in _GEN_AI_ATTRIBUTES: + with self.subTest(key=key): + self.assertIn(key, filtered) + self.assertEqual(len(filtered[key]), max_length_for_genai_attributes) + def test_nanoseconds_to_duration(self): ns_to_duration = _utils.ns_to_duration self.assertEqual(ns_to_duration(0), "0.00:00:00.000") From eb752c813b03d557f98453de1e784b8d26df896b Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 10:02:20 -0700 Subject: [PATCH 2/9] Update CHANGELOG --- sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index ea9d14bdf808..567b24599dff 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features Added - Added a 256kb custom properties truncation limit on specific GenAI attributes + ([#45749](https://github.com/Azure/azure-sdk-for-python/pull/45749)) - Add Browser SDK loader SDK Stats feature bit ([#42904](https://github.com/Azure/azure-sdk-for-python/pull/44162)) ### Breaking Changes From 0176e93126eb0553ae79ffe3e3d5be9f37bdacbf Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 10:12:20 -0700 Subject: [PATCH 3/9] Move max_length declaration to the top --- .../azure/monitor/opentelemetry/exporter/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py index 8d2d95cdd7cf..7e9b9d6a2695 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py @@ -352,6 +352,7 @@ def _is_any_synthetic_source(properties: Optional[Any]) -> bool: # pylint: disable=W0622 def _filter_custom_properties(properties: Attributes, filter=None) -> Dict[str, str]: max_length = 64 * 1024 + max_length_for_genai_attributes = 256 * 1024 processed_properties: Dict[str, str] = {} if not properties: return processed_properties @@ -365,7 +366,6 @@ def _filter_custom_properties(properties: Attributes, filter=None) -> Dict[str, if not key or len(key) > 150 or val is None: continue if key in _GEN_AI_ATTRIBUTES: - max_length_for_genai_attributes = 256 * 1024 processed_properties[key] = str(val)[:max_length_for_genai_attributes] else: processed_properties[key] = str(val)[:max_length] From e85daec9f490f44bcd1ed7c0c7215764997d6bf5 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 10:40:44 -0700 Subject: [PATCH 4/9] Retrigger CI/CD pipeline From 9471b7e1ed8fc8cec1c3a33b2d631a8bf6c92542 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 11:35:46 -0700 Subject: [PATCH 5/9] Fix cspell error --- .../azure/monitor/opentelemetry/exporter/_utils.py | 4 ++-- .../tests/test_utils.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py index 7e9b9d6a2695..a01120e5a8fa 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_utils.py @@ -352,7 +352,7 @@ def _is_any_synthetic_source(properties: Optional[Any]) -> bool: # pylint: disable=W0622 def _filter_custom_properties(properties: Attributes, filter=None) -> Dict[str, str]: max_length = 64 * 1024 - max_length_for_genai_attributes = 256 * 1024 + max_length_for_gen_ai_attributes = 256 * 1024 processed_properties: Dict[str, str] = {} if not properties: return processed_properties @@ -366,7 +366,7 @@ def _filter_custom_properties(properties: Attributes, filter=None) -> Dict[str, if not key or len(key) > 150 or val is None: continue if key in _GEN_AI_ATTRIBUTES: - processed_properties[key] = str(val)[:max_length_for_genai_attributes] + processed_properties[key] = str(val)[:max_length_for_gen_ai_attributes] else: processed_properties[key] = str(val)[:max_length] return processed_properties diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py index 0de901d6e394..984c056014dd 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_utils.py @@ -83,7 +83,7 @@ def test_filter_custom_properties_non_gen_ai_truncated_at_64kb(self): def test_filter_custom_properties_mixed_gen_ai_and_regular(self): # Gen AI attributes truncated at 256kb, regular ones are truncated at 64kb max_length = 64 * 1024 - max_length_for_genai_attributes = 256 * 1024 + max_length_for_gen_ai_attributes = 256 * 1024 large_value = "z" * (1024 * 1024 + 3000) properties = { "gen_ai.input.messages": large_value, @@ -94,8 +94,8 @@ def test_filter_custom_properties_mixed_gen_ai_and_regular(self): } filtered = _utils._filter_custom_properties(properties) - self.assertEqual(len(filtered["gen_ai.input.messages"]), max_length_for_genai_attributes) - self.assertEqual(len(filtered["gen_ai.output.messages"]), max_length_for_genai_attributes) + self.assertEqual(len(filtered["gen_ai.input.messages"]), max_length_for_gen_ai_attributes) + self.assertEqual(len(filtered["gen_ai.output.messages"]), max_length_for_gen_ai_attributes) self.assertEqual(len(filtered["gen_ai.agent.version"]), max_length) self.assertEqual(len(filtered["span_kind"]), max_length) @@ -103,14 +103,14 @@ def test_filter_custom_properties_mixed_gen_ai_and_regular(self): def test_custom_properties_gen_ai_attributes_truncated_at_256kb(self): # All values in _GEN_AI_ATTRIBUTES should be truncated when > 256kb - max_length_for_genai_attributes = 256 * 1024 + max_length_for_gen_ai_attributes = 256 * 1024 large_value = "x" * (256 * 1024 + 1000) properties = {key: large_value for key in _GEN_AI_ATTRIBUTES} filtered = _utils._filter_custom_properties(properties) for key in _GEN_AI_ATTRIBUTES: with self.subTest(key=key): self.assertIn(key, filtered) - self.assertEqual(len(filtered[key]), max_length_for_genai_attributes) + self.assertEqual(len(filtered[key]), max_length_for_gen_ai_attributes) def test_nanoseconds_to_duration(self): ns_to_duration = _utils.ns_to_duration From b90847032aa13e432ac2d980f402f992ea874507 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 13:08:31 -0700 Subject: [PATCH 6/9] Retrigger CI/CD pipeline From b4bab0ed03e48d10e92a33038ea15489f07f65fd Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 13:13:44 -0700 Subject: [PATCH 7/9] Retrigger CI/CD pipeline From b75cf82ea9123d867aaa9d89299742fb2c269efd Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 13:38:57 -0700 Subject: [PATCH 8/9] Retrigger CI/CD pipeline From 3c9403ea4688fc9427afb727296dec4401dcb25d Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 17 Mar 2026 14:01:02 -0700 Subject: [PATCH 9/9] Retrigger CI/CD pipeline