Skip to content

OpenTelemetry: ActivitySources, span names, attributes

The .NET suite emits OpenTelemetry spans on three named ActivitySources. The names, span shapes, and attribute keys are stable contracts — span names won’t change between minor releases.

ActivitySourceEmitted bySpan name pattern
Paylera.SdkPaylera.Sdk outbound API callsPaylera.<Resource>.<Verb>
Paylera.RelayPaylera.AspNetCore relay routesPaylera.Relay.<Op>
Paylera.WebhookPaylera.AspNetCore webhook receiverPaylera.Webhook.<EventType>

All three are no-ops when the host hasn’t registered an OTel SDK — zero allocations, zero CPU cost when unused.

Register a tracer provider

using OpenTelemetry.Trace;
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Paylera.Sdk")
.AddSource("Paylera.Relay")
.AddSource("Paylera.Webhook")
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter());

You can opt into a subset — e.g. only Paylera.Sdk if you don’t use the relay — but the three names are the only thing you need to know.

Paylera.Sdk spans

One span per outbound API call. Span name is Paylera.<Resource>.<Verb> (e.g. Paylera.Customers.Get, Paylera.Subscriptions.Create, Paylera.Plans.List).

AttributeTypeNotes
paylera.endpointstringResolved API path, e.g. /v1/customers/{id}. Path parameters are templated — actual IDs go on their own attributes.
paylera.api_modestring"live" or "test" — derived from the token prefix.
paylera.idempotency_keystringFirst 8 characters only. Set only on state-changing verbs.
paylera.customer_idstringSet when the call carries a customer scope.
paylera.subscription_idstringSet when the call carries a subscription scope.
http.methodstringOTel HTTP semantic convention.
http.status_codeintOTel HTTP semantic convention.

Spans set Activity.Status to Error on any response that maps to a typed PayleraException. The exception is recorded via Activity.AddException(...) so trace UIs render it inline.

Paylera.Relay spans

One span per relay route. Span name is Paylera.Relay.<Op> where <Op> is the operation name from the relay protocol:

Span nameRoute
Paylera.Relay.CsrfTokenGET /csrf-token
Paylera.Relay.AttachPOST /attach
Paylera.Relay.CheckPOST /check, GET /check
Paylera.Relay.TrackPOST /track
Paylera.Relay.EntitlementsGET /entitlements
Paylera.Relay.MeGET /me
Paylera.Relay.PlansGET /plans
Paylera.Relay.InvoicesGET /invoices
Paylera.Relay.BillingPortalPOST /billing-portal
Paylera.Relay.UpgradePOST /subscriptions/{id}/upgrade
Paylera.Relay.CancelPOST /subscriptions/{id}/cancel

Attributes:

AttributeWhen set
paylera.endpointAlways — the upstream Paylera path (e.g. /v1/check).
paylera.customer_idOnce the relay has resolved the caller.
paylera.feature_codeOn /check and /track.
paylera.subscription_idOn /entitlements, /upgrade, /cancel.
paylera.tenant_idAlways — mirrors PayleraRelayOptions.TenantId.
http.method, http.routeOTel HTTP semantic conventions.

The relay span is the parent of the Paylera.Sdk span it generates, so a relay trace shows both legs (browser → relay → Paylera) in a single waterfall.

Paylera.Webhook spans

One span per inbound delivery. Span name is Paylera.Webhook.<EventType> — the literal Paylera event-type string, e.g. Paylera.Webhook.invoice.paid.

The span covers both signature verification and handler dispatch, so a 401 (bad signature) shows up as Error status on the same span the handler would have run on.

AttributeNotes
paylera.event_idOnce the body parses.
paylera.event_typeAlways — even on signature failure (taken from the body before validation, then revalidated against the verified signature; the attribute is set last so it’s never trusted ahead of the verify).
paylera.tenant_idMirrors PayleraRelayOptions.TenantId when set.

Handler exceptions are recorded via Activity.AddException. The HTTP response is 500 and Paylera retries per its delivery policy.

Correlation across surfaces

Paylera.Sdk propagates W3C traceparent outbound, so an outbound call inherits the parent context from whatever span called it. Combined with Paylera.AspNetCore’s AddAspNetCoreInstrumentation (or your gRPC / queue instrumentation), a trace can span:

  1. Inbound HTTP request to your API (ASP.NET Core span).
  2. Outbound Paylera call from a service (Paylera.Sdk span).
  3. Paylera’s own processing (proprietary).
  4. Webhook delivery to your receiver (Paylera.Webhook span).

The same trace id ties all four together when the consumer connects the W3C traceparent it received from Paylera back into the receiver context — which the receiver does automatically when the header is present.

Sampling

The .NET suite emits spans unconditionally — sampling is the consumer’s decision and is wired through the OTel TracerProvider. A reasonable default for high-traffic merchants:

.SetSampler(new ParentBasedSampler(new TraceIdRatioBasedSampler(0.10)))

Keep paylera.event_id and paylera.customer_id on the sampled subset so support cases (“we got a duplicate webhook with event_id …”) are addressable from your APM rather than only from Paylera support.