From b968ecce3ebce1453947fef4784a099c04178fae Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 16 Jul 2026 20:51:01 +1200 Subject: [PATCH] docs(dotnet): Document RecordTransaction for already-completed transactions Adds a third section to the .NET custom instrumentation page covering the RecordTransaction API added in getsentry/sentry-dotnet#5333, which records transactions and spans that completed elsewhere (e.g. replayed through a proxy) with explicit timing. Co-Authored-By: Claude Fable 5 --- .../custom-instrumentation/index.mdx | 2 + .../record-completed-transactions/dotnet.mdx | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 platform-includes/performance/record-completed-transactions/dotnet.mdx diff --git a/docs/platforms/dotnet/common/tracing/instrumentation/custom-instrumentation/index.mdx b/docs/platforms/dotnet/common/tracing/instrumentation/custom-instrumentation/index.mdx index 1ccb9b0eb02be..73bcf1eb92f00 100644 --- a/docs/platforms/dotnet/common/tracing/instrumentation/custom-instrumentation/index.mdx +++ b/docs/platforms/dotnet/common/tracing/instrumentation/custom-instrumentation/index.mdx @@ -15,3 +15,5 @@ To capture transactions and spans customized to your organization's needs, you m + + diff --git a/platform-includes/performance/record-completed-transactions/dotnet.mdx b/platform-includes/performance/record-completed-transactions/dotnet.mdx new file mode 100644 index 0000000000000..633a9e0c06629 --- /dev/null +++ b/platform-includes/performance/record-completed-transactions/dotnet.mdx @@ -0,0 +1,40 @@ +## Record Already-Completed Transactions + + + +Requires Sentry SDK version `6.8.0` or higher. + + + +`StartTransaction` measures work as it happens, using a live stopwatch. Sometimes, though, the work you want to report has already finished somewhere else — for example, spans measured on another machine or in another process and relayed to your application through a proxy. For those cases, use `SentrySdk.RecordTransaction` to record a transaction whose timing you supply explicitly: + +```csharp +SentrySdk.RecordTransaction( + "checkout", // name + "http.server", // operation + originalStartTimestamp, // when the work started (DateTimeOffset) + originalDuration, // how long it ran (TimeSpan, must not be negative) + traceId: originTraceId, // optional: preserve the trace id from the originating system + spanId: originRootSpanId, // optional: preserve the root span id + parentSpanId: originParentId, // optional: continue a trace from another service + configure: transaction => + { + transaction.Release = "1.2.3"; + transaction.SetTag("origin", "proxy"); + + // Child spans also take their timing up front and can be nested arbitrarily + transaction.RecordSpan("db.query", queryStart, queryDuration, configure: span => + { + span.Description = "SELECT * FROM orders"; + span.RecordSpan("db.connection", connectStart, connectDuration); + }); + }); +``` + +The transaction is captured and sent to Sentry once the `configure` callback returns — there is no `Finish()` to call. + +Because a recorded transaction represents work that happened elsewhere, it behaves differently from one started with `StartTransaction`: + +- **Timing is explicit.** Each transaction and span takes a start timestamp and a duration up front, rather than being measured live. +- **Sampling is skipped.** Recorded transactions are always sent, regardless of or . Your callback still runs, so you can filter or scrub there. +- **The current scope doesn't apply.** The transaction is captured against a clean scope, so breadcrumbs, user data, and tags from the current process won't leak onto work that happened elsewhere. To attach scope data that belongs with the original trace, use `transaction.ConfigureScope` inside the callback, or the `Release` and `Environment` setters.