diff --git a/packages/cli/src/serve/run-qwen-serve.test.ts b/packages/cli/src/serve/run-qwen-serve.test.ts index 0105e578d11..59e948f34e8 100644 --- a/packages/cli/src/serve/run-qwen-serve.test.ts +++ b/packages/cli/src/serve/run-qwen-serve.test.ts @@ -1174,6 +1174,46 @@ describe('runQwenServe telemetry validation', () => { await handle.close(); } }); + + it('awaits telemetry initialization before daemon metrics', async () => { + tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'qws-tm-'))); + const callOrder: string[] = []; + vi.spyOn(qwenCore, 'initializeTelemetry').mockImplementation(async () => { + callOrder.push('telemetry-start'); + await Promise.resolve(); + callOrder.push('telemetry-resolved'); + }); + vi.spyOn(qwenCore, 'initializeDaemonMetrics').mockImplementation(() => { + callOrder.push('daemon-metrics'); + }); + vi.spyOn(qwenCore, 'resolveTelemetrySettings').mockResolvedValue({ + enabled: true, + sensitiveSpanAttributeMaxLength: 1024 * 1024, + }); + const handle = await runQwenServe( + { + port: 0, + hostname: '127.0.0.1', + mode: 'http-bridge', + workspace: tmpDir, + maxSessions: 1, + serveWebShell: false, + }, + { + bridge: makeRuntimeBridge(), + daemonLogBaseDir: path.join(tmpDir, 'debug'), + }, + ); + try { + expect(callOrder).toEqual([ + 'telemetry-start', + 'telemetry-resolved', + 'daemon-metrics', + ]); + } finally { + await handle.close(); + } + }); }); /** diff --git a/packages/core/src/telemetry/sdk-impl.ts b/packages/core/src/telemetry/sdk-impl.ts index b8c7770a522..d87ba30252d 100644 --- a/packages/core/src/telemetry/sdk-impl.ts +++ b/packages/core/src/telemetry/sdk-impl.ts @@ -419,6 +419,13 @@ export async function startTelemetrySdk( : logToSpanProcessor ? [logToSpanProcessor] : [], + // Metrics uses the singular `metricReader` field because + // `@opentelemetry/sdk-node@0.203.0` only accepts one reader; there is no + // `metricReaders: []` opt-out. The SDK's `start()` calls + // `configureMetricProviderFromEnv()` unconditionally, so env-based readers + // are only suppressed when an explicit reader is provided. This is + // intentionally asymmetric with `spanProcessors`/`logRecordProcessors`, + // where an empty array disables env fallback for those signals. ...(metricReader && { metricReader }), instrumentations: [ new HttpInstrumentation({