OpenTelemetry
OpenTelemetry
OpenTelemetry is a vendor-neutral standard for collecting telemetry data from running services. Posit Workbench can export structured log events and trace spans over OpenTelemetry Protocol (OTLP) HTTP to any compatible backend.
The OpenTelemetry export sits alongside the existing Prometheus metrics endpoint. Both can be enabled at once, since they cover different signals and answer different questions.
Prerequisites
Using OpenTelemetry to export data requires the Monitoring feature, which is included in the Basic, Enhanced, and Advanced license tiers. Without an active license at one of these tiers, the server accepts the configuration options below but emits no signals.
You also need an OTLP/HTTP-compatible receiver. The OpenTelemetry Collector is one option. Many observability platforms also accept OTLP/HTTP directly.
Configuration
Configure OpenTelemetry export in rserver.conf. The primary option otel-enabled gates all signals. Per-signal options (otel-logs-enabled, otel-traces-enabled) decide which signal types are active.
To send both logs and traces to a single OTLP/HTTP receiver, set:
# /etc/rstudio/rserver.conf
otel-enabled=1
otel-endpoint=http://collector.example.org:4318
otel-logs-enabled=1
otel-traces-enabled=1Workbench appends /v1/logs and /v1/traces to otel-endpoint automatically. Restart Workbench with rstudio-server restart after changing these options.
To send logs and traces to different receivers, set otel-logs-endpoint and otel-traces-endpoint explicitly. Pass headers (for example, authentication tokens) with otel-logs-headers and otel-traces-headers, comma-separated key=value pairs. If otel-traces-headers is empty, Workbench reuses otel-logs-headers for traces.
Below is the full set of OpenTelemetry configuration options:
| Config Option | Description | Possible Values | Default Value |
|---|---|---|---|
| otel-enabled | Primary switch for OpenTelemetry export. When 0, no OTLP signals are emitted regardless of per-signal options. |
0 or 1 |
0 |
| otel-endpoint | Base OTLP/HTTP endpoint URL. /v1/logs and /v1/traces are appended automatically. Takes precedence over otel-logs-endpoint for logs. |
A valid URL, for example http://collector:4318 |
empty |
| otel-logs-enabled | Enable export of OpenTelemetry log events. Requires otel-enabled=1. |
0 or 1 |
0 |
| otel-logs-endpoint | OTLP/HTTP endpoint for log events. Required when otel-logs-enabled=1 and otel-endpoint is not set. |
A valid URL | empty |
| otel-logs-headers | Comma-separated key=value headers for OTLP log export requests. |
For example Authorization=Bearer xyz,X-Tenant=acme |
empty |
| otel-logs-batch-interval-ms | Interval in milliseconds between log batch exports. | Integer ≥ 500 | 5000 |
| otel-traces-enabled | Enable trace export for session launches. Requires otel-enabled=1. |
0 or 1 |
0 |
| otel-traces-endpoint | OTLP/HTTP endpoint for traces. Takes precedence over otel-endpoint for traces when set. |
A valid URL | empty |
| otel-traces-headers | Comma-separated key=value headers for OTLP trace export requests. Falls back to otel-logs-headers when empty. |
For example Authorization=Bearer xyz |
empty |
| otel-traces-batch-interval-ms | Interval in milliseconds between trace batch exports. Clamped to [500, 60000]. |
Integer in [500, 60000] |
5000 |
See the rserver.conf reference for the canonical descriptions of each option.
Disabling OpenTelemetry
Set otel-enabled=0 in rserver.conf and restart Workbench. The per-signal options do not need to change; the primary option suppresses all export.
Signal catalog
Workbench exports two signal types:
- Log events for sign-in, session lifecycle, session state transitions, and browser-side IDE launch performance.
- Trace spans that wrap each session launch with one child span per launch phase.
This section lists every signal Workbench emits, including event names, attributes, types, and allowed values. Operators building dashboards, alerts, or queries against the OTLP stream should treat this section as the contract.
Conventions
Attribute names follow the OpenTelemetry semantic conventions where one applies. Workbench-specific attributes use the session.*, browser.*, and transition_type namespaces.
In every table:
- Required attributes are present on every emission of the event.
- Optional attributes are emitted only when a value is available (for example,
session.r_versionis emitted only for R sessions, and only after the version has been resolved). - Type uses OTLP attribute types:
string,int64, orbool.
Every log event also carries an event.name attribute equal to the event name in the heading. The event.name row is omitted from each table for brevity.
Log events
workbench.user.login
Emitted on each successful sign-in.
Severity: INFO
| Attribute | Type | Required | Description |
|---|---|---|---|
user.id |
string | yes | The user’s Globally Unique Identifier (GUID), resolved server-side. |
workbench.session
Emitted when a session starts, suspends, completes normally, fails, or is terminated.
Severity: INFO
| Attribute | Type | Required | Description |
|---|---|---|---|
session.id |
string | yes | Workbench session identifier. |
user.id |
string | yes | The user’s GUID. |
session.type |
string | yes | Normalized IDE name. Allowed values: RStudio, Positron, JupyterLab, VS Code. |
session.status |
string | yes | Lifecycle status. Allowed values: Started, Suspended, Terminated, Failed, Completed. |
session.r_version |
string | no | R version, when applicable. Emitted only when non-empty. |
session.project |
string | no | Initial project path. Emitted only when non-empty. |
session.cluster |
string | no | Job Launcher cluster name in load-balanced deployments. Emitted only when non-empty. |
session.exit_code |
int64 | no | Process exit code, on terminal events. |
session.exit_reason |
string | no | Human-readable exit classification. Allowed values: NormalExit, Killed, Crashed, MemoryLimitExceeded, TooManyOpenFiles, NotEnoughMemory, Unknown. |
session.startup_duration_ms |
int64 | no | Time from launch request to running, on Started events. |
workbench.session.transition
Emitted on every session state change. Use this event to reconstruct per-session timelines without enabling traces.
Severity: INFO
| Attribute | Type | Required | Description |
|---|---|---|---|
session.id |
string | yes | Workbench session identifier. |
session.status |
string | yes | The state the session has transitioned to. |
session.previous_status |
string | no | The state the session transitioned from. Emitted only when non-empty. |
transition_type |
string | yes | Classification of the transition. Allowed values: milestone, internal. |
user.id |
string | no | The user’s GUID. Emitted only when resolvable from the username. |
milestone transitions correspond to user-visible state changes (for example, running, suspended, finished); internal transitions cover intermediate states such as starting or resuming.
browser.session.launch
Emitted by the IDE-launch performance beacon in the user’s browser. Each session launch produces multiple events, one per phase. Heartbeat events fire periodically while the IDE is open.
Severity: DEBUG for heartbeat, INFO for all other phases.
| Attribute | Type | Required | Description |
|---|---|---|---|
session.id |
string | yes | Workbench session identifier. |
user.id |
string | no | The user’s GUID, resolved server-side from the authenticated username. Never trusted from the client payload. |
browser.ide |
string | yes | IDE type, emitted as a lowercase identifier rather than a display name. Allowed values: rstudio, jupyterlab, positron, vscode. |
browser.phase |
string | yes | Launch phase or heartbeat. Allowed values: fcp, load, app_init, ready, heartbeat, pagehide. |
browser.nav_id |
string | yes | Browser-generated ID for one IDE navigation. Use to correlate the phase events from a single launch. |
browser.last_phase_reached |
string | no | The most recent phase the browser observed before this event was sent. |
browser.click_to_phase_ms |
int64 | no | Milliseconds from the user’s click on the launch link to this phase. Present when click-time is known. |
browser.nav_start_to_phase_ms |
int64 | no | Milliseconds from navigationStart to this phase. Always derivable in modern browsers. |
browser.launch_t0_ms |
int64 | no | Browser-side T0 timestamp (ms since epoch). |
browser.timings.<key> |
int64 | string | no | Detailed PerformanceTiming-style breakdown attached to the first beacon of a launch only. The <key> suffix matches the underlying timing field. |
The launch milestones, in chronological order, are fcp (first contentful paint), load, app_init, and ready. heartbeat fires periodically while the IDE is active. pagehide fires when the user navigates away or closes the tab.
Trace spans
session.launch
Wraps a single session launch from request to running (or to terminal failure). The root span carries identity and routing attributes; one child span is open at a time, named after the current launch phase.
Span hierarchy:
session.launch (root)
├── session.launch.launching (child, first phase)
├── session.launch.starting (child, next phase)
├── session.launch.running (child, terminal on success)
└── session.launch.<other-state> (one child per intermediate state)
Child spans close as the session transitions out of the matching state. The root span closes when the session reaches running or fails.
Root span attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
session.id |
string | yes | Workbench session identifier. |
user.id |
string | yes | The user’s GUID. |
session.type |
string | yes | Normalized IDE name (see workbench.session for allowed values). |
session.cluster |
string | yes | Job Launcher cluster name. Empty string for non-load-balanced deployments. |
Child span names take the form session.launch.<state> where <state> is the activity state. Common values include launching, starting, running, suspending, and resuming. Child spans inherit the trace context from the root and carry no additional attributes; phase identity is encoded by the span name.
A failed launch closes the root span with status ERROR. A relaunch attempt arriving during an active launch closes the previous root span with status ERROR and message "overwritten", then opens a new one.