@juspay/neurolink 9.41.0 → 9.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +7 -1
- package/dist/auth/anthropicOAuth.d.ts +18 -3
- package/dist/auth/anthropicOAuth.js +149 -4
- package/dist/auth/providers/firebase.js +5 -1
- package/dist/auth/providers/jwt.js +5 -1
- package/dist/auth/providers/workos.js +5 -1
- package/dist/auth/sessionManager.d.ts +1 -1
- package/dist/auth/sessionManager.js +58 -27
- package/dist/browser/neurolink.min.js +354 -334
- package/dist/cli/commands/mcp.d.ts +6 -0
- package/dist/cli/commands/mcp.js +188 -181
- package/dist/cli/commands/proxy.d.ts +2 -1
- package/dist/cli/commands/proxy.js +713 -431
- package/dist/cli/commands/task.js +3 -0
- package/dist/cli/factories/commandFactory.d.ts +2 -0
- package/dist/cli/factories/commandFactory.js +38 -0
- package/dist/cli/parser.js +4 -3
- package/dist/client/aiSdkAdapter.js +3 -0
- package/dist/client/streamingClient.js +30 -10
- package/dist/core/baseProvider.d.ts +6 -1
- package/dist/core/baseProvider.js +208 -230
- package/dist/core/factory.d.ts +3 -0
- package/dist/core/factory.js +138 -188
- package/dist/core/modules/GenerationHandler.js +3 -2
- package/dist/core/redisConversationMemoryManager.js +7 -3
- package/dist/evaluation/BatchEvaluator.js +4 -1
- package/dist/evaluation/hooks/observabilityHooks.js +5 -3
- package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
- package/dist/evaluation/pipeline/evaluationPipeline.js +24 -9
- package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
- package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
- package/dist/evaluation/scorers/scorerRegistry.d.ts +3 -0
- package/dist/evaluation/scorers/scorerRegistry.js +353 -282
- package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
- package/dist/lib/auth/anthropicOAuth.js +149 -4
- package/dist/lib/auth/providers/firebase.js +5 -1
- package/dist/lib/auth/providers/jwt.js +5 -1
- package/dist/lib/auth/providers/workos.js +5 -1
- package/dist/lib/auth/sessionManager.d.ts +1 -1
- package/dist/lib/auth/sessionManager.js +58 -27
- package/dist/lib/client/aiSdkAdapter.js +3 -0
- package/dist/lib/client/streamingClient.js +30 -10
- package/dist/lib/core/baseProvider.d.ts +6 -1
- package/dist/lib/core/baseProvider.js +208 -230
- package/dist/lib/core/factory.d.ts +3 -0
- package/dist/lib/core/factory.js +138 -188
- package/dist/lib/core/modules/GenerationHandler.js +3 -2
- package/dist/lib/core/redisConversationMemoryManager.js +7 -3
- package/dist/lib/evaluation/BatchEvaluator.js +4 -1
- package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
- package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
- package/dist/lib/evaluation/pipeline/evaluationPipeline.js +24 -9
- package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
- package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
- package/dist/lib/evaluation/scorers/scorerRegistry.d.ts +3 -0
- package/dist/lib/evaluation/scorers/scorerRegistry.js +353 -282
- package/dist/lib/mcp/toolRegistry.d.ts +2 -0
- package/dist/lib/mcp/toolRegistry.js +32 -31
- package/dist/lib/neurolink.d.ts +41 -2
- package/dist/lib/neurolink.js +1616 -1681
- package/dist/lib/observability/otelBridge.d.ts +2 -2
- package/dist/lib/observability/otelBridge.js +12 -3
- package/dist/lib/providers/amazonBedrock.js +2 -4
- package/dist/lib/providers/anthropic.d.ts +9 -5
- package/dist/lib/providers/anthropic.js +19 -14
- package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
- package/dist/lib/providers/anthropicBaseProvider.js +5 -4
- package/dist/lib/providers/azureOpenai.d.ts +1 -1
- package/dist/lib/providers/azureOpenai.js +5 -4
- package/dist/lib/providers/googleAiStudio.js +30 -6
- package/dist/lib/providers/googleVertex.d.ts +10 -0
- package/dist/lib/providers/googleVertex.js +437 -423
- package/dist/lib/providers/huggingFace.d.ts +3 -3
- package/dist/lib/providers/huggingFace.js +6 -8
- package/dist/lib/providers/litellm.d.ts +1 -0
- package/dist/lib/providers/litellm.js +76 -55
- package/dist/lib/providers/mistral.js +2 -1
- package/dist/lib/providers/ollama.js +93 -23
- package/dist/lib/providers/openAI.d.ts +2 -0
- package/dist/lib/providers/openAI.js +141 -141
- package/dist/lib/providers/openRouter.js +2 -1
- package/dist/lib/providers/openaiCompatible.d.ts +4 -4
- package/dist/lib/providers/openaiCompatible.js +4 -4
- package/dist/lib/proxy/claudeFormat.d.ts +3 -2
- package/dist/lib/proxy/claudeFormat.js +27 -14
- package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
- package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
- package/dist/lib/proxy/modelRouter.js +3 -0
- package/dist/lib/proxy/oauthFetch.d.ts +1 -1
- package/dist/lib/proxy/oauthFetch.js +289 -316
- package/dist/lib/proxy/proxyConfig.js +46 -24
- package/dist/lib/proxy/proxyEnv.d.ts +19 -0
- package/dist/lib/proxy/proxyEnv.js +73 -0
- package/dist/lib/proxy/proxyFetch.js +291 -217
- package/dist/lib/proxy/proxyTracer.d.ts +133 -0
- package/dist/lib/proxy/proxyTracer.js +645 -0
- package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
- package/dist/lib/proxy/rawStreamCapture.js +83 -0
- package/dist/lib/proxy/requestLogger.d.ts +32 -5
- package/dist/lib/proxy/requestLogger.js +503 -47
- package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
- package/dist/lib/proxy/sseInterceptor.js +427 -0
- package/dist/lib/proxy/usageStats.d.ts +4 -3
- package/dist/lib/proxy/usageStats.js +25 -12
- package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
- package/dist/lib/rag/chunking/markdownChunker.js +15 -6
- package/dist/lib/server/routes/claudeProxyRoutes.d.ts +17 -3
- package/dist/lib/server/routes/claudeProxyRoutes.js +3032 -1349
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
- package/dist/lib/services/server/ai/observability/instrumentation.js +337 -161
- package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
- package/dist/lib/tasks/backends/bullmqBackend.js +35 -22
- package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
- package/dist/lib/tasks/store/redisTaskStore.js +54 -39
- package/dist/lib/tasks/taskManager.d.ts +5 -0
- package/dist/lib/tasks/taskManager.js +158 -30
- package/dist/lib/telemetry/index.d.ts +2 -1
- package/dist/lib/telemetry/index.js +2 -1
- package/dist/lib/telemetry/telemetryService.d.ts +3 -0
- package/dist/lib/telemetry/telemetryService.js +69 -5
- package/dist/lib/types/cli.d.ts +10 -0
- package/dist/lib/types/proxyTypes.d.ts +160 -5
- package/dist/lib/types/streamTypes.d.ts +25 -3
- package/dist/lib/utils/messageBuilder.js +3 -2
- package/dist/lib/utils/providerHealth.d.ts +19 -0
- package/dist/lib/utils/providerHealth.js +279 -33
- package/dist/lib/utils/providerUtils.js +17 -22
- package/dist/lib/utils/toolChoice.d.ts +4 -0
- package/dist/lib/utils/toolChoice.js +7 -0
- package/dist/mcp/toolRegistry.d.ts +2 -0
- package/dist/mcp/toolRegistry.js +32 -31
- package/dist/neurolink.d.ts +41 -2
- package/dist/neurolink.js +1616 -1681
- package/dist/observability/otelBridge.d.ts +2 -2
- package/dist/observability/otelBridge.js +12 -3
- package/dist/providers/amazonBedrock.js +2 -4
- package/dist/providers/anthropic.d.ts +9 -5
- package/dist/providers/anthropic.js +19 -14
- package/dist/providers/anthropicBaseProvider.d.ts +3 -3
- package/dist/providers/anthropicBaseProvider.js +5 -4
- package/dist/providers/azureOpenai.d.ts +1 -1
- package/dist/providers/azureOpenai.js +5 -4
- package/dist/providers/googleAiStudio.js +30 -6
- package/dist/providers/googleVertex.d.ts +10 -0
- package/dist/providers/googleVertex.js +437 -423
- package/dist/providers/huggingFace.d.ts +3 -3
- package/dist/providers/huggingFace.js +6 -7
- package/dist/providers/litellm.d.ts +1 -0
- package/dist/providers/litellm.js +76 -55
- package/dist/providers/mistral.js +2 -1
- package/dist/providers/ollama.js +93 -23
- package/dist/providers/openAI.d.ts +2 -0
- package/dist/providers/openAI.js +141 -141
- package/dist/providers/openRouter.js +2 -1
- package/dist/providers/openaiCompatible.d.ts +4 -4
- package/dist/providers/openaiCompatible.js +4 -3
- package/dist/proxy/claudeFormat.d.ts +3 -2
- package/dist/proxy/claudeFormat.js +27 -14
- package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
- package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
- package/dist/proxy/modelRouter.js +3 -0
- package/dist/proxy/oauthFetch.d.ts +1 -1
- package/dist/proxy/oauthFetch.js +289 -316
- package/dist/proxy/proxyConfig.js +46 -24
- package/dist/proxy/proxyEnv.d.ts +19 -0
- package/dist/proxy/proxyEnv.js +72 -0
- package/dist/proxy/proxyFetch.js +291 -217
- package/dist/proxy/proxyTracer.d.ts +133 -0
- package/dist/proxy/proxyTracer.js +644 -0
- package/dist/proxy/rawStreamCapture.d.ts +10 -0
- package/dist/proxy/rawStreamCapture.js +82 -0
- package/dist/proxy/requestLogger.d.ts +32 -5
- package/dist/proxy/requestLogger.js +503 -47
- package/dist/proxy/sseInterceptor.d.ts +97 -0
- package/dist/proxy/sseInterceptor.js +426 -0
- package/dist/proxy/usageStats.d.ts +4 -3
- package/dist/proxy/usageStats.js +25 -12
- package/dist/rag/chunkers/MarkdownChunker.js +13 -5
- package/dist/rag/chunking/markdownChunker.js +15 -6
- package/dist/server/routes/claudeProxyRoutes.d.ts +17 -3
- package/dist/server/routes/claudeProxyRoutes.js +3032 -1349
- package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
- package/dist/services/server/ai/observability/instrumentation.js +337 -161
- package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
- package/dist/tasks/backends/bullmqBackend.js +35 -22
- package/dist/tasks/store/redisTaskStore.d.ts +1 -0
- package/dist/tasks/store/redisTaskStore.js +54 -39
- package/dist/tasks/taskManager.d.ts +5 -0
- package/dist/tasks/taskManager.js +158 -30
- package/dist/telemetry/index.d.ts +2 -1
- package/dist/telemetry/index.js +2 -1
- package/dist/telemetry/telemetryService.d.ts +3 -0
- package/dist/telemetry/telemetryService.js +69 -5
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/proxyTypes.d.ts +160 -5
- package/dist/types/streamTypes.d.ts +25 -3
- package/dist/utils/messageBuilder.js +3 -2
- package/dist/utils/providerHealth.d.ts +19 -0
- package/dist/utils/providerHealth.js +279 -33
- package/dist/utils/providerUtils.js +18 -22
- package/dist/utils/toolChoice.d.ts +4 -0
- package/dist/utils/toolChoice.js +6 -0
- package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
- package/docs/changelog.md +252 -0
- package/package.json +19 -2
- package/scripts/observability/check-proxy-telemetry.mjs +235 -0
- package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
- package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
- package/scripts/observability/manage-local-openobserve.sh +215 -0
- package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
- package/scripts/observability/proxy-observability.env.example +23 -0
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { LangfuseSpanProcessor } from "@langfuse/otel";
|
|
10
10
|
import { trace } from "@opentelemetry/api";
|
|
11
|
-
import
|
|
11
|
+
import { LoggerProvider } from "@opentelemetry/sdk-logs";
|
|
12
|
+
import { type SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
12
13
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
13
14
|
import type { LangfuseConfig } from "../../../../types/observability.js";
|
|
14
15
|
/**
|
|
@@ -104,6 +105,11 @@ export declare function getLangfuseSpanProcessor(): LangfuseSpanProcessor | null
|
|
|
104
105
|
* Get the tracer provider
|
|
105
106
|
*/
|
|
106
107
|
export declare function getTracerProvider(): NodeTracerProvider | null;
|
|
108
|
+
/**
|
|
109
|
+
* Get the logger provider for emitting OTLP log records.
|
|
110
|
+
* Returns null if OTLP is not configured or LoggerProvider was not created.
|
|
111
|
+
*/
|
|
112
|
+
export declare function getLoggerProvider(): LoggerProvider | null;
|
|
107
113
|
/**
|
|
108
114
|
* Check if OpenTelemetry is initialized
|
|
109
115
|
*/
|
|
@@ -7,15 +7,90 @@
|
|
|
7
7
|
* Flow: Vercel AI SDK → OpenTelemetry Spans → LangfuseSpanProcessor → Langfuse Platform
|
|
8
8
|
*/
|
|
9
9
|
import { LangfuseSpanProcessor } from "@langfuse/otel";
|
|
10
|
-
import { trace } from "@opentelemetry/api";
|
|
10
|
+
import { metrics, trace } from "@opentelemetry/api";
|
|
11
|
+
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
12
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
13
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
14
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
11
15
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
16
|
+
import { MeterProvider, PeriodicExportingMetricReader, } from "@opentelemetry/sdk-metrics";
|
|
17
|
+
import { BatchLogRecordProcessor, LoggerProvider, } from "@opentelemetry/sdk-logs";
|
|
18
|
+
import { BatchSpanProcessor, } from "@opentelemetry/sdk-trace-base";
|
|
12
19
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
13
20
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions";
|
|
14
21
|
import { AsyncLocalStorage } from "async_hooks";
|
|
15
22
|
import { logger } from "../../../../utils/logger.js";
|
|
16
23
|
const LOG_PREFIX = "[OpenTelemetry]";
|
|
24
|
+
function createOtelResource(config, serviceName) {
|
|
25
|
+
return resourceFromAttributes({
|
|
26
|
+
[ATTR_SERVICE_NAME]: serviceName,
|
|
27
|
+
[ATTR_SERVICE_VERSION]: config.release || "v1.0.0",
|
|
28
|
+
"deployment.environment": config.environment || "dev",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName) {
|
|
32
|
+
if (!otlpEndpoint) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const metricExporter = new OTLPMetricExporter({
|
|
37
|
+
url: `${otlpEndpoint}/v1/metrics`,
|
|
38
|
+
});
|
|
39
|
+
const metricReader = new PeriodicExportingMetricReader({
|
|
40
|
+
exporter: metricExporter,
|
|
41
|
+
exportIntervalMillis: 15000,
|
|
42
|
+
exportTimeoutMillis: 10000,
|
|
43
|
+
});
|
|
44
|
+
meterProvider = new MeterProvider({
|
|
45
|
+
resource,
|
|
46
|
+
readers: [metricReader],
|
|
47
|
+
});
|
|
48
|
+
metrics.setGlobalMeterProvider(meterProvider);
|
|
49
|
+
logger.info(`${LOG_PREFIX} OTLP metric exporter added — MeterProvider registered globally`, {
|
|
50
|
+
endpoint: `${otlpEndpoint}/v1/metrics`,
|
|
51
|
+
exportIntervalMs: 15000,
|
|
52
|
+
serviceName,
|
|
53
|
+
meterProviderType: meterProvider.constructor.name,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (metricsError) {
|
|
57
|
+
logger.warn(`${LOG_PREFIX} Failed to create OTLP metric exporter (non-fatal)`, {
|
|
58
|
+
error: metricsError instanceof Error
|
|
59
|
+
? metricsError.message
|
|
60
|
+
: String(metricsError),
|
|
61
|
+
endpoint: otlpEndpoint,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const logExporter = new OTLPLogExporter({
|
|
66
|
+
url: `${otlpEndpoint}/v1/logs`,
|
|
67
|
+
});
|
|
68
|
+
const logProcessor = new BatchLogRecordProcessor(logExporter, {
|
|
69
|
+
maxQueueSize: 2048,
|
|
70
|
+
maxExportBatchSize: 512,
|
|
71
|
+
scheduledDelayMillis: 2000,
|
|
72
|
+
exportTimeoutMillis: 30000,
|
|
73
|
+
});
|
|
74
|
+
loggerProvider = new LoggerProvider({
|
|
75
|
+
resource,
|
|
76
|
+
processors: [logProcessor],
|
|
77
|
+
});
|
|
78
|
+
logger.info(`${LOG_PREFIX} OTLP log exporter added — LoggerProvider created`, {
|
|
79
|
+
endpoint: `${otlpEndpoint}/v1/logs`,
|
|
80
|
+
serviceName,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (logsError) {
|
|
84
|
+
logger.warn(`${LOG_PREFIX} Failed to create OTLP log exporter (non-fatal)`, {
|
|
85
|
+
error: logsError instanceof Error ? logsError.message : String(logsError),
|
|
86
|
+
endpoint: otlpEndpoint,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
17
90
|
const contextStorage = new AsyncLocalStorage();
|
|
18
91
|
let tracerProvider = null;
|
|
92
|
+
let meterProvider = null;
|
|
93
|
+
let loggerProvider = null;
|
|
19
94
|
let langfuseProcessor = null;
|
|
20
95
|
let isInitialized = false;
|
|
21
96
|
let isCredentialsValid = false;
|
|
@@ -328,48 +403,20 @@ class ContextEnricher {
|
|
|
328
403
|
return Promise.resolve();
|
|
329
404
|
}
|
|
330
405
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
export function initializeOpenTelemetry(config) {
|
|
346
|
-
// Guard against multiple initializations — but always update config
|
|
347
|
-
// so that later NeuroLink instances can change traceNameFormat,
|
|
348
|
-
// autoDetectOperationName, and other configuration preferences
|
|
349
|
-
// without re-initializing the OTEL infrastructure.
|
|
350
|
-
if (isInitialized) {
|
|
351
|
-
currentConfig = config;
|
|
352
|
-
logger.debug(`${LOG_PREFIX} Already initialized, config updated`, {
|
|
353
|
-
usingExternalProvider,
|
|
354
|
-
hasLangfuseProcessor: !!langfuseProcessor,
|
|
355
|
-
hasTraceNameFormat: typeof config.traceNameFormat === "function",
|
|
356
|
-
});
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
// FIRST: Check for external provider mode - bypasses enabled check
|
|
360
|
-
// NOTE: When autoDetectExternalProvider is true, we trust the flag directly rather than
|
|
361
|
-
// calling hasExternalTracerProvider(). This is because Neurolink may bundle its own copy
|
|
362
|
-
// of @opentelemetry/api, which has a separate global state from the host application.
|
|
363
|
-
// The hasExternalTracerProvider() check would query Neurolink's bundled @opentelemetry/api
|
|
364
|
-
// global state (which has no provider registered), not the host's global state.
|
|
365
|
-
// By trusting autoDetectExternalProvider=true, we let the host application signal that
|
|
366
|
-
// it has already registered a TracerProvider.
|
|
367
|
-
const shouldUseExternal = config?.useExternalTracerProvider === true ||
|
|
368
|
-
config?.autoDetectExternalProvider === true;
|
|
369
|
-
if (shouldUseExternal) {
|
|
370
|
-
// Validate credentials even in external mode
|
|
371
|
-
if (!config?.publicKey || !config?.secretKey) {
|
|
372
|
-
logger.warn(`${LOG_PREFIX} External provider mode but missing credentials, skipping initialization`, {
|
|
406
|
+
function createLangfuseProcessor(config) {
|
|
407
|
+
return new LangfuseSpanProcessor({
|
|
408
|
+
publicKey: config.publicKey,
|
|
409
|
+
secretKey: config.secretKey,
|
|
410
|
+
baseUrl: config.baseUrl || "https://cloud.langfuse.com",
|
|
411
|
+
environment: config.environment || "dev",
|
|
412
|
+
release: config.release || "v1.0.0",
|
|
413
|
+
shouldExportSpan: () => true,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
function initializeExternalOpenTelemetryMode(config, resource, otlpEndpoint, serviceName, langfuseRequested, hasLangfuseCreds) {
|
|
417
|
+
if (langfuseRequested && !hasLangfuseCreds) {
|
|
418
|
+
if (!otlpEndpoint) {
|
|
419
|
+
logger.warn(`${LOG_PREFIX} External provider mode requested Langfuse but credentials are missing, and no OTLP endpoint is configured; skipping initialization`, {
|
|
373
420
|
hasPublicKey: !!config?.publicKey,
|
|
374
421
|
hasSecretKey: !!config?.secretKey,
|
|
375
422
|
});
|
|
@@ -377,154 +424,164 @@ export function initializeOpenTelemetry(config) {
|
|
|
377
424
|
isCredentialsValid = false;
|
|
378
425
|
return;
|
|
379
426
|
}
|
|
427
|
+
logger.warn(`${LOG_PREFIX} External provider mode missing Langfuse credentials; continuing with OTLP-only metrics/logs`, {
|
|
428
|
+
hasPublicKey: !!config?.publicKey,
|
|
429
|
+
hasSecretKey: !!config?.secretKey,
|
|
430
|
+
otlpEnabled: true,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
currentConfig = config;
|
|
435
|
+
isCredentialsValid = hasLangfuseCreds;
|
|
436
|
+
langfuseProcessor =
|
|
437
|
+
langfuseRequested && hasLangfuseCreds
|
|
438
|
+
? createLangfuseProcessor(config)
|
|
439
|
+
: null;
|
|
440
|
+
usingExternalProvider = true;
|
|
441
|
+
isInitialized = true;
|
|
442
|
+
initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName);
|
|
380
443
|
try {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
baseUrl: config.baseUrl || "https://cloud.langfuse.com",
|
|
389
|
-
environment: config.environment || "dev",
|
|
390
|
-
release: config.release || "v1.0.0",
|
|
391
|
-
shouldExportSpan: () => true,
|
|
392
|
-
});
|
|
393
|
-
usingExternalProvider = true;
|
|
394
|
-
isInitialized = true;
|
|
395
|
-
// Auto-register ContextEnricher with the global TracerProvider
|
|
396
|
-
// This ensures trace names are set even when host doesn't call getSpanProcessors()
|
|
397
|
-
try {
|
|
398
|
-
const globalProvider = trace.getTracerProvider();
|
|
399
|
-
// Check if it's a real provider with addSpanProcessor method (not the no-op default)
|
|
400
|
-
if (globalProvider &&
|
|
401
|
-
typeof globalProvider
|
|
402
|
-
.addSpanProcessor === "function") {
|
|
403
|
-
const provider = globalProvider;
|
|
404
|
-
// Add ContextEnricher for trace name enrichment
|
|
405
|
-
provider.addSpanProcessor(new ContextEnricher());
|
|
406
|
-
// Only add LangfuseSpanProcessor if the host has not already registered one.
|
|
407
|
-
// When skipLangfuseSpanProcessor is true, the host (e.g. Curator) already
|
|
408
|
-
// registers its own LangfuseSpanProcessor via a DeferredSpanProcessor, so
|
|
409
|
-
// adding another one here would cause duplicate trace exports to Langfuse.
|
|
410
|
-
const skipLangfuse = config.skipLangfuseSpanProcessor === true;
|
|
411
|
-
if (!skipLangfuse) {
|
|
412
|
-
provider.addSpanProcessor(langfuseProcessor);
|
|
413
|
-
}
|
|
414
|
-
logger.info(`${LOG_PREFIX} Auto-registered processors with global TracerProvider`, {
|
|
415
|
-
processors: skipLangfuse
|
|
416
|
-
? ["ContextEnricher"]
|
|
417
|
-
: ["ContextEnricher", "LangfuseSpanProcessor"],
|
|
418
|
-
reason: "External provider mode with auto-registration",
|
|
419
|
-
skippedLangfuseSpanProcessor: skipLangfuse,
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
// No real provider found - host will need to add processors manually
|
|
424
|
-
logger.info(`${LOG_PREFIX} Using external TracerProvider mode`, {
|
|
425
|
-
reason: config.useExternalTracerProvider
|
|
426
|
-
? "useExternalTracerProvider=true"
|
|
427
|
-
: "autoDetectExternalProvider=true (trusting host signal)",
|
|
428
|
-
instructions: "Add span processors to your TracerProvider using getSpanProcessors()",
|
|
429
|
-
});
|
|
430
|
-
logger.info(`${LOG_PREFIX} Span processors ready for external use`, {
|
|
431
|
-
processors: ["ContextEnricher", "LangfuseSpanProcessor"],
|
|
432
|
-
usage: "import { getSpanProcessors } from '@juspay/neurolink'",
|
|
433
|
-
});
|
|
444
|
+
const globalProvider = trace.getTracerProvider();
|
|
445
|
+
const provider = globalProvider;
|
|
446
|
+
if (globalProvider && typeof provider.addSpanProcessor === "function") {
|
|
447
|
+
provider.addSpanProcessor(new ContextEnricher());
|
|
448
|
+
const skipLangfuse = config.skipLangfuseSpanProcessor === true || !langfuseProcessor;
|
|
449
|
+
if (!skipLangfuse && langfuseProcessor) {
|
|
450
|
+
provider.addSpanProcessor(langfuseProcessor);
|
|
434
451
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
: String(autoRegisterError),
|
|
442
|
-
instructions: "Add span processors to your TracerProvider using getSpanProcessors()",
|
|
452
|
+
logger.info(`${LOG_PREFIX} Auto-registered processors with global TracerProvider`, {
|
|
453
|
+
processors: skipLangfuse
|
|
454
|
+
? ["ContextEnricher"]
|
|
455
|
+
: ["ContextEnricher", "LangfuseSpanProcessor"],
|
|
456
|
+
reason: "External provider mode with auto-registration",
|
|
457
|
+
skippedLangfuseSpanProcessor: skipLangfuse,
|
|
443
458
|
});
|
|
459
|
+
return;
|
|
444
460
|
}
|
|
445
|
-
|
|
461
|
+
logger.info(`${LOG_PREFIX} Using external TracerProvider mode`, {
|
|
462
|
+
reason: config.useExternalTracerProvider
|
|
463
|
+
? "useExternalTracerProvider=true"
|
|
464
|
+
: "autoDetectExternalProvider=true (trusting host signal)",
|
|
465
|
+
instructions: "Add span processors to your TracerProvider using getSpanProcessors()",
|
|
466
|
+
});
|
|
467
|
+
logger.info(`${LOG_PREFIX} Span processors ready for external use`, {
|
|
468
|
+
processors: langfuseProcessor
|
|
469
|
+
? ["ContextEnricher", "LangfuseSpanProcessor"]
|
|
470
|
+
: ["ContextEnricher"],
|
|
471
|
+
usage: "import { getSpanProcessors } from '@juspay/neurolink'",
|
|
472
|
+
});
|
|
446
473
|
}
|
|
447
|
-
catch (
|
|
448
|
-
logger.
|
|
449
|
-
error:
|
|
450
|
-
|
|
474
|
+
catch (autoRegisterError) {
|
|
475
|
+
logger.warn(`${LOG_PREFIX} Auto-registration failed, manual registration required`, {
|
|
476
|
+
error: autoRegisterError instanceof Error
|
|
477
|
+
? autoRegisterError.message
|
|
478
|
+
: String(autoRegisterError),
|
|
479
|
+
instructions: "Add span processors to your TracerProvider using getSpanProcessors()",
|
|
451
480
|
});
|
|
452
|
-
isInitialized = true;
|
|
453
|
-
return;
|
|
454
481
|
}
|
|
455
482
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
483
|
+
catch (error) {
|
|
484
|
+
logger.error(`${LOG_PREFIX} Failed to create span processor for external mode`, {
|
|
485
|
+
error: error instanceof Error ? error.message : String(error),
|
|
486
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
487
|
+
});
|
|
488
|
+
isInitialized = true;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function initializeStandaloneOpenTelemetryMode(config, resource, otlpEndpoint, serviceName, langfuseRequested, hasLangfuseCreds) {
|
|
492
|
+
if ((!langfuseRequested || !hasLangfuseCreds) && !otlpEndpoint) {
|
|
493
|
+
if (langfuseRequested && !hasLangfuseCreds) {
|
|
494
|
+
logger.warn(`${LOG_PREFIX} Langfuse requested but credentials are missing, and no OTLP endpoint is configured; skipping initialization`, {
|
|
495
|
+
hasPublicKey: !!config.publicKey,
|
|
496
|
+
hasSecretKey: !!config.secretKey,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
logger.debug(`${LOG_PREFIX} Langfuse disabled and OTLP endpoint missing, skipping initialization`);
|
|
501
|
+
}
|
|
459
502
|
isInitialized = true;
|
|
460
503
|
return;
|
|
461
504
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
logger.warn(`${LOG_PREFIX} Langfuse enabled but missing credentials, skipping initialization`, {
|
|
505
|
+
if (langfuseRequested && !hasLangfuseCreds) {
|
|
506
|
+
logger.warn(`${LOG_PREFIX} Langfuse requested but credentials are missing; continuing with OTLP-only telemetry`, {
|
|
465
507
|
hasPublicKey: !!config.publicKey,
|
|
466
508
|
hasSecretKey: !!config.secretKey,
|
|
509
|
+
otlpEnabled: !!otlpEndpoint,
|
|
467
510
|
});
|
|
468
|
-
isInitialized = true;
|
|
469
|
-
isCredentialsValid = false;
|
|
470
|
-
return;
|
|
471
511
|
}
|
|
472
512
|
try {
|
|
473
513
|
currentConfig = config;
|
|
474
|
-
isCredentialsValid =
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
release: config.release || "v1.0.0",
|
|
483
|
-
shouldExportSpan: () => true,
|
|
484
|
-
});
|
|
485
|
-
logger.debug(`${LOG_PREFIX} Created LangfuseSpanProcessor`, {
|
|
514
|
+
isCredentialsValid = hasLangfuseCreds;
|
|
515
|
+
langfuseProcessor =
|
|
516
|
+
langfuseRequested && hasLangfuseCreds
|
|
517
|
+
? createLangfuseProcessor(config)
|
|
518
|
+
: null;
|
|
519
|
+
logger.debug(`${LOG_PREFIX} Standalone observability mode`, {
|
|
520
|
+
langfuseEnabled: !!langfuseProcessor,
|
|
521
|
+
otlpEnabled: !!otlpEndpoint,
|
|
486
522
|
baseUrl: config.baseUrl || "https://cloud.langfuse.com",
|
|
487
523
|
environment: config.environment || "dev",
|
|
488
524
|
});
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
525
|
+
const spanProcessors = [new ContextEnricher()];
|
|
526
|
+
if (langfuseProcessor) {
|
|
527
|
+
spanProcessors.push(langfuseProcessor);
|
|
528
|
+
}
|
|
529
|
+
if (otlpEndpoint) {
|
|
530
|
+
try {
|
|
531
|
+
const otlpExporter = new OTLPTraceExporter({
|
|
532
|
+
url: `${otlpEndpoint}/v1/traces`,
|
|
533
|
+
});
|
|
534
|
+
spanProcessors.push(new BatchSpanProcessor(otlpExporter, {
|
|
535
|
+
maxQueueSize: 2048,
|
|
536
|
+
maxExportBatchSize: 512,
|
|
537
|
+
scheduledDelayMillis: 1000,
|
|
538
|
+
exportTimeoutMillis: 30000,
|
|
539
|
+
}));
|
|
540
|
+
logger.info(`${LOG_PREFIX} OTLP trace exporter added`, {
|
|
541
|
+
endpoint: `${otlpEndpoint}/v1/traces`,
|
|
542
|
+
serviceName,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
catch (otlpError) {
|
|
546
|
+
logger.warn(`${LOG_PREFIX} Failed to create OTLP exporter (non-fatal)`, {
|
|
547
|
+
error: otlpError instanceof Error
|
|
548
|
+
? otlpError.message
|
|
549
|
+
: String(otlpError),
|
|
550
|
+
endpoint: otlpEndpoint,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
tracerProvider = new NodeTracerProvider({ resource, spanProcessors });
|
|
555
|
+
tracerProvider.register({
|
|
556
|
+
propagator: new W3CTraceContextPropagator(),
|
|
498
557
|
});
|
|
499
|
-
// Step 4: Register globally
|
|
500
|
-
tracerProvider.register();
|
|
501
558
|
usingExternalProvider = false;
|
|
502
559
|
isInitialized = true;
|
|
503
|
-
|
|
560
|
+
initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName);
|
|
561
|
+
logger.info(`${LOG_PREFIX} Observability initialized`, {
|
|
504
562
|
baseUrl: config.baseUrl || "https://cloud.langfuse.com",
|
|
505
563
|
environment: config.environment || "dev",
|
|
506
564
|
release: config.release || "v1.0.0",
|
|
507
565
|
mode: "standalone",
|
|
566
|
+
langfuseEnabled: !!langfuseProcessor,
|
|
567
|
+
otlpEnabled: !!otlpEndpoint,
|
|
568
|
+
serviceName,
|
|
508
569
|
});
|
|
509
570
|
}
|
|
510
571
|
catch (error) {
|
|
511
|
-
// Check if this is a duplicate registration error
|
|
512
572
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
513
573
|
const isDuplicateError = errorMessage.includes("duplicate registration") ||
|
|
514
574
|
errorMessage.includes("already registered") ||
|
|
515
575
|
errorMessage.includes("already set");
|
|
516
576
|
if (isDuplicateError) {
|
|
517
|
-
// Graceful handling: switch to external mode
|
|
518
577
|
logger.warn(`${LOG_PREFIX} TracerProvider already registered, switching to external mode`, {
|
|
519
578
|
error: errorMessage,
|
|
520
579
|
recommendation: "Set useExternalTracerProvider=true or autoDetectExternalProvider=true in config",
|
|
521
580
|
});
|
|
522
581
|
usingExternalProvider = true;
|
|
523
582
|
isInitialized = true;
|
|
524
|
-
// Don't throw - processors are still usable
|
|
525
583
|
return;
|
|
526
584
|
}
|
|
527
|
-
// Other errors: log and re-throw
|
|
528
585
|
logger.error(`${LOG_PREFIX} Initialization failed`, {
|
|
529
586
|
error: errorMessage,
|
|
530
587
|
stack: error instanceof Error ? error.stack : undefined,
|
|
@@ -532,6 +589,55 @@ export function initializeOpenTelemetry(config) {
|
|
|
532
589
|
throw error;
|
|
533
590
|
}
|
|
534
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Initialize OpenTelemetry with Langfuse span processor
|
|
594
|
+
*
|
|
595
|
+
* This connects Vercel AI SDK's experimental_telemetry to Langfuse by:
|
|
596
|
+
* 1. Creating LangfuseSpanProcessor with Langfuse credentials
|
|
597
|
+
* 2. Creating a NodeTracerProvider with service metadata and span processor
|
|
598
|
+
* 3. Registering the provider globally for AI SDK to use
|
|
599
|
+
*
|
|
600
|
+
* NEW: If useExternalTracerProvider is true or autoDetectExternalProvider detects
|
|
601
|
+
* an existing provider, steps 2 and 3 are skipped. The span processors are still
|
|
602
|
+
* created and can be retrieved via getSpanProcessors().
|
|
603
|
+
*
|
|
604
|
+
* @param config - Langfuse configuration passed from parent application
|
|
605
|
+
*/
|
|
606
|
+
export function initializeOpenTelemetry(config) {
|
|
607
|
+
// Guard against multiple initializations — but always update config
|
|
608
|
+
// so that later NeuroLink instances can change traceNameFormat,
|
|
609
|
+
// autoDetectOperationName, and other configuration preferences
|
|
610
|
+
// without re-initializing the OTEL infrastructure.
|
|
611
|
+
if (isInitialized) {
|
|
612
|
+
currentConfig = config;
|
|
613
|
+
logger.debug(`${LOG_PREFIX} Already initialized, config updated`, {
|
|
614
|
+
usingExternalProvider,
|
|
615
|
+
hasLangfuseProcessor: !!langfuseProcessor,
|
|
616
|
+
hasTraceNameFormat: typeof config.traceNameFormat === "function",
|
|
617
|
+
});
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
// FIRST: Check for external provider mode - bypasses enabled check
|
|
621
|
+
// NOTE: When autoDetectExternalProvider is true, we trust the flag directly rather than
|
|
622
|
+
// calling hasExternalTracerProvider(). This is because Neurolink may bundle its own copy
|
|
623
|
+
// of @opentelemetry/api, which has a separate global state from the host application.
|
|
624
|
+
// The hasExternalTracerProvider() check would query Neurolink's bundled @opentelemetry/api
|
|
625
|
+
// global state (which has no provider registered), not the host's global state.
|
|
626
|
+
// By trusting autoDetectExternalProvider=true, we let the host application signal that
|
|
627
|
+
// it has already registered a TracerProvider.
|
|
628
|
+
const shouldUseExternal = config?.useExternalTracerProvider === true ||
|
|
629
|
+
config?.autoDetectExternalProvider === true;
|
|
630
|
+
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
631
|
+
const langfuseRequested = config?.enabled === true;
|
|
632
|
+
const hasLangfuseCreds = !!config.publicKey && !!config.secretKey;
|
|
633
|
+
const serviceName = process.env.OTEL_SERVICE_NAME || "neurolink";
|
|
634
|
+
const resource = createOtelResource(config, serviceName);
|
|
635
|
+
if (shouldUseExternal) {
|
|
636
|
+
initializeExternalOpenTelemetryMode(config, resource, otlpEndpoint, serviceName, langfuseRequested, hasLangfuseCreds);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
initializeStandaloneOpenTelemetryMode(config, resource, otlpEndpoint, serviceName, langfuseRequested, hasLangfuseCreds);
|
|
640
|
+
}
|
|
535
641
|
/**
|
|
536
642
|
* Flush all pending spans to Langfuse
|
|
537
643
|
*/
|
|
@@ -540,22 +646,75 @@ export async function flushOpenTelemetry() {
|
|
|
540
646
|
logger.debug(`${LOG_PREFIX} Not initialized, skipping flush`);
|
|
541
647
|
return;
|
|
542
648
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
649
|
+
const failures = [];
|
|
650
|
+
if (langfuseProcessor) {
|
|
651
|
+
try {
|
|
652
|
+
logger.info(`${LOG_PREFIX} Flushing Langfuse spans...`);
|
|
653
|
+
await langfuseProcessor.forceFlush();
|
|
654
|
+
}
|
|
655
|
+
catch (error) {
|
|
656
|
+
failures.push({ signal: "langfuse", error });
|
|
657
|
+
logger.error(`${LOG_PREFIX} Langfuse flush failed`, {
|
|
658
|
+
error: error instanceof Error ? error.message : String(error),
|
|
659
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
660
|
+
});
|
|
661
|
+
}
|
|
546
662
|
}
|
|
547
|
-
|
|
548
|
-
logger.
|
|
549
|
-
await langfuseProcessor.forceFlush();
|
|
550
|
-
logger.info(`${LOG_PREFIX} Successfully flushed spans to Langfuse`);
|
|
663
|
+
else {
|
|
664
|
+
logger.debug(`${LOG_PREFIX} Langfuse disabled, skipping Langfuse flush`);
|
|
551
665
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
557
|
-
|
|
666
|
+
if (tracerProvider && !usingExternalProvider) {
|
|
667
|
+
try {
|
|
668
|
+
logger.info(`${LOG_PREFIX} Flushing OTLP traces...`);
|
|
669
|
+
await tracerProvider.forceFlush();
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
failures.push({ signal: "traces", error });
|
|
673
|
+
logger.error(`${LOG_PREFIX} Trace flush failed`, {
|
|
674
|
+
error: error instanceof Error ? error.message : String(error),
|
|
675
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
558
678
|
}
|
|
679
|
+
else {
|
|
680
|
+
logger.debug(`${LOG_PREFIX} No TracerProvider to flush`);
|
|
681
|
+
}
|
|
682
|
+
if (meterProvider) {
|
|
683
|
+
try {
|
|
684
|
+
logger.info(`${LOG_PREFIX} Flushing OTLP metrics...`);
|
|
685
|
+
await meterProvider.forceFlush();
|
|
686
|
+
}
|
|
687
|
+
catch (error) {
|
|
688
|
+
failures.push({ signal: "metrics", error });
|
|
689
|
+
logger.error(`${LOG_PREFIX} Metric flush failed`, {
|
|
690
|
+
error: error instanceof Error ? error.message : String(error),
|
|
691
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
logger.debug(`${LOG_PREFIX} No MeterProvider to flush`);
|
|
697
|
+
}
|
|
698
|
+
if (loggerProvider) {
|
|
699
|
+
try {
|
|
700
|
+
logger.info(`${LOG_PREFIX} Flushing OTLP logs...`);
|
|
701
|
+
await loggerProvider.forceFlush();
|
|
702
|
+
}
|
|
703
|
+
catch (error) {
|
|
704
|
+
failures.push({ signal: "logs", error });
|
|
705
|
+
logger.error(`${LOG_PREFIX} Log flush failed`, {
|
|
706
|
+
error: error instanceof Error ? error.message : String(error),
|
|
707
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
logger.debug(`${LOG_PREFIX} No LoggerProvider to flush`);
|
|
713
|
+
}
|
|
714
|
+
if (failures.length > 0) {
|
|
715
|
+
throw new Error(`${LOG_PREFIX} Flush failed for: ${failures.map((f) => f.signal).join(", ")}`);
|
|
716
|
+
}
|
|
717
|
+
logger.info(`${LOG_PREFIX} Flush complete`);
|
|
559
718
|
}
|
|
560
719
|
/**
|
|
561
720
|
* Shutdown OpenTelemetry and Langfuse span processor
|
|
@@ -577,7 +736,17 @@ export async function shutdownOpenTelemetry() {
|
|
|
577
736
|
if (cachedContextEnricher) {
|
|
578
737
|
await cachedContextEnricher.shutdown();
|
|
579
738
|
}
|
|
739
|
+
// Shutdown MeterProvider if we created it
|
|
740
|
+
if (meterProvider) {
|
|
741
|
+
await meterProvider.shutdown();
|
|
742
|
+
}
|
|
743
|
+
// Shutdown LoggerProvider if we created it
|
|
744
|
+
if (loggerProvider) {
|
|
745
|
+
await loggerProvider.shutdown();
|
|
746
|
+
}
|
|
580
747
|
tracerProvider = null;
|
|
748
|
+
meterProvider = null;
|
|
749
|
+
loggerProvider = null;
|
|
581
750
|
langfuseProcessor = null;
|
|
582
751
|
cachedContextEnricher = null;
|
|
583
752
|
isInitialized = false;
|
|
@@ -603,6 +772,13 @@ export function getLangfuseSpanProcessor() {
|
|
|
603
772
|
export function getTracerProvider() {
|
|
604
773
|
return tracerProvider;
|
|
605
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Get the logger provider for emitting OTLP log records.
|
|
777
|
+
* Returns null if OTLP is not configured or LoggerProvider was not created.
|
|
778
|
+
*/
|
|
779
|
+
export function getLoggerProvider() {
|
|
780
|
+
return loggerProvider;
|
|
781
|
+
}
|
|
606
782
|
/**
|
|
607
783
|
* Check if OpenTelemetry is initialized
|
|
608
784
|
*/
|