From 88878cc98e3c6021b7a73fec076d29d3b64377c5 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Mon, 28 Sep 2026 11:13:05 -0400 Subject: [PATCH 1/4] fix(observability): resource attributes follow OTel semantic conventions Signed-off-by: Yordis Prieto --- .../src/app/DesktopObservability.test.ts | 7 +-- apps/desktop/src/app/DesktopObservability.ts | 10 ++++- apps/server/src/cloud/relayTracing.ts | 4 +- apps/server/src/config.ts | 12 +++-- apps/server/src/server.test.ts | 3 +- apps/server/src/serverLogger.test.ts | 3 +- apps/web/src/observability/clientTracing.ts | 34 ++++++++++++-- .../0026-telemetry-says-which-app-sent-it.md | 44 +++++++++++++++++++ docs/fork/README.md | 2 + docs/operations/observability.md | 16 +++++-- packages/shared/src/observability.ts | 13 ++++++ packages/shared/src/relayTracing.ts | 4 +- 12 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 docs/fork/0026-telemetry-says-which-app-sent-it.md diff --git a/apps/desktop/src/app/DesktopObservability.test.ts b/apps/desktop/src/app/DesktopObservability.test.ts index e23d78aa2161..16de927e106b 100644 --- a/apps/desktop/src/app/DesktopObservability.test.ts +++ b/apps/desktop/src/app/DesktopObservability.test.ts @@ -412,7 +412,8 @@ describe("DesktopObservability", () => { const [request] = requests; assert.strictEqual(request?.url, "https://collector.example.com/v1/logs"); assert.include(request?.body ?? "", "desktop log export"); - assert.include(request?.body ?? "", "service.runtime"); + assert.include(request?.body ?? "", "deployment.environment.name"); + assert.include(request?.body ?? "", "process.runtime.name"); assert.strictEqual(request?.headers["x-scope"], "desktop"); // The log record is the export now, so the same message must not also @@ -497,7 +498,7 @@ describe("DesktopObservability", () => { assert.lengthOf(requests, 1); const body = requests[0]?.body ?? ""; assert.include(body, '"stringValue":"t3code-desktop"'); - assert.include(body, "deployment.environment.name"); + assert.include(body, '"key":"deployment.environment.name","value":{"stringValue":"staging"}'); assert.include(body, '"key":"service.namespace","value":{"stringValue":"t3code"}'); assert.notInclude(body, "renamed"); }).pipe( @@ -511,7 +512,7 @@ describe("DesktopObservability", () => { env: { OTEL_SERVICE_NAME: "renamed", OTEL_RESOURCE_ATTRIBUTES: - "service.name=renamed,service.namespace=renamed,deployment.environment.name=development", + "service.name=renamed,service.namespace=renamed,deployment.environment.name=staging", }, }), ), diff --git a/apps/desktop/src/app/DesktopObservability.ts b/apps/desktop/src/app/DesktopObservability.ts index ce8233e56612..cfde3b322ddb 100644 --- a/apps/desktop/src/app/DesktopObservability.ts +++ b/apps/desktop/src/app/DesktopObservability.ts @@ -2,6 +2,7 @@ import { PRIMARY_LOCAL_ENVIRONMENT_ID } from "@t3tools/contracts"; import { makeLocalFileTracer, makeTraceSink, + nodeProcessRuntimeAttributes, otlpSerializationLayer, type SignalExport, } from "@t3tools/shared/observability"; @@ -627,10 +628,15 @@ const telemetryLayer = Layer.unwrap( const endpoints = yield* resolveOtlpEndpoints; const resource = { serviceName: "t3code-desktop", + serviceVersion: environment.appVersion, attributes: { "service.namespace": "t3code", - "service.runtime": "desktop", - "service.mode": environment.isDevelopment ? "development" : "packaged", + // Effect lets explicit attributes beat `OTEL_RESOURCE_ATTRIBUTES`, so an + // operator's tier has to be carried over by hand to keep winning. + "deployment.environment.name": + endpoints.resourceAttributes["deployment.environment.name"] ?? + (environment.isDevelopment ? "development" : "production"), + ...nodeProcessRuntimeAttributes(), }, }; diff --git a/apps/server/src/cloud/relayTracing.ts b/apps/server/src/cloud/relayTracing.ts index eeea28a2b68f..3e6a2576c2fe 100644 --- a/apps/server/src/cloud/relayTracing.ts +++ b/apps/server/src/cloud/relayTracing.ts @@ -8,14 +8,14 @@ export const headlessRelayClientTracingLayer = makeRelayClientTracingLayer( relayClientTracingConfig, { serviceName: "t3code-server", - runtime: "node", + runtime: "nodejs", client: "headless-cli", }, ); export const serverRelayBrokerTracingLayer = makeRelayClientTracingLayer(relayClientTracingConfig, { serviceName: "t3code-server", - runtime: "node", + runtime: "nodejs", client: "environment-server", component: "relay-broker", }); diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index d8dad5ae4d24..c9c0b51c132c 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -16,8 +16,13 @@ import * as Path from "effect/Path"; import type * as Redacted from "effect/Redacted"; import * as Schema from "effect/Schema"; +import packageJson from "../package.json" with { type: "json" }; import { sweepStalePendingAttachments } from "./attachmentStore.ts"; -import { DEFAULT_SIGNAL_EXPORT, type SignalExport } from "@t3tools/shared/observability"; +import { + DEFAULT_SIGNAL_EXPORT, + nodeProcessRuntimeAttributes, + type SignalExport, +} from "@t3tools/shared/observability"; import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; export const DEFAULT_PORT = 3773; @@ -118,10 +123,11 @@ export const make = (config: ServerConfig["Service"]) => ServerConfig.of(config) */ export const otlpResource = (config: ServerConfig["Service"]) => ({ serviceName: "t3code-server", + serviceVersion: packageJson.version, attributes: { "service.namespace": "t3code", - "service.runtime": "t3-server", - "service.mode": config.mode, + "t3.server.managed_by": config.mode === "desktop" ? "desktop" : "standalone", + ...nodeProcessRuntimeAttributes(), }, }); diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index dc9e63d8010e..1cd8456fbc0a 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -496,8 +496,7 @@ const makeBrowserOtlpPayload = (spanName: string) => resource: { serviceName: "t3code-web", attributes: { - "service.runtime": "t3-web", - "service.mode": "browser", + "t3.client.surface": "web", "service.version": "test", }, }, diff --git a/apps/server/src/serverLogger.test.ts b/apps/server/src/serverLogger.test.ts index 43843b249eea..9246b7f48207 100644 --- a/apps/server/src/serverLogger.test.ts +++ b/apps/server/src/serverLogger.test.ts @@ -147,7 +147,8 @@ describe("ServerLoggerLive", () => { assert.strictEqual(request?.url, "https://collector.example.com/v1/logs"); assert.include(request?.body ?? "", "server logger under test"); assert.include(request?.body ?? "", "t3code-server"); - assert.include(request?.body ?? "", "service.runtime"); + assert.include(request?.body ?? "", "t3.server.managed_by"); + assert.include(request?.body ?? "", "process.runtime.name"); }), ); diff --git a/apps/web/src/observability/clientTracing.ts b/apps/web/src/observability/clientTracing.ts index e8ccd4173d35..9aa99c8655b8 100644 --- a/apps/web/src/observability/clientTracing.ts +++ b/apps/web/src/observability/clientTracing.ts @@ -14,15 +14,41 @@ import { isElectron } from "../env"; import { APP_VERSION } from "~/branding"; const DEFAULT_EXPORT_INTERVAL_MS = 1_000; +interface NavigatorUserAgentData { + readonly platform: string; + readonly mobile: boolean; + readonly brands: ReadonlyArray<{ readonly brand: string; readonly version: string }>; +} + +/** + * The same `browser.*` and `user_agent.original` attributes the OpenTelemetry + * browser resource detector reports. `userAgentData` exists only in Chromium, + * which includes the desktop app. + */ +const browserResourceAttributes = (): Record => { + if (typeof navigator === "undefined") return {}; + const userAgentData = (navigator as Navigator & { userAgentData?: NavigatorUserAgentData }) + .userAgentData; + return { + "user_agent.original": navigator.userAgent, + "browser.language": navigator.language, + ...(userAgentData && { + "browser.platform": userAgentData.platform, + "browser.mobile": userAgentData.mobile, + "browser.brands": userAgentData.brands.map(({ brand, version }) => `${brand} ${version}`), + }), + }; +}; + const CLIENT_TRACING_RESOURCE = { serviceName: "t3code-web", + serviceVersion: APP_VERSION, attributes: { "service.namespace": "t3code", - "service.runtime": "t3-web", - "service.mode": isElectron ? "electron" : "browser", - "service.version": APP_VERSION, + "t3.client.surface": isElectron ? "desktop" : "web", + ...browserResourceAttributes(), }, -} as const; +}; const delegateRuntimeLayer = Layer.mergeAll( primaryEnvironmentHttpLayer, diff --git a/docs/fork/0026-telemetry-says-which-app-sent-it.md b/docs/fork/0026-telemetry-says-which-app-sent-it.md new file mode 100644 index 000000000000..f83a74d93f5e --- /dev/null +++ b/docs/fork/0026-telemetry-says-which-app-sent-it.md @@ -0,0 +1,44 @@ +# 0026: Telemetry says which app sent it + +- PR: [TrogonStack/t3code#68](https://github.com/TrogonStack/t3code/pull/68) +- Status: active + +## What you can do now + +- Filter UI traces to the desktop window or to a browser tab with + `t3.client.surface`, instead of guessing from a key named `service.mode`. +- Tell a server the desktop app launched from one you started yourself with + `t3.server.managed_by`. +- Query every T3 Code signal with the resource attributes a collector, + dashboard, or vendor already understands: `service.version`, + `deployment.environment.name`, `process.runtime.*`, `user_agent.original`, + and `browser.*`. +- Set `deployment.environment.name` through `OTEL_RESOURCE_ATTRIBUTES` and + have the desktop app keep it. + +## Why + +The desktop window runs the web UI, so its traces arrive as `t3code-web`, and +the only thing separating them from a browser tab was `service.mode`. That +key sat in the `service.*` namespace, which OpenTelemetry reserves for its +own attributes, and it meant something different in each service: where the +UI runs in one, the build type in another, and who launched the process in +the third. Nobody reading a trace could know that without reading the code. + +Following the semantic conventions puts every attribute where tooling +already looks for it, and keeps the one question the conventions have no +answer for, which surface of the product sent this, under the `t3.` prefix +the relay tracing already used. + +## Upstream considerations + +A plausible upstream submission. The attributes came from upstream, and the +change carries no fork-specific intent. Dashboards or saved queries that +filter on `service.mode`, `service.runtime`, or `service.component` have to +move to the new keys, which is the part upstream would want to weigh. + +The server's own `mode` values (`web` for any standalone launch) are left +alone, since renaming them touches the CLI flag, `T3CODE_MODE`, and persisted +session data. Telemetry maps `mode` to `t3.server.managed_by` instead. A +sync that takes upstream's copy of any resource definition brings the old +keys back without any test going red outside the ones changed here. diff --git a/docs/fork/README.md b/docs/fork/README.md index df2d9d4f76c3..920688eb97f3 100644 --- a/docs/fork/README.md +++ b/docs/fork/README.md @@ -55,3 +55,5 @@ Each entry uses these sections: active, [#38](https://github.com/TrogonStack/t3code/pull/38) - **0025** [A test run leaves no processes behind](./0025-a-test-run-leaves-no-processes-behind.md) active, [#57](https://github.com/TrogonStack/t3code/pull/57) +- **0026** [Telemetry says which app sent it](./0026-telemetry-says-which-app-sent-it.md) + active, [#68](https://github.com/TrogonStack/t3code/pull/68) diff --git a/docs/operations/observability.md b/docs/operations/observability.md index 910bd018bcc7..6baa6702c8f7 100644 --- a/docs/operations/observability.md +++ b/docs/operations/observability.md @@ -611,10 +611,18 @@ an `http` or `https` URL, a protocol other than `http/protobuf` or `http/json` s headers that are not `key=value` pairs with percent-encoded values turn that signal's export off with a startup warning, rather than sending it to the Settings endpoint. -Service names are fixed: `t3code-server` for the backend and `t3code-desktop` for the desktop main -process, both in `service.namespace` `t3code`. `OTEL_SERVICE_NAME` and a `service.name` or -`service.namespace` in `OTEL_RESOURCE_ATTRIBUTES` are ignored. Tell installations apart with other -resource attributes, such as `OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=development`. +Service names are fixed: `t3code-server` for the backend, `t3code-desktop` for the desktop main +process, and `t3code-web` for the UI, all in `service.namespace` `t3code`. `OTEL_SERVICE_NAME` and a +`service.name` or `service.namespace` in `OTEL_RESOURCE_ATTRIBUTES` are ignored. Tell installations +apart with other resource attributes, such as +`OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=staging`. The desktop main process reports +`deployment.environment.name` as `development` or `production` on its own, and a value from +`OTEL_RESOURCE_ATTRIBUTES` replaces it. + +The UI runs as `t3code-web` in both a browser and the desktop window, since it is the same code. Its +`t3.client.surface` resource attribute is `desktop` or `web`, and `user_agent.original` carries the +full user agent. The server's `t3.server.managed_by` is `desktop` when the desktop app launched it +and `standalone` otherwise. If the OTLP URLs are unset, local tracing still works, metrics stay in-process only, and logs stay on stdout only. diff --git a/packages/shared/src/observability.ts b/packages/shared/src/observability.ts index b4dbdf88651a..9daa12f5027b 100644 --- a/packages/shared/src/observability.ts +++ b/packages/shared/src/observability.ts @@ -28,6 +28,19 @@ export interface SignalExport { readonly exportIntervalMs: number; } +/** + * `process.runtime.*` resource attributes for a Node process, matching the + * OpenTelemetry Node process detector. Electron embeds Node, so an Electron + * process reports Node and names Electron in the description. + */ +export const nodeProcessRuntimeAttributes = (): Record => ({ + "process.runtime.name": "nodejs", + "process.runtime.version": process.versions.node, + "process.runtime.description": process.versions.electron + ? `Electron ${process.versions.electron}` + : "Node.js", +}); + /** What T3 Code exports with when nothing configured a signal. */ export const DEFAULT_SIGNAL_EXPORT: SignalExport = { protocol: "http/json", diff --git a/packages/shared/src/relayTracing.ts b/packages/shared/src/relayTracing.ts index 76954558eb07..89f91d67a485 100644 --- a/packages/shared/src/relayTracing.ts +++ b/packages/shared/src/relayTracing.ts @@ -143,8 +143,8 @@ export function makeRelayClientTracingLayer( serviceVersion: resource.serviceVersion, attributes: { "service.namespace": "t3code", - "service.runtime": resource.runtime, - "service.component": resource.component ?? "relay-client", + "process.runtime.name": resource.runtime, + "t3.component": resource.component ?? "relay-client", "t3.client.surface": resource.client, }, }, From d9a31f6d932451d653c88cb610b047c4f7e0d183 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 30 Sep 2026 21:31:46 -0400 Subject: [PATCH 2/4] fix(web): UI traces name the browser runtime Signed-off-by: Yordis Prieto --- apps/web/src/observability/clientTracing.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/observability/clientTracing.ts b/apps/web/src/observability/clientTracing.ts index 9aa99c8655b8..85826f76c908 100644 --- a/apps/web/src/observability/clientTracing.ts +++ b/apps/web/src/observability/clientTracing.ts @@ -22,8 +22,9 @@ interface NavigatorUserAgentData { /** * The same `browser.*` and `user_agent.original` attributes the OpenTelemetry - * browser resource detector reports. `userAgentData` exists only in Chromium, - * which includes the desktop app. + * browser resource detector reports, plus the `process.runtime.*` values the + * semantic conventions give a web browser. `userAgentData` exists only in + * Chromium, which includes the desktop app. */ const browserResourceAttributes = (): Record => { if (typeof navigator === "undefined") return {}; @@ -31,6 +32,8 @@ const browserResourceAttributes = (): Record => { .userAgentData; return { "user_agent.original": navigator.userAgent, + "process.runtime.name": "browser", + "process.runtime.version": navigator.userAgent, "browser.language": navigator.language, ...(userAgentData && { "browser.platform": userAgentData.platform, From 6e137d54f8698308094f1ccb9e317c6191ac5fc4 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 30 Sep 2026 21:40:02 -0400 Subject: [PATCH 3/4] fix(relay): relay traces use the same resource keys Signed-off-by: Yordis Prieto --- infra/relay/src/observability.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/relay/src/observability.ts b/infra/relay/src/observability.ts index 2325d7e0432a..119f3e499814 100644 --- a/infra/relay/src/observability.ts +++ b/infra/relay/src/observability.ts @@ -225,8 +225,8 @@ export const makeRelayTraceLayer = (input: { serviceName: "t3code-relay", attributes: { "service.namespace": "t3code", - "service.runtime": "cloudflare-worker", - "service.component": "relay", + "process.runtime.name": "cloudflare-worker", + "t3.component": "relay", }, }, headers: { From 9bbb65df7f86440b6869cdb4eee2fd1b6f2d83e0 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 30 Sep 2026 21:56:26 -0400 Subject: [PATCH 4/4] fix(observability): custom attributes use the t3code prefix Signed-off-by: Yordis Prieto --- apps/server/src/config.ts | 2 +- apps/server/src/server.test.ts | 2 +- apps/server/src/serverLogger.test.ts | 2 +- apps/web/src/observability/clientTracing.ts | 2 +- .../0026-telemetry-says-which-app-sent-it.md | 16 +++++++++------- docs/operations/observability.md | 4 ++-- infra/relay/src/observability.ts | 2 +- packages/shared/src/relayTracing.ts | 4 ++-- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index c9c0b51c132c..689a16fc9d49 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -126,7 +126,7 @@ export const otlpResource = (config: ServerConfig["Service"]) => ({ serviceVersion: packageJson.version, attributes: { "service.namespace": "t3code", - "t3.server.managed_by": config.mode === "desktop" ? "desktop" : "standalone", + "t3code.server.managed_by": config.mode === "desktop" ? "desktop" : "standalone", ...nodeProcessRuntimeAttributes(), }, }); diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index 1cd8456fbc0a..7bc8a085bbac 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -496,7 +496,7 @@ const makeBrowserOtlpPayload = (spanName: string) => resource: { serviceName: "t3code-web", attributes: { - "t3.client.surface": "web", + "t3code.client.surface": "web", "service.version": "test", }, }, diff --git a/apps/server/src/serverLogger.test.ts b/apps/server/src/serverLogger.test.ts index 9246b7f48207..cff7e8a249f3 100644 --- a/apps/server/src/serverLogger.test.ts +++ b/apps/server/src/serverLogger.test.ts @@ -147,7 +147,7 @@ describe("ServerLoggerLive", () => { assert.strictEqual(request?.url, "https://collector.example.com/v1/logs"); assert.include(request?.body ?? "", "server logger under test"); assert.include(request?.body ?? "", "t3code-server"); - assert.include(request?.body ?? "", "t3.server.managed_by"); + assert.include(request?.body ?? "", "t3code.server.managed_by"); assert.include(request?.body ?? "", "process.runtime.name"); }), ); diff --git a/apps/web/src/observability/clientTracing.ts b/apps/web/src/observability/clientTracing.ts index 85826f76c908..406ae8cc2d90 100644 --- a/apps/web/src/observability/clientTracing.ts +++ b/apps/web/src/observability/clientTracing.ts @@ -48,7 +48,7 @@ const CLIENT_TRACING_RESOURCE = { serviceVersion: APP_VERSION, attributes: { "service.namespace": "t3code", - "t3.client.surface": isElectron ? "desktop" : "web", + "t3code.client.surface": isElectron ? "desktop" : "web", ...browserResourceAttributes(), }, }; diff --git a/docs/fork/0026-telemetry-says-which-app-sent-it.md b/docs/fork/0026-telemetry-says-which-app-sent-it.md index f83a74d93f5e..91839bb6db7c 100644 --- a/docs/fork/0026-telemetry-says-which-app-sent-it.md +++ b/docs/fork/0026-telemetry-says-which-app-sent-it.md @@ -6,9 +6,9 @@ ## What you can do now - Filter UI traces to the desktop window or to a browser tab with - `t3.client.surface`, instead of guessing from a key named `service.mode`. + `t3code.client.surface`, instead of guessing from a key named `service.mode`. - Tell a server the desktop app launched from one you started yourself with - `t3.server.managed_by`. + `t3code.server.managed_by`. - Query every T3 Code signal with the resource attributes a collector, dashboard, or vendor already understands: `service.version`, `deployment.environment.name`, `process.runtime.*`, `user_agent.original`, @@ -27,18 +27,20 @@ the third. Nobody reading a trace could know that without reading the code. Following the semantic conventions puts every attribute where tooling already looks for it, and keeps the one question the conventions have no -answer for, which surface of the product sent this, under the `t3.` prefix -the relay tracing already used. +answer for, which surface of the product sent this, under the `t3code.` +prefix. That is the application's own name, which the conventions recommend +for custom attributes, and unlike `t3` it cannot be mistaken for anything +else. ## Upstream considerations A plausible upstream submission. The attributes came from upstream, and the change carries no fork-specific intent. Dashboards or saved queries that -filter on `service.mode`, `service.runtime`, or `service.component` have to -move to the new keys, which is the part upstream would want to weigh. +filter on `service.mode`, `service.runtime`, `service.component`, or the +relay's `t3.client.surface` have to move to the new keys, which is the part upstream would want to weigh. The server's own `mode` values (`web` for any standalone launch) are left alone, since renaming them touches the CLI flag, `T3CODE_MODE`, and persisted -session data. Telemetry maps `mode` to `t3.server.managed_by` instead. A +session data. Telemetry maps `mode` to `t3code.server.managed_by` instead. A sync that takes upstream's copy of any resource definition brings the old keys back without any test going red outside the ones changed here. diff --git a/docs/operations/observability.md b/docs/operations/observability.md index 6baa6702c8f7..8234b3f0c959 100644 --- a/docs/operations/observability.md +++ b/docs/operations/observability.md @@ -620,8 +620,8 @@ apart with other resource attributes, such as `OTEL_RESOURCE_ATTRIBUTES` replaces it. The UI runs as `t3code-web` in both a browser and the desktop window, since it is the same code. Its -`t3.client.surface` resource attribute is `desktop` or `web`, and `user_agent.original` carries the -full user agent. The server's `t3.server.managed_by` is `desktop` when the desktop app launched it +`t3code.client.surface` resource attribute is `desktop` or `web`, and `user_agent.original` carries the +full user agent. The server's `t3code.server.managed_by` is `desktop` when the desktop app launched it and `standalone` otherwise. If the OTLP URLs are unset, local tracing still works, metrics stay in-process only, and logs stay diff --git a/infra/relay/src/observability.ts b/infra/relay/src/observability.ts index 119f3e499814..4b18c87b5bf7 100644 --- a/infra/relay/src/observability.ts +++ b/infra/relay/src/observability.ts @@ -226,7 +226,7 @@ export const makeRelayTraceLayer = (input: { attributes: { "service.namespace": "t3code", "process.runtime.name": "cloudflare-worker", - "t3.component": "relay", + "t3code.component": "relay", }, }, headers: { diff --git a/packages/shared/src/relayTracing.ts b/packages/shared/src/relayTracing.ts index 89f91d67a485..4bc1d54ea077 100644 --- a/packages/shared/src/relayTracing.ts +++ b/packages/shared/src/relayTracing.ts @@ -144,8 +144,8 @@ export function makeRelayClientTracingLayer( attributes: { "service.namespace": "t3code", "process.runtime.name": resource.runtime, - "t3.component": resource.component ?? "relay-client", - "t3.client.surface": resource.client, + "t3code.component": resource.component ?? "relay-client", + "t3code.client.surface": resource.client, }, }, }).pipe(Layer.provide(OtlpSerialization.layerJson));