From 86c933bdf3297e0c0093053fe189db0f8ebdd20d Mon Sep 17 00:00:00 2001 From: aaronabbott Date: Tue, 11 Aug 2026 02:00:06 +0000 Subject: [PATCH] Update legacy Cloud Trace exporter attribute and span limits to match backend The Cloud Trace backend attribute and size limits have been updated to align with the OpenTelemetry OTLP Telemetry limits (https://cloud.google.com/trace/docs/quotas#telemetry-limits). This PR updates the legacy Cloud Trace exporter constants and truncation bounds accordingly: - `MAX_SPAN_ATTRS`: 32 -> 1024 - `MAX_LINK_ATTRS`: 32 -> 1024 - `MAX_EVENT_ATTRS`: 4 -> 1024 - `MAX_NUM_EVENTS`: 32 -> 256 - `MAX_ATTR_KEY_BYTES`: 128 -> 512 - `MAX_ATTR_VAL_BYTES`: 16 KiB -> 64 KiB - Span name display length limit: 128 -> 1024 - Event name description length limit: 256 -> 1024 --- opentelemetry-exporter-gcp-trace/CHANGELOG.md | 2 + .../exporter/cloud_trace/__init__.py | 16 +++--- .../tests/test_cloud_trace_exporter.py | 50 ++++++++++++++----- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/opentelemetry-exporter-gcp-trace/CHANGELOG.md b/opentelemetry-exporter-gcp-trace/CHANGELOG.md index feb5eb93..f5779172 100644 --- a/opentelemetry-exporter-gcp-trace/CHANGELOG.md +++ b/opentelemetry-exporter-gcp-trace/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Update span and attribute limits to match Cloud Trace backend + ## Version 1.14.0 Released 2026-08-03 diff --git a/opentelemetry-exporter-gcp-trace/src/opentelemetry/exporter/cloud_trace/__init__.py b/opentelemetry-exporter-gcp-trace/src/opentelemetry/exporter/cloud_trace/__init__.py index 4b06e25e..11b296a1 100644 --- a/opentelemetry-exporter-gcp-trace/src/opentelemetry/exporter/cloud_trace/__init__.py +++ b/opentelemetry-exporter-gcp-trace/src/opentelemetry/exporter/cloud_trace/__init__.py @@ -137,12 +137,12 @@ MAX_NUM_LINKS = 128 -MAX_NUM_EVENTS = 32 -MAX_EVENT_ATTRS = 4 -MAX_LINK_ATTRS = 32 -MAX_SPAN_ATTRS = 32 -MAX_ATTR_KEY_BYTES = 128 -MAX_ATTR_VAL_BYTES = 16 * 1024 # 16 kilobytes +MAX_NUM_EVENTS = 256 +MAX_EVENT_ATTRS = 1024 +MAX_LINK_ATTRS = 1024 +MAX_SPAN_ATTRS = 1024 +MAX_ATTR_KEY_BYTES = 512 +MAX_ATTR_VAL_BYTES = 64 * 1024 # 64 kilobytes def _create_default_client() -> TraceServiceClient: @@ -260,7 +260,7 @@ def _translate_to_cloud_trace( trace_types.Span( name=span_name, span_id=span_id, - display_name=_get_truncatable_str_object(span.name, 128), + display_name=_get_truncatable_str_object(span.name, 1024), start_time=start_time, end_time=end_time, parent_span_id=parent_id, @@ -400,7 +400,7 @@ def _extract_events( trace_types.Span.TimeEvent( time=_get_time_from_ns(event.timestamp), annotation=trace_types.Span.TimeEvent.Annotation( - description=_get_truncatable_str_object(event.name, 256), + description=_get_truncatable_str_object(event.name, 1024), attributes=_extract_attributes( event.attributes, MAX_EVENT_ATTRS ), diff --git a/opentelemetry-exporter-gcp-trace/tests/test_cloud_trace_exporter.py b/opentelemetry-exporter-gcp-trace/tests/test_cloud_trace_exporter.py index 69c3bced..9abe4499 100644 --- a/opentelemetry-exporter-gcp-trace/tests/test_cloud_trace_exporter.py +++ b/opentelemetry-exporter-gcp-trace/tests/test_cloud_trace_exporter.py @@ -102,8 +102,12 @@ def setUpClass(cls): cls.example_span_id = "95bb5edabd45950f" cls.example_time_in_ns = 1589919268850900051 cls.example_time_stamp = _get_time_from_ns(cls.example_time_in_ns) - cls.str_20kb = "a" * 20 * 1024 - cls.str_16kb = "a" * 16 * 1024 + cls.str_70kb = "a" * 70 * 1024 + cls.str_64kb = "a" * 64 * 1024 + cls.str_1100 = "a" * 1100 + cls.str_1024 = "a" * 1024 + cls.str_600 = "a" * 600 + cls.str_512 = "a" * 512 cls.str_300 = "a" * 300 cls.str_256 = "a" * 256 cls.str_128 = "a" * 128 @@ -338,10 +342,10 @@ def test_agent_attribute_priority(self): def test_attribute_value_truncation(self): # shouldn't truncate self.assertEqual( - _format_attribute_value(self.str_300), + _format_attribute_value(self.str_600), AttributeValue( string_value=TruncatableString( - value=self.str_300, + value=self.str_600, truncated_byte_count=0, ) ), @@ -349,11 +353,11 @@ def test_attribute_value_truncation(self): # huge string should truncate self.assertEqual( - _format_attribute_value(self.str_20kb), + _format_attribute_value(self.str_70kb), AttributeValue( string_value=TruncatableString( - value=self.str_16kb, - truncated_byte_count=(20 - 16) * 1024, + value=self.str_64kb, + truncated_byte_count=(70 - 64) * 1024, ) ), ) @@ -395,11 +399,11 @@ def test_list_attribute_value(self): def test_attribute_key_truncation(self): self.assertEqual( _extract_attributes( - {self.str_300: "attr_value"}, num_attrs_limit=4 + {self.str_600: "attr_value"}, num_attrs_limit=4 ), ProtoSpan.Attributes( attribute_map={ - self.str_128: AttributeValue( + self.str_512: AttributeValue( string_value=TruncatableString( value="attr_value", truncated_byte_count=0 ) @@ -504,7 +508,9 @@ def test_extract_multiple_events(self): def test_event_name_truncation(self): event1 = Event( - name=self.str_300, attributes={}, timestamp=self.example_time_in_ns + name=self.str_1100, + attributes={}, + timestamp=self.example_time_in_ns, ) self.assertEqual( _extract_events([event1]), @@ -514,8 +520,8 @@ def test_event_name_truncation(self): "time": self.example_time_stamp, "annotation": { "description": TruncatableString( - value=self.str_256, - truncated_byte_count=300 - 256, + value=self.str_1024, + truncated_byte_count=1100 - 1024, ), "attributes": {}, }, @@ -780,3 +786,23 @@ def test_extract_span_kind(self): self.assertEqual( _extract_span_kind(-1), ProtoSpan.SpanKind.SPAN_KIND_UNSPECIFIED ) + + def test_span_name_truncation(self): + exporter = CloudTraceSpanExporter(self.project_id) + span_data = Span( + name=self.str_1100, + context=SpanContext( + trace_id=int(self.example_trace_id, 16), + span_id=int(self.example_span_id, 16), + is_remote=False, + ), + ) + # pylint: disable=protected-access + translated_spans = exporter._translate_to_cloud_trace([span_data]) + self.assertEqual( + translated_spans[0].display_name, + TruncatableString( + value=self.str_1024, + truncated_byte_count=1100 - 1024, + ), + )