@latitude-data/telemetry 3.0.0-alpha.0 → 3.0.0-alpha.2

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/dist/index.js CHANGED
@@ -1,3 +1,165 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/sdk/context.ts
9
+ import { context, createContextKey, trace } from "@opentelemetry/api";
10
+ var LATITUDE_CONTEXT_KEY = createContextKey("latitude-internal-context");
11
+ var CAPTURE_TRACER_NAME = "so.latitude.instrumentation.capture";
12
+ function getLatitudeContext(ctx) {
13
+ return ctx.getValue(LATITUDE_CONTEXT_KEY);
14
+ }
15
+ function mergeArrays(a, b) {
16
+ if (!a && !b) return void 0;
17
+ if (!a) return b;
18
+ if (!b) return a;
19
+ return [.../* @__PURE__ */ new Set([...a, ...b])];
20
+ }
21
+ function capture(name, fn, options = {}) {
22
+ const currentContext = context.active();
23
+ const existingData = getLatitudeContext(currentContext);
24
+ const mergedData = {
25
+ name: options.name ?? name,
26
+ tags: mergeArrays(existingData?.tags, options.tags),
27
+ metadata: { ...existingData?.metadata, ...options.metadata },
28
+ sessionId: options.sessionId ?? existingData?.sessionId,
29
+ userId: options.userId ?? existingData?.userId
30
+ };
31
+ const newContext = currentContext.setValue(LATITUDE_CONTEXT_KEY, mergedData);
32
+ const existingSpan = trace.getSpan(currentContext);
33
+ if (existingSpan) {
34
+ return context.with(newContext, fn);
35
+ }
36
+ const tracer = trace.getTracer(CAPTURE_TRACER_NAME);
37
+ return tracer.startActiveSpan(name, { attributes: { "latitude.capture.root": true } }, newContext, (span) => {
38
+ let result;
39
+ try {
40
+ result = fn();
41
+ } catch (error) {
42
+ span.recordException(error);
43
+ span.end();
44
+ throw error;
45
+ }
46
+ if (result instanceof Promise) {
47
+ return result.catch((error) => {
48
+ span.recordException(error);
49
+ throw error;
50
+ }).finally(() => {
51
+ span.end();
52
+ });
53
+ }
54
+ span.end();
55
+ return result;
56
+ });
57
+ }
58
+
59
+ // src/sdk/init.ts
60
+ import { context as context2, propagation } from "@opentelemetry/api";
61
+ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
62
+ import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from "@opentelemetry/core";
63
+ import { resourceFromAttributes } from "@opentelemetry/resources";
64
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
65
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
66
+
67
+ // src/sdk/instrumentations.ts
68
+ import { registerInstrumentations } from "@opentelemetry/instrumentation";
69
+ import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
70
+ import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
71
+ import { CohereInstrumentation } from "@traceloop/instrumentation-cohere";
72
+ import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
73
+ import { LlamaIndexInstrumentation } from "@traceloop/instrumentation-llamaindex";
74
+ import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
75
+ import { TogetherInstrumentation } from "@traceloop/instrumentation-together";
76
+ import { AIPlatformInstrumentation, VertexAIInstrumentation } from "@traceloop/instrumentation-vertexai";
77
+ var INSTRUMENTATION_MAP = {
78
+ openai: { ctor: OpenAIInstrumentation, moduleName: "openai", defaultEnrichTokens: true },
79
+ anthropic: { ctor: AnthropicInstrumentation, moduleName: "@anthropic-ai/sdk" },
80
+ bedrock: { ctor: BedrockInstrumentation, moduleName: "@aws-sdk/client-bedrock-runtime" },
81
+ cohere: { ctor: CohereInstrumentation, moduleName: "cohere-ai" },
82
+ langchain: { ctor: LangChainInstrumentation, moduleName: "langchain" },
83
+ llamaindex: { ctor: LlamaIndexInstrumentation, moduleName: "llamaindex" },
84
+ togetherai: { ctor: TogetherInstrumentation, moduleName: "together-ai", defaultEnrichTokens: false },
85
+ vertexai: { ctor: VertexAIInstrumentation, moduleName: "@google-cloud/vertexai" },
86
+ aiplatform: { ctor: AIPlatformInstrumentation, moduleName: "@google-cloud/aiplatform" }
87
+ };
88
+ async function createLatitudeInstrumentations(options) {
89
+ const result = [];
90
+ for (const type of options.instrumentations) {
91
+ const config = INSTRUMENTATION_MAP[type];
92
+ if (!config) {
93
+ console.warn(`[Latitude] Unknown instrumentation type: ${type}`);
94
+ continue;
95
+ }
96
+ const enrichTokens = options.enrichTokens?.[type] ?? config.defaultEnrichTokens;
97
+ const inst = new config.ctor(enrichTokens !== void 0 ? { enrichTokens } : void 0);
98
+ const moduleRef = options.modules?.[type] ?? await tryRequire(config.moduleName);
99
+ if (!moduleRef) {
100
+ console.warn(
101
+ `[Latitude] Module not found for ${type}: ${config.moduleName}. Install it or pass it explicitly in 'modules'.`
102
+ );
103
+ continue;
104
+ }
105
+ inst.manuallyInstrument?.(moduleRef);
106
+ result.push(inst);
107
+ }
108
+ return result;
109
+ }
110
+ async function tryRequire(moduleName) {
111
+ try {
112
+ return __require(moduleName);
113
+ } catch {
114
+ try {
115
+ const mod = await import(moduleName);
116
+ return mod.default ?? mod;
117
+ } catch {
118
+ return void 0;
119
+ }
120
+ }
121
+ }
122
+ async function registerLatitudeInstrumentations(options) {
123
+ const instrumentations = await createLatitudeInstrumentations(options);
124
+ registerInstrumentations({
125
+ instrumentations,
126
+ tracerProvider: options.tracerProvider
127
+ });
128
+ }
129
+
130
+ // src/sdk/processor.ts
131
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
132
+ import {
133
+ BatchSpanProcessor,
134
+ SimpleSpanProcessor
135
+ } from "@opentelemetry/sdk-trace-node";
136
+
137
+ // src/constants/attributes.ts
138
+ var ATTRIBUTES = {
139
+ name: "latitude.capture.name",
140
+ tags: "latitude.tags",
141
+ metadata: "latitude.metadata",
142
+ sessionId: "session.id",
143
+ userId: "user.id"
144
+ };
145
+
146
+ // src/constants/scope.ts
147
+ var SCOPE_LATITUDE = "so.latitude.instrumentation";
148
+
149
+ // src/env/env.ts
150
+ var DEFAULT_EXPORTER_URL = {
151
+ production: "https://ingest.latitude.so",
152
+ development: "http://localhost:3002",
153
+ test: "http://localhost:3002"
154
+ }[process.env.NODE_ENV ?? "development"] ?? "http://localhost:3002";
155
+ function getExporterUrl() {
156
+ if (process.env.LATITUDE_TELEMETRY_URL) {
157
+ return process.env.LATITUDE_TELEMETRY_URL;
158
+ }
159
+ return DEFAULT_EXPORTER_URL;
160
+ }
161
+ var env = { EXPORTER_URL: getExporterUrl() };
162
+
1
163
  // src/sdk/redact.ts
