Skip to content

Commit c3aab39

Browse files
authored
feat(gcp): Add span streaming support to GCP Cloud Functions integration (#6440)
Migrate GCP integration to support spans-first tracing via the span streaming API. Fixes PY-2324 and #6022
1 parent 4488d15 commit c3aab39

2 files changed

Lines changed: 300 additions & 30 deletions

File tree

sentry_sdk/integrations/gcp.py

Lines changed: 68 additions & 12 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", errors="replace"
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,50 @@ 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+
function_name = environ.get("FUNCTION_NAME", "<unknown GCP function>")
120+
121+
if environ.get("GCP_PROJECT"):
122+
additional_attributes["gcp.project.id"] = environ.get("GCP_PROJECT")
123+
124+
if environ.get("FUNCTION_IDENTITY"):
125+
additional_attributes["faas.identity"] = environ.get(
126+
"FUNCTION_IDENTITY"
127+
)
128+
129+
if environ.get("ENTRY_POINT"):
130+
additional_attributes["faas.entry_point"] = environ.get("ENTRY_POINT")
131+
132+
if has_span_streaming_enabled(client.options):
133+
sentry_sdk.traces.continue_trace(headers)
134+
Scope.set_custom_sampling_context(sampling_context)
135+
span_ctx = sentry_sdk.traces.start_span(
136+
name=function_name,
137+
parent_span=None,
138+
attributes={
139+
"sentry.op": OP.FUNCTION_GCP,
140+
"sentry.origin": GcpIntegration.origin,
141+
"sentry.span.source": SegmentSource.COMPONENT,
142+
"cloud.provider": CLOUD_PROVIDER.GCP,
143+
"faas.name": function_name,
144+
**header_attributes,
145+
**additional_attributes,
146+
},
147+
)
148+
else:
149+
transaction = continue_trace(
150+
headers,
151+
op=OP.FUNCTION_GCP,
152+
name=environ.get("FUNCTION_NAME", ""),
153+
source=TransactionSource.COMPONENT,
154+
origin=GcpIntegration.origin,
155+
)
156+
157+
span_ctx = sentry_sdk.start_transaction(
158+
transaction, custom_sampling_context=sampling_context
159+
)
160+
161+
with span_ctx:
108162
try:
109163
return func(functionhandler, gcp_event, *args, **kwargs)
110164
except Exception:
@@ -181,7 +235,9 @@ def event_processor(event: "Event", hint: "Hint") -> "Optional[Event]":
181235
request["method"] = gcp_event.method
182236

183237
if hasattr(gcp_event, "query_string"):
184-
request["query_string"] = gcp_event.query_string.decode("utf-8")
238+
request["query_string"] = gcp_event.query_string.decode(
239+
"utf-8", errors="replace"
240+
)
185241

186242
if hasattr(gcp_event, "headers"):
187243
request["headers"] = _filter_headers(gcp_event.headers)

0 commit comments

Comments
 (0)