From 12aeb8bf37dc74efbec53f9d272d1e8f046d8f81 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 11 May 2026 11:23:19 +0200 Subject: [PATCH] feat(core-shared): OTel resource builder --- .../src/instrumentation/otel/resource.test.ts | 38 +++++++++++++++++++ .../src/instrumentation/otel/resource.ts | 22 +++++++++++ 2 files changed, 60 insertions(+) create mode 100644 packages/core-shared/src/instrumentation/otel/resource.test.ts create mode 100644 packages/core-shared/src/instrumentation/otel/resource.ts diff --git a/packages/core-shared/src/instrumentation/otel/resource.test.ts b/packages/core-shared/src/instrumentation/otel/resource.test.ts new file mode 100644 index 0000000..8b61102 --- /dev/null +++ b/packages/core-shared/src/instrumentation/otel/resource.test.ts @@ -0,0 +1,38 @@ +import { describe, it, expect } from "vitest"; +import { buildResource } from "./resource"; +import { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, + ATTR_DEPLOYMENT_ENVIRONMENT_NAME, +} from "@opentelemetry/semantic-conventions/incubating"; + +describe("buildResource", () => { + it("populates service name, version, and environment", () => { + const r = buildResource({ + serviceName: "web-next", + serviceVersion: "1.0.0", + environment: "production", + }); + expect(r.attributes[ATTR_SERVICE_NAME]).toBe("web-next"); + expect(r.attributes[ATTR_SERVICE_VERSION]).toBe("1.0.0"); + expect(r.attributes[ATTR_DEPLOYMENT_ENVIRONMENT_NAME]).toBe("production"); + }); + + it("populates namespace when provided", () => { + const r = buildResource({ + serviceName: "web-next", + environment: "production", + namespace: "template-vertical", + }); + expect(r.attributes["service.namespace"]).toBe("template-vertical"); + }); + + it("omits version and namespace when not provided", () => { + const r = buildResource({ + serviceName: "web-next", + environment: "production", + }); + expect(r.attributes[ATTR_SERVICE_VERSION]).toBeUndefined(); + expect(r.attributes["service.namespace"]).toBeUndefined(); + }); +}); diff --git a/packages/core-shared/src/instrumentation/otel/resource.ts b/packages/core-shared/src/instrumentation/otel/resource.ts new file mode 100644 index 0000000..dfccd37 --- /dev/null +++ b/packages/core-shared/src/instrumentation/otel/resource.ts @@ -0,0 +1,22 @@ +import { Resource } from "@opentelemetry/resources"; + +export type BuildResourceOpts = { + serviceName: string; + serviceVersion?: string; + environment: string; + namespace?: string; +}; + +/** + * Builds an OpenTelemetry Resource with semantic-convention attributes. + * Each app constructs its own resource at startup (per-app service name). + */ +export function buildResource(opts: BuildResourceOpts): Resource { + const attrs: Record = { + "service.name": opts.serviceName, + "deployment.environment.name": opts.environment, + }; + if (opts.serviceVersion) attrs["service.version"] = opts.serviceVersion; + if (opts.namespace) attrs["service.namespace"] = opts.namespace; + return new Resource(attrs); +}