From dafafd8ccda81d819895238bbcbec43fe3f66fbd Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 25 Jul 2025 18:01:25 -0300 Subject: [PATCH 01/19] 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 c58e25199..42e50960a 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 7754ea1ab..abd225595 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 389ccc7f3e7627545446b022f51dddd286f8ef35 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 4 Aug 2025 18:03:35 -0300 Subject: [PATCH 02/19] 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 68a6304cb5734a8d9732807a213c52d575bcfc50 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 4 Aug 2025 18:13:21 -0300 Subject: [PATCH 03/19] 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 8b9bdb58bdf6cff8cbc63f7d351e402b0bceb827 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Mon, 4 Aug 2025 18:23:33 -0300 Subject: [PATCH 04/19] 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 3c09471e86ee9a0cbabe8c8216ca36f655b213fc Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 5 Aug 2025 09:24:26 -0300 Subject: [PATCH 05/19] Add resolution for @grpc/grpc-js dependency --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 42e50960a..c5b0631ec 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 a847fa6b456104ac32f69c07dc5688868bd77abc Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 5 Aug 2025 09:29:50 -0300 Subject: [PATCH 06/19] 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 c5b0631ec..c4abbbe27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vtex/api", - "version": "6.49.7", + "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 abd225595..0f9e33119 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 9499c947297877e0e6a224993db4bb181cc88081 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 12 Aug 2025 16:57:49 -0300 Subject: [PATCH 07/19] 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 76759b6aca96a1402648302c2f4f4a620a87b8cb Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Tue, 12 Aug 2025 22:48:40 -0300 Subject: [PATCH 08/19] 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 fadd23656074c917130970a7bd07ca2708215bcf Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 17:08:49 -0300 Subject: [PATCH 09/19] 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 c4abbbe27..51a1226d4 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 0f9e33119..2c6b71c67 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" @@ -5145,6 +5161,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 3aeb38a1278cbbdb62a1fa7c84648e9690784265 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 17:02:27 -0300 Subject: [PATCH 10/19] 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 5e9dc0e056f94c7dd7674a5b8b63ea918a350aa4 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 15:00:01 -0300 Subject: [PATCH 11/19] 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 a0f6fcbb36d1fce0b15f415f6e348b9dea0c1d5f Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:05:32 -0300 Subject: [PATCH 12/19] 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 d41ec8aa4e5b3f60f24227ffd34f286adfb70ebe Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:12:29 -0300 Subject: [PATCH 13/19] 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 4b3a2bf2d6794d99b07cd3fbce03806476328ab6 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:17:08 -0300 Subject: [PATCH 14/19] 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 e8540bac016448660872ccff37b7e9611d86eb7c Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:32:59 -0300 Subject: [PATCH 15/19] 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 96a16fa48..57e0f1607 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'; @@ -79,6 +80,7 @@ class TelemetryClientSingleton { const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), + new KoaInstrumentation(), ]; telemetryClient.registerInstrumentations(instrumentations); From 96f26d3fc7d5918341dff31ef79ce4e3ba4a8bf5 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:45:45 -0300 Subject: [PATCH 16/19] 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 803dfa9e3ab9ac96851402dc7c6b2481662d2d88 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:38:19 -0300 Subject: [PATCH 17/19] 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 027d84b36b944f931ab79e07ff9b3ac911dff6a3 Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Fri, 8 Aug 2025 16:42:23 -0300 Subject: [PATCH 18/19] 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 57e0f1607..db42e276d 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'; @@ -81,6 +82,10 @@ class TelemetryClientSingleton { const instrumentations = [ ...Instrumentation.CommonInstrumentations.minimal(), new KoaInstrumentation(), + new HostMetricsInstrumentation({ + name: 'host-metrics-instrumentation', + meterProvider: metricsClient.provider(), + }), ]; telemetryClient.registerInstrumentations(instrumentations); From 2724ec83034fb1292a675fd9b820d0a8042799fa Mon Sep 17 00:00:00 2001 From: Daniyel Rocha Date: Thu, 14 Aug 2025 18:34:18 -0300 Subject: [PATCH 19/19] Release v6.49.8-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51a1226d4..901bfd7f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vtex/api", - "version": "6.49.8-beta.0", + "version": "6.49.8-beta.2", "description": "VTEX I/O API client", "main": "lib/index.js", "typings": "lib/index.d.ts",