From 1e0ab19b4ba3ea1ee3ff53294aef31144c14d3ae Mon Sep 17 00:00:00 2001 From: Sam Brenner Date: Mon, 30 Mar 2026 13:48:41 -0400 Subject: [PATCH] use startup logs for v6 and startup logs enabled, else console.error --- .../dd-trace/src/llmobs/constants/text.js | 3 + packages/dd-trace/src/llmobs/index.js | 13 +++-- packages/dd-trace/src/startup-log.js | 9 +++ packages/dd-trace/test/llmobs/index.spec.js | 58 ++++++++++++------- 4 files changed, 59 insertions(+), 24 deletions(-) diff --git a/packages/dd-trace/src/llmobs/constants/text.js b/packages/dd-trace/src/llmobs/constants/text.js index 7cc17897950..9cea585ba7b 100644 --- a/packages/dd-trace/src/llmobs/constants/text.js +++ b/packages/dd-trace/src/llmobs/constants/text.js @@ -3,4 +3,7 @@ module.exports = { DROPPED_VALUE_TEXT: "[This value has been dropped because this span's size exceeds the 1MB size limit.]", UNSERIALIZABLE_VALUE_TEXT: 'Unserializable value', + INCOMPATIBLE_INITIALIZATION: + 'Cannot send LLM Observability data without a running agent or without both a Datadog API key and site. ' + + 'Ensure these configurations are set before running your application.', } diff --git a/packages/dd-trace/src/llmobs/index.js b/packages/dd-trace/src/llmobs/index.js index 3a4099f9da5..f3a4972a7a8 100644 --- a/packages/dd-trace/src/llmobs/index.js +++ b/packages/dd-trace/src/llmobs/index.js @@ -3,6 +3,8 @@ const { channel } = require('dc-polyfill') const log = require('../log') +const { DD_MAJOR } = require('../../../../version') +const startupLogs = require('../startup-log') const { ML_APP, PROPAGATED_ML_APP_KEY, @@ -15,6 +17,7 @@ const LLMObsEvalMetricsWriter = require('./writers/evaluations') const LLMObsTagger = require('./tagger') const LLMObsSpanWriter = require('./writers/spans') const { setAgentStrategy } = require('./writers/util') +const { INCOMPATIBLE_INITIALIZATION } = require('./constants/text') const spanFinishCh = channel('dd-trace:span:finish') const evalMetricAppendCh = channel('llmobs:eval-metric:append') @@ -66,10 +69,12 @@ function enable (config) { setAgentStrategy(config, useAgentless => { if (useAgentless && !(config.apiKey && config.site)) { - throw new Error( - 'Cannot send LLM Observability data without a running agent or without both a Datadog API key and site.\n' + - 'Ensure these configurations are set before running your application.' - ) + if (DD_MAJOR < 6 || !config?.startupLogs) { + // eslint-disable-next-line no-console + console.error(INCOMPATIBLE_INITIALIZATION) + } else { + startupLogs.logGenericError(INCOMPATIBLE_INITIALIZATION) + } } evalWriter?.setAgentless(useAgentless) diff --git a/packages/dd-trace/src/startup-log.js b/packages/dd-trace/src/startup-log.js index 3289b0fe4a2..4a4bde152ef 100644 --- a/packages/dd-trace/src/startup-log.js +++ b/packages/dd-trace/src/startup-log.js @@ -63,6 +63,14 @@ function logAgentError (agentError) { } } +function logGenericError (message) { + if (!config?.startupLogs) { + return + } + + warn('DATADOG TRACER DIAGNOSTIC - Generic Error: ' + message) +} + /** * Returns config info without integrations (used by startupLog). * @returns {Record} @@ -143,4 +151,5 @@ module.exports = { setSamplingRules, tracerInfo, errors, + logGenericError, } diff --git a/packages/dd-trace/test/llmobs/index.spec.js b/packages/dd-trace/test/llmobs/index.spec.js index 10eecd52827..6dbdbf152aa 100644 --- a/packages/dd-trace/test/llmobs/index.spec.js +++ b/packages/dd-trace/test/llmobs/index.spec.js @@ -7,6 +7,8 @@ const { after, afterEach, beforeEach, describe, it } = require('mocha') const proxyquire = require('proxyquire') const sinon = require('sinon') +const { DD_MAJOR } = require('../../../../version') +const { INCOMPATIBLE_INITIALIZATION } = require('../../src/llmobs/constants/text') const { removeDestroyHandler } = require('./util') const spanFinishCh = channel('dd-trace:span:finish') @@ -23,6 +25,9 @@ describe('module', () => { let LLMObsEvalMetricsWriterSpy let fetchAgentInfoStub + /** @type {import('sinon').SinonStub} */ + let startupLogStub + beforeEach(() => { store = {} logger = { debug: sinon.stub() } @@ -40,7 +45,7 @@ describe('module', () => { fetchAgentInfoStub = sinon.stub() - llmobsModule = proxyquire('../../../dd-trace/src/llmobs', { + const llmobsModuleProxyRequireMeta = { './writers/spans': LLMObsSpanWriterSpy, './writers/evaluations': LLMObsEvalMetricsWriterSpy, '../log': logger, @@ -56,7 +61,19 @@ describe('module', () => { fetchAgentInfo: fetchAgentInfoStub, }, }), - }) + } + + if (DD_MAJOR < 6) { + startupLogStub = sinon.stub(console, 'error') + } else { + startupLogStub = sinon.stub() + + llmobsModuleProxyRequireMeta['../startup-log'] = { + logGenericError: startupLogStub, + } + } + + llmobsModule = proxyquire('../../../dd-trace/src/llmobs', llmobsModuleProxyRequireMeta) removeDestroyHandler() }) @@ -117,22 +134,22 @@ describe('module', () => { describe('with agentlessEnabled set to `true`', () => { describe('when no api key is provided', () => { it('throws an error', () => { - assert.throws(() => llmobsModule.enable({ + llmobsModule.enable({ llmobs: { agentlessEnabled: true, }, - }), - { - message: 'Cannot send LLM Observability data without a running agent ' + - 'or without both a Datadog API key and site.\n' + - 'Ensure these configurations are set before running your application.', + startupLogs: true, }) + + sinon.assert.calledWith(startupLogStub, INCOMPATIBLE_INITIALIZATION) }) }) describe('when no site is provided', () => { it('throws an error', () => { - assert.throws(() => llmobsModule.enable({ llmobs: { agentlessEnabled: true, apiKey: 'test' } })) + llmobsModule.enable({ llmobs: { agentlessEnabled: true, apiKey: 'test' }, startupLogs: true }) + + sinon.assert.calledWith(startupLogStub, INCOMPATIBLE_INITIALIZATION) }) }) @@ -180,13 +197,17 @@ describe('module', () => { describe('when no API key is provided', () => { it('throws an error', () => { - assert.throws(() => llmobsModule.enable({ llmobs: { mlApp: 'test', site: 'datadoghq.com' } })) + llmobsModule.enable({ llmobs: { mlApp: 'test', site: 'datadoghq.com' }, startupLogs: true }) + + sinon.assert.calledWith(startupLogStub, INCOMPATIBLE_INITIALIZATION) }) }) describe('when no site is provided', () => { it('throws an error', () => { - assert.throws(() => llmobsModule.enable({ llmobs: { mlApp: 'test', apiKey: 'test' } })) + llmobsModule.enable({ llmobs: { mlApp: 'test', apiKey: 'test' }, startupLogs: true }) + + sinon.assert.calledWith(startupLogStub, INCOMPATIBLE_INITIALIZATION) }) }) @@ -227,20 +248,17 @@ describe('module', () => { describe('when no API key is provided', () => { it('throws an error', () => { - assert.throws( - () => llmobsModule.enable({ llmobs: { mlApp: 'test', site: 'datadoghq.com' } }), - { - message: 'Cannot send LLM Observability data without a running agent ' + - 'or without both a Datadog API key and site.\n' + - 'Ensure these configurations are set before running your application.', - } - ) + llmobsModule.enable({ llmobs: { mlApp: 'test', site: 'datadoghq.com' }, startupLogs: true }) + + sinon.assert.calledWith(startupLogStub, INCOMPATIBLE_INITIALIZATION) }) }) describe('when no site is provided', () => { it('throws an error', () => { - assert.throws(() => llmobsModule.enable({ llmobs: {}, apiKey: 'test' })) + llmobsModule.enable({ llmobs: {}, apiKey: 'test', startupLogs: true }) + + sinon.assert.calledWith(startupLogStub, INCOMPATIBLE_INITIALIZATION) }) })