Skip to content

Commit c6f834d

Browse files
committed
feat(gcp): Add span streaming support to GCP Cloud Functions integration
Migrate GCP integration to support spans-first tracing via the span streaming API. The integration now supports both: - Span streaming (new trace lifecycle) - Traditional transaction-based approach (fallback) Add request attributes to spans including headers, HTTP method, and query string. Include cloud provider context and segment source in span metadata. Add comprehensive tests for span streaming functionality including error handling, trace context propagation, and PII filtering for query strings. Depends on getsentry/sentry-conventions#403 being merged first. Fixes PY-2324 Fixes #6022
1 parent 7bf1d6e commit c6f834d

2 files changed

Lines changed: 263 additions & 29 deletions

File tree

sentry_sdk/integrations/gcp.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
from sentry_sdk.consts import OP
1111
from sentry_sdk.integrations import Integration
1212
from sentry_sdk.integrations._wsgi_common import _filter_headers
13-
from sentry_sdk.scope import should_send_default_pii
13+
from sentry_sdk.integrations.cloud_resource_context import CLOUD_PROVIDER
14+
from sentry_sdk.scope import Scope, should_send_default_pii
15+
from sentry_sdk.traces import SegmentSource
1416
from sentry_sdk.tracing import TransactionSource
17+
from sentry_sdk.tracing_utils import has_span_streaming_enabled
1518
from sentry_sdk.utils import (
1619
AnnotatedValue,
1720
TimeoutThread,
@@ -82,16 +85,26 @@ def sentry_func(
8285
timeout_thread.start()
8386

8487
headers = {}
88+
header_attributes: "dict[str, Any]" = {}
8589
if hasattr(gcp_event, "headers"):
8690
headers = gcp_event.headers
91+
for header, header_value in _filter_headers(
92+
headers, use_annotated_value=False
93+
).items():
94+
header_attributes[f"http.request.header.{header.lower()}"] = (
95+
# header_value will always be a string because we set `use_annotated_value` to false above
96+
header_value
97+
)
98+
99+
additional_attributes = {}
100+
if hasattr(gcp_event, "method"):
101+
additional_attributes["http.request.method"] = gcp_event.method
102+
103+
if should_send_default_pii() and hasattr(gcp_event, "query_string"):
104+
additional_attributes["url.query"] = gcp_event.query_string.decode(
105+
"utf-8"
106+
)
87107

88-
transaction = continue_trace(
89-
headers,
90-
op=OP.FUNCTION_GCP,
91-
name=environ.get("FUNCTION_NAME", ""),
92-
source=TransactionSource.COMPONENT,
93-
origin=GcpIntegration.origin,
94-
)
95108
sampling_context = {
96109
"gcp_env": {
97110
"function_name": environ.get("FUNCTION_NAME"),
@@ -102,9 +115,36 @@ def sentry_func(
102115
},
103116
"gcp_event": gcp_event,
104117
}
105-
with sentry_sdk.start_transaction(
106-
transaction, custom_sampling_context=sampling_context
107-
):
118+
119+
if has_span_streaming_enabled(client.options):
120+
sentry_sdk.traces.continue_trace(headers)
121+
Scope.set_custom_sampling_context(sampling_context)
122+
span_ctx = sentry_sdk.traces.start_span(
123+
name=environ.get("FUNCTION_NAME", ""),
124+
parent_span=None,
125+
attributes={
126+
"sentry.op": OP.FUNCTION_GCP,
127+
"sentry.origin": GcpIntegration.origin,
128+
"sentry.span.source": SegmentSource.COMPONENT,
129+
"cloud.provider": CLOUD_PROVIDER.GCP,
130+
**header_attributes,
131+
**additional_attributes,
132+
},
133+
)
134+
else:
135+
transaction = continue_trace(
136+
headers,
137+
op=OP.FUNCTION_GCP,
138+
name=environ.get("FUNCTION_NAME", ""),
139+
source=TransactionSource.COMPONENT,
140+
origin=GcpIntegration.origin,
141+
)
142+
143+
span_ctx = sentry_sdk.start_transaction(
144+
transaction, custom_sampling_context=sampling_context
145+
)
146+
147+
with span_ctx:
108148
try:
109149
return func(functionhandler, gcp_event, *args, **kwargs)
110150
except Exception:

0 commit comments

Comments
 (0)