1/** 2 * @factory/observability — service resource attributes (TASK-0135). 3 * 4 * Every span/metric/log emitted by the four apps carries the SAME resource 5 * so the collector can demux by `service.name`. The names are fixed by the 6 * phase-15 spec (NOT the bare "api"/"worker" the NFR doc sketched — the spec 7 * wins; recorded conflict, AGENTS rule 29): `factory-web`, `factory-api`, 8 * `factory-worker`, `factory-realtime`. 9 * 10 * NOTE: OTel JS 2.x line (T-190b) — SDK core 2.12.0 + exporters 0.223.0 + 11 * semantic-conventions 1.43.0 + `@prisma/instrumentation` 6.19.3 (Prisma 6.x 12 * pairs with the 2.x train; the 1.x pairing predates it). In 2.x the 13 * `Resource` class is GONE — resources are built with the 14 * `resourceFromAttributes(...)` factory, and `Resource` is the TYPE (the old 15 * `IResource` was renamed to `Resource`). 16 */ 17import { resourceFromAttributes, type Resource } from "@opentelemetry/resources"; 18 19/** The four app service names (phase-15 spec, verbatim). */ 20export const SERVICE_NAMES = { 21 web: "factory-web", 22 api: "factory-api", 23 worker: "factory-worker", 24 realtime: "factory-realtime" 25} as const; 26 27export type ServiceName = (typeof SERVICE_NAMES)[keyof typeof SERVICE_NAMES]; 28 29export function isServiceName(name: string): name is ServiceName { 30 return Object.values(SERVICE_NAMES).includes(name as ServiceName); 31} 32 33export interface BuildResourceOptions { 34 /** `deployment.environment` attribute value (NODE_ENV). */ 35 environment?: string; 36 /** Service version — defaults to the package version (0.1.0). */ 37 version?: string; 38 /** Host name — defaults to the HOSTNAME/HOST env or "local". */ 39 hostName?: string; 40} 41 42/** 43 * Build the OTel `Resource` for one app. Stable attributes only (host name, 44 * not IP — a pod IP churns on every reschedule and would fragment the 45 * series). 46 */ 47export function buildResource(serviceName: ServiceName, opts: BuildResourceOptions = {}): Resource { 48 const { environment, version, hostName } = opts; 49 const versionValue = version ?? "0.1.0"; 50 return resourceFromAttributes({ 51 "service.name": serviceName, 52 "service.version": versionValue, 53 "deployment.environment": environment ?? process.env.NODE_ENV ?? "development", 54 "host.name": hostName ?? safeHostname() 55 }); 56} 57 58function safeHostname(): string { 59 return process.env.HOSTNAME || process.env.HOST || "local"; 60} 61