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

Turn on OpenTelemetry export in rserver.conf. The primary option otel-enabled is the master switch that gates all signals. If you desire to collect only OpenTelemetry logs or only traces, you can disable the unused signal type to reduce buffering overhead with otel-logs-enabled and otel-traces-enabled.

/etc/rstudio/rserver.conf
otel-enabled=1
Config Option Description Possible Values Default Value
otel-enabled Master switch for OpenTelemetry export. When 0, no OTLP signals are emitted regardless of per-signal options or any otel.conf destination. 0 or 1 0
otel-logs-enabled Enable export of OpenTelemetry log events. Requires otel-enabled=1. Defaults to 1, so it effectively follows otel-enabled. Set to 0 explicitly to disable logs while leaving traces on. 0 or 1 1
otel-traces-enabled Enable trace export for session launches. Requires otel-enabled=1. Defaults to 1, so it effectively follows otel-enabled. Set to 0 explicitly to disable traces while leaving logs on. 0 or 1 1

See the rserver.conf reference for the canonical descriptions of each option.

When enabled, endpoint(s) can be defined for export in otel.conf, in the same directory as rserver.conf (/etc/rstudio in a default install). Workbench installs a commented template there, owned by root with mode 0600. Uncomment or add one [OTLPEndpoint "name"] section per destination. The name is a label of your choosing and appears in the Workbench log:

/etc/rstudio/otel.conf
[OTLPEndpoint "local"]
Endpoint = http://localhost:4318
Logs = true
Traces = true

Restart Workbench after changing either file.

Exporting to multiple destinations

Define destinations in /etc/rstudio/otel.conf, one [OTLPEndpoint "name"] section per destination, to send signals to one or more OTLP/HTTP receivers. For example, to send to Posit Chronicle and a third-party observability platform, define one destination per receiver. Each destination gets its own headers, so credentials for one platform are never sent to another. If an endpoint is also configured directly in rserver.conf (see below), the otel.conf destinations are added alongside it, not in place of it.

Destination keys

Key Description Possible Values Default Value
Endpoint Base OTLP/HTTP endpoint URL for this destination. /v1/logs and /v1/traces are appended automatically. A valid URL empty
Logs Send log events to this destination. true or false true
Traces Send traces to this destination. true or false true
Metrics Accepted for compatibility with Posit Connect and ignored. Workbench does not export OTLP metrics. true or false ignored
Headers Comma-separated key=value headers sent to this destination, for both logs and traces. May be encrypted; see Encrypting destination headers. For example DD-API-KEY=xyz empty
BatchIntervalMs Interval in milliseconds between batch exports to this destination. Clamped to [500, 60000]. Integer in [500, 60000] 5000
LogsBatchIntervalMs Batch interval for logs only. Overrides BatchIntervalMs if the key is present at all, including a value of 0 or lower (which is clamped to 500, not treated as “not set”). Integer, clamped to [500, 60000] falls back to BatchIntervalMs when omitted entirely
TracesBatchIntervalMs Batch interval for traces only. Overrides BatchIntervalMs if the key is present at all, including a value of 0 or lower (which is clamped to 500, not treated as “not set”). Integer, clamped to [500, 60000] falls back to BatchIntervalMs when omitted entirely
TimeoutMs How long a single export attempt to this destination may block before it is abandoned. Any positive value always applies, overriding OTEL_EXPORTER_OTLP_TIMEOUT and its per-signal variants; set to 0 to leave those environment variables (or the SDK’s own default) in effect instead. See Unresponsive destinations. Integer in milliseconds, 0 to defer to the environment 3000
Enabled Whether this destination is active. Set to false to keep a destination in the file without exporting to it. true or false true

Key names are matched case-insensitively, and - and _ are ignored, so BatchIntervalMs, batchintervalms, and batch-interval-ms are equivalent. Values may be quoted; inside quotes, \" and \\ are escapes and ;/# do not start a comment. Repeated sections and repeated keys are merged, with the last value winning.

Every destination’s Endpoint and Headers apply to both logs and traces. To send a signal to a different endpoint, or with different headers, than its sibling, define two destinations: one with Logs = true, Traces = false and one with Logs = false, Traces = true, each with its own Endpoint and Headers. See the two-destination example below. Batch intervals do not require this: a single destination can flush each signal on its own cadence with LogsBatchIntervalMs and TracesBatchIntervalMs.

A file with two destinations, one of them traces-only:

/etc/rstudio/otel.conf
[OTLPEndpoint "datadog"]
; It is strongly recommended to encrypt Headers values using: rstudio-server encrypt-password
Endpoint = https://otlp-http.datadoghq.com
Headers = "DD-API-KEY=xxxxxxxx"

[OTLPEndpoint "grafana"]
Endpoint = https://otlp-gateway.example.org
Logs = false
Traces = true
Headers = "Authorization=Basic xxxxxxxx"
BatchIntervalMs = 1000

Notes:

  • otel-enabled, otel-logs-enabled, and otel-traces-enabled in rserver.conf still gate everything. A destination with Traces = true exports nothing while otel-traces-enabled=0.
  • default is reserved for the destination configured in rserver.conf and cannot be used as a destination name.
  • Because this file may hold API keys, restrict it: chown root:root /etc/rstudio/otel.conf && chmod 0600 /etc/rstudio/otel.conf. Workbench logs a warning if the file is readable more broadly.
  • A syntax error, a non-boolean Logs/Traces/Metrics/Enabled, a non-numeric interval or timeout, or a negative TimeoutMs prevents Workbench from starting, with the reason in the startup log. Keys Workbench does not recognize are logged as a warning and ignored. A destination with no usable endpoint, or with both signals off, is skipped entirely with a warning. A destination whose name duplicates an earlier one is also skipped, while one that merely repeats an endpoint URL already claimed keeps its other signal and loses only the duplicated one.

Encrypting destination headers

Encrypting the Headers values in /etc/rstudio/otel.conf (and the otel-logs-headers and otel-traces-headers values in rserver.conf) protects your API keys if you back up your configuration, save it to a repository, or share it with Workbench Support.

Note

Before configuring Workbench to export directly to a hosted observability platform, check that platform’s own documentation for the deployment it recommends. Most vendors recommend running a local collector agent on each host, which receives telemetry over an unauthenticated loopback endpoint and handles authentication, batching, and retry on the way to the hosted service. Where that is available it is the more robust arrangement, and it needs no Headers in this file. Direct authenticated export is offered as a convenience, useful for prototypes and small deployments where standing up a local collector is not worth the effort.

Use the following steps to encrypt a headers value:

  • Run sudo rstudio-server encrypt-password and enter the entire headers string, for example DD-API-KEY=xxxxxxxx.
  • Copy the resulting encrypted value printed in the terminal.
  • Replace the plaintext value with the encrypted value. An example resulting line should look like Headers = <encrypted-text>.
  • Restart Workbench, then confirm the Workbench logs contain no warning containing “An unencrypted value is being used for”.

Encrypt the whole value as a single string, including any commas separating multiple headers. Do not encrypt each header separately.

Workbench reads encrypted and plaintext values transparently, so pre-existing plaintext configurations continue to work.

Note

Encrypted values are tied to the server’s secure-cookie key. If you rotate that key, follow the re-encryption step before restarting Workbench. If Workbench finds a value that appears to be encrypted but cannot be decrypted, it logs an error and exports to that destination without headers rather than sending a corrupted credential; export to other destinations is unaffected.

Sharing configuration with Posit Connect

As a convenience, a subset of common configuration options supported by Workbench and Connect use identical syntax. The Endpoint, Logs, and Traces keys match Posit Connect’s OTLPEndpoint configuration, so a snippet limited to those keys can be pasted into both /etc/rstudio/otel.conf and rstudio-connect.gcfg:

[OTLPEndpoint "mybackend"]
Endpoint = http://collector:4318
Logs = true
Traces = true

This allows Posit Team administrators using templatized configuration files to maintain a single snippet that is inserted into otel.conf and rstudio-connect.gcfg.

More advanced configurations may require custom configuration per product. Consult Posit Connect’s OTLPEndpoint configuration reference for the full set of currently supported keys. Sections and keys Workbench does not recognize are ignored with a log message rather than treated as errors, so a Connect file’s other sections do not prevent Workbench from starting; Connect makes no such guarantee about Workbench’s.

Configuring an endpoint directly in rserver.conf (deprecated)

Before otel.conf existed, the only way to point Workbench at an OTLP/HTTP receiver was a set of endpoint options in rserver.conf. Those options continue to work unchanged, but configuring endpoints in otel.conf (above) is now the recommended path. This section is kept for deployments already relying on it.

To send both logs and traces to a single OTLP/HTTP receiver this way:

# /etc/rstudio/rserver.conf
otel-enabled=1
otel-endpoint=http://collector.example.org:4318

Workbench 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; either overrides otel-endpoint for its own signal when both are set. 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.

Config Option (deprecated) Description Possible Values Default Value
otel-endpoint Base OTLP/HTTP endpoint URL. /v1/logs and /v1/traces are appended automatically. Used for a signal only when that signal’s own endpoint option (otel-logs-endpoint / otel-traces-endpoint) is not set. A valid URL, for example http://collector:4318 empty
otel-logs-endpoint OTLP/HTTP endpoint for log events. Overrides otel-endpoint for logs when set. Required for log export when neither otel-endpoint nor an otel.conf destination with Logs = true is configured. 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. Clamped to [500, 60000]. Integer ≥ 500 5000
otel-traces-endpoint OTLP/HTTP endpoint for traces. Overrides 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

Workbench checks each enabled signal separately at startup. If a signal is enabled but neither an rserver.conf endpoint option nor any usable otel.conf destination applies to it, Workbench logs a warning naming that signal and disables it. A signal that is turned off never warns, whether or not a destination exists for it.

See the rserver.conf reference for the canonical descriptions of each option.

Unresponsive destinations

Each destination gets its own export queue and its own worker thread, so a destination that stops responding cannot delay or drop telemetry bound for the others. Records queued for the unresponsive destination are dropped once its queue fills, and are not retried.

A single export attempt may block for up to TimeoutMs (3 seconds by default). Because Workbench flushes pending telemetry as it shuts down, that timeout also bounds how long an unresponsive destination can delay a restart. Raising it gives a slow but working link more room to succeed; lowering it makes restarts more predictable at the cost of dropping more batches.

Any positive TimeoutMs (including the 3000 default) always applies and takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT (or the per-signal OTEL_EXPORTER_OTLP_LOGS_TIMEOUT / OTEL_EXPORTER_OTLP_TRACES_TIMEOUT). Set TimeoutMs = 0 to leave those environment variables, or the OpenTelemetry SDK’s own default, in effect for that destination instead. Note that the SDK interprets a value with no unit suffix as seconds, so OTEL_EXPORTER_OTLP_TIMEOUT=9000 means 9000 seconds rather than 9000 milliseconds. Prefer an explicit TimeoutMs to avoid the ambiguity.

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_version is emitted only for R sessions, and only after the version has been resolved).
  • Type uses OTLP attribute types: string, int64, or bool.

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.

Back to top