2
164
  var RedactSpanProcessor = class {
3
165
  options;
@@ -48,349 +210,276 @@ var RedactSpanProcessor = class {
48
210
  };
49
211
  var DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
50
212
  attributes: [
213
+ // HTTP security headers
51
214
  /^http\.request\.header\.authorization$/i,
52
215
  /^http\.request\.header\.cookie$/i,
53
216
  /^http\.request\.header\.x[-_]api[-_]key$/i,
217
+ // Database statements may contain sensitive data
54
218
  /^db\.statement$/i
55
219
  ]
56
220
  });
57
221
 
58
- // src/sdk/sdk.ts
59
- import * as otel2 from "@opentelemetry/api";
60
- import { context, propagation as propagation2 } from "@opentelemetry/api";
61
- import { ALLOW_ALL_BAGGAGE_KEYS, BaggageSpanProcessor } from "@opentelemetry/baggage-span-processor";
62
- import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
63
- import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from "@opentelemetry/core";
64
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
65
- import { registerInstrumentations } from "@opentelemetry/instrumentation";
66
- import { Resource } from "@opentelemetry/resources";
67
- import {
68
- BatchSpanProcessor,
69
- NodeTracerProvider,
70
- SimpleSpanProcessor
71
- } from "@opentelemetry/sdk-trace-node";
72
- import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
73
- import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
74
- import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
75
- import { CohereInstrumentation } from "@traceloop/instrumentation-cohere";
76
- import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
77
- import { LlamaIndexInstrumentation } from "@traceloop/instrumentation-llamaindex";
78
- import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
79
- import { TogetherInstrumentation } from "@traceloop/instrumentation-together";
80
- import { AIPlatformInstrumentation, VertexAIInstrumentation } from "@traceloop/instrumentation-vertexai";
81
-
82
- // src/constants/attributes.ts
83
- var ATTRIBUTES = {
84
- tags: "latitude.tags",
85
- metadata: "latitude.metadata",
86
- sessionId: "session.id",
87
- userId: "user.id"
88
- };
89
-
90
- // src/constants/scope.ts
91
- var SCOPE_LATITUDE = "so.latitude.instrumentation";
92
-
93
- // src/env/env.ts
94
- var DEFAULT_EXPORTER_URL = {
95
- production: "https://ingest.latitude.so",
96
- development: "http://localhost:3002",
97
- test: "http://localhost:3002"
98
- }[process.env.NODE_ENV ?? "development"] ?? "http://localhost:3002";
99
- function getExporterUrl() {
100
- if (process.env.LATITUDE_TELEMETRY_URL) {
101
- return process.env.LATITUDE_TELEMETRY_URL;
102
- }
103
- return DEFAULT_EXPORTER_URL;
222
+ // src/sdk/span-filter.ts
223
+ var GEN_AI_PREFIX = "gen_ai.";
224
+ var LLM_PREFIX = "llm.";
225
+ var OPENINFERENCE_KIND = "openinference.span.kind";
226
+ var OTEL_LLM_INSTRUMENTATION_SCOPE_PREFIXES = [
227
+ "opentelemetry.instrumentation.alephalpha",
228
+ "opentelemetry.instrumentation.anthropic",
229
+ "opentelemetry.instrumentation.bedrock",
230
+ "opentelemetry.instrumentation.cohere",
231
+ "opentelemetry.instrumentation.crewai",
232
+ "opentelemetry.instrumentation.google_generativeai",
233
+ "opentelemetry.instrumentation.groq",
234
+ "opentelemetry.instrumentation.haystack",
235
+ "opentelemetry.instrumentation.langchain",
236
+ "opentelemetry.instrumentation.llamaindex",
237
+ "opentelemetry.instrumentation.mistralai",
238
+ "opentelemetry.instrumentation.ollama",
239
+ "opentelemetry.instrumentation.openai",
240
+ "opentelemetry.instrumentation.replicate",
241
+ "opentelemetry.instrumentation.sagemaker",
242
+ "opentelemetry.instrumentation.together",
243
+ "opentelemetry.instrumentation.transformers",
244
+ "opentelemetry.instrumentation.vertexai",
245
+ "opentelemetry.instrumentation.watsonx",
246
+ "openinference.instrumentation"
247
+ ];
248
+ var LLM_SCOPE_SUBSTRINGS = ["openinference", "traceloop", "langsmith", "litellm"];
249
+ function buildShouldExportSpanFromFields(fields) {
250
+ return buildShouldExportSpan({
251
+ ...fields.disableSmartFilter !== void 0 ? { disableSmartFilter: fields.disableSmartFilter } : {},
252
+ ...fields.shouldExportSpan !== void 0 ? { shouldExportSpan: fields.shouldExportSpan } : {},
253
+ ...fields.blockedInstrumentationScopes !== void 0 ? { blockedInstrumentationScopes: fields.blockedInstrumentationScopes } : {}
254
+ });
104
255
  }
105
- var env = { EXPORTER_URL: getExporterUrl() };
106
-
107
- // src/instrumentations/manual.ts
108
- import * as otel from "@opentelemetry/api";
109
- import { propagation, trace } from "@opentelemetry/api";
110
- var ManualInstrumentation = class {
111
- enabled;
112
- _tracer;
113
- constructor(tracer) {
114
- this.enabled = false;
115
- this._tracer = tracer;
116
- }
117
- get tracer() {
118
- return this._tracer;
119
- }
120
- isEnabled() {
121
- return this.enabled;
122
- }
123
- enable() {
124
- this.enabled = true;
125
- }
126
- disable() {
127
- this.enabled = false;
128
- }
129
- resume(ctx) {
130
- const parts = ctx.traceparent.split("-");
131
- if (parts.length !== 4) {
132
- return otel.ROOT_CONTEXT;
133
- }
134
- const [, traceId, spanId, flags] = parts;
135
- if (!traceId || !spanId) {
136
- return otel.ROOT_CONTEXT;
137
- }
138
- const spanContext = {
139
- traceId,
140
- spanId,
141
- traceFlags: parseInt(flags ?? "01", 16),
142
- isRemote: true
143
- };
144
- let context2 = trace.setSpanContext(otel.ROOT_CONTEXT, spanContext);
145
- if (ctx.baggage) {
146
- const baggageEntries = {};
147
- for (const pair of ctx.baggage.split(",")) {
148
- const [key, value] = pair.split("=", 2);
149
- if (key && value) {
150
- baggageEntries[decodeURIComponent(key)] = { value: decodeURIComponent(value) };
151
- }
152
- }
153
- const baggage = propagation.createBaggage(baggageEntries);
154
- context2 = propagation.setBaggage(context2, baggage);
155
- }
156
- return context2;
256
+ function attributeKeys(span) {
257
+ const attrs = span.attributes;
258
+ if (!attrs || typeof attrs !== "object") return [];
259
+ return Object.keys(attrs);
260
+ }
261
+ function instrumentationScopeName(span) {
262
+ return span.instrumentationScope?.name ?? "";
263
+ }
264
+ function isGenAiOrLlmAttributeSpan(span) {
265
+ for (const key of attributeKeys(span)) {
266
+ if (key.startsWith(GEN_AI_PREFIX) || key.startsWith(LLM_PREFIX)) return true;
267
+ if (key === OPENINFERENCE_KIND || key.startsWith("openinference.")) return true;
268
+ if (key.startsWith("ai.")) return true;
269
+ if (key.startsWith("latitude.")) return true;
270
+ }
271
+ return false;
272
+ }
273
+ function isLatitudeInstrumentationSpan(span) {
274
+ const name = instrumentationScopeName(span);
275
+ return name === SCOPE_LATITUDE || name.startsWith(`${SCOPE_LATITUDE}.`);
276
+ }
277
+ function isKnownLlmInstrumentationScope(span) {
278
+ const name = instrumentationScopeName(span);
279
+ if (!name) return false;
280
+ for (const prefix of OTEL_LLM_INSTRUMENTATION_SCOPE_PREFIXES) {
281
+ if (name === prefix || name.startsWith(`${prefix}.`)) return true;
157
282
  }
158
- };
159
-
160
- // src/sdk/sdk.ts
161
- var TRACES_URL = `${env.EXPORTER_URL}/v1/traces`;
162
- var SERVICE_NAME = process.env.npm_package_name || "unknown";
163
- var SCOPE_VERSION = process.env.npm_package_version || "unknown";
164
- var ContextManager = class {
165
- telemetry;
166
- constructor(telemetry) {
167
- this.telemetry = telemetry;
283
+ const lower = name.toLowerCase();
284
+ for (const part of LLM_SCOPE_SUBSTRINGS) {
285
+ if (lower.includes(part)) return true;
168
286
  }
169
- resume(ctx) {
170
- return this.telemetry.resume(ctx);
287
+ return false;
288
+ }
289
+ function isDefaultExportSpan(span) {
290
+ if (isLatitudeInstrumentationSpan(span)) return true;
291
+ if (isGenAiOrLlmAttributeSpan(span)) return true;
292
+ if (isKnownLlmInstrumentationScope(span)) return true;
293
+ return false;
294
+ }
295
+ function buildShouldExportSpan(options) {
296
+ if (options.disableSmartFilter) return () => true;
297
+ const blocked = new Set(options.blockedInstrumentationScopes ?? []);
298
+ const extra = options.shouldExportSpan;
299
+ return (span) => {
300
+ const scope = instrumentationScopeName(span);
301
+ if (blocked.has(scope)) return false;
302
+ if (isDefaultExportSpan(span)) return true;
303
+ if (extra?.(span)) return true;
304
+ return false;
305
+ };
306
+ }
307
+ var ExportFilterSpanProcessor = class {
308
+ shouldExport;
309
+ inner;
310
+ constructor(shouldExport, inner) {
311
+ this.shouldExport = shouldExport;
312
+ this.inner = inner;
171
313
  }
172
- active() {
173
- return context.active();
314
+ onStart(span, parentContext) {
315
+ this.inner.onStart(span, parentContext);
174
316
  }
175
- with(ctx, fn, thisArg, ...args) {
176
- return context.with(ctx, fn, thisArg, ...args);
317
+ onEnd(span) {
318
+ if (!this.shouldExport(span)) return;
319
+ this.inner.onEnd(span);
177
320
  }
178
- };
179
- var InstrumentationManager = class {
180
- instrumentations;
181
- constructor(instrumentations) {
182
- this.instrumentations = instrumentations;
183
- }
184
- enable() {
185
- for (const instrumentation of this.instrumentations) {
186
- if (!instrumentation.isEnabled()) instrumentation.enable();
187
- }
321
+ forceFlush() {
322
+ return this.inner.forceFlush();
188
323
  }
189
- disable() {
190
- for (const instrumentation of this.instrumentations) {
191
- if (instrumentation.isEnabled()) instrumentation.disable();
192
- }
324
+ shutdown() {
325
+ return this.inner.shutdown();
193
326
  }
194
327
  };
195
- var TracerManager = class {
196
- nodeProvider;
197
- scopeVersion;
198
- constructor(nodeProvider, scopeVersion) {
199
- this.nodeProvider = nodeProvider;
200
- this.scopeVersion = scopeVersion;
328
+ var RedactThenExportSpanProcessor = class {
329
+ redact;
330
+ exportProcessor;
331
+ constructor(redact, exportProcessor) {
332
+ this.redact = redact;
333
+ this.exportProcessor = exportProcessor;
201
334
  }
202
- get(scope) {
203
- return this.provider(scope).getTracer("");
335
+ onStart(span, parentContext) {
336
+ this.redact?.onStart(span, parentContext);
337
+ this.exportProcessor.onStart(span, parentContext);
204
338
  }
205
- provider(scope) {
206
- return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${scope}`, this.scopeVersion, this.nodeProvider);
207
- }
208
- };
209
- var ScopedTracerProvider = class {
210
- scope;
211
- version;
212
- provider;
213
- constructor(scope, version, provider) {
214
- this.scope = scope;
215
- this.version = version;
216
- this.provider = provider;
217
- }
218
- getTracer(_name, _version, options) {
219
- return this.provider.getTracer(this.scope, this.version, options);
220
- }
221
- };
222
- var LifecycleManager = class {
223
- nodeProvider;
224
- exporter;
225
- constructor(nodeProvider, exporter) {
226
- this.nodeProvider = nodeProvider;
227
- this.exporter = exporter;
339
+ onEnd(span) {
340
+ this.redact?.onEnd(span);
341
+ this.exportProcessor.onEnd(span);
228
342
  }
229
- async flush() {
230
- await this.nodeProvider.forceFlush();
231
- await this.exporter.forceFlush?.();
343
+ forceFlush() {
344
+ return this.exportProcessor.forceFlush();
232
345
  }
233
- async shutdown() {
234
- await this.flush();
235
- await this.nodeProvider.shutdown();
236
- await this.exporter.shutdown?.();
346
+ shutdown() {
347
+ return this.exportProcessor.shutdown();
237
348
  }
238
349
  };
239
- var DEFAULT_SPAN_EXPORTER = (apiKey, projectSlug) => new OTLPTraceExporter({
240
- url: TRACES_URL,
241
- headers: {
242
- Authorization: `Bearer ${apiKey}`,
243
- "Content-Type": "application/json",
244
- "X-Latitude-Project": projectSlug
245
- },
246
- timeoutMillis: 30 * 1e3
247
- });
248
- var Instrumentation = /* @__PURE__ */ ((Instrumentation2) => {
249
- Instrumentation2["Anthropic"] = "anthropic";
250
- Instrumentation2["AIPlatform"] = "aiplatform";
251
- Instrumentation2["Bedrock"] = "bedrock";
252
- Instrumentation2["Cohere"] = "cohere";
253
- Instrumentation2["Langchain"] = "langchain";
254
- Instrumentation2["LlamaIndex"] = "llamaindex";
255
- Instrumentation2["Manual"] = "manual";
256
- Instrumentation2["OpenAI"] = "openai";
257
- Instrumentation2["TogetherAI"] = "togetherai";
258
- Instrumentation2["VertexAI"] = "vertexai";
259
- return Instrumentation2;
260
- })(Instrumentation || {});
261
- var LatitudeTelemetry = class {
262
- options;
263
- nodeProvider;
264
- instrumentationsList;
265
- /** OpenTelemetry tracer for creating custom spans. */
266
- tracer;
267
- context;
268
- instrumentation;
269
- lifecycle;
350
+
351
+ // src/sdk/processor.ts
352
+ var LatitudeSpanProcessor = class {
353
+ tail;
270
354
  constructor(apiKey, projectSlug, options) {
271
- this.options = options || {};
272
- if (!this.options.exporter) {
273
- this.options.exporter = DEFAULT_SPAN_EXPORTER(apiKey, projectSlug);
274
- }
275
- context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
276
- propagation2.setGlobalPropagator(
277
- new CompositePropagator({
278
- propagators: [...this.options.propagators || [], new W3CTraceContextPropagator(), new W3CBaggagePropagator()]
279
- })
280
- );
281
- const spanProcessors = [
282
- // Must run before the exporter span processors
283
- new BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS),
284
- ...this.options.processors ?? [DEFAULT_REDACT_SPAN_PROCESSOR()],
285
- this.options.disableBatch ? new SimpleSpanProcessor(this.options.exporter) : new BatchSpanProcessor(this.options.exporter)
286
- ];
287
- this.nodeProvider = new NodeTracerProvider({
288
- resource: new Resource({ [ATTR_SERVICE_NAME]: this.options.serviceName || SERVICE_NAME }),
289
- spanProcessors
290
- });
291
- this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
292
- this.nodeProvider.register();
293
- process.on("SIGTERM", () => this.shutdown());
294
- process.on("SIGINT", () => this.shutdown());
295
- this.instrumentationsList = [];
296
- const tracerManager = new TracerManager(this.nodeProvider, SCOPE_VERSION);
297
- const manualTracer = tracerManager.get("manual" /* Manual */);
298
- const manualInstrumentation = new ManualInstrumentation(manualTracer);
299
- this.instrumentationsList.push(manualInstrumentation);
300
- this.tracer = manualTracer;
301
- this.initProviderInstrumentations(tracerManager);
302
- this.instrumentation = new InstrumentationManager(this.instrumentationsList);
303
- this.instrumentation.enable();
304
- this.context = new ContextManager(manualInstrumentation);
305
- }
306
- async flush() {
307
- await this.lifecycle.flush();
308
- }
309
- async shutdown() {
310
- await this.lifecycle.shutdown();
311
- }
312
- initProviderInstrumentations(tracerManager) {
313
- const configure = (type, Ctor, opts) => {
314
- const providerPkg = this.options.instrumentations?.[type];
315
- if (!providerPkg) return;
316
- const provider = tracerManager.provider(type);
317
- const inst = new Ctor(opts);
318
- inst.setTracerProvider(provider);
319
- inst.manuallyInstrument(providerPkg);
320
- registerInstrumentations({
321
- instrumentations: [inst],
322
- tracerProvider: provider
323
- });
324
- this.instrumentationsList.push(inst);
325
- };
326
- configure("anthropic" /* Anthropic */, AnthropicInstrumentation);
327
- configure("aiplatform" /* AIPlatform */, AIPlatformInstrumentation);
328
- configure("bedrock" /* Bedrock */, BedrockInstrumentation);
329
- configure("cohere" /* Cohere */, CohereInstrumentation);
330
- configure("langchain" /* Langchain */, LangChainInstrumentation);
331
- configure("llamaindex" /* LlamaIndex */, LlamaIndexInstrumentation);
332
- configure("openai" /* OpenAI */, OpenAIInstrumentation, { enrichTokens: true });
333
- configure("togetherai" /* TogetherAI */, TogetherInstrumentation, { enrichTokens: false });
334
- configure("vertexai" /* VertexAI */, VertexAIInstrumentation);
335
- }
336
- /**
337
- * Wrap a block of code with trace-wide context attributes.
338
- * Baggage entries (tags, metadata, sessionId, userId) are propagated
339
- * to all spans created within the callback via BaggageSpanProcessor.
340
- *
341
- * If there is no active span, a root span is created so all child spans
342
- * are grouped under a single trace. If a span is already active, only
343
- * baggage is set without creating an extra wrapper span.
344
- */
345
- async capture(options, fn) {
346
- const baggageEntries = {};
347
- if (options.tags?.length) {
348
- baggageEntries[ATTRIBUTES.tags] = { value: JSON.stringify(options.tags) };
349
- }
350
- if (options.metadata) {
351
- baggageEntries[ATTRIBUTES.metadata] = { value: JSON.stringify(options.metadata) };
352
- }
353
- if (options.sessionId) {
354
- baggageEntries[ATTRIBUTES.sessionId] = { value: options.sessionId };
355
+ if (!apiKey || apiKey.trim() === "") {
356
+ throw new Error("[Latitude] apiKey is required and cannot be empty");
355
357
  }
356
- if (options.userId) {
357
- baggageEntries[ATTRIBUTES.userId] = { value: options.userId };
358
+ if (!projectSlug || projectSlug.trim() === "") {
359
+ throw new Error("[Latitude] projectSlug is required and cannot be empty");
358
360
  }
359
- const baggage = propagation2.createBaggage(baggageEntries);
360
- const activeSpan = otel2.trace.getSpan(context.active());
361
- if (activeSpan) {
362
- const ctx = propagation2.setBaggage(context.active(), baggage);
363
- return await context.with(ctx, async () => await fn(ctx));
364
- }
365
- const rootContext = propagation2.setBaggage(otel2.ROOT_CONTEXT, baggage);
366
- return await this.tracer.startActiveSpan(
367
- "latitude.trace",
368
- { kind: otel2.SpanKind.INTERNAL },
369
- rootContext,
370
- async (span) => {
371
- try {
372
- const result = await fn(context.active());
373
- span.setStatus({ code: otel2.SpanStatusCode.OK });
374
- return result;
375
- } catch (error) {
376
- span.setStatus({
377
- code: otel2.SpanStatusCode.ERROR,
378
- message: error instanceof Error ? error.message : String(error)
379
- });
380
- span.recordException(error instanceof Error ? error : new Error(String(error)));
381
- throw error;
382
- } finally {
383
- span.end();
361
+ const exporter = options?.exporter ?? new OTLPTraceExporter({
362
+ url: `${env.EXPORTER_URL}/v1/traces`,
363
+ headers: {
364
+ Authorization: `Bearer ${apiKey}`,
365
+ "Content-Type": "application/json",
366
+ "X-Latitude-Project": projectSlug
367
+ },
368
+ timeoutMillis: 3e4
369
+ });
370
+ const redact = options?.disableRedact ? null : options?.redact ? new RedactSpanProcessor(options.redact) : DEFAULT_REDACT_SPAN_PROCESSOR();
371
+ const batchOrSimple = options?.disableBatch ? new SimpleSpanProcessor(exporter) : new BatchSpanProcessor(exporter);
372
+ const shouldExport = buildShouldExportSpanFromFields({
373
+ disableSmartFilter: options?.disableSmartFilter,
374
+ shouldExportSpan: options?.shouldExportSpan,
375
+ blockedInstrumentationScopes: options?.blockedInstrumentationScopes
376
+ });
377
+ const redactThenExport = new RedactThenExportSpanProcessor(redact, batchOrSimple);
378
+ this.tail = new ExportFilterSpanProcessor(shouldExport, redactThenExport);
379
+ }
380
+ onStart(span, parentContext) {
381
+ const latitudeData = getLatitudeContext(parentContext);
382
+ if (latitudeData) {
383
+ if (latitudeData.name) {
384
+ span.setAttribute(ATTRIBUTES.name, latitudeData.name);
385
+ if (span.attributes["latitude.capture.root"]) {
386
+ span.updateName(latitudeData.name);
384
387
  }
385
388
  }
386
- );
389
+ if (latitudeData.tags && latitudeData.tags.length > 0) {
390
+ span.setAttribute(ATTRIBUTES.tags, JSON.stringify(latitudeData.tags));
391
+ }
392
+ if (latitudeData.metadata && Object.keys(latitudeData.metadata).length > 0) {
393
+ span.setAttribute(ATTRIBUTES.metadata, JSON.stringify(latitudeData.metadata));
394
+ }
395
+ if (latitudeData.sessionId) {
396
+ span.setAttribute(ATTRIBUTES.sessionId, latitudeData.sessionId);
397
+ }
398
+ if (latitudeData.userId) {
399
+ span.setAttribute(ATTRIBUTES.userId, latitudeData.userId);
400
+ }
401
+ }
402
+ this.tail.onStart(span, parentContext);
403
+ }
404
+ onEnd(span) {
405
+ this.tail.onEnd(span);
406
+ }
407
+ async forceFlush() {
408
+ await this.tail.forceFlush();
409
+ }
410
+ async shutdown() {
411
+ await this.tail.shutdown();
387
412
  }
388
413
  };
414
+
415
+ // src/sdk/init.ts
416
+ var SERVICE_NAME = process.env.npm_package_name || "unknown";
417
+ var shutdownHandlersRegistered = false;
418
+ function initLatitude(options) {
419
+ const { apiKey, projectSlug, instrumentations = [], ...processorOptions } = options;
420
+ if (!apiKey || apiKey.trim() === "") {
421
+ throw new Error("[Latitude] apiKey is required and cannot be empty");
422
+ }
423
+ if (!projectSlug || projectSlug.trim() === "") {
424
+ throw new Error("[Latitude] projectSlug is required and cannot be empty");
425
+ }
426
+ const contextManager = new AsyncLocalStorageContextManager();
427
+ contextManager.enable();
428
+ const propagator = new CompositePropagator({
429
+ propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()]
430
+ });
431
+ context2.setGlobalContextManager(contextManager);
432
+ propagation.setGlobalPropagator(propagator);
433
+ const provider = new NodeTracerProvider({
434
+ resource: resourceFromAttributes({
435
+ [ATTR_SERVICE_NAME]: SERVICE_NAME
436
+ }),
437
+ spanProcessors: [new LatitudeSpanProcessor(apiKey, projectSlug, processorOptions)]
438
+ });
439
+ provider.register();
440
+ const ready = registerLatitudeInstrumentations({
441
+ instrumentations,
442
+ tracerProvider: provider
443
+ }).catch((err) => {
444
+ console.warn("[Latitude] Failed to register instrumentations:", err);
445
+ });
446
+ const shutdown = async () => {
447
+ await provider.shutdown();
448
+ };
449
+ const flush = async () => {
450
+ await provider.forceFlush();
451
+ };
452
+ const handleShutdown = async () => {
453
+ try {
454
+ await shutdown();
455
+ } catch (err) {
456
+ console.error("Error during Latitude Telemetry shutdown:", err);
457
+ }
458
+ };
459
+ if (!shutdownHandlersRegistered) {
460
+ process.once("SIGTERM", handleShutdown);
461
+ process.once("SIGINT", handleShutdown);
462
+ shutdownHandlersRegistered = true;
463
+ }
464
+ return {
465
+ provider,
466
+ flush,
467
+ shutdown,
468
+ ready
469
+ };
470
+ }
389
471
  export {
390
- DEFAULT_REDACT_SPAN_PROCESSOR,
391
- DEFAULT_SPAN_EXPORTER,
392
- Instrumentation,
393
- LatitudeTelemetry,
394
- RedactSpanProcessor
472
+ ExportFilterSpanProcessor,
473
+ LatitudeSpanProcessor,
474
+ RedactSpanProcessor,
475
+ RedactThenExportSpanProcessor,
476
+ buildShouldExportSpan,
477
+ buildShouldExportSpanFromFields,
478
+ capture,
479
+ initLatitude,
480
+ isDefaultExportSpan,
481
+ isGenAiOrLlmAttributeSpan,
482
+ isLatitudeInstrumentationSpan,
483
+ registerLatitudeInstrumentations
395
484
  };
396
485
  //# sourceMappingURL=index.js.map