| author | |
| committer | |
| log | 2a9832b8019039f812706639c11ccc24ce4c4e05 |
| tree | 25faa1b78412c96c4af7c5dd8f6c8b2662ead145 |
| parent | 595a1e1f537f403eba408817c2aba35e61c2ac29 |
| signature | Signed by PGP key B5690EEEBB952194 |
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>20 files changed, 116 insertions(+), 35 deletions(-)
apps/desktop/src/app/DesktopObservability.test.ts+43| ... | @@ -477,6 +477,49 @@ describe("DesktopObservability", () => { | ... | @@ -477,6 +477,49 @@ describe("DesktopObservability", () => { |
| 477 | ); | 477 | ); |
| 478 | }); | 478 | }); |
| 479 | 479 | ||
| 480 | it.effect("keeps its service name while OTEL resource attributes add dimensions", () => { | ||
| 481 | const requests: Array<ExportedRequest> = []; | ||
| 482 | return Effect.gen(function* () { | ||
| 483 | const fileSystem = yield* FileSystem.FileSystem; | ||
| 484 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | ||
| 485 | prefix: "t3-desktop-observability-test-", | ||
| 486 | }); | ||
| 487 | const environmentLayer = makeEnvironmentLayer(baseDir, true, { | ||
| 488 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", | ||
| 489 | }); | ||
| 490 | |||
| 491 | yield* Effect.scoped( | ||
| 492 | Effect.logInfo("desktop service name").pipe( | ||
| 493 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | ||
| 494 | ), | ||
| 495 | ); | ||
| 496 | |||
| 497 | assert.lengthOf(requests, 1); | ||
| 498 | const body = requests[0]?.body ?? ""; | ||
| 499 | assert.include(body, '"stringValue":"t3code-desktop"'); | ||
| 500 | assert.include(body, "deployment.environment.name"); | ||
| 501 | assert.include(body, '"key":"service.namespace","value":{"stringValue":"t3code"}'); | ||
| 502 | assert.notInclude(body, "renamed"); | ||
| 503 | }).pipe( | ||
| 504 | Effect.scoped, | ||
| 505 | Effect.provide( | ||
| 506 | Layer.mergeAll( | ||
| 507 | NodeServices.layer, | ||
| 508 | collectorLayer(requests), | ||
| 509 | ConfigProvider.layer( | ||
| 510 | ConfigProvider.fromEnv({ | ||
| 511 | env: { | ||
| 512 | OTEL_SERVICE_NAME: "renamed", | ||
| 513 | OTEL_RESOURCE_ATTRIBUTES: | ||
| 514 | "service.name=renamed,service.namespace=renamed,deployment.environment.name=development", | ||
| 515 | }, | ||
| 516 | }), | ||
| 517 | ), | ||
| 518 | ), | ||
| 519 | ), | ||
| 520 | ); | ||
| 521 | }); | ||
| 522 | |||
| 480 | it.effect("exports nothing to Settings for logs an unusable OTEL endpoint claimed", () => { | 523 | it.effect("exports nothing to Settings for logs an unusable OTEL endpoint claimed", () => { |
| 481 | const requests: Array<ExportedRequest> = []; | 524 | const requests: Array<ExportedRequest> = []; |
| 482 | return Effect.gen(function* () { | 525 | return Effect.gen(function* () { |
apps/desktop/src/app/DesktopObservability.ts+2-1| ... | @@ -626,8 +626,9 @@ const telemetryLayer = Layer.unwrap( | ... | @@ -626,8 +626,9 @@ const telemetryLayer = Layer.unwrap( |
| 626 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 626 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 627 | const endpoints = yield* resolveOtlpEndpoints; | 627 | const endpoints = yield* resolveOtlpEndpoints; |
| 628 | const resource = { | 628 | const resource = { |
| 629 | serviceName: "desktop", | 629 | serviceName: "t3code-desktop", |
| 630 | attributes: { | 630 | attributes: { |
| 631 | "service.namespace": "t3code", | ||
| 631 | "service.runtime": "desktop", | 632 | "service.runtime": "desktop", |
| 632 | "service.mode": environment.isDevelopment ? "development" : "packaged", | 633 | "service.mode": environment.isDevelopment ? "development" : "packaged", |
| 633 | }, | 634 | }, |
apps/mobile/src/features/observability/tracing.ts+1-1| ... | @@ -25,7 +25,7 @@ export function resolveTracingConfig(): TracingConfig | null { | ... | @@ -25,7 +25,7 @@ export function resolveTracingConfig(): TracingConfig | null { |
| 25 | 25 | ||
| 26 | export function makeTracingLayer(config: TracingConfig | null, resource: TracingResource) { | 26 | export function makeTracingLayer(config: TracingConfig | null, resource: TracingResource) { |
| 27 | return makeRelayClientTracingLayer(config, { | 27 | return makeRelayClientTracingLayer(config, { |
| 28 | serviceName: "t3-mobile-relay-client", | 28 | serviceName: "t3code-mobile", |
| 29 | serviceVersion: resource.serviceVersion, | 29 | serviceVersion: resource.serviceVersion, |
| 30 | runtime: "react-native", | 30 | runtime: "react-native", |
| 31 | client: `mobile-${resource.appVariant}`, | 31 | client: `mobile-${resource.appVariant}`, |
apps/server/src/bin.test.ts-1| ... | @@ -106,7 +106,6 @@ const makeCliTestServerConfig = (baseDir: string) => | ... | @@ -106,7 +106,6 @@ const makeCliTestServerConfig = (baseDir: string) => |
| 106 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 106 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 107 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 107 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 108 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 108 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 109 | otlpServiceName: "t3-server", | ||
| 110 | otelEnvironment: OtelEnvironment.none, | 109 | otelEnvironment: OtelEnvironment.none, |
| 111 | mode: "web", | 110 | mode: "web", |
| 112 | port: 0, | 111 | port: 0, |
apps/server/src/cli/config.test.ts-1| ... | @@ -56,7 +56,6 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { | ... | @@ -56,7 +56,6 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { |
| 56 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 56 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 57 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 57 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 58 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 58 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 59 | otlpServiceName: "t3-server", | ||
| 60 | otelEnvironment: OtelEnvironment.none, | 59 | otelEnvironment: OtelEnvironment.none, |
| 61 | devAllowedOrigins: [], | 60 | devAllowedOrigins: [], |
| 62 | } as const; | 61 | } as const; |
apps/server/src/cli/config.ts-2| ... | @@ -108,7 +108,6 @@ const EnvServerConfig = Config.all({ | ... | @@ -108,7 +108,6 @@ const EnvServerConfig = Config.all({ |
| 108 | otlpExportIntervalMs: Config.Int("T3CODE_OTLP_EXPORT_INTERVAL_MS").pipe( | 108 | otlpExportIntervalMs: Config.Int("T3CODE_OTLP_EXPORT_INTERVAL_MS").pipe( |
| 109 | Config.withDefault(10_000), | 109 | Config.withDefault(10_000), |
| 110 | ), | 110 | ), |
| 111 | otlpServiceName: Config.String("T3CODE_OTLP_SERVICE_NAME").pipe(Config.withDefault("t3-server")), | ||
| 112 | otlpHeaders: Config.schema(OtlpHeadersFromString, "T3CODE_OTLP_HEADERS").pipe( | 111 | otlpHeaders: Config.schema(OtlpHeadersFromString, "T3CODE_OTLP_HEADERS").pipe( |
| 113 | Config.option, | 112 | Config.option, |
| 114 | Config.map(Option.getOrUndefined), | 113 | Config.map(Option.getOrUndefined), |
| ... | @@ -431,7 +430,6 @@ export const resolveServerConfig = ( | ... | @@ -431,7 +430,6 @@ export const resolveServerConfig = ( |
| 431 | otlpTracesExport: traces?.export ?? signalExport, | 430 | otlpTracesExport: traces?.export ?? signalExport, |
| 432 | otlpMetricsExport: metrics?.export ?? signalExport, | 431 | otlpMetricsExport: metrics?.export ?? signalExport, |
| 433 | otlpLogsExport: logs?.export ?? signalExport, | 432 | otlpLogsExport: logs?.export ?? signalExport, |
| 434 | otlpServiceName: env.otlpServiceName, | ||
| 435 | otelEnvironment: otel, | 433 | otelEnvironment: otel, |
| 436 | mode, | 434 | mode, |
| 437 | port, | 435 | port, |
apps/server/src/cli/pair.ts-1| ... | @@ -326,7 +326,6 @@ const makePairServerConfig = Effect.fn(function* (input: { | ... | @@ -326,7 +326,6 @@ const makePairServerConfig = Effect.fn(function* (input: { |
| 326 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 326 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 327 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 327 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 328 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 328 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 329 | otlpServiceName: "t3-server", | ||
| 330 | otelEnvironment: OtelEnvironment.none, | 329 | otelEnvironment: OtelEnvironment.none, |
| 331 | mode: "web", | 330 | mode: "web", |
| 332 | port: state.port, | 331 | port: state.port, |
apps/server/src/cloud/relayTracing.ts+2-2| ... | @@ -7,14 +7,14 @@ const relayClientTracingConfig = resolveRelayClientTracingConfig(); | ... | @@ -7,14 +7,14 @@ const relayClientTracingConfig = resolveRelayClientTracingConfig(); |
| 7 | export const headlessRelayClientTracingLayer = makeRelayClientTracingLayer( | 7 | export const headlessRelayClientTracingLayer = makeRelayClientTracingLayer( |
| 8 | relayClientTracingConfig, | 8 | relayClientTracingConfig, |
| 9 | { | 9 | { |
| 10 | serviceName: "t3-headless-relay-client", | 10 | serviceName: "t3code-server", |
| 11 | runtime: "node", | 11 | runtime: "node", |
| 12 | client: "headless-cli", | 12 | client: "headless-cli", |
| 13 | }, | 13 | }, |
| 14 | ); | 14 | ); |
| 15 | 15 | ||
| 16 | export const serverRelayBrokerTracingLayer = makeRelayClientTracingLayer(relayClientTracingConfig, { | 16 | export const serverRelayBrokerTracingLayer = makeRelayClientTracingLayer(relayClientTracingConfig, { |
| 17 | serviceName: "t3-server", | 17 | serviceName: "t3code-server", |
| 18 | runtime: "node", | 18 | runtime: "node", |
| 19 | client: "environment-server", | 19 | client: "environment-server", |
| 20 | component: "relay-broker", | 20 | component: "relay-broker", |
apps/server/src/config.ts+2-3| ... | @@ -82,7 +82,6 @@ export class ServerConfig extends Context.Service< | ... | @@ -82,7 +82,6 @@ export class ServerConfig extends Context.Service< |
| 82 | readonly otlpTracesExport: SignalExport; | 82 | readonly otlpTracesExport: SignalExport; |
| 83 | readonly otlpMetricsExport: SignalExport; | 83 | readonly otlpMetricsExport: SignalExport; |
| 84 | readonly otlpLogsExport: SignalExport; | 84 | readonly otlpLogsExport: SignalExport; |
| 85 | readonly otlpServiceName: string; | ||
| 86 | readonly otelEnvironment: OtelEnvironment.OtelEnvironment; | 85 | readonly otelEnvironment: OtelEnvironment.OtelEnvironment; |
| 87 | readonly mode: RuntimeMode; | 86 | readonly mode: RuntimeMode; |
| 88 | readonly port: number; | 87 | readonly port: number; |
| ... | @@ -119,8 +118,9 @@ export const make = (config: ServerConfig["Service"]) => ServerConfig.of(config) | ... | @@ -119,8 +118,9 @@ export const make = (config: ServerConfig["Service"]) => ServerConfig.of(config) |
| 119 | * logs report the same service identity to the collector. | 118 | * logs report the same service identity to the collector. |
| 120 | */ | 119 | */ |
| 121 | export const otlpResource = (config: ServerConfig["Service"]) => ({ | 120 | export const otlpResource = (config: ServerConfig["Service"]) => ({ |
| 122 | serviceName: config.otlpServiceName, | 121 | serviceName: "t3code-server", |
| 123 | attributes: { | 122 | attributes: { |
| 123 | "service.namespace": "t3code", | ||
| 124 | "service.runtime": "t3-server", | 124 | "service.runtime": "t3-server", |
| 125 | "service.mode": config.mode, | 125 | "service.mode": config.mode, |
| 126 | }, | 126 | }, |
| ... | @@ -222,7 +222,6 @@ const makeTest = Effect.fn("ServerConfig.makeTest")(function* ( | ... | @@ -222,7 +222,6 @@ const makeTest = Effect.fn("ServerConfig.makeTest")(function* ( |
| 222 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 222 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 223 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 223 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 224 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 224 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 225 | otlpServiceName: "t3-server", | ||
| 226 | otelEnvironment: OtelEnvironment.none, | 225 | otelEnvironment: OtelEnvironment.none, |
| 227 | cwd, | 226 | cwd, |
| 228 | baseDir, | 227 | baseDir, |
apps/server/src/environment/ServerEnvironment.test.ts-1| ... | @@ -59,7 +59,6 @@ const makeServerConfig = Effect.fn(function* (baseDir: string) { | ... | @@ -59,7 +59,6 @@ const makeServerConfig = Effect.fn(function* (baseDir: string) { |
| 59 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 59 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 60 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 60 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 61 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 61 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 62 | otlpServiceName: "t3-server", | ||
| 63 | otelEnvironment: OtelEnvironment.none, | 62 | otelEnvironment: OtelEnvironment.none, |
| 64 | cwd: process.cwd(), | 63 | cwd: process.cwd(), |
| 65 | baseDir, | 64 | baseDir, |
apps/server/src/persistence/Layers/Sqlite.ts+1-1| ... | @@ -32,7 +32,7 @@ export const makeSqlitePersistenceLive = Effect.fn("makeSqlitePersistenceLive")( | ... | @@ -32,7 +32,7 @@ export const makeSqlitePersistenceLive = Effect.fn("makeSqlitePersistenceLive")( |
| 32 | filename: dbPath, | 32 | filename: dbPath, |
| 33 | spanAttributes: { | 33 | spanAttributes: { |
| 34 | "db.name": path.basename(dbPath), | 34 | "db.name": path.basename(dbPath), |
| 35 | "service.name": "t3-server", | 35 | "service.name": "t3code-server", |
| 36 | }, | 36 | }, |
| 37 | }), | 37 | }), |
| 38 | ); | 38 | ); |
apps/server/src/server.test.ts+5-6| ... | @@ -489,7 +489,7 @@ const makeBrowserOtlpPayload = (spanName: string) => | ... | @@ -489,7 +489,7 @@ const makeBrowserOtlpPayload = (spanName: string) => |
| 489 | url: collector.url, | 489 | url: collector.url, |
| 490 | exportInterval: "10 millis", | 490 | exportInterval: "10 millis", |
| 491 | resource: { | 491 | resource: { |
| 492 | serviceName: "t3-web", | 492 | serviceName: "t3code-web", |
| 493 | attributes: { | 493 | attributes: { |
| 494 | "service.runtime": "t3-web", | 494 | "service.runtime": "t3-web", |
| 495 | "service.mode": "browser", | 495 | "service.mode": "browser", |
| ... | @@ -588,7 +588,6 @@ const buildAppUnderTest = (options?: { | ... | @@ -588,7 +588,6 @@ const buildAppUnderTest = (options?: { |
| 588 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 588 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 589 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 589 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 590 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 590 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 591 | otlpServiceName: "t3-server", | ||
| 592 | otelEnvironment: OtelEnvironment.none, | 591 | otelEnvironment: OtelEnvironment.none, |
| 593 | mode: "desktop", | 592 | mode: "desktop", |
| 594 | port: 0, | 593 | port: 0, |
| ... | @@ -5283,7 +5282,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { | ... | @@ -5283,7 +5282,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 5283 | attributes: [ | 5282 | attributes: [ |
| 5284 | { | 5283 | { |
| 5285 | key: "service.name", | 5284 | key: "service.name", |
| 5286 | value: { stringValue: "t3-web" }, | 5285 | value: { stringValue: "t3code-web" }, |
| 5287 | }, | 5286 | }, |
| 5288 | ], | 5287 | ], |
| 5289 | }, | 5288 | }, |
| ... | @@ -5425,7 +5424,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { | ... | @@ -5425,7 +5424,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 5425 | "rpc.method": "server.getSettings", | 5424 | "rpc.method": "server.getSettings", |
| 5426 | }, | 5425 | }, |
| 5427 | resourceAttributes: { | 5426 | resourceAttributes: { |
| 5428 | "service.name": "t3-web", | 5427 | "service.name": "t3code-web", |
| 5429 | }, | 5428 | }, |
| 5430 | scope: { | 5429 | scope: { |
| 5431 | name: "effect", | 5430 | name: "effect", |
| ... | @@ -5556,7 +5555,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { | ... | @@ -5556,7 +5555,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 5556 | // the stub's utf8 decode even though the surrounding bytes don't. | 5555 | // the stub's utf8 decode even though the surrounding bytes don't. |
| 5557 | assert.notEqual(forwarded.body[0], "{"); | 5556 | assert.notEqual(forwarded.body[0], "{"); |
| 5558 | assert.include(forwarded.body, "client.protobuf.test"); | 5557 | assert.include(forwarded.body, "client.protobuf.test"); |
| 5559 | assert.include(forwarded.body, "t3-web"); | 5558 | assert.include(forwarded.body, "t3code-web"); |
| 5560 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), | 5559 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), |
| 5561 | ); | 5560 | ); |
| 5562 | 5561 | ||
| ... | @@ -5657,7 +5656,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { | ... | @@ -5657,7 +5656,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { |
| 5657 | assert.deepEqual(record.links, []); | 5656 | assert.deepEqual(record.links, []); |
| 5658 | assert.equal(record.scope.name, scopeSpan.scope.name); | 5657 | assert.equal(record.scope.name, scopeSpan.scope.name); |
| 5659 | assert.deepEqual(record.scope.attributes, {}); | 5658 | assert.deepEqual(record.scope.attributes, {}); |
| 5660 | assert.equal(record.resourceAttributes["service.name"], "t3-web"); | 5659 | assert.equal(record.resourceAttributes["service.name"], "t3code-web"); |
| 5661 | assert.equal(record.status?.code, String(span.status.code)); | 5660 | assert.equal(record.status?.code, String(span.status.code)); |
| 5662 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), | 5661 | }).pipe(Effect.provide(NodeHttpServer.layerTest)), |
| 5663 | ); | 5662 | ); |
apps/server/src/serverLogger.test.ts+29-2| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import * as NodePath from "@effect/platform-node/NodePath"; | 1 | import * as NodePath from "@effect/platform-node/NodePath"; |
| 2 | import { assert, describe, it } from "@effect/vitest"; | 2 | import { assert, describe, it } from "@effect/vitest"; |
| 3 | import * as NodeOS from "node:os"; | 3 | import * as NodeOS from "node:os"; |
| 4 | import * as ConfigProvider from "effect/ConfigProvider"; | ||
| 4 | import * as Effect from "effect/Effect"; | 5 | import * as Effect from "effect/Effect"; |
| 5 | import * as Layer from "effect/Layer"; | 6 | import * as Layer from "effect/Layer"; |
| 6 | import * as Path from "effect/Path"; | 7 | import * as Path from "effect/Path"; |
| ... | @@ -57,7 +58,6 @@ const configLayer = (overrides: Partial<ServerConfig.ServerConfig["Service"]>) = | ... | @@ -57,7 +58,6 @@ const configLayer = (overrides: Partial<ServerConfig.ServerConfig["Service"]>) = |
| 57 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, | 58 | otlpTracesExport: DEFAULT_SIGNAL_EXPORT, |
| 58 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, | 59 | otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, |
| 59 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, | 60 | otlpLogsExport: DEFAULT_SIGNAL_EXPORT, |
| 60 | otlpServiceName: "t3-server", | ||
| 61 | otelEnvironment: OtelEnvironment.none, | 61 | otelEnvironment: OtelEnvironment.none, |
| 62 | cwd: baseDir, | 62 | cwd: baseDir, |
| 63 | baseDir, | 63 | baseDir, |
| ... | @@ -146,11 +146,38 @@ describe("ServerLoggerLive", () => { | ... | @@ -146,11 +146,38 @@ describe("ServerLoggerLive", () => { |
| 146 | const [request] = requests; | 146 | const [request] = requests; |
| 147 | assert.strictEqual(request?.url, "https://collector.example.com/v1/logs"); | 147 | assert.strictEqual(request?.url, "https://collector.example.com/v1/logs"); |
| 148 | assert.include(request?.body ?? "", "server logger under test"); | 148 | assert.include(request?.body ?? "", "server logger under test"); |
| 149 | assert.include(request?.body ?? "", "t3-server"); | 149 | assert.include(request?.body ?? "", "t3code-server"); |
| 150 | assert.include(request?.body ?? "", "service.runtime"); | 150 | assert.include(request?.body ?? "", "service.runtime"); |
| 151 | }), | 151 | }), |
| 152 | ); | 152 | ); |
| 153 | 153 | ||
| 154 | it.effect("keeps its service name while OTEL resource attributes add dimensions", () => | ||
| 155 | Effect.gen(function* () { | ||
| 156 | const requests = yield* logThrough({ | ||
| 157 | otlpLogsUrl: "https://collector.example.com/v1/logs", | ||
| 158 | }).pipe( | ||
| 159 | Effect.provide( | ||
| 160 | ConfigProvider.layer( | ||
| 161 | ConfigProvider.fromEnv({ | ||
| 162 | env: { | ||
| 163 | OTEL_SERVICE_NAME: "renamed", | ||
| 164 | OTEL_RESOURCE_ATTRIBUTES: | ||
| 165 | "service.name=renamed,service.namespace=renamed,deployment.environment.name=development", | ||
| 166 | }, | ||
| 167 | }), | ||
| 168 | ), | ||
| 169 | ), | ||
| 170 | ); | ||
| 171 | |||
| 172 | assert.lengthOf(requests, 1); | ||
| 173 | const body = requests[0]?.body ?? ""; | ||
| 174 | assert.include(body, '"stringValue":"t3code-server"'); | ||
| 175 | assert.include(body, "deployment.environment.name"); | ||
| 176 | assert.include(body, '"key":"service.namespace","value":{"stringValue":"t3code"}'); | ||
| 177 | assert.notInclude(body, "renamed"); | ||
| 178 | }), | ||
| 179 | ); | ||
| 180 | |||
| 154 | it.effect("stays off the network when no logs endpoint is configured", () => | 181 | it.effect("stays off the network when no logs endpoint is configured", () => |
| 155 | Effect.gen(function* () { | 182 | Effect.gen(function* () { |
| 156 | const requests = yield* logThrough({}); | 183 | const requests = yield* logThrough({}); |
apps/web/src/lib/runtime.ts+1-1| ... | @@ -19,7 +19,7 @@ function configuredRelayUrl(): string { | ... | @@ -19,7 +19,7 @@ function configuredRelayUrl(): string { |
| 19 | 19 | ||
| 20 | const httpClientLayer = remoteHttpClientLayer((input, init) => globalThis.fetch(input, init)); | 20 | const httpClientLayer = remoteHttpClientLayer((input, init) => globalThis.fetch(input, init)); |
| 21 | const relayTracingLayer = makeRelayClientTracingLayer(resolveRelayTracingConfig(), { | 21 | const relayTracingLayer = makeRelayClientTracingLayer(resolveRelayTracingConfig(), { |
| 22 | serviceName: "t3-web-relay-client", | 22 | serviceName: "t3code-web", |
| 23 | serviceVersion: import.meta.env.APP_VERSION, | 23 | serviceVersion: import.meta.env.APP_VERSION, |
| 24 | runtime: "browser", | 24 | runtime: "browser", |
| 25 | client: typeof window !== "undefined" && window.desktopBridge ? "desktop" : "web", | 25 | client: typeof window !== "undefined" && window.desktopBridge ? "desktop" : "web", |
apps/web/src/observability/clientTracing.ts+2-1| ... | @@ -15,8 +15,9 @@ import { APP_VERSION } from "~/branding"; | ... | @@ -15,8 +15,9 @@ import { APP_VERSION } from "~/branding"; |
| 15 | 15 | ||
| 16 | const DEFAULT_EXPORT_INTERVAL_MS = 1_000; | 16 | const DEFAULT_EXPORT_INTERVAL_MS = 1_000; |
| 17 | const CLIENT_TRACING_RESOURCE = { | 17 | const CLIENT_TRACING_RESOURCE = { |
| 18 | serviceName: "t3-web", | 18 | serviceName: "t3code-web", |
| 19 | attributes: { | 19 | attributes: { |
| 20 | "service.namespace": "t3code", | ||
| 20 | "service.runtime": "t3-web", | 21 | "service.runtime": "t3-web", |
| 21 | "service.mode": isElectron ? "electron" : "browser", | 22 | "service.mode": isElectron ? "electron" : "browser", |
| 22 | "service.version": APP_VERSION, | 23 | "service.version": APP_VERSION, |
docs/operations/observability.md+14-10| ... | @@ -129,7 +129,7 @@ Default Grafana login: | ... | @@ -129,7 +129,7 @@ Default Grafana login: |
| 129 | export T3CODE_OTLP_TRACES_URL=http://localhost:4318/v1/traces | 129 | export T3CODE_OTLP_TRACES_URL=http://localhost:4318/v1/traces |
| 130 | export T3CODE_OTLP_METRICS_URL=http://localhost:4318/v1/metrics | 130 | export T3CODE_OTLP_METRICS_URL=http://localhost:4318/v1/metrics |
| 131 | export T3CODE_OTLP_LOGS_URL=http://localhost:4318/v1/logs | 131 | export T3CODE_OTLP_LOGS_URL=http://localhost:4318/v1/logs |
| 132 | export T3CODE_OTLP_SERVICE_NAME=t3-local | 132 | export OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=development |
| 133 | ``` | 133 | ``` |
| 134 | 134 | ||
| 135 | Optional: | 135 | Optional: |
| ... | @@ -169,7 +169,6 @@ macOS app bundle example: | ... | @@ -169,7 +169,6 @@ macOS app bundle example: |
| 169 | T3CODE_OTLP_TRACES_URL=http://localhost:4318/v1/traces \ | 169 | T3CODE_OTLP_TRACES_URL=http://localhost:4318/v1/traces \ |
| 170 | T3CODE_OTLP_METRICS_URL=http://localhost:4318/v1/metrics \ | 170 | T3CODE_OTLP_METRICS_URL=http://localhost:4318/v1/metrics \ |
| 171 | T3CODE_OTLP_LOGS_URL=http://localhost:4318/v1/logs \ | 171 | T3CODE_OTLP_LOGS_URL=http://localhost:4318/v1/logs \ |
| 172 | T3CODE_OTLP_SERVICE_NAME=t3-desktop \ | ||
| 173 | "/Applications/T3 Code.app/Contents/MacOS/T3 Code" | 172 | "/Applications/T3 Code.app/Contents/MacOS/T3 Code" |
| 174 | ``` | 173 | ``` |
| 175 | 174 | ||
| ... | @@ -179,7 +178,6 @@ Direct binary example: | ... | @@ -179,7 +178,6 @@ Direct binary example: |
| 179 | T3CODE_OTLP_TRACES_URL=http://localhost:4318/v1/traces \ | 178 | T3CODE_OTLP_TRACES_URL=http://localhost:4318/v1/traces \ |
| 180 | T3CODE_OTLP_METRICS_URL=http://localhost:4318/v1/metrics \ | 179 | T3CODE_OTLP_METRICS_URL=http://localhost:4318/v1/metrics \ |
| 181 | T3CODE_OTLP_LOGS_URL=http://localhost:4318/v1/logs \ | 180 | T3CODE_OTLP_LOGS_URL=http://localhost:4318/v1/logs \ |
| 182 | T3CODE_OTLP_SERVICE_NAME=t3-desktop \ | ||
| 183 | ./path/to/your/desktop-app-binary | 181 | ./path/to/your/desktop-app-binary |
| 184 | ``` | 182 | ``` |
| 185 | 183 | ||
| ... | @@ -311,11 +309,13 @@ Recommended flow in Grafana: | ... | @@ -311,11 +309,13 @@ Recommended flow in Grafana: |
| 311 | 2. Pick the `Tempo` data source. | 309 | 2. Pick the `Tempo` data source. |
| 312 | 3. Set the time range to something recent like `Last 15 minutes`. | 310 | 3. Set the time range to something recent like `Last 15 minutes`. |
| 313 | 4. Start broad. Do not begin with a very narrow query. | 311 | 4. Start broad. Do not begin with a very narrow query. |
| 314 | 5. Look for spans from your configured service name, then narrow by span name or attributes. | 312 | 5. Look for spans from the `t3code-server` or `t3code-desktop` service, then narrow by span name or |
| 313 | attributes. | ||
| 315 | 314 | ||
| 316 | Good first searches: | 315 | Good first searches: |
| 317 | 316 | ||
| 318 | - service name such as `t3-local`, `t3-dev`, or `t3-desktop` | 317 | - service name `t3code-server` or `t3code-desktop`, plus a resource attribute such as |
| 318 | `deployment.environment.name` | ||
| 319 | - span names like `sendTurn` or a Git operation such as `GitVcsDriver.statusDetails.status` | 319 | - span names like `sendTurn` or a Git operation such as `GitVcsDriver.statusDetails.status` |
| 320 | - Git spans whose `git.operation` attribute identifies the operation | 320 | - Git spans whose `git.operation` attribute identifies the operation |
| 321 | - orchestration spans with attributes like `orchestration.command_type` | 321 | - orchestration spans with attributes like `orchestration.command_type` |
| ... | @@ -525,10 +525,10 @@ It provides: | ... | @@ -525,10 +525,10 @@ It provides: |
| 525 | The desktop main process is a second producer, assembled in | 525 | The desktop main process is a second producer, assembled in |
| 526 | `apps/desktop/src/app/DesktopObservability.ts`. It reads the same `T3CODE_OTLP_*` names and the same | 526 | `apps/desktop/src/app/DesktopObservability.ts`. It reads the same `T3CODE_OTLP_*` names and the same |
| 527 | Settings entries as the backend it supervises, and covers work the backend cannot see: app startup, | 527 | Settings entries as the backend it supervises, and covers work the backend cannot see: app startup, |
| 528 | window and menu handling, backend supervision, and updates. It reports as service `desktop` | 528 | window and menu handling, backend supervision, and updates. It reports as service |
| 529 | regardless of `T3CODE_OTLP_SERVICE_NAME`, so a collector shows it alongside the backend rather than | 529 | `t3code-desktop`, so a collector shows it alongside the backend rather than mixed into it. It |
| 530 | mixed into it. It exports traces and logs only; the main process records no metrics, so the metrics | 530 | exports traces and logs only; the main process records no metrics, so the metrics endpoint applies |
| 531 | endpoint applies to the backend alone. | 531 | to the backend alone. |
| 532 | 532 | ||
| 533 | ### Env Vars | 533 | ### Env Vars |
| 534 | 534 | ||
| ... | @@ -547,7 +547,6 @@ OTLP export: | ... | @@ -547,7 +547,6 @@ OTLP export: |
| 547 | - `T3CODE_OTLP_METRICS_URL`: OTLP metric endpoint | 547 | - `T3CODE_OTLP_METRICS_URL`: OTLP metric endpoint |
| 548 | - `T3CODE_OTLP_LOGS_URL`: OTLP log endpoint | 548 | - `T3CODE_OTLP_LOGS_URL`: OTLP log endpoint |
| 549 | - `T3CODE_OTLP_EXPORT_INTERVAL_MS`: export interval, default `10000` | 549 | - `T3CODE_OTLP_EXPORT_INTERVAL_MS`: export interval, default `10000` |
| 550 | - `T3CODE_OTLP_SERVICE_NAME`: service name, default `t3-server` | ||
| 551 | - `T3CODE_OTLP_HEADERS`: extra headers for all three exporters, same format as | 550 | - `T3CODE_OTLP_HEADERS`: extra headers for all three exporters, same format as |
| 552 | `OTEL_EXPORTER_OTLP_HEADERS`: comma-separated `key=value` pairs with percent-encoded values. | 551 | `OTEL_EXPORTER_OTLP_HEADERS`: comma-separated `key=value` pairs with percent-encoded values. |
| 553 | - `T3CODE_OTLP_PROTOCOL`: `http/json` (default) or `http/protobuf` | 552 | - `T3CODE_OTLP_PROTOCOL`: `http/json` (default) or `http/protobuf` |
| ... | @@ -565,6 +564,11 @@ an `http` or `https` URL, a protocol other than `http/protobuf` or `http/json` s | ... | @@ -565,6 +564,11 @@ an `http` or `https` URL, a protocol other than `http/protobuf` or `http/json` s |
| 565 | headers that are not `key=value` pairs with percent-encoded values turn that signal's export off | 564 | headers that are not `key=value` pairs with percent-encoded values turn that signal's export off |
| 566 | with a startup warning, rather than sending it to the Settings endpoint. | 565 | with a startup warning, rather than sending it to the Settings endpoint. |
| 567 | 566 | ||
| 567 | Service names are fixed: `t3code-server` for the backend and `t3code-desktop` for the desktop main | ||
| 568 | process, both in `service.namespace` `t3code`. `OTEL_SERVICE_NAME` and a `service.name` or | ||
| 569 | `service.namespace` in `OTEL_RESOURCE_ATTRIBUTES` are ignored. Tell installations apart with other | ||
| 570 | resource attributes, such as `OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=development`. | ||
| 571 | |||
| 568 | If the OTLP URLs are unset, local tracing still works, metrics stay in-process only, and logs stay | 572 | If the OTLP URLs are unset, local tracing still works, metrics stay in-process only, and logs stay |
| 569 | on stdout only. | 573 | on stdout only. |
| 570 | 574 |
infra/relay/src/observability.test.ts+9| ... | @@ -62,6 +62,11 @@ it.effect("exports schema error fields as span attributes", () => | ... | @@ -62,6 +62,11 @@ it.effect("exports schema error fields as span attributes", () => |
| 62 | 62 | ||
| 63 | const request = yield* Deferred.await(exportedRequest).pipe(Effect.timeout("1 second")); | 63 | const request = yield* Deferred.await(exportedRequest).pipe(Effect.timeout("1 second")); |
| 64 | const payload = (yield* decodeJson(request.body)) as OtlpTracer.TraceData; | 64 | const payload = (yield* decodeJson(request.body)) as OtlpTracer.TraceData; |
| 65 | const resourceAttributes = Object.fromEntries( | ||
| 66 | payload.resourceSpans | ||
| 67 | .flatMap((resourceSpan) => resourceSpan.resource.attributes) | ||
| 68 | .map((attribute) => [attribute.key, otlpAttributeValue(attribute.value)]), | ||
| 69 | ); | ||
| 65 | const span = payload.resourceSpans | 70 | const span = payload.resourceSpans |
| 66 | .flatMap((resourceSpan) => resourceSpan.scopeSpans) | 71 | .flatMap((resourceSpan) => resourceSpan.scopeSpans) |
| 67 | .flatMap((scopeSpan) => scopeSpan.spans) | 72 | .flatMap((scopeSpan) => scopeSpan.spans) |
| ... | @@ -75,6 +80,10 @@ it.effect("exports schema error fields as span attributes", () => | ... | @@ -75,6 +80,10 @@ it.effect("exports schema error fields as span attributes", () => |
| 75 | 80 | ||
| 76 | expect(request.authorization).toBe("Bearer test-token"); | 81 | expect(request.authorization).toBe("Bearer test-token"); |
| 77 | expect(request.dataset).toBe("relay-test-traces"); | 82 | expect(request.dataset).toBe("relay-test-traces"); |
| 83 | expect(resourceAttributes).toMatchObject({ | ||
| 84 | "service.name": "t3code-relay", | ||
| 85 | "service.namespace": "t3code", | ||
| 86 | }); | ||
| 78 | expect(attributes).toMatchObject({ | 87 | expect(attributes).toMatchObject({ |
| 79 | "error.type": "EnvironmentConnectNotAuthorized", | 88 | "error.type": "EnvironmentConnectNotAuthorized", |
| 80 | "error.environmentId": "environment-1", | 89 | "error.environmentId": "environment-1", |
infra/relay/src/observability.ts+2-1| ... | @@ -222,8 +222,9 @@ export const makeRelayTraceLayer = (input: { | ... | @@ -222,8 +222,9 @@ export const makeRelayTraceLayer = (input: { |
| 222 | OtlpTracer.make({ | 222 | OtlpTracer.make({ |
| 223 | url: input.tracesEndpoint, | 223 | url: input.tracesEndpoint, |
| 224 | resource: { | 224 | resource: { |
| 225 | serviceName: "t3-code-relay-worker", | 225 | serviceName: "t3code-relay", |
| 226 | attributes: { | 226 | attributes: { |
| 227 | "service.namespace": "t3code", | ||
| 227 | "service.runtime": "cloudflare-worker", | 228 | "service.runtime": "cloudflare-worker", |
| 228 | "service.component": "relay", | 229 | "service.component": "relay", |
| 229 | }, | 230 | }, |
packages/shared/src/relayTracing.test.ts+2| ... | @@ -97,6 +97,8 @@ describe("withRelayClientTracing", () => { | ... | @@ -97,6 +97,8 @@ describe("withRelayClientTracing", () => { |
| 97 | const payload = new TextDecoder().decode(fetchFn.mock.calls[0]?.[1]?.body as Uint8Array); | 97 | const payload = new TextDecoder().decode(fetchFn.mock.calls[0]?.[1]?.body as Uint8Array); |
| 98 | expect(payload).toContain("relay request failed"); | 98 | expect(payload).toContain("relay request failed"); |
| 99 | expect(payload).toContain("relay socket closed"); | 99 | expect(payload).toContain("relay socket closed"); |
| 100 | expect(payload).toContain('"key":"service.name","value":{"stringValue":"relay-test"}'); | ||
| 101 | expect(payload).toContain('"key":"service.namespace","value":{"stringValue":"t3code"}'); | ||
| 100 | }), | 102 | }), |
| 101 | ), | 103 | ), |
| 102 | ); | 104 | ); |
packages/shared/src/relayTracing.ts+1| ... | @@ -142,6 +142,7 @@ export function makeRelayClientTracingLayer( | ... | @@ -142,6 +142,7 @@ export function makeRelayClientTracingLayer( |
| 142 | serviceName: resource.serviceName, | 142 | serviceName: resource.serviceName, |
| 143 | serviceVersion: resource.serviceVersion, | 143 | serviceVersion: resource.serviceVersion, |
| 144 | attributes: { | 144 | attributes: { |
| 145 | "service.namespace": "t3code", | ||
| 145 | "service.runtime": resource.runtime, | 146 | "service.runtime": resource.runtime, |
| 146 | "service.component": resource.component ?? "relay-client", | 147 | "service.component": resource.component ?? "relay-client", |
| 147 | "t3.client.surface": resource.client, | 148 | "t3.client.surface": resource.client, |