From df51513ea3383ad3cda15debfbbbc8d389fcc45c Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 25 Jul 2025 18:01:25 -0300 Subject: [PATCH 01/38] Bump diagnostics-nodejs version --- package.json | 2 +- yarn.lock | 61 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index de2af9f83..a72c00aa9 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "dependencies": { "@types/koa": "^2.11.0", "@types/koa-compose": "^3.2.3", - "@vtex/diagnostics-nodejs": "0.1.0-beta.10", + "@vtex/diagnostics-nodejs": "0.1.0-io-beta.19", "@vtex/node-error-report": "^0.0.3", "@wry/equality": "^0.1.9", "agentkeepalive": "^4.0.2", diff --git a/yarn.lock b/yarn.lock index fd49540e1..c2308b223 100644 --- a/yarn.lock +++ b/yarn.lock @@ -236,6 +236,14 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@grpc/grpc-js@^1.13.4": + version "1.13.4" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.13.4.tgz#922fbc496e229c5fa66802d2369bf181c1df1c5a" + integrity sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg== + dependencies: + "@grpc/proto-loader" "^0.7.13" + "@js-sdsl/ordered-map" "^4.4.2" + "@grpc/grpc-js@^1.7.1": version "1.13.3" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.13.3.tgz#6ad08d186c2a8651697085f790c5c68eaca45904" @@ -470,12 +478,19 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/context-async-hooks@1.30.1": +"@opentelemetry/baggage-span-processor@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/baggage-span-processor/-/baggage-span-processor-0.3.1.tgz#8bca006ad0ca5e43d452a615ac2469a09cab7711" + integrity sha512-m4XXch3/NraA0XEogdQgdMbhg0ZWQWnwXRxuWZJLskIFvIatvUZwoWZm+8gApEZNJNz/Jk/dwtMylUQZBwcyYA== + dependencies: + "@opentelemetry/sdk-trace-base" "^1.0.0" + +"@opentelemetry/context-async-hooks@1.30.1", "@opentelemetry/context-async-hooks@^1.30.1": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz#4f76280691a742597fd0bf682982126857622948" integrity sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA== -"@opentelemetry/core@1.30.1", "@opentelemetry/core@^1.0.0", "@opentelemetry/core@^1.30.1": +"@opentelemetry/core@1.30.1", "@opentelemetry/core@^1.0.0", "@opentelemetry/core@^1.30.1", "@opentelemetry/core@^1.8.0": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.30.1.tgz#a0b468bb396358df801881709ea38299fc30ab27" integrity sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ== @@ -494,7 +509,7 @@ "@opentelemetry/otlp-transformer" "0.57.2" "@opentelemetry/sdk-logs" "0.57.2" -"@opentelemetry/exporter-logs-otlp-http@0.57.2", "@opentelemetry/exporter-logs-otlp-http@^0.57.2": +"@opentelemetry/exporter-logs-otlp-http@0.57.2": version "0.57.2" resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.57.2.tgz#01d4668b8f781540f94592da9284b92fd6a2ccd8" integrity sha512-0rygmvLcehBRp56NQVLSleJ5ITTduq/QfU7obOkyWgPpFHulwpw2LYTqNIz5TczKZuy5YY+5D3SDnXZL1tXImg== @@ -532,7 +547,7 @@ "@opentelemetry/resources" "1.30.1" "@opentelemetry/sdk-metrics" "1.30.1" -"@opentelemetry/exporter-metrics-otlp-http@0.57.2", "@opentelemetry/exporter-metrics-otlp-http@^0.57.2": +"@opentelemetry/exporter-metrics-otlp-http@0.57.2": version "0.57.2" resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.57.2.tgz#0983b28a4a36dee3af2c258394214004e4c68b53" integrity sha512-ttb9+4iKw04IMubjm3t0EZsYRNWr3kg44uUuzfo9CaccYlOh8cDooe4QObDUkvx9d5qQUrbEckhrWKfJnKhemA== @@ -577,7 +592,7 @@ "@opentelemetry/resources" "1.30.1" "@opentelemetry/sdk-trace-base" "1.30.1" -"@opentelemetry/exporter-trace-otlp-http@0.57.2", "@opentelemetry/exporter-trace-otlp-http@^0.57.2": +"@opentelemetry/exporter-trace-otlp-http@0.57.2": version "0.57.2" resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.57.2.tgz#0ab8e97dc30dbabb8252b68128b80c4685f7c691" integrity sha512-sB/gkSYFu+0w2dVQ0PWY9fAMl172PKMZ/JrHkkW8dmjCL0CYkmXeE+ssqIL/yBUTPOvpLIpenX5T9RwXRBW/3g== @@ -609,6 +624,15 @@ "@opentelemetry/sdk-trace-base" "1.30.1" "@opentelemetry/semantic-conventions" "1.28.0" +"@opentelemetry/instrumentation-express@^0.47.1": + version "0.47.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz#7cf74f35e43cc3c8186edd1249fdb225849c48b2" + integrity sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.57.1" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-http@^0.57.2": version "0.57.2" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz#f425eda67b6241c3abe08e4ea972169b85ef3064" @@ -745,7 +769,7 @@ "@opentelemetry/sdk-trace-node" "1.30.1" "@opentelemetry/semantic-conventions" "1.28.0" -"@opentelemetry/sdk-trace-base@1.30.1", "@opentelemetry/sdk-trace-base@^1.30.1": +"@opentelemetry/sdk-trace-base@1.30.1", "@opentelemetry/sdk-trace-base@^1.0.0", "@opentelemetry/sdk-trace-base@^1.30.1": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz#41a42234096dc98e8f454d24551fc80b816feb34" integrity sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg== @@ -1219,21 +1243,22 @@ dependencies: "@types/yargs-parser" "*" -"@vtex/diagnostics-nodejs@0.1.0-beta.10": - version "0.1.0-beta.10" - resolved "https://registry.yarnpkg.com/@vtex/diagnostics-nodejs/-/diagnostics-nodejs-0.1.0-beta.10.tgz#af255418c0777bf49d02f1e650d654d20f11e513" - integrity sha512-w5IOo+P1RcGXYZZw5RV4guQFIKpIqmq7reEQRx6qJYDh0RwLFFhr7NS8MNGm792xOs59hyYMulhx8FMmcOXVxA== +"@vtex/diagnostics-nodejs@0.1.0-io-beta.19": + version "0.1.0-io-beta.19" + resolved "https://registry.yarnpkg.com/@vtex/diagnostics-nodejs/-/diagnostics-nodejs-0.1.0-io-beta.19.tgz#e8ebc6a0b44014d0ffcda9df736291c52604ee9e" + integrity sha512-p3bcfpCI12k1DXqD2wJvO53dDdrULYsK7muJFHlwiZio4WvnSfPU1Ay5ni9Y8alD/MLX4fP4EEjRYXTGc7wFWQ== dependencies: + "@grpc/grpc-js" "^1.13.4" "@opentelemetry/api" "^1.9.0" "@opentelemetry/api-logs" "^0.200.0" + "@opentelemetry/baggage-span-processor" "^0.3.1" + "@opentelemetry/context-async-hooks" "^1.30.1" "@opentelemetry/core" "^1.30.1" "@opentelemetry/exporter-logs-otlp-grpc" "^0.57.2" - "@opentelemetry/exporter-logs-otlp-http" "^0.57.2" "@opentelemetry/exporter-metrics-otlp-grpc" "^0.57.2" - "@opentelemetry/exporter-metrics-otlp-http" "^0.57.2" "@opentelemetry/exporter-trace-otlp-grpc" "^0.57.2" - "@opentelemetry/exporter-trace-otlp-http" "^0.57.2" "@opentelemetry/instrumentation" "^0.57.2" + "@opentelemetry/instrumentation-express" "^0.47.1" "@opentelemetry/instrumentation-http" "^0.57.2" "@opentelemetry/instrumentation-net" "^0.43.1" "@opentelemetry/propagator-b3" "^1.30.1" @@ -1245,14 +1270,14 @@ "@opentelemetry/sdk-trace-base" "^1.30.1" "@opentelemetry/sdk-trace-node" "^1.30.1" "@opentelemetry/semantic-conventions" "^1.30.0" - "@vtex/diagnostics-semconv" "0.1.0-beta.10" + "@vtex/diagnostics-semconv" "0.1.0-beta.11" tslib "^2.8.1" uuid "^11.1.0" -"@vtex/diagnostics-semconv@0.1.0-beta.10": - version "0.1.0-beta.10" - resolved "https://registry.yarnpkg.com/@vtex/diagnostics-semconv/-/diagnostics-semconv-0.1.0-beta.10.tgz#f5b6aa4444cbb4bc1fb0c9e38ca52594d9c2b939" - integrity sha512-a5D8tlBJjBqJBTPsbm3la4gy6hiduWyTTWzjOqoICdgsmCNB9E8mw6NcloEMkBILQ7n8X3EDfXZvkea6OILibQ== +"@vtex/diagnostics-semconv@0.1.0-beta.11": + version "0.1.0-beta.11" + resolved "https://registry.yarnpkg.com/@vtex/diagnostics-semconv/-/diagnostics-semconv-0.1.0-beta.11.tgz#2ddfff7dffdc1c052d23b335f914de91653d9659" + integrity sha512-H3KM5fYAFmcxhlA4wT5iPgWJtgKsumFqGkkxjcA/BSwC5tgSWezN82sZDKvBsVo24EoZxVGgLlsjNw1tsp9U3Q== "@vtex/node-error-report@^0.0.3": version "0.0.3" From aa2e3bb76b73f8c9948066b544ed39873f5fcf9b Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 4 Aug 2025 18:03:35 -0300 Subject: [PATCH 02/38] Refactor telemetry client to support all signals Enables this client to support logs, metrics, and tracing signals from the diagnostics-nodejs library, making them available already initialized. Additionally, this change enables registration of built-in and community instrumentation. --- src/service/telemetry/client.ts | 86 +++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 19 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index efe704d78..91edea039 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -1,11 +1,27 @@ -import { NewTelemetryClient } from '@vtex/diagnostics-nodejs'; -import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; +import { + NewTelemetryClient, + Instrumentation, + Exporters, + Logs, + Metrics, + Traces, +} from '@vtex/diagnostics-nodejs'; import { APP } from '../../constants'; +const CLIENT_NAME = APP.NAME || 'node-vtex-api'; +const APPLICATION_ID = APP.ID || 'vtex-io-app'; +const EXPORTER_OTLP_ENDPOINT = process.env.EXPORTER_OTLP_ENDPOINT; + +interface TelemetryClients { + logsClient: Logs.LogClient; + metricsClient: Metrics.MetricsClient; + tracesClient: Traces.TraceClient; +} + class TelemetryClientSingleton { private static instance: TelemetryClientSingleton; - private telemetryClient: TelemetryClient | undefined; - private initializationPromise: Promise | undefined = undefined; + private telemetryClients: TelemetryClients | undefined; + private initializationPromise: Promise | undefined = undefined; private constructor() {} @@ -16,11 +32,12 @@ class TelemetryClientSingleton { return TelemetryClientSingleton.instance; } - private async initTelemetryClient(): Promise { + private async initializeTelemetryClients(): Promise { try { const telemetryClient = await NewTelemetryClient( + APPLICATION_ID, + CLIENT_NAME, 'node-vtex-api', - APP.ID || 'vtex-app', { additionalAttrs: { 'version': APP.VERSION || '', @@ -29,41 +46,72 @@ class TelemetryClientSingleton { } ); - this.telemetryClient = telemetryClient; - return telemetryClient; + const tracesClient = await telemetryClient.newTracesClient({ + exporter: Exporters.CreateExporter(Exporters.CreateTracesExporterConfig({ + endpoint: EXPORTER_OTLP_ENDPOINT, + }), 'otlp'), + }); + + const metricsClient = await telemetryClient.newMetricsClient({ + exporter: Exporters.CreateExporter(Exporters.CreateMetricsExporterConfig({ + endpoint: EXPORTER_OTLP_ENDPOINT, + interval: 5, + timeoutSeconds: 5, + }), 'otlp'), + }); + + const logsClient = await telemetryClient.newLogsClient({ + exporter: Exporters.CreateExporter(Exporters.CreateLogsExporterConfig({ + endpoint: EXPORTER_OTLP_ENDPOINT, + }), 'otlp'), + loggerName: `node-vtex-api-${APPLICATION_ID}`, + }); + + const instrumentations = [ + ...Instrumentation.CommonInstrumentations.minimal(), + ]; + + telemetryClient.registerInstrumentations(instrumentations); + + const clients: TelemetryClients = { + logsClient, + metricsClient, + tracesClient, + }; + + this.telemetryClients = clients; + return clients; } catch (error) { - console.error('Failed to initialize telemetry client:', error); + console.error('Failed to initialize telemetry clients:', error); throw error; } finally { this.initializationPromise = undefined; } } - public async getClient(): Promise { - if (this.telemetryClient) { - return this.telemetryClient; + public async getTelemetryClients(): Promise { + if (this.telemetryClients) { + return this.telemetryClients; } if (this.initializationPromise) { return this.initializationPromise; } - this.initializationPromise = this.initTelemetryClient(); - + this.initializationPromise = this.initializeTelemetryClients(); return this.initializationPromise; } public reset(): void { - this.telemetryClient = undefined; + this.telemetryClients = undefined; this.initializationPromise = undefined; } - } -export async function getTelemetryClient(): Promise { - return TelemetryClientSingleton.getInstance().getClient(); +export async function initializeTelemetry(): Promise { + return TelemetryClientSingleton.getInstance().getTelemetryClients(); } -export function resetTelemetryClient(): void { +export function resetTelemetry(): void { TelemetryClientSingleton.getInstance().reset(); } From 35428ee8c6df24156f18e221364d01382eef7e30 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 4 Aug 2025 18:13:21 -0300 Subject: [PATCH 03/38] Refactor getLogClient to simplify initialization Remove unused parameters and use logClient already initialized --- src/service/logger/client.ts | 42 +++++++++++------------------------- src/service/logger/logger.ts | 2 +- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/service/logger/client.ts b/src/service/logger/client.ts index 1850d7a5d..87ce517aa 100644 --- a/src/service/logger/client.ts +++ b/src/service/logger/client.ts @@ -1,15 +1,15 @@ -import { Exporters } from '@vtex/diagnostics-nodejs'; +import { Types } from '@vtex/diagnostics-nodejs'; import { LogClient } from '@vtex/diagnostics-nodejs/dist/types'; -import { getTelemetryClient } from '../telemetry'; +import { initializeTelemetry } from '../telemetry'; -let logClient: LogClient | undefined; +let client: LogClient | undefined; let isInitializing = false; let initPromise: Promise | undefined = undefined; -export async function getLogClient(account: string, workspace: string, appName: string): Promise { +export async function getLogClient(): Promise { - if (logClient) { - return logClient; + if (client) { + return client; } if (initPromise) { @@ -17,36 +17,20 @@ export async function getLogClient(account: string, workspace: string, appName: } isInitializing = true; - initPromise = initializeClient(account, workspace, appName); + initPromise = initializeClient(); return initPromise; } -async function initializeClient(account: string, workspace: string, appName: string): Promise { +async function initializeClient(): Promise { try { - const telemetryClient = await getTelemetryClient(); - - const logsConfig = Exporters.CreateLogsExporterConfig({ - endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, - path: process.env.OTEL_EXPORTER_OTLP_PATH || '/v1/logs', - protocol: 'http', - interval: 5, - timeoutSeconds: 5, - headers: { 'Content-Type': 'application/json' }, - }); - - const logsExporter = Exporters.CreateExporter(logsConfig, 'otlp'); - await logsExporter.initialize(); - - const clientKey = `${account}-${workspace}-${appName}`; - logClient = await telemetryClient.newLogsClient({ - exporter: logsExporter, - loggerName: `node-vtex-api-${clientKey}`, - }); - - return logClient; + const { logsClient } = await initializeTelemetry(); + client = logsClient; + initPromise = undefined; + return logsClient; } catch (error) { console.error('Failed to initialize logs client:', error); + initPromise = undefined; throw error; } finally { isInitializing = false; diff --git a/src/service/logger/logger.ts b/src/service/logger/logger.ts index 06a467f05..a52a1a733 100644 --- a/src/service/logger/logger.ts +++ b/src/service/logger/logger.ts @@ -47,7 +47,7 @@ export class Logger { }); this.logClient = await Promise.race([ - getLogClient(this.account, this.workspace, APP.NAME), + getLogClient(), timeoutPromise ]); From 536e16caf1daa38e586820d1f2b3ae4f5f473fcf Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 4 Aug 2025 18:23:33 -0300 Subject: [PATCH 04/38] Refactor logger client types Refactor client types to use Types.LogClient for consistency --- src/service/logger/client.ts | 7 +++---- src/service/logger/logger.ts | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/service/logger/client.ts b/src/service/logger/client.ts index 87ce517aa..c307d89b7 100644 --- a/src/service/logger/client.ts +++ b/src/service/logger/client.ts @@ -1,12 +1,11 @@ import { Types } from '@vtex/diagnostics-nodejs'; -import { LogClient } from '@vtex/diagnostics-nodejs/dist/types'; import { initializeTelemetry } from '../telemetry'; -let client: LogClient | undefined; +let client: Types.LogClient | undefined; let isInitializing = false; -let initPromise: Promise | undefined = undefined; +let initPromise: Promise | undefined = undefined; -export async function getLogClient(): Promise { +export async function getLogClient(): Promise { if (client) { return client; diff --git a/src/service/logger/logger.ts b/src/service/logger/logger.ts index a52a1a733..1441fb55e 100644 --- a/src/service/logger/logger.ts +++ b/src/service/logger/logger.ts @@ -1,7 +1,7 @@ import { APP, LOG_CLIENT_INIT_TIMEOUT_MS } from '../../constants' import { cleanError } from '../../utils/error' import { cleanLog } from '../../utils/log' -import { LogClient } from '@vtex/diagnostics-nodejs/dist/types'; +import { Types } from '@vtex/diagnostics-nodejs'; import { LoggerContext, LogLevel, TracingState } from './loggerTypes' import { getLogClient } from './client' @@ -15,8 +15,8 @@ export class Logger { private requestId: string private production: boolean private tracingState?: TracingState - private logClient: LogClient | undefined = undefined - private clientInitPromise: Promise | undefined = undefined + private logClient: Types.LogClient | undefined = undefined + private clientInitPromise: Promise | undefined = undefined constructor(ctx: LoggerContext) { this.account = ctx.account @@ -35,7 +35,7 @@ export class Logger { // this.initLogClient(); } - private initLogClient(): Promise { + private initLogClient(): Promise { if (this.clientInitPromise) { return this.clientInitPromise; } From b5b2425a2847b01b4ab493fb6e3d1cbff0c6d5f6 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 5 Aug 2025 09:24:26 -0300 Subject: [PATCH 05/38] Add resolution for @grpc/grpc-js dependency --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index a72c00aa9..9905e94f2 100644 --- a/package.json +++ b/package.json @@ -124,5 +124,8 @@ "typemoq": "^2.1.0", "typescript": "^4.4.4", "typescript-json-schema": "^0.52.0" + }, + "resolutions": { + "@grpc/grpc-js": "^1.13.4" } } From ec13074044a1a7b174920f854c6e35ae480d9abd Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 5 Aug 2025 09:29:50 -0300 Subject: [PATCH 06/38] Update package version to 6.49.8-beta.0 --- package.json | 2 +- yarn.lock | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 9905e94f2..c3179dcf2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vtex/api", - "version": "6.49.6", + "version": "6.49.8-beta.0", "description": "VTEX I/O API client", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/yarn.lock b/yarn.lock index c2308b223..cf8658985 100644 --- a/yarn.lock +++ b/yarn.lock @@ -236,7 +236,7 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@grpc/grpc-js@^1.13.4": +"@grpc/grpc-js@^1.13.4", "@grpc/grpc-js@^1.7.1": version "1.13.4" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.13.4.tgz#922fbc496e229c5fa66802d2369bf181c1df1c5a" integrity sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg== @@ -244,14 +244,6 @@ "@grpc/proto-loader" "^0.7.13" "@js-sdsl/ordered-map" "^4.4.2" -"@grpc/grpc-js@^1.7.1": - version "1.13.3" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.13.3.tgz#6ad08d186c2a8651697085f790c5c68eaca45904" - integrity sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg== - dependencies: - "@grpc/proto-loader" "^0.7.13" - "@js-sdsl/ordered-map" "^4.4.2" - "@grpc/proto-loader@^0.7.13": version "0.7.15" resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz#4cdfbf35a35461fc843abe8b9e2c0770b5095e60" From 00afbcb4db49adc70f978cb1790d81456d82d40b Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 12 Aug 2025 16:57:49 -0300 Subject: [PATCH 07/38] Refactor singleton to use dedicated initialization methods for telemetry clients --- src/service/telemetry/client.ts | 48 +++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index 91edea039..08127186e 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -7,6 +7,7 @@ import { Traces, } from '@vtex/diagnostics-nodejs'; import { APP } from '../../constants'; +import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; const CLIENT_NAME = APP.NAME || 'node-vtex-api'; const APPLICATION_ID = APP.ID || 'vtex-io-app'; @@ -32,6 +33,30 @@ class TelemetryClientSingleton { return TelemetryClientSingleton.instance; } + private initializeTracesClient = async (telemetryClient: TelemetryClient) => + await telemetryClient.newTracesClient({ + exporter: Exporters.CreateExporter(Exporters.CreateTracesExporterConfig({ + endpoint: EXPORTER_OTLP_ENDPOINT, + }), 'otlp'), + }); + + private initializeMetricsClient = async (telemetryClient: TelemetryClient) => + await telemetryClient.newMetricsClient({ + exporter: Exporters.CreateExporter(Exporters.CreateMetricsExporterConfig({ + endpoint: EXPORTER_OTLP_ENDPOINT, + interval: 5, + timeoutSeconds: 5, + }), 'otlp'), + }); + + private initializeLogsClient = async (telemetryClient: TelemetryClient) => + await telemetryClient.newLogsClient({ + exporter: Exporters.CreateExporter(Exporters.CreateLogsExporterConfig({ + endpoint: EXPORTER_OTLP_ENDPOINT, + }), 'otlp'), + loggerName: `node-vtex-api-${APPLICATION_ID}`, + }); + private async initializeTelemetryClients(): Promise { try { const telemetryClient = await NewTelemetryClient( @@ -46,26 +71,9 @@ class TelemetryClientSingleton { } ); - const tracesClient = await telemetryClient.newTracesClient({ - exporter: Exporters.CreateExporter(Exporters.CreateTracesExporterConfig({ - endpoint: EXPORTER_OTLP_ENDPOINT, - }), 'otlp'), - }); - - const metricsClient = await telemetryClient.newMetricsClient({ - exporter: Exporters.CreateExporter(Exporters.CreateMetricsExporterConfig({ - endpoint: EXPORTER_OTLP_ENDPOINT, - interval: 5, - timeoutSeconds: 5, - }), 'otlp'), - }); - - const logsClient = await telemetryClient.newLogsClient({ - exporter: Exporters.CreateExporter(Exporters.CreateLogsExporterConfig({ - endpoint: EXPORTER_OTLP_ENDPOINT, - }), 'otlp'), - loggerName: `node-vtex-api-${APPLICATION_ID}`, - }); + const tracesClient = await this.initializeTracesClient(telemetryClient); + const metricsClient = await this.initializeMetricsClient(telemetryClient); + const logsClient = await this.initializeLogsClient(telemetryClient); const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), From f3d9c05894dcd3bd2dfaf696664c4ea194639e50 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 12 Aug 2025 22:48:40 -0300 Subject: [PATCH 08/38] Refactor telemetry client initialization logic Improve telemetry client initialization using Promise.all to optimize asynchronous calls --- src/service/telemetry/client.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index 08127186e..96a16fa48 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -71,9 +71,11 @@ class TelemetryClientSingleton { } ); - const tracesClient = await this.initializeTracesClient(telemetryClient); - const metricsClient = await this.initializeMetricsClient(telemetryClient); - const logsClient = await this.initializeLogsClient(telemetryClient); + const [tracesClient, metricsClient, logsClient] = await Promise.all([ + this.initializeTracesClient(telemetryClient), + this.initializeMetricsClient(telemetryClient), + this.initializeLogsClient(telemetryClient), + ]); const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), From 58d03c948cf6e14c5b1c4914625b3482f637a190 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 17:08:49 -0300 Subject: [PATCH 09/38] Update package.json and yarn.lock Adds OpenTelemetry dependencies for host metrics and Koa instrumentation --- package.json | 4 ++++ yarn.lock | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/package.json b/package.json index c3179dcf2..c1bd07ab9 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,10 @@ }, "license": "MIT", "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/host-metrics": "0.35.5", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/instrumentation-koa": "0.47.1", "@types/koa": "^2.11.0", "@types/koa-compose": "^3.2.3", "@vtex/diagnostics-nodejs": "0.1.0-io-beta.19", diff --git a/yarn.lock b/yarn.lock index cf8658985..0d548c37f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -616,6 +616,13 @@ "@opentelemetry/sdk-trace-base" "1.30.1" "@opentelemetry/semantic-conventions" "1.28.0" +"@opentelemetry/host-metrics@0.35.5": + version "0.35.5" + resolved "https://registry.yarnpkg.com/@opentelemetry/host-metrics/-/host-metrics-0.35.5.tgz#1bb7453558b2623c8331d0fea5b7766c995a68f1" + integrity sha512-Zf9Cjl7H6JalspnK5KD1+LLKSVecSinouVctNmUxRy+WP+20KwHq+qg4hADllkEmJ99MZByLLmEmzrr7s92V6g== + dependencies: + systeminformation "5.23.8" + "@opentelemetry/instrumentation-express@^0.47.1": version "0.47.1" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz#7cf74f35e43cc3c8186edd1249fdb225849c48b2" @@ -636,6 +643,15 @@ forwarded-parse "2.1.2" semver "^7.5.2" +"@opentelemetry/instrumentation-koa@0.47.1": + version "0.47.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz#ba57eccd44a75ec59e3129757fda4e8c8dd7ce2c" + integrity sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.57.1" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-net@^0.43.1": version "0.43.1" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-net/-/instrumentation-net-0.43.1.tgz#10a3030fe090ed76204ac025179501f902dcf282" @@ -5252,6 +5268,11 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +systeminformation@5.23.8: + version "5.23.8" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.23.8.tgz#b8efa73b36221cbcb432e3fe83dc1878a43f986a" + integrity sha512-Osd24mNKe6jr/YoXLLK3k8TMdzaxDffhpCxgkfgBHcapykIkd50HXThM3TCEuHO2pPuCsSx2ms/SunqhU5MmsQ== + tar-fs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" From cfac3a1a36dd626354255614a9564b34fef39748 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 17:02:27 -0300 Subject: [PATCH 10/38] Update startApp function to proper init telemetry Updates startApp function to initialize telemetry beforehand and uses dynamic imports for startMaster and startWorker. This ensures that telemetry clients and libraries are proper initialized so they can use hooks with application libraries that will be loaded later --- src/service/index.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/service/index.ts b/src/service/index.ts index 190150df7..fd5a8373d 100644 --- a/src/service/index.ts +++ b/src/service/index.ts @@ -1,20 +1,23 @@ +import { initializeTelemetry } from './telemetry' import cluster from 'cluster' import { HTTP_SERVER_PORT } from '../constants' import { getServiceJSON } from './loaders' import { LogLevel, logOnceToDevConsole } from './logger' -import { startMaster } from './master' -import { startWorker } from './worker' -export const startApp = () => { +export const startApp = async () => { + await initializeTelemetry() const serviceJSON = getServiceJSON() try { // if it is a master process then call setting up worker process if(cluster.isMaster) { + const { startMaster } = await import('./master') startMaster(serviceJSON) } else { // to setup server configurations and share port address for incoming requests - startWorker(serviceJSON).listen(HTTP_SERVER_PORT) + const { startWorker } = await import('./worker') + const app = await startWorker(serviceJSON) + app.listen(HTTP_SERVER_PORT) } } catch (err: any) { logOnceToDevConsole(err.stack || err.message, LogLevel.Error) From 48127f331d9bfc2090754ea047d42054ae61774e Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 15:00:01 -0300 Subject: [PATCH 11/38] Add metrics client Add getMetricClient function to make the client available and creates the asynchronous initialization function to retrieve it --- src/service/metrics/client.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/service/metrics/client.ts diff --git a/src/service/metrics/client.ts b/src/service/metrics/client.ts new file mode 100644 index 000000000..e45eb5376 --- /dev/null +++ b/src/service/metrics/client.ts @@ -0,0 +1,35 @@ +import { Types } from "@vtex/diagnostics-nodejs"; +import { initializeTelemetry } from '../telemetry'; + +let client: Types.MetricClient | undefined; +let isInitializing = false; +let initPromise: Promise | undefined = undefined; + +export async function getMetricClient(): Promise { + if (client) { + return client; + } + + if (initPromise) { + return initPromise; + } + + isInitializing = true; + initPromise = initializeClient(); + + return initPromise; +} +async function initializeClient(): Promise { + try { + const { metricsClient } = await initializeTelemetry(); + client = metricsClient; + initPromise = undefined; + return metricsClient; + } catch (error) { + console.error('Failed to initialize metrics client:', error); + initPromise = undefined; + throw error; + } finally { + isInitializing = false; + } +} From e10016dcfc83781837d9edd5969bdad125d30d50 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:05:32 -0300 Subject: [PATCH 12/38] Add metrics instruments for monitoring HTTP requests --- src/service/metrics/metrics.ts | 101 +++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/service/metrics/metrics.ts diff --git a/src/service/metrics/metrics.ts b/src/service/metrics/metrics.ts new file mode 100644 index 000000000..1c6f561f9 --- /dev/null +++ b/src/service/metrics/metrics.ts @@ -0,0 +1,101 @@ +import { Types } from '@vtex/diagnostics-nodejs' +import { getMetricClient } from './client' + +export const enum RequestsMetricLabels { + STATUS_CODE = 'status_code', + REQUEST_HANDLER = 'handler', +} + +export interface OtelRequestInstruments { + concurrentRequests: Types.Gauge + requestTimings: Types.Histogram + totalRequests: Types.Counter + responseSizes: Types.Histogram + abortedRequests: Types.Counter +} + +let instruments: OtelRequestInstruments | undefined +let initializingPromise: Promise | undefined + +const createOtelConcurrentRequestsInstrument = async (): Promise => { + const metricsClient = await getMetricClient() + return metricsClient.createGauge('io_http_requests_current', { + description: 'The current number of requests in course.', + unit: '1' + }) +} + +const createOtelRequestsTimingsInstrument = async (): Promise => { + const metricsClient = await getMetricClient() + return metricsClient.createHistogram('runtime_http_requests_duration_milliseconds', { + description: 'The incoming http requests total duration.', + unit: 'ms' + }) +} + +const createOtelTotalRequestsInstrument = async (): Promise => { + const metricsClient = await getMetricClient() + return metricsClient.createCounter('runtime_http_requests_total', { + description: 'The total number of HTTP requests.', + unit: '1' + }) +} + +const createOtelRequestsResponseSizesInstrument = async (): Promise => { + const metricsClient = await getMetricClient() + return metricsClient.createHistogram('runtime_http_response_size_bytes', { + description: 'The outgoing response sizes (only applicable when the response isn\'t a stream).', + unit: 'bytes' + }) +} + +const createOtelTotalAbortedRequestsInstrument = async (): Promise => { + const metricsClient = await getMetricClient() + return metricsClient.createCounter('runtime_http_aborted_requests_total', { + description: 'The total number of HTTP requests aborted.', + unit: '1' + }) +} + +export const getOtelInstruments = async (): Promise => { + if (instruments) { + return instruments + } + + if (initializingPromise) { + return initializingPromise + } + + initializingPromise = initializeOtelInstruments() + + try { + instruments = await initializingPromise + return instruments + } finally { + initializingPromise = undefined + } +} + +const initializeOtelInstruments = async (): Promise => { + const [ + concurrentRequests, + requestTimings, + totalRequests, + responseSizes, + abortedRequests + ] = await Promise.all([ + createOtelConcurrentRequestsInstrument(), + createOtelRequestsTimingsInstrument(), + createOtelTotalRequestsInstrument(), + createOtelRequestsResponseSizesInstrument(), + createOtelTotalAbortedRequestsInstrument() + ]) + + return { + concurrentRequests, + requestTimings, + totalRequests, + responseSizes, + abortedRequests + } +} From c1d29a9b9a3b7fb22aa7f0a53a8e15832d754d65 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:12:29 -0300 Subject: [PATCH 13/38] Add middleware for request metrics This commit creates a middleware for request metrics using OpenTelemetry instruments. This change is inspired by `requestMetricsMiddleware.ts` and follows the same logic, only changing the way it is instrumented to use OTel standard --- .../metrics/otelRequestMetricsMiddleware.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/service/metrics/otelRequestMetricsMiddleware.ts diff --git a/src/service/metrics/otelRequestMetricsMiddleware.ts b/src/service/metrics/otelRequestMetricsMiddleware.ts new file mode 100644 index 000000000..d62da9469 --- /dev/null +++ b/src/service/metrics/otelRequestMetricsMiddleware.ts @@ -0,0 +1,82 @@ +import { finished as onStreamFinished } from 'stream' +import { hrToMillisFloat } from '../../utils' +import { getOtelInstruments, RequestsMetricLabels, OtelRequestInstruments } from './metrics' +import { ServiceContext } from '../worker/runtime/typings' + +const INSTRUMENTS_INITIALIZATION_TIMEOUT = 500 + +export const addOtelRequestMetricsMiddleware = () => { + let instruments: OtelRequestInstruments | undefined + + return async function addOtelRequestMetrics(ctx: ServiceContext, next: () => Promise) { + if (!instruments) { + try { + instruments = await Promise.race([ + getOtelInstruments(), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), + INSTRUMENTS_INITIALIZATION_TIMEOUT) + ) + ]) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + console.warn(`OpenTelemetry instruments not ready for request ${ctx.requestHandlerName}: ${errorMessage}`) + await next() + return + } + } + + const start = process.hrtime() + instruments.concurrentRequests.add(1) + + ctx.req.once('aborted', () => { + if (instruments) { + instruments.abortedRequests.add(1, { [RequestsMetricLabels.REQUEST_HANDLER]: ctx.requestHandlerName }) + } + }) + + let responseClosed = false + ctx.res.once('close', () => (responseClosed = true)) + + try { + await next() + } finally { + const responseLength = ctx.response.length + if (responseLength && instruments) { + instruments.responseSizes.record( + responseLength, + { [RequestsMetricLabels.REQUEST_HANDLER]: ctx.requestHandlerName } + ) + } + + if (instruments) { + instruments.totalRequests.add( + 1, + { + [RequestsMetricLabels.REQUEST_HANDLER]: ctx.requestHandlerName, + [RequestsMetricLabels.STATUS_CODE]: ctx.response.status, + } + ) + } + + const onResFinished = () => { + if (instruments) { + instruments.requestTimings.record( + hrToMillisFloat(process.hrtime(start)), + { + [RequestsMetricLabels.REQUEST_HANDLER]: ctx.requestHandlerName, + } + ) + + instruments.concurrentRequests.subtract(1) + } + } + + if (responseClosed) { + onResFinished() + } else { + onStreamFinished(ctx.res, onResFinished) + } + } + } +} From 4549879009c879cbbdd0aefb6f07553bb60d4342 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:17:08 -0300 Subject: [PATCH 14/38] Add middleware usage on app Add the use of OpenTelemetry metrics middleware for request monitoring --- src/service/worker/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/service/worker/index.ts b/src/service/worker/index.ts index 420e10ff1..a09f5ee0e 100644 --- a/src/service/worker/index.ts +++ b/src/service/worker/index.ts @@ -13,6 +13,7 @@ import { getService } from '../loaders' import { logOnceToDevConsole } from '../logger/console' import { LogLevel } from '../logger/loggerTypes' import { addRequestMetricsMiddleware } from '../metrics/requestMetricsMiddleware' +import { addOtelRequestMetricsMiddleware } from '../metrics/otelRequestMetricsMiddleware' import { TracerSingleton } from '../tracing/TracerSingleton' import { addTracingMiddleware } from '../tracing/tracingMiddlewares' import { addProcessListeners, logger } from './listeners' @@ -223,6 +224,7 @@ export const startWorker = (serviceJSON: ServiceJSON) => { .use(prometheusLoggerMiddleware()) .use(addTracingMiddleware(tracer)) .use(addRequestMetricsMiddleware()) + .use(addOtelRequestMetricsMiddleware()) .use(addMetricsLoggerMiddleware()) .use(concurrentRateLimiter(serviceJSON?.rateLimitPerReplica?.concurrent)) .use(compress()) From cf53947f202d3d405dd8f6dea0fc304c59452848 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:32:59 -0300 Subject: [PATCH 15/38] Add Koa instrumentation to telemetry client Add Koa instrumentation to the list of instruments that will be registered and used by the telemetry client. This enables automatic instrumentation for the Koa module, as well as automatic collection and export of telemetry data. --- src/service/telemetry/client.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index 08127186e..d43c11dfb 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -8,6 +8,7 @@ import { } from '@vtex/diagnostics-nodejs'; import { APP } from '../../constants'; import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; +import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa'; const CLIENT_NAME = APP.NAME || 'node-vtex-api'; const APPLICATION_ID = APP.ID || 'vtex-io-app'; @@ -77,6 +78,7 @@ class TelemetryClientSingleton { const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), + new KoaInstrumentation(), ]; telemetryClient.registerInstrumentations(instrumentations); From 7faeac7c1d2d94ea66bd27f773902099b6e87632 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:45:45 -0300 Subject: [PATCH 16/38] Add Koa context propagation middleware to app worker --- src/service/worker/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/service/worker/index.ts b/src/service/worker/index.ts index a09f5ee0e..0e64fcf29 100644 --- a/src/service/worker/index.ts +++ b/src/service/worker/index.ts @@ -1,3 +1,4 @@ +import { Instrumentation } from '@vtex/diagnostics-nodejs'; import { request } from 'http' import Koa from 'koa' import compress from 'koa-compress' @@ -221,6 +222,7 @@ export const startWorker = (serviceJSON: ServiceJSON) => { app.proxy = true app .use(error) + .use(Instrumentation.Middlewares.ContextMiddlewares.Koa.ContextPropagationMiddleware()) .use(prometheusLoggerMiddleware()) .use(addTracingMiddleware(tracer)) .use(addRequestMetricsMiddleware()) From 41f7dee1f479c49138590439b61c96189b6c41fa Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:38:19 -0300 Subject: [PATCH 17/38] Add host-metrics instrumentation This commit creates a wrapper for the host-metrics module, which provides automatic collection for system metrics - such as CPU, memory, and network --- .../metrics/instruments/hostMetrics.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/service/metrics/instruments/hostMetrics.ts diff --git a/src/service/metrics/instruments/hostMetrics.ts b/src/service/metrics/instruments/hostMetrics.ts new file mode 100644 index 000000000..0851c1096 --- /dev/null +++ b/src/service/metrics/instruments/hostMetrics.ts @@ -0,0 +1,41 @@ +import { InstrumentationBase, InstrumentationConfig } from "@opentelemetry/instrumentation"; +import { MeterProvider } from '@opentelemetry/api'; +import { HostMetrics } from "@opentelemetry/host-metrics"; + +interface HostMetricsInstrumentationConfig extends InstrumentationConfig { + name?: string; + meterProvider?: MeterProvider; +} + +export class HostMetricsInstrumentation extends InstrumentationBase { + private hostMetrics?: HostMetrics; + + constructor(config: HostMetricsInstrumentationConfig = {}) { + const instrumentation_name = config.name || 'host-metrics-instrumentation'; + const instrumentation_version = '1.0.0'; + super(instrumentation_name, instrumentation_version, config); + } + + init(): void {} + + enable(): void { + if (!this._config.meterProvider) { + throw new Error('MeterProvider is required for HostMetricsInstrumentation'); + } + + this.hostMetrics = new HostMetrics({ + meterProvider: this._config.meterProvider, + name: this._config.name || 'host-metrics', + }); + + this.hostMetrics.start(); + console.debug('HostMetricsInstrumentation enabled'); + } + + disable(): void { + if (this.hostMetrics) { + this.hostMetrics = undefined; + console.debug('HostMetricsInstrumentation disabled'); + } + } +} From 5b5146d2347ee673c2069f505d3851db4fbd13eb Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:42:23 -0300 Subject: [PATCH 18/38] Add host-metrics instrumentation to telemetry client --- src/service/telemetry/client.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index d43c11dfb..d74f1f8fd 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -9,6 +9,7 @@ import { import { APP } from '../../constants'; import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa'; +import { HostMetricsInstrumentation } from '../metrics/instruments/hostMetrics'; const CLIENT_NAME = APP.NAME || 'node-vtex-api'; const APPLICATION_ID = APP.ID || 'vtex-io-app'; @@ -79,6 +80,10 @@ class TelemetryClientSingleton { const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), new KoaInstrumentation(), + new HostMetricsInstrumentation({ + name: 'host-metrics-instrumentation', + meterProvider: metricsClient.provider(), + }), ]; telemetryClient.registerInstrumentations(instrumentations); From 6503f4d5d80236f1bff8aa07f5b73f01a4013c34 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Thu, 14 Aug 2025 16:37:32 -0300 Subject: [PATCH 19/38] Release v7.0.1 --- CHANGELOG.md | 6 +++++- package.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c67d1a6c..2364e763a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] -## [7.0.0] - 2025-07-25 +## [7.0.1] - 2025-08-14 +### Changed +- Update `diagnostics-nodejs` version, simplifying client initialization, consolidating telemetry logic, and improving type safety + +## [7.0.0] - 2025-08-13 ### Changed - Version created to keep track of Runtime 7-compatible implementation of this lib diff --git a/package.json b/package.json index 5248e7aa4..262c530d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vtex/api", - "version": "7.0.0", + "version": "7.0.1", "description": "VTEX I/O API client", "main": "lib/index.js", "typings": "lib/index.d.ts", From ff3bbb972173198dbba5263e6c13a89e3b42ee52 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Thu, 14 Aug 2025 17:00:40 -0300 Subject: [PATCH 20/38] Release v7.1.0-beta.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 40fbba011..9768a48af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vtex/api", - "version": "7.0.1", + "version": "7.1.0-beta.0", "description": "VTEX I/O API client", "main": "lib/index.js", "typings": "lib/index.d.ts", From b54e41310d16f96afda77194f1ead6b5fceb1bf4 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 18 Aug 2025 16:09:26 -0300 Subject: [PATCH 21/38] Improves code formatting for setTimeout --- src/service/metrics/otelRequestMetricsMiddleware.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/service/metrics/otelRequestMetricsMiddleware.ts b/src/service/metrics/otelRequestMetricsMiddleware.ts index d62da9469..e8568c6ad 100644 --- a/src/service/metrics/otelRequestMetricsMiddleware.ts +++ b/src/service/metrics/otelRequestMetricsMiddleware.ts @@ -14,8 +14,10 @@ export const addOtelRequestMetricsMiddleware = () => { instruments = await Promise.race([ getOtelInstruments(), new Promise((_, reject) => - setTimeout(() => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), - INSTRUMENTS_INITIALIZATION_TIMEOUT) + setTimeout( + () => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), + INSTRUMENTS_INITIALIZATION_TIMEOUT + ) ) ]) } catch (error) { From b596cc7683d2718b18e13d1c1cd720524d42d517 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 18 Aug 2025 17:08:40 -0300 Subject: [PATCH 22/38] Refactor instrument init logic on middleware Refactors the instrument initialization logic to improve readability and code structure --- .../metrics/otelRequestMetricsMiddleware.ts | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/service/metrics/otelRequestMetricsMiddleware.ts b/src/service/metrics/otelRequestMetricsMiddleware.ts index e8568c6ad..6bfee65bd 100644 --- a/src/service/metrics/otelRequestMetricsMiddleware.ts +++ b/src/service/metrics/otelRequestMetricsMiddleware.ts @@ -8,24 +8,29 @@ const INSTRUMENTS_INITIALIZATION_TIMEOUT = 500 export const addOtelRequestMetricsMiddleware = () => { let instruments: OtelRequestInstruments | undefined + const tryGetInstruments = async (ctx: ServiceContext): Promise => { + try { + return await Promise.race([ + getOtelInstruments(), + new Promise((_, reject) => + setTimeout( + () => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), + INSTRUMENTS_INITIALIZATION_TIMEOUT + ) + ) + ]) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + console.warn(`OpenTelemetry instruments not ready for request ${ctx.requestHandlerName}: ${errorMessage}`) + return undefined + } + } + return async function addOtelRequestMetrics(ctx: ServiceContext, next: () => Promise) { + instruments = instruments ? instruments : await tryGetInstruments(ctx) if (!instruments) { - try { - instruments = await Promise.race([ - getOtelInstruments(), - new Promise((_, reject) => - setTimeout( - () => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), - INSTRUMENTS_INITIALIZATION_TIMEOUT - ) - ) - ]) - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error) - console.warn(`OpenTelemetry instruments not ready for request ${ctx.requestHandlerName}: ${errorMessage}`) - await next() - return - } + await next() + return } const start = process.hrtime() From 08bef4ef9c48e43672f8dae311b54a5886355721 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 25 Aug 2025 21:14:53 -0300 Subject: [PATCH 23/38] Refactor metric client module Refactor MetricClient to use Singleton pattern for improved client management --- src/service/metrics/client.ts | 61 +++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/service/metrics/client.ts b/src/service/metrics/client.ts index e45eb5376..eaabd27dc 100644 --- a/src/service/metrics/client.ts +++ b/src/service/metrics/client.ts @@ -1,35 +1,46 @@ import { Types } from "@vtex/diagnostics-nodejs"; import { initializeTelemetry } from '../telemetry'; -let client: Types.MetricClient | undefined; -let isInitializing = false; -let initPromise: Promise | undefined = undefined; +class MetricClientSingleton { + private static instance: MetricClientSingleton | undefined; + private client: Types.MetricClient | undefined; + private initPromise: Promise | undefined; -export async function getMetricClient(): Promise { - if (client) { - return client; - } + private constructor() {} - if (initPromise) { - return initPromise; + public static getInstance(): MetricClientSingleton { + if (!MetricClientSingleton.instance) { + MetricClientSingleton.instance = new MetricClientSingleton(); + } + return MetricClientSingleton.instance; } - isInitializing = true; - initPromise = initializeClient(); + public async getClient(): Promise { + if (this.client) { + return this.client; + } - return initPromise; -} -async function initializeClient(): Promise { - try { - const { metricsClient } = await initializeTelemetry(); - client = metricsClient; - initPromise = undefined; - return metricsClient; - } catch (error) { - console.error('Failed to initialize metrics client:', error); - initPromise = undefined; - throw error; - } finally { - isInitializing = false; + if (this.initPromise) { + return this.initPromise; + } + + this.initPromise = this.initializeClient(); + + return this.initPromise; + } + + private async initializeClient(): Promise { + try { + const { metricsClient } = await initializeTelemetry(); + this.client = metricsClient; + this.initPromise = undefined; + return metricsClient; + } catch (error) { + console.error('Failed to initialize metrics client:', error); + this.initPromise = undefined; + throw error; + } } } + +export const getMetricClient = () => MetricClientSingleton.getInstance().getClient(); From 1477054e899ac0a158c11e15fa0a15a6e0ba8947 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 25 Aug 2025 21:34:12 -0300 Subject: [PATCH 24/38] Refactor metrics instruments module Refactor OTel instruments initialization to use singleton pattern --- src/service/metrics/metrics.ts | 88 ++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/src/service/metrics/metrics.ts b/src/service/metrics/metrics.ts index 1c6f561f9..ba2f12eaf 100644 --- a/src/service/metrics/metrics.ts +++ b/src/service/metrics/metrics.ts @@ -14,9 +14,6 @@ export interface OtelRequestInstruments { abortedRequests: Types.Counter } -let instruments: OtelRequestInstruments | undefined -let initializingPromise: Promise | undefined - const createOtelConcurrentRequestsInstrument = async (): Promise => { const metricsClient = await getMetricClient() return metricsClient.createGauge('io_http_requests_current', { @@ -57,45 +54,62 @@ const createOtelTotalAbortedRequestsInstrument = async (): Promise => { - if (instruments) { - return instruments - } +class OtelInstrumentsSingleton { + private static instance: OtelInstrumentsSingleton | undefined; + private instruments: OtelRequestInstruments | undefined; + private initializingPromise: Promise | undefined; - if (initializingPromise) { - return initializingPromise + private constructor() {} + + public static getInstance(): OtelInstrumentsSingleton { + if (!OtelInstrumentsSingleton.instance) { + OtelInstrumentsSingleton.instance = new OtelInstrumentsSingleton(); + } + return OtelInstrumentsSingleton.instance; } - initializingPromise = initializeOtelInstruments() + public async getInstruments(): Promise { + if (this.instruments) { + return this.instruments; + } + + if (this.initializingPromise) { + return this.initializingPromise; + } - try { - instruments = await initializingPromise - return instruments - } finally { - initializingPromise = undefined + this.initializingPromise = this.initializeInstruments(); + + try { + this.instruments = await this.initializingPromise; + return this.instruments; + } finally { + this.initializingPromise = undefined; + } } -} -const initializeOtelInstruments = async (): Promise => { - const [ - concurrentRequests, - requestTimings, - totalRequests, - responseSizes, - abortedRequests - ] = await Promise.all([ - createOtelConcurrentRequestsInstrument(), - createOtelRequestsTimingsInstrument(), - createOtelTotalRequestsInstrument(), - createOtelRequestsResponseSizesInstrument(), - createOtelTotalAbortedRequestsInstrument() - ]) - - return { - concurrentRequests, - requestTimings, - totalRequests, - responseSizes, - abortedRequests + private async initializeInstruments(): Promise { + const [ + concurrentRequests, + requestTimings, + totalRequests, + responseSizes, + abortedRequests + ] = await Promise.all([ + createOtelConcurrentRequestsInstrument(), + createOtelRequestsTimingsInstrument(), + createOtelTotalRequestsInstrument(), + createOtelRequestsResponseSizesInstrument(), + createOtelTotalAbortedRequestsInstrument() + ]) + + return { + concurrentRequests, + requestTimings, + totalRequests, + responseSizes, + abortedRequests + } } } + +export const getOtelInstruments = () => OtelInstrumentsSingleton.getInstance().getInstruments(); From 6733ee2b8d6bc8783835ccb0a2779942d879c765 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 25 Aug 2025 21:35:33 -0300 Subject: [PATCH 25/38] Add error handling when init instruments --- src/service/metrics/metrics.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/service/metrics/metrics.ts b/src/service/metrics/metrics.ts index ba2f12eaf..b5b5fbc7d 100644 --- a/src/service/metrics/metrics.ts +++ b/src/service/metrics/metrics.ts @@ -82,6 +82,10 @@ class OtelInstrumentsSingleton { try { this.instruments = await this.initializingPromise; return this.instruments; + } catch (error) { + console.error('Failed to initialize OTel instruments:', error); + this.initializingPromise = undefined; + throw error; } finally { this.initializingPromise = undefined; } From 274859c9f60e682ee71032ece4732f6b77dfe46d Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 2 Sep 2025 16:16:15 -0300 Subject: [PATCH 26/38] Bump @vtex/diagnostics-nodejs to 0.1.0-io-beta.20 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 40fbba011..dc6889a35 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@opentelemetry/instrumentation-koa": "0.47.1", "@types/koa": "^2.11.0", "@types/koa-compose": "^3.2.3", - "@vtex/diagnostics-nodejs": "0.1.0-io-beta.19", + "@vtex/diagnostics-nodejs": "0.1.0-io-beta.20", "@vtex/node-error-report": "^0.0.3", "@wry/equality": "^0.1.9", "agentkeepalive": "^4.0.2", diff --git a/yarn.lock b/yarn.lock index 0d548c37f..2aaebf317 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1251,10 +1251,10 @@ dependencies: "@types/yargs-parser" "*" -"@vtex/diagnostics-nodejs@0.1.0-io-beta.19": - version "0.1.0-io-beta.19" - resolved "https://registry.yarnpkg.com/@vtex/diagnostics-nodejs/-/diagnostics-nodejs-0.1.0-io-beta.19.tgz#e8ebc6a0b44014d0ffcda9df736291c52604ee9e" - integrity sha512-p3bcfpCI12k1DXqD2wJvO53dDdrULYsK7muJFHlwiZio4WvnSfPU1Ay5ni9Y8alD/MLX4fP4EEjRYXTGc7wFWQ== +"@vtex/diagnostics-nodejs@0.1.0-io-beta.20": + version "0.1.0-io-beta.20" + resolved "https://registry.yarnpkg.com/@vtex/diagnostics-nodejs/-/diagnostics-nodejs-0.1.0-io-beta.20.tgz#79ee25a88827a501206d1a8ef0715674fa1521b5" + integrity sha512-8DbibPcPEd2yJCRKZOM2JAf4FYr9W90lD+DRV/mOwgMd1ZGdZuXupfoqqOzOdrkOidXhCeLBPhRXne8O2N3QEQ== dependencies: "@grpc/grpc-js" "^1.13.4" "@opentelemetry/api" "^1.9.0" From 5f78b47b73b93653dfff2f97ae5b843c6143d1fb Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 16:53:23 -0300 Subject: [PATCH 27/38] Add new constants for diagnostics configuration This commit creates new constants based on environment variables that will be used to proper configure diagnostics-nodejs on node-vtex-api --- src/constants.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/constants.ts b/src/constants.ts index 3860b0af3..a5889f85d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -74,3 +74,9 @@ export const INSPECT_DEBUGGER_PORT = 5858 export const cancellableMethods = new Set(['GET', 'OPTIONS', 'HEAD']) export const LOG_CLIENT_INIT_TIMEOUT_MS = 5000 + +export const OTEL_EXPORTER_OTLP_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT as string; + +export const DK_APP_ID = process.env.NODE_VTEX_API_DK_APP_ID as string || "apps"; + +export const DIAGNOSTICS_TELEMETRY_ENABLED = process.env.VTEX_DIAGNOSTICS_TELEMETRY_ENABLED === 'true'; \ No newline at end of file From 8c792fa969521b75bb407439722ece4b70bae79a Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 16:54:30 -0300 Subject: [PATCH 28/38] Enable log client initialization --- src/service/logger/logger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/logger/logger.ts b/src/service/logger/logger.ts index 1441fb55e..3c4678364 100644 --- a/src/service/logger/logger.ts +++ b/src/service/logger/logger.ts @@ -32,7 +32,7 @@ export class Logger { } } - // this.initLogClient(); + this.initLogClient(); } private initLogClient(): Promise { From d236aa6ce2229d0a512d748dab10f49e3850f15c Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 16:58:08 -0300 Subject: [PATCH 29/38] Add new constants to client.ts Make the new constants available on clients.ts file, which is responsible for create telemetry client. --- src/service/telemetry/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index db42e276d..b52761902 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -6,7 +6,7 @@ import { Metrics, Traces, } from '@vtex/diagnostics-nodejs'; -import { APP } from '../../constants'; +import { APP, OTEL_EXPORTER_OTLP_ENDPOINT, DK_APP_ID, DIAGNOSTICS_TELEMETRY_ENABLED } from '../../constants'; import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa'; import { HostMetricsInstrumentation } from '../metrics/instruments/hostMetrics'; From 43fb4b1e8ad0457e09bf81cc8db53736ed66a30c Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 17:01:50 -0300 Subject: [PATCH 30/38] Make clients use new constant for endpoint This commit replaces and updates the telemetry clients to utilize the OTEL_EXPORTER_OTLP_ENDPOINT constant for exporting telemetry data --- src/service/telemetry/client.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index b52761902..f9026b13b 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -13,7 +13,6 @@ import { HostMetricsInstrumentation } from '../metrics/instruments/hostMetrics'; const CLIENT_NAME = APP.NAME || 'node-vtex-api'; const APPLICATION_ID = APP.ID || 'vtex-io-app'; -const EXPORTER_OTLP_ENDPOINT = process.env.EXPORTER_OTLP_ENDPOINT; interface TelemetryClients { logsClient: Logs.LogClient; @@ -38,14 +37,14 @@ class TelemetryClientSingleton { private initializeTracesClient = async (telemetryClient: TelemetryClient) => await telemetryClient.newTracesClient({ exporter: Exporters.CreateExporter(Exporters.CreateTracesExporterConfig({ - endpoint: EXPORTER_OTLP_ENDPOINT, + endpoint: OTEL_EXPORTER_OTLP_ENDPOINT, }), 'otlp'), }); private initializeMetricsClient = async (telemetryClient: TelemetryClient) => await telemetryClient.newMetricsClient({ exporter: Exporters.CreateExporter(Exporters.CreateMetricsExporterConfig({ - endpoint: EXPORTER_OTLP_ENDPOINT, + endpoint: OTEL_EXPORTER_OTLP_ENDPOINT, interval: 5, timeoutSeconds: 5, }), 'otlp'), @@ -54,7 +53,7 @@ class TelemetryClientSingleton { private initializeLogsClient = async (telemetryClient: TelemetryClient) => await telemetryClient.newLogsClient({ exporter: Exporters.CreateExporter(Exporters.CreateLogsExporterConfig({ - endpoint: EXPORTER_OTLP_ENDPOINT, + endpoint: OTEL_EXPORTER_OTLP_ENDPOINT, }), 'otlp'), loggerName: `node-vtex-api-${APPLICATION_ID}`, }); From 51f31eaee61ceffc0c204c927b89438acbc66060 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 17:07:53 -0300 Subject: [PATCH 31/38] Use DK_APP_ID on telemetry client creation Small change on parameter to follow o11y team guidelines regarding this field --- src/service/telemetry/client.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index f9026b13b..cd273449a 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -61,8 +61,7 @@ class TelemetryClientSingleton { private async initializeTelemetryClients(): Promise { try { const telemetryClient = await NewTelemetryClient( - APPLICATION_ID, - CLIENT_NAME, + DK_APP_ID, 'node-vtex-api', { additionalAttrs: { From f41b42154043f563d3282e1fc2098c3bf072645c Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 17:12:31 -0300 Subject: [PATCH 32/38] Refactor telemetry client parameters Updates parameters on telemetry client creation, making use of APPLICATION_ID and adding new additional attributes. Also, we remove unused constant CLIENT_NAME. --- src/service/telemetry/client.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index cd273449a..1fd1bcb28 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -11,7 +11,6 @@ import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa'; import { HostMetricsInstrumentation } from '../metrics/instruments/hostMetrics'; -const CLIENT_NAME = APP.NAME || 'node-vtex-api'; const APPLICATION_ID = APP.ID || 'vtex-io-app'; interface TelemetryClients { @@ -63,8 +62,11 @@ class TelemetryClientSingleton { const telemetryClient = await NewTelemetryClient( DK_APP_ID, 'node-vtex-api', + APPLICATION_ID, { additionalAttrs: { + 'app.id': APPLICATION_ID, + 'vendor': APP.VENDOR, 'version': APP.VERSION || '', 'environment': process.env.VTEX_WORKSPACE || 'development', }, From 1c9f5d64877547f67d64135c602a1a9d11f18e7e Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Wed, 3 Sep 2025 17:18:25 -0300 Subject: [PATCH 33/38] Refactor telemetry client initialization with checks This update adds console logging for diagnostics configuration and checks if telemetry is enabled before registering instrumentations. It ensures that the telemetry client operates correctly based on the DIAGNOSTICS_TELEMETRY_ENABLED constant. Also, whenever the app vendor is not enabled it will use a no-op client for diagnostics, meaning any telemetry signal will be ignored --- src/service/telemetry/client.ts | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index 1fd1bcb28..ed16c4296 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -58,12 +58,19 @@ class TelemetryClientSingleton { }); private async initializeTelemetryClients(): Promise { + // Check if telemetry is enabled for this app + if (!DIAGNOSTICS_TELEMETRY_ENABLED) { + console.warn(`Telemetry disabled for app: ${APP.ID} (vendor: ${APP.VENDOR})`); + } + try { const telemetryClient = await NewTelemetryClient( DK_APP_ID, 'node-vtex-api', APPLICATION_ID, { + // Use built-in no-op functionality when telemetry is disabled + noop: !DIAGNOSTICS_TELEMETRY_ENABLED, additionalAttrs: { 'app.id': APPLICATION_ID, 'vendor': APP.VENDOR, @@ -79,16 +86,18 @@ class TelemetryClientSingleton { this.initializeLogsClient(telemetryClient), ]); - const instrumentations = [ - ...Instrumentation.CommonInstrumentations.minimal(), - new KoaInstrumentation(), - new HostMetricsInstrumentation({ - name: 'host-metrics-instrumentation', - meterProvider: metricsClient.provider(), - }), - ]; - - telemetryClient.registerInstrumentations(instrumentations); + if (DIAGNOSTICS_TELEMETRY_ENABLED) { + const instrumentations = [ + ...Instrumentation.CommonInstrumentations.minimal(), + new KoaInstrumentation(), + new HostMetricsInstrumentation({ + name: 'host-metrics-instrumentation', + meterProvider: metricsClient.provider(), + }), + ]; + + telemetryClient.registerInstrumentations(instrumentations); + } const clients: TelemetryClients = { logsClient, From 92e0b751107ebfc84d2d70a799397bcd451371b1 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Thu, 4 Sep 2025 09:46:39 -0300 Subject: [PATCH 34/38] Update constant default value This change modifies the DK_APP_ID constant to default to 'apps-team' instead of 'apps' --- src/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.ts b/src/constants.ts index a5889f85d..156860b6c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -77,6 +77,6 @@ export const LOG_CLIENT_INIT_TIMEOUT_MS = 5000 export const OTEL_EXPORTER_OTLP_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT as string; -export const DK_APP_ID = process.env.NODE_VTEX_API_DK_APP_ID as string || "apps"; +export const DK_APP_ID = process.env.NODE_VTEX_API_DK_APP_ID as string || "apps-team"; export const DIAGNOSTICS_TELEMETRY_ENABLED = process.env.VTEX_DIAGNOSTICS_TELEMETRY_ENABLED === 'true'; \ No newline at end of file From ed60b8e7e03b2c8dffb48c022c3b1865f9c35124 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Thu, 4 Sep 2025 10:16:27 -0300 Subject: [PATCH 35/38] improve logging approach for telemetry status Replace negative warning message when telemetry is disabled with positive confirmation message when enabled. This change aligns with the principle of treating disabled telemetry as the default state. --- src/service/telemetry/client.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index ed16c4296..de3f9a52a 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -58,10 +58,6 @@ class TelemetryClientSingleton { }); private async initializeTelemetryClients(): Promise { - // Check if telemetry is enabled for this app - if (!DIAGNOSTICS_TELEMETRY_ENABLED) { - console.warn(`Telemetry disabled for app: ${APP.ID} (vendor: ${APP.VENDOR})`); - } try { const telemetryClient = await NewTelemetryClient( @@ -87,6 +83,8 @@ class TelemetryClientSingleton { ]); if (DIAGNOSTICS_TELEMETRY_ENABLED) { + console.log(`Telemetry enabled for app: ${APP.ID} (vendor: ${APP.VENDOR})`); + const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), new KoaInstrumentation(), From bd3149b6f44e9594bd93822f7cd5d33137b91b32 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 5 Sep 2025 15:43:33 -0300 Subject: [PATCH 36/38] Add new attributes to telemetry client --- src/service/telemetry/client.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/service/telemetry/client.ts b/src/service/telemetry/client.ts index de3f9a52a..2a5312703 100644 --- a/src/service/telemetry/client.ts +++ b/src/service/telemetry/client.ts @@ -6,7 +6,7 @@ import { Metrics, Traces, } from '@vtex/diagnostics-nodejs'; -import { APP, OTEL_EXPORTER_OTLP_ENDPOINT, DK_APP_ID, DIAGNOSTICS_TELEMETRY_ENABLED } from '../../constants'; +import { APP, OTEL_EXPORTER_OTLP_ENDPOINT, DK_APP_ID, DIAGNOSTICS_TELEMETRY_ENABLED, WORKSPACE, PRODUCTION } from '../../constants'; import { TelemetryClient } from '@vtex/diagnostics-nodejs/dist/telemetry'; import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa'; import { HostMetricsInstrumentation } from '../metrics/instruments/hostMetrics'; @@ -71,7 +71,8 @@ class TelemetryClientSingleton { 'app.id': APPLICATION_ID, 'vendor': APP.VENDOR, 'version': APP.VERSION || '', - 'environment': process.env.VTEX_WORKSPACE || 'development', + 'workspace': WORKSPACE, + 'production': PRODUCTION.toString(), }, } ); From a5de07ed64a6196ae9dd89b0d38f87101a7fc5aa Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 8 Sep 2025 09:44:41 -0300 Subject: [PATCH 37/38] release v7.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 38b3810d1..c632418e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vtex/api", - "version": "7.1.0", + "version": "7.1.1", "description": "VTEX I/O API client", "main": "lib/index.js", "typings": "lib/index.d.ts", From 9ee3b6b6ff9709dc875367651b119a13a8fec848 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 8 Sep 2025 09:47:23 -0300 Subject: [PATCH 38/38] update CHANGELOG --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72f351d4d..98dd78952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [7.1.1] - 2025-09-08 +### Changed +- Add conditional enabling for diagnostics telemetry signals + +## [7.1.0] - 2025-09-08 +### Changed +- Add metrics instrumentation based on diagnostics lib + ## [7.1.0] - 2025-09-02 ### Changed - Add new scope argument into auth directive