diff --git a/docs/platforms/godot/configuration/options.mdx b/docs/platforms/godot/configuration/options.mdx index 3798c248dd6f46..7822135dd7984a 100644 --- a/docs/platforms/godot/configuration/options.mdx +++ b/docs/platforms/godot/configuration/options.mdx @@ -167,6 +167,44 @@ This option currently only affects Web exports; other platforms always use the s + + +Controls which downstream services can receive tracing headers. + +When you pass a URL to `SentrySpan.get_trace_headers(url)`, the SDK returns headers only if that URL matches one of the targets. + +- A `String` matches when it appears anywhere in the URL. The special string `".*"` matches every URL and is the only entry by default. +- A `RegEx` searches the complete URL. Use an anchored expression to match a specific origin. +- An empty array blocks headers for all URLs passed to `get_trace_headers(url)`. + +Omitting the URL when calling `get_trace_headers()` bypasses this option, including an empty target list. This option doesn't attach headers automatically. + +In **Project Settings > Sentry > Options**, **Trace Propagation Targets** accepts strings only. Add `RegEx` entries in your initialization callback. See Limiting Trace Propagation for an example. + + + + + +Controls whether outgoing tracing headers include the W3C `traceparent` header alongside `sentry-trace` and `baggage`. + +Set this option to `true` when your backend uses OpenTelemetry or another W3C Trace Context-compatible library to continue traces. `SentrySpan.get_trace_headers()` then includes `traceparent` in the returned headers. + +[`trace_propagation_targets`](#trace_propagation_targets) controls where this header is allowed when you pass a URL. For Web exports, also allow `traceparent` in your backend's CORS configuration. + + + + + + + +The organization ID for your Sentry project. + +The SDK tries to extract the ID from your DSN. If it can't be found, or you need to override it when using self-hosted Sentry or a local Relay, set it with this option. + +The ID is included as `sentry-org_id` in the outgoing `baggage` header. Downstream services can use it to check whether an incoming trace belongs to the same organization before continuing it. + + + ## GUI-only Options These options are only available in the **Project Settings** window. diff --git a/docs/platforms/godot/tracing/distributed-tracing/custom-trace-propagation/index.mdx b/docs/platforms/godot/tracing/distributed-tracing/custom-trace-propagation/index.mdx new file mode 100644 index 00000000000000..381213ad8ebe28 --- /dev/null +++ b/docs/platforms/godot/tracing/distributed-tracing/custom-trace-propagation/index.mdx @@ -0,0 +1,86 @@ +--- +title: Custom Trace Propagation +description: "Learn how to attach trace headers to requests and start separate traces in your Godot game." +sidebar_order: 10 +--- + +To follow an operation from your game into a backend service, attach the span's trace headers to the outgoing request. First, set up distributed tracing in both your game and backend. + +## Add Trace Headers to Requests + +`SentrySpan.get_trace_headers(url)` returns a `PackedStringArray` of `Name: Value` strings. You can pass these directly to `HTTPRequest.request()`, `HTTPClient.request()`, or `WebSocketPeer.handshake_headers`. Pass the destination URL so the SDK applies your trace propagation targets. + +This example measures a leaderboard request and adds tracing headers: + +```GDScript {filename:leaderboard.gd} +extends Node + +func fetch_scores() -> void: + var url := "https://api.example.com/scores" + + # Add the request node to the scene tree and limit how long it can wait. + var request := HTTPRequest.new() + request.timeout = 10.0 + add_child(request) + + # Start an inactive span to measure the request, using the active span as its parent if available. + var span := SentrySDK.start_span( + "GET /scores", + {"sentry.op": "http.client"}, + SentrySDK.get_active_span(), + false, # Keep it inactive so telemetry captured while it runs isn't associated with it. + ) + + # Get trace headers to connect the backend operation to this span. + # Passing the URL applies the configured trace propagation targets. + var headers := span.get_trace_headers(url) + + var error := request.request(url, headers) + if error != OK: + # The request couldn't start, so end the span and clean up immediately. + span.set_status(SentrySpan.SPAN_STATUS_ERROR) + span.set_attribute("error.message", error_string(error)) + span.end() + request.queue_free() + return + + # Keep the span open until the request completes or fails. + var response: Array = await request.request_completed + var result: int = response[0] + var status_code: int = response[1] + # Record the HTTP status and check for both network and HTTP errors. + span.set_attribute("http.response.status_code", status_code) + if result == HTTPRequest.RESULT_SUCCESS and status_code < 400: + span.set_status(SentrySpan.SPAN_STATUS_OK) + else: + span.set_status(SentrySpan.SPAN_STATUS_ERROR) + # Finish measuring the request and remove the temporary request node. + span.end() + request.queue_free() +``` + +The span is inactive, so telemetry captured while it runs isn't associated with it. Its trace headers still connect the backend operation to the span. + +Read the headers before ending the span and from the thread that created it. An ended span or a call from another thread returns an empty array and reports an error. + +## Start a New Trace + +Start a separate trace when one piece of work ends and unrelated work begins. For example, call `SentrySDK.start_new_trace()` before each match so its spans and events are grouped separately from earlier matches: + +```GDScript +func prepare_match(match_id: String) -> void: + SentrySDK.start_new_trace() + SentrySDK.with_span("prepare_match", func(span: SentrySpan) -> void: + span.set_attribute("match.id", match_id) + _load_arena() + _spawn_players() + ) +``` + +Existing spans keep the trace they started on, as does telemetry captured while they are active. End the previous operation's spans before starting a new trace if you want subsequent work to use only the new trace. + +## Verify + +Run the request with a sample rate of `1.0`. Inspect the outgoing headers using your backend's request logging or, for Web exports, the browser's network tools. Confirm that `sentry-trace` and `baggage` reach the backend, along with `traceparent` if enabled. + +Then check Sentry for the game span and backend operation in the same trace. Headers alone confirm that the game sent the context; the connected trace confirms that the backend continued it. If a Web request is blocked, check CORS configuration. diff --git a/docs/platforms/godot/tracing/distributed-tracing/dealing-with-cors-issues/index.mdx b/docs/platforms/godot/tracing/distributed-tracing/dealing-with-cors-issues/index.mdx new file mode 100644 index 00000000000000..439638a0df7195 --- /dev/null +++ b/docs/platforms/godot/tracing/distributed-tracing/dealing-with-cors-issues/index.mdx @@ -0,0 +1,27 @@ +--- +title: Dealing with CORS Issues +description: "Learn how to allow Sentry trace headers on requests from Godot Web exports to your backend." +sidebar_order: 80 +--- + +If your Web game calls a backend on another origin, adding tracing headers can cause the browser to block the request unless the backend allows them. For example, a game at `https://game.example.com` calling `https://api.example.com` needs the backend's permission to make that cross-origin request. + +## Allow Trace Headers on Your Backend + +Configure your backend's CORS headers to allow `sentry-trace` and `baggage`: + +```http +Access-Control-Allow-Headers: sentry-trace, baggage +``` + +The exact server configuration depends on your setup. + +If you enable `propagate_traceparent`, also allow that header: + +```http +Access-Control-Allow-Headers: sentry-trace, baggage, traceparent +``` + +## Check the SDK Targets Too + +If you've restricted trace propagation targets, make sure they include your backend URL so `get_trace_headers(url)` returns tracing headers for it. diff --git a/docs/platforms/godot/tracing/distributed-tracing/index.mdx b/docs/platforms/godot/tracing/distributed-tracing/index.mdx new file mode 100644 index 00000000000000..4b55844b392de7 --- /dev/null +++ b/docs/platforms/godot/tracing/distributed-tracing/index.mdx @@ -0,0 +1,43 @@ +--- +title: Set Up Distributed Tracing +description: "Learn how to connect operations in your Godot game with requests to your backend services." +sidebar_order: 30 +--- + + + +Distributed tracing connects work in your game with work in your backend. For example, a trace can show how much time a leaderboard request spends in the game, your API, and its database. + +## Connect Your Game and Backend + +The Godot SDK provides trace headers that you attach to outgoing requests manually. It doesn't instrument HTTP requests automatically. + +1. Enable tracing in your game. +2. Set up the [Sentry SDK](/platforms/) for your backend and enable its distributed tracing support. +3. Follow Custom Trace Propagation to attach headers from a span to your request. + +The receiving service uses these headers to continue the same trace: + +- `sentry-trace` identifies the trace, the sending span, and its sampling decision. +- `baggage` carries additional trace metadata, such as the sample rate and organization ID. +- `traceparent` provides the W3C trace context when you enable `propagate_traceparent`. + +Restrict headers to your own services with Limiting Trace Propagation. For Web exports, also configure your backend to allow the headers through CORS. + +Make sure any proxies, gateways, or firewalls between your game and backend preserve the tracing headers. + +## Trace Duration + +The SDK starts a trace when it initializes. Spans and events share that trace until you start a new one. Ending a span finishes only that operation; it doesn't start a new trace. + +To separate unrelated work, such as game matches, start a new trace before starting the next operation. + +## How Sampling Propagates + +The outgoing `sentry-trace` header carries the span's sampling decision so downstream services can honor it. With `traces_sample_rate` set to its default of `0.0`, the SDK still provides trace headers, but the span isn't sent and the headers mark the trace as unsampled. + +Use a sample rate of `1.0` in your game and backend while verifying the connection, then adjust it for production. + +## Next Steps + + diff --git a/docs/platforms/godot/tracing/distributed-tracing/limiting-trace-propagation/index.mdx b/docs/platforms/godot/tracing/distributed-tracing/limiting-trace-propagation/index.mdx new file mode 100644 index 00000000000000..66a913c18edcac --- /dev/null +++ b/docs/platforms/godot/tracing/distributed-tracing/limiting-trace-propagation/index.mdx @@ -0,0 +1,25 @@ +--- +title: Limiting Trace Propagation +description: "Learn how to restrict outgoing trace headers to trusted backend services in your Godot game." +sidebar_order: 100 +--- + +By default, the SDK returns tracing headers for any destination URL. To restrict propagation to specific services, configure `trace_propagation_targets`: + +```GDScript {filename:ProjectMainLoop.gd} +class_name ProjectMainLoop +extends SceneTree + +func _initialize() -> void: + SentrySDK.init(func(options: SentryOptions) -> void: + options.trace_propagation_targets = [ + RegEx.create_from_string("^https://api\\.example\\.com/"), + ] + ) +``` + +This matches URLs such as `https://api.example.com/scores`. See Programmatic Configuration for the manual initialization setup. + +Pass the destination URL to `get_trace_headers(url)` to apply these targets. Unmatched URLs receive no tracing headers; an empty target list blocks all URLs. Calling `get_trace_headers()` without a URL bypasses filtering. You still need to attach the returned headers to your request. + +The option reference explains string and regular expression matching and configuration through Project Settings. diff --git a/docs/platforms/godot/tracing/index.mdx b/docs/platforms/godot/tracing/index.mdx index 1cdb45821d7af9..4e96b07a4f2100 100644 --- a/docs/platforms/godot/tracing/index.mdx +++ b/docs/platforms/godot/tracing/index.mdx @@ -42,6 +42,10 @@ Web exports can also stream completed spans instead of holding them until the ro Create and end a span around a known operation, then run the game and confirm that the trace appears in Sentry. The SDK for Godot Engine doesn't create spans automatically yet, so continue with Instrumentation to add one. +## Distributed Tracing + +To follow a request from your game into your backend, set up distributed tracing and attach the span's trace headers to the request. + ## Next Steps diff --git a/docs/platforms/godot/tracing/instrumentation/index.mdx b/docs/platforms/godot/tracing/instrumentation/index.mdx index 080a72b58247a9..1c10e37704573c 100644 --- a/docs/platforms/godot/tracing/instrumentation/index.mdx +++ b/docs/platforms/godot/tracing/instrumentation/index.mdx @@ -115,6 +115,8 @@ child_span.end() Pass `null` as `parent_span` to force a new root span. Call `SentrySDK.get_active_span()` when lower-level code needs to read the span attached to the current scope. +Starting a span doesn't start a new trace. To start a separate trace—for example, for a new match—call `SentrySDK.start_new_trace()`. See Start a New Trace. + ## Start an Inactive Span Use an inactive span when work should be grouped under a parent but remain independent of the currently active span. Because it doesn't become active, it doesn't affect `SentrySDK.get_active_span()`, make new spans its children automatically, or add its trace context to telemetry captured alongside it. @@ -179,3 +181,7 @@ A span without an explicit status is treated as successful. ## Spans and Threads Spans belong to the thread that created them. Call their methods only from that thread. If work moves to another thread, start and end its spans inside that threaded function instead of passing a span across threads. + +## Distributed Tracing + +To connect a span with work in a backend service, attach its trace headers to the outgoing request.