Concepts

OpenTelemetry

How Yorker emits standard OTLP signals and correlates with your existing observability stack.

OpenTelemetry

Yorker is OTel-native. Every check emits standard OpenTelemetry signals -- metrics and traces -- using the OTLP HTTP JSON protocol. There is no proprietary telemetry format. If your backend speaks OTel, it works with Yorker.

Why OTel-native matters

Most synthetic monitoring tools store results in a proprietary system. When something breaks, you switch between your monitoring dashboard and your observability platform, manually correlating timestamps and URLs.

Yorker eliminates that context switch. Your synthetic check results land in the same backend as your application traces, logs, and metrics. A failing health check and the 500 error it triggered share the same trace ID.

Metrics emitted

Runner-direct OTLP emission (today: browser checks on any location, plus HTTP/MCP checks on private locations that have OTLP_ENDPOINT set) produces these metrics as OTLP gauge data points:

MetricTypeDescription
synthetics.http.response_timeGauge (ms)Total response time from request start to last byte received.
synthetics.check.successGauge (0 or 1)Whether the check passed all assertions.
synthetics.dns.lookup_durationGauge (ms)Time spent resolving DNS.
synthetics.tls.handshake_durationGauge (ms)Time spent on TLS handshake.
synthetics.browser.lcpGauge (ms)Largest Contentful Paint (browser checks only).
synthetics.browser.fcpGauge (ms)First Contentful Paint (browser checks only).
synthetics.browser.clsGauge (score)Cumulative Layout Shift (browser checks only).

These metrics follow OpenTelemetry semantic conventions for synthetic monitoring where they exist, and use the synthetics.* namespace for domain-specific signals.

Once you have configured an OTLP endpoint under Settings > Telemetry (OTLP), the control plane outbox path produces matching log events (synthetics.check.completed, synthetics.check.failed) for every check regardless of type or location. Those log event bodies carry the same response time, status, and timing breakdown as the runner-emitted metrics, so HTTP and MCP checks on Yorker-hosted locations still land observable data in your collector; you just query log events instead of gauges for those. Until you configure an endpoint, no outbox events are enqueued at all.

Resource attributes

Every metric, trace, and log event includes resource attributes that identify the check, location, and run:

AttributeExampleDescription
synthetics.check.idchk_abc123Unique check identifier.
synthetics.check.nameHomepageHuman-readable check name.
synthetics.check.typehttp, browser, or mcpCheck type.
synthetics.location.idloc_us_eastLocation identifier.
synthetics.location.nameUS East (Ashburn)Human-readable location name.
synthetics.location.typehosted or privateWhether the location is Yorker-hosted or a private location.
synthetics.run.idrun_xyz789Unique identifier for this specific execution.
url.fullhttps://example.comThe URL being monitored.
service.namesyntheticsService name used by both runner-direct emissions and control-plane outbox events.

These attributes let you filter, group, and alert on synthetic check data in your observability backend the same way you would with any other OTel-instrumented service.

Labels as resource attributes

Any labels attached to a check are emitted as additional resource attributes on every metric and trace. This lets you slice telemetry by your own dimensions (environment, service, team, criticality) without having to map check IDs back to metadata in your observability backend.

Label formatResource attribute
env:productionyorker.label.env="production"
service:paymentsyorker.label.service="payments"
critical (no colon)yorker.label.critical="true"

See Create a Monitor: Labels for how to attach labels.

Third-party network attribution

Browser checks emit four additional span attributes on the root synthetics.check.run span that describe the third-party network activity observed during the page load. These attributes are emitted whenever the browser executor produced a network summary, which is typical on every completed browser-check run. They are not emitted when the browser executor failed before the network summary was produced (for example, a browser launch failure), and they are not emitted on HTTP checks.

AttributeTypeUnitDescription
synthetics.third_party.countintrequestsNumber of network requests classified as third-party (hostname differs from the page origin).
synthetics.third_party.total_bytesintbytesSum of decoded response sizes for all third-party requests in the page load.
synthetics.third_party.domainsstring[]--Unique third-party hostnames observed, capped at 50 entries. Example: ["cdn.tagmanager.net", "fonts.googleapis.com"].
synthetics.third_party.domains_truncatedboolean--true when the domains list was capped (the page had more than 50 unique third-party hostnames); false otherwise. Use this attribute for deterministic truncation detection. A page can legitimately have exactly 50 unique third-party domains, so domains.length == 50 alone is not a reliable truncation signal.

A request is classified as third-party when its hostname differs from the top-level page hostname. Subdomains are treated as separate domains (cdn.example.com is third-party to example.com).

When a page load has zero third-party requests all four attributes are still emitted (count=0, total_bytes=0, domains=[], domains_truncated=false) so queries can distinguish "confirmed zero third parties" from "no data" (the attributes are absent on HTTP checks).

Cross-monitor correlation events

When two or more browser monitors in a team fail within a 5-minute window and observe the same third-party dependency, Yorker emits one synthetics.correlation.detected OTLP log event per distinct correlation slice. The event is co-occurrence-only: it proves the same dependency was observed in multiple failing runs, not that the dependency caused the failures.

The event fires for browser monitors only. HTTP and MCP checks do not populate the network-summary data the detection reads, so no correlation event is emitted for those check types. If a team has only HTTP or MCP checks, this event will never fire.

Attributes

AttributeTypeDescription
synthetics.correlation.affected_monitorsstring[]Check IDs identified in the scan as failing and observing the shared dependency. Includes the triggering check. Sorted lexicographically. The array is capped at 50 entries to keep OTLP payloads under per-attribute size limits enforced by some backends; use affected_monitors_truncated to detect when the cap was hit. The underlying detection scan is itself bounded by 200 candidate rows per ingest.
synthetics.correlation.affected_monitors_truncatedbooleantrue when the emitted affected_monitors array was capped (more than 50 distinct check IDs were identified); false otherwise. When true, treat affected_count (not affected_monitors.length) as the authoritative cardinality.
synthetics.correlation.affected_countintNumber of distinct monitors identified in the scan. Always >= 2 for the event to fire. Authoritative cardinality, unaffected by the affected_monitors cap. The detection scan is bounded by 200 candidate rows per ingest, so under exceptionally high failure volume affected_count itself may be a lower bound on the full set.
synthetics.correlation.shared_dependencystringThe third-party domain indexed by the most distinct affected check IDs among domains the triggering result also observed.
event.namestringAlways synthetics.correlation.detected.

The standard resource attributes (synthetics.check.id, synthetics.check.name, synthetics.check.type, synthetics.location.id, synthetics.location.name, synthetics.run.id, plus any user labels) come from the triggering check that produced the event.

Threshold and dedup contract

The event fires when, for a triggering failing result with parseable third-party domains, two or more distinct check IDs (including the triggering one) within the 5-minute window have failed AND observed at least one of the triggering result's domains.

The window is [triggering result's DB-stamped created_at - 5 min, triggering result's created_at], inclusive on both ends. Anchored to DB timestamps, not wall clock.

Dedup is keyed by the tuple (team, shared_dependency, affected_monitor_set). A sustained outage that keeps the same set of monitors failing produces one event per window for that combination. If the affected-monitor set changes (a new monitor joins the correlation, or a recovering monitor drops out) the combination key changes and a new event is emitted for the updated slice. This is the intended behaviour: the event represents a specific co-occurrence slice, and a different slice deserves its own event.

What this is and is not

The event is a co-occurrence signal. It indicates "these N monitors all failed within 5 minutes and they all touch this third-party domain." It is not root-cause attribution: the third-party may itself be a victim of a deeper outage your monitors cannot observe, or the failures may be coincidental and the shared domain irrelevant. Investigate before paging the vendor.

If you want fine-grained correlation evidence, the alerting UI already provides it (per-incident hypothesis, scoped dimensions). The OTel event is for getting the same signal into your observability backend without polling Yorker's API.

Trace correlation

Yorker injects a W3C traceparent header into outbound requests during check execution. This is how it works:

  1. The runner generates a trace ID for the check execution.
  2. The traceparent header is added to the HTTP request (or injected into the browser's network requests for browser checks).
  3. Your backend application picks up the trace context via its own OTel instrumentation.
  4. The synthetic check span and your backend request span share the same distributed trace.

The result: when a check fails, you can click from the Yorker alert directly to the distributed trace in your observability backend. You see the synthetic request, the backend handler, the database query, and the error -- in one view.

Backend compatibility

Yorker works with any OTel-compatible backend. All Yorker telemetry is emitted as OTLP HTTP JSON, the most widely supported OTel transport. Tested backends include:

  • ClickStack (ClickHouse + HyperDX)
  • Grafana Cloud (Tempo + Mimir)
  • Datadog
  • Honeycomb
  • New Relic
  • Jaeger
  • Any OTLP-compatible collector (OpenTelemetry Collector, Alloy, Vector)

Emission model

Yorker has two OTel emitters, and they both target the same otlpEndpoint you configure on your team:

  • Runners emit OTLP metrics, traces, and logs directly to your collector, but only for browser checks today. Hosted HTTP and MCP runners do not emit OTLP from the runner process; private-location operators can enable runner-direct emission by setting OTLP_ENDPOINT/OTLP_API_KEY on their runner container when they start it.
  • The orchestrator drains an emission outbox that the control plane writes to, and ships every OTel log/span event Yorker generates to your collector, including synthetics.check.completed, synthetics.check.failed, synthetics.step.completed, alert state changes, SLO burn warnings, TLS certificate events, monitor/team insights, deployment markers, and maintenance-window events. The control plane enqueues; the orchestrator polls the outbox every ~10 seconds, runs SSRF guards, and POSTs.

The metrics catalogued above (synthetics.http.response_time, synthetics.check.success, and friends) are currently produced by the runner-direct path, which means you will see them for browser checks. For HTTP and MCP checks on hosted locations, the same information reaches your collector via the synthetics.check.completed / check.failed log events: they carry responseTimeMs, status, assertion results, timing breakdown, and the same resource attributes as the metrics, so dashboards and queries can key off either signal.

See the Telemetry flow section in Architecture for the full table of which check type and location combinations take which path.

Setup

To configure OTel emission for your team:

  1. Go to Settings > Telemetry (OTLP) in the Yorker dashboard.
  2. Enter your OTLP endpoint URL (e.g., https://otel-collector.example.com:4318).
  3. Add any required authentication headers (API key, bearer token).
  4. Click Test Connection. Yorker's control plane dispatches a test payload and reports success or failure.
  5. Save.

From this point, the control plane starts enqueueing events for the orchestrator to ship (for every check type and every location), and browser-check runners start including the endpoint in each execution payload for runner-direct metric/trace emission. Team-level OTLP credentials are stored on the team, not per-check.