Unifies frontend and backend events under a single user session span. Adds an allowlist in settings for IJ Platform subsystem metrics.
Useful to provide observability for IDEs
This plugin works only in Remote Development IDE-s while being installed on both frontend and backend sides.
Set your OTLP headers on the backend only using either:
Environment Variable:
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer your_token"OR System Property:
-Dotel.exporter.otlp.headers=authorization=Bearer your_token
Headers follow the standard OTEL_EXPORTER_OTLP_HEADERS format: comma-separated key=value pairs. This works with any OTLP-compatible backend (Jaeger, Grafana, OpenTelemetry Collector, etc.).
The plugin automatically propagates the headers securely from backend to frontend via encrypted RPC.
Signal-specific headers are also supported and override common headers with the same key:
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="x-honeycomb-dataset=your_trace_dataset"
export OTEL_EXPORTER_OTLP_METRICS_HEADERS="x-honeycomb-dataset=your_metrics_dataset"Equivalent system properties:
-Dotel.exporter.otlp.traces.headers=x-honeycomb-dataset=your_trace_dataset
-Dotel.exporter.otlp.metrics.headers=x-honeycomb-dataset=your_metrics_datasetFor Honeycomb, set OTEL_EXPORTER_OTLP_METRICS_HEADERS (or otel.exporter.otlp.metrics.headers) when metrics should go to a dataset other than Honeycomb's fallback unknown_metrics.
Environment Variable:
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhostOR System Property:
-Dotel.exporter.otlp.endpoint=http://localhostDefault: http://localhost
Supported values:
http/protobuf(default)grpc
Set using either:
Environment Variable:
export OTEL_EXPORTER_OTLP_PROTOCOL=grpcOR System Property:
-Dotel.exporter.otlp.protocol=grpcIn Remote Development mode, the backend OTLP protocol is propagated to the frontend together with the rest of the OTLP connection config. Setting it on the frontend is only needed as a fallback when backend config is unavailable.
Set this on the backend using either:
Environment variable:
export RDCT_COMMON_SPAN_ATTRIBUTES="option1=value1,option2=value2"Or system property:
-Drdct.common.span.attributes=option1=value1,option2=value2The value uses comma-separated key=value pairs, so:
-Drdct.common.span.attributes=deployment.environment=stagingaddsdeployment.environment=staging-Drdct.common.span.attributes=deployment.environment=staging,service.instance.id=rd-backend-1adds both attributes
In Remote Development mode, these backend-defined attributes are propagated to the frontend together with the rest of the OTLP connection config and are added to every exported span and metric on both sides.
Set this on the backend using either:
Environment variable:
export RDCT_COMMON_METRIC_ATTRIBUTES="option1=value1,option2=value2"Or system property:
-Drdct.common.metric.attributes=option1=value1,option2=value2The value uses the same comma-separated key=value format as global span attributes. These attributes are added only to exported metrics. If a metric attribute uses the same key as a global span attribute, the metric-specific value wins for metrics.
In Remote Development mode, these backend-defined metric attributes are propagated to the frontend together with the rest of the OTLP connection config and are added to exported metrics on both sides.
The plugin also automatically adds:
plugin.version: installed plugin versionidea.version: IDE product version, for example2026.1.1idea.build: IDE product build number, for exampleIU-261.23567.138
Set this on the backend as a system property:
-Drdct.diagnostic.frontend.xmx.mb=2048The value is interpreted as a positive integer number of MiB. On frontend startup, the plugin fetches this backend property and writes the corresponding -Xmx value into the JetBrains Client VM options.
This updates the client configuration for the next JetBrains Client start. The current JVM heap size cannot be changed in-place at runtime.
isPluginSpanFilterEnabled is resolved in this order:
- System property/env override (authoritative):
rdct.diagnostic.otlp.plugin.span.filter.enabledRDCT_DIAGNOSTIC_OTLP_PLUGIN_SPAN_FILTER_ENABLED
- Saved plugin setting (
OpenTelemetry Diagnosticsettings UI) - Default:
true
true: keep only spans from this diagnostic plugin tracer (com.jetbrains.otp.diagnostic)false: do not apply plugin-tracer-only filtering at this stage
isMetricsExportEnabled is resolved in this order:
- System property/env override (authoritative):
rdct.diagnostic.otlp.metrics.enabledRDCT_DIAGNOSTIC_OTLP_METRICS_ENABLED
- Saved plugin setting (
OpenTelemetry Diagnosticsettings UI) - Default:
true
Other plugins can add attributes to all exported metrics by registering:
<extensions defaultExtensionNs="com.jetbrains.otp.diagnostic">
<metricAttributesProvider implementation="com.example.OtherPluginMetricAttributesProvider"/>
</extensions>Use getStaticAttributes() for values that are calculated once and cannot change during the process lifetime, for example an xmx value.
Use getDynamicAttributeProviders() for values that may change but should not be recalculated on every metric export. Each dynamic provider has its own cache key and TTL in seconds.
class OtherPluginMetricAttributesProvider : MetricAttributesProvider {
override fun getStaticAttributes(): Attributes {
return Attributes.of(AttributeKey.longKey("jvm.xmx.mb"), maxHeapMb().toLong())
}
override fun getDynamicAttributeProviders(): List<DynamicMetricAttributesProvider> {
return listOf(CellMetricAttributesProvider())
}
}
private class CellMetricAttributesProvider : DynamicMetricAttributesProvider {
override fun getCacheKey(): String = "cell"
override fun getCacheTtlSeconds(): Long = 60
override fun getAttributes(): Attributes {
return Attributes.of(AttributeKey.stringKey("cell"), resolveCell())
}
}The feature is intended for diagnostic analysis of transient performance incidents (for example, UI thread freezes or remote connection drops).
When triggered, it emits high-frequency CPU and memory metrics for the preceding 5-minute window and continues emitting for the subsequent 5 minutes to capture post-incident recovery behavior.
frequentPerformanceMetricsReportingEnabled is resolved in this order:
- System property/env override (authoritative):
rdct.diagnostic.otlp.frequent.performance.metrics.reporting.enabledRDCT_DIAGNOSTIC_OTLP_FREQUENT_PERFORMANCE_METRICS_REPORTING_ENABLED
- Saved plugin setting (
OpenTelemetry Diagnosticsettings UI) - Default:
true
In Settings | Tools | OpenTelemetry Diagnostic, you can configure:
Show only a short list of diagnostic spans(plugin span filter)Enable on-demand performance report (last 5 minutes)(frequent performance metrics reporting)Enable frequent spans- hierarchical span categories (parent/child checkboxes)
Category and toggle settings are synced frontend -> backend.
The plugin creates the following OpenTelemetry spans to track various aspects of your remote development session:
A short-lived metadata span created at session initialization to store session information.
Created when: Session is initialized (backend notifies frontend of session start)
Attributes:
session.trace_id(string): The trace ID of the sessionsession.span_id(string): The span ID of the sessionhost.name(string): Name of the backend hostsession.id(string): Unique identifier for the session
Parent: Root span
Purpose: This span is useful for finding and identifying sessions in your telemetry backend even when the main remote-dev-session span hasn't closed yet (see note about long-running spans below).
Tracks an active remote development session from start to finish.
Created when: Client connects to remote backend Ended when: Session is terminated or connection ends
Attributes:
session.id(string): Remote Development session identifier. In direct RD links it is derived from the backend certificate fingerprint. If the fingerprint is unavailable, the plugin falls back to the remote connection address, for exampletcp://127.0.0.1:5985.
Parent: Root span
Note: This span can be long-running (hours or days) and may not close until the session ends. Use the session-metadata span to identify active sessions.
Lazy-created default span that captures telemetry events before the first remote development session starts.
Created when: First telemetry event occurs before any session starts Ended when: First remote session begins or application shuts down Note: This span is only created if events occur before a session starts. It prevents telemetry data loss during application initialization.
Tracks the duration of connection drops and reconnection attempts.
Created when: Connection to remote backend is lost Ended when: Connection is successfully re-established
Attributes:
project(string): Name of the affected projectreconnection.throttled.drop_count(long): Number of reconnect-drop transitions suppressed before this span
Parent: Current session span (remote-dev-session or application-idle)
Use case: Measure reconnection latency and identify connection stability issues.
Note: Reconnection spans use a process-wide adaptive cooldown. After an emitted span closes, another span is allowed after 6 minutes; every suppressed reconnect attempt during that cooldown extends the next cooldown exponentially, capped at 1 hour. Suppressed reconnect flaps do not force on-demand performance metrics or UI-thread status checks.
Captures UI freeze events with accurate timing based on freeze duration.
Created when: UI freeze is detected and reported by the platform Timing: Uses calculated start time (end time - duration) for accurate span placement
Parent: Current session span (remote-dev-session or application-idle)
Note: This span is created retroactively with explicit start/end timestamps calculated from the freeze duration.
Captures uncaught IDE exceptions as short-lived error spans.
Created when: The IntelliJ Platform logs an error with a throwable
Name: ide-exception.<ExceptionClass> (for example ide-exception.IllegalStateException)
Attributes:
exception.type(string): Exception class nameexception.message(string): Exception message when availableexception.stacktrace(string): Exception stack trace, truncated to 64 KiBexception.source(string): Reporting path (platform-log)plugin(string): Plugin ID blamed by the platform when availablelog.logger(string): Logger name from the platform log recordlog.level(string): Log level from the platform log recordlog.message(string): Message from the platform log record
Parent: Current session span (remote-dev-session or application-idle)
Note: Coroutine exceptions are reported through IntelliJ's platform exception handler, which logs them once via Logger.error; this plugin converts that platform log record into a span instead of installing a second coroutine reporter.
Some spans, particularly remote-dev-session, are designed to be long-running and may remain open for extended periods (hours or even days) until the session ends. This is normal behavior for session-tracking spans.
When querying your telemetry backend:
- Use the
session-metadataspan to identify and locate sessions, as it closes quickly after session initialization - The main
remote-dev-sessionspan will only close when the actual session terminates - All other spans are short-lived and close immediately after their tracked operations complete
To build the plugin distribution:
./gradlew buildPluginThe built plugin will be available in the build/distributions/ directory as a .zip file that can be installed in IntelliJ IDEA.