@pingops/otel 0.1.0

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.
Files changed (42) hide show
  1. package/README.md +93 -0
  2. package/dist/config.d.ts +75 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +5 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/index.d.ts +10 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +9 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/instrumentations/body-extractor.d.ts +48 -0
  11. package/dist/instrumentations/body-extractor.d.ts.map +1 -0
  12. package/dist/instrumentations/body-extractor.js +361 -0
  13. package/dist/instrumentations/body-extractor.js.map +1 -0
  14. package/dist/instrumentations/http.d.ts +12 -0
  15. package/dist/instrumentations/http.d.ts.map +1 -0
  16. package/dist/instrumentations/http.js +38 -0
  17. package/dist/instrumentations/http.js.map +1 -0
  18. package/dist/instrumentations/index.d.ts +17 -0
  19. package/dist/instrumentations/index.d.ts.map +1 -0
  20. package/dist/instrumentations/index.js +32 -0
  21. package/dist/instrumentations/index.js.map +1 -0
  22. package/dist/instrumentations/undici.d.ts +12 -0
  23. package/dist/instrumentations/undici.d.ts.map +1 -0
  24. package/dist/instrumentations/undici.js +38 -0
  25. package/dist/instrumentations/undici.js.map +1 -0
  26. package/dist/processor.d.ts +82 -0
  27. package/dist/processor.d.ts.map +1 -0
  28. package/dist/processor.js +264 -0
  29. package/dist/processor.js.map +1 -0
  30. package/dist/span-processor.d.ts +78 -0
  31. package/dist/span-processor.d.ts.map +1 -0
  32. package/dist/span-processor.js +272 -0
  33. package/dist/span-processor.js.map +1 -0
  34. package/dist/span-wrapper.d.ts +60 -0
  35. package/dist/span-wrapper.d.ts.map +1 -0
  36. package/dist/span-wrapper.js +118 -0
  37. package/dist/span-wrapper.js.map +1 -0
  38. package/dist/tracer-provider.d.ts +57 -0
  39. package/dist/tracer-provider.d.ts.map +1 -0
  40. package/dist/tracer-provider.js +182 -0
  41. package/dist/tracer-provider.js.map +1 -0
  42. package/package.json +43 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * HTTP instrumentation for OpenTelemetry
3
+ */
4
+ import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
5
+ import { context } from "@opentelemetry/api";
6
+ import { PINGOPS_HTTP_ENABLED } from "@pingops/core";
7
+ /**
8
+ * Creates an HTTP instrumentation instance
9
+ *
10
+ * @param isGlobalInstrumentationEnabled - Function that checks if global instrumentation is enabled
11
+ * @returns HttpInstrumentation instance
12
+ */
13
+ export function createHttpInstrumentation(isGlobalInstrumentationEnabled) {
14
+ return new HttpInstrumentation({
15
+ ignoreIncomingRequestHook: () => true, // Only instrument outgoing requests
16
+ ignoreOutgoingRequestHook: () => {
17
+ // If global instrumentation is enabled, instrument all outgoing requests
18
+ if (isGlobalInstrumentationEnabled()) {
19
+ return false;
20
+ }
21
+ // If global instrumentation is NOT enabled, only instrument when PINGOPS_HTTP_ENABLED is true
22
+ return context.active().getValue(PINGOPS_HTTP_ENABLED) !== true;
23
+ },
24
+ requestHook: (span, request) => {
25
+ const headers = request.headers;
26
+ for (const [key, value] of Object.entries(headers)) {
27
+ span.setAttribute(`http.request.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(",") : String(value));
28
+ }
29
+ },
30
+ responseHook: (span, response) => {
31
+ const headers = response.headers;
32
+ for (const [key, value] of Object.entries(headers)) {
33
+ span.setAttribute(`http.response.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(",") : String(value));
34
+ }
35
+ },
36
+ });
37
+ }
38
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/instrumentations/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAIrD;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,8BAA6C;IAE7C,OAAO,IAAI,mBAAmB,CAAC;QAC7B,yBAAyB,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,oCAAoC;QAC3E,yBAAyB,EAAE,GAAG,EAAE;YAC9B,yEAAyE;YACzE,IAAI,8BAA8B,EAAE,EAAE,CAAC;gBACrC,OAAO,KAAK,CAAC;YACf,CAAC;YACD,8FAA8F;YAC9F,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC,KAAK,IAAI,CAAC;QAClE,CAAC;QACD,WAAW,EAAE,CAAC,IAAU,EAAE,OAAwC,EAAE,EAAE;YACpE,MAAM,OAAO,GAAI,OAA2B,CAAC,OAAO,CAAC;YAErD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,YAAY,CACf,uBAAuB,GAAG,CAAC,WAAW,EAAE,EAAE,EAC1C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACJ,CAAC;QACH,CAAC;QACD,YAAY,EAAE,CAAC,IAAU,EAAE,QAA0C,EAAE,EAAE;YACvE,MAAM,OAAO,GAAI,QAA4B,CAAC,OAAO,CAAC;YACtD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,YAAY,CACf,wBAAwB,GAAG,CAAC,WAAW,EAAE,EAAE,EAC3C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Instrumentation setup for HTTP, fetch, undici, and GenAI
3
+ */
4
+ import type { Instrumentation } from "@opentelemetry/instrumentation";
5
+ /**
6
+ * Registers instrumentations for Node.js environment.
7
+ * This function is idempotent and can be called multiple times safely.
8
+ *
9
+ * Instrumentation behavior:
10
+ * - If global instrumentation is enabled: all HTTP requests are instrumented
11
+ * - If global instrumentation is NOT enabled: only requests within wrapHttp blocks are instrumented
12
+ *
13
+ * @param isGlobalInstrumentationEnabled - Function that checks if global instrumentation is enabled
14
+ * @returns Array of Instrumentation instances
15
+ */
16
+ export declare function getInstrumentations(isGlobalInstrumentationEnabled: () => boolean): Instrumentation[];
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/instrumentations/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAMtE;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,8BAA8B,EAAE,MAAM,OAAO,GAC5C,eAAe,EAAE,CAcnB"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Instrumentation setup for HTTP, fetch, undici, and GenAI
3
+ */
4
+ import { registerInstrumentations } from "@opentelemetry/instrumentation";
5
+ import { createHttpInstrumentation } from "./http";
6
+ import { createUndiciInstrumentation } from "./undici";
7
+ let installed = false;
8
+ /**
9
+ * Registers instrumentations for Node.js environment.
10
+ * This function is idempotent and can be called multiple times safely.
11
+ *
12
+ * Instrumentation behavior:
13
+ * - If global instrumentation is enabled: all HTTP requests are instrumented
14
+ * - If global instrumentation is NOT enabled: only requests within wrapHttp blocks are instrumented
15
+ *
16
+ * @param isGlobalInstrumentationEnabled - Function that checks if global instrumentation is enabled
17
+ * @returns Array of Instrumentation instances
18
+ */
19
+ export function getInstrumentations(isGlobalInstrumentationEnabled) {
20
+ if (installed) {
21
+ return [];
22
+ }
23
+ registerInstrumentations({
24
+ instrumentations: [
25
+ createHttpInstrumentation(isGlobalInstrumentationEnabled),
26
+ createUndiciInstrumentation(isGlobalInstrumentationEnabled),
27
+ ],
28
+ });
29
+ installed = true;
30
+ return [];
31
+ }
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/instrumentations/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAAE,yBAAyB,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEvD,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,8BAA6C;IAE7C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,wBAAwB,CAAC;QACvB,gBAAgB,EAAE;YAChB,yBAAyB,CAAC,8BAA8B,CAAC;YACzD,2BAA2B,CAAC,8BAA8B,CAAC;SAC5D;KACF,CAAC,CAAC;IAEH,SAAS,GAAG,IAAI,CAAC;IACjB,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Undici instrumentation for OpenTelemetry
3
+ */
4
+ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
5
+ /**
6
+ * Creates an Undici instrumentation instance
7
+ *
8
+ * @param isGlobalInstrumentationEnabled - Function that checks if global instrumentation is enabled
9
+ * @returns UndiciInstrumentation instance
10
+ */
11
+ export declare function createUndiciInstrumentation(isGlobalInstrumentationEnabled: () => boolean): UndiciInstrumentation;
12
+ //# sourceMappingURL=undici.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undici.d.ts","sourceRoot":"","sources":["../../src/instrumentations/undici.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAK9E;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,8BAA8B,EAAE,MAAM,OAAO,GAC5C,qBAAqB,CA+BvB"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Undici instrumentation for OpenTelemetry
3
+ */
4
+ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
5
+ import { context } from "@opentelemetry/api";
6
+ import { PINGOPS_HTTP_ENABLED } from "@pingops/core";
7
+ /**
8
+ * Creates an Undici instrumentation instance
9
+ *
10
+ * @param isGlobalInstrumentationEnabled - Function that checks if global instrumentation is enabled
11
+ * @returns UndiciInstrumentation instance
12
+ */
13
+ export function createUndiciInstrumentation(isGlobalInstrumentationEnabled) {
14
+ return new UndiciInstrumentation({
15
+ enabled: true,
16
+ ignoreRequestHook: () => {
17
+ // If global instrumentation is enabled, instrument all requests
18
+ if (isGlobalInstrumentationEnabled()) {
19
+ return false;
20
+ }
21
+ // If global instrumentation is NOT enabled, only instrument when PINGOPS_HTTP_ENABLED is true
22
+ return context.active().getValue(PINGOPS_HTTP_ENABLED) !== true;
23
+ },
24
+ requestHook: (span, request) => {
25
+ const headers = request.headers;
26
+ for (const [key, value] of Object.entries(headers)) {
27
+ span.setAttribute(`http.request.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(",") : String(value));
28
+ }
29
+ },
30
+ responseHook: (span, { response }) => {
31
+ const headers = response.headers;
32
+ for (const [key, value] of Object.entries(headers)) {
33
+ span.setAttribute(`http.response.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(",") : String(value));
34
+ }
35
+ },
36
+ });
37
+ }
38
+ //# sourceMappingURL=undici.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undici.js","sourceRoot":"","sources":["../../src/instrumentations/undici.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGrD;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,8BAA6C;IAE7C,OAAO,IAAI,qBAAqB,CAAC;QAC/B,OAAO,EAAE,IAAI;QACb,iBAAiB,EAAE,GAAG,EAAE;YACtB,gEAAgE;YAChE,IAAI,8BAA8B,EAAE,EAAE,CAAC;gBACrC,OAAO,KAAK,CAAC;YACf,CAAC;YACD,8FAA8F;YAC9F,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC,KAAK,IAAI,CAAC;QAClE,CAAC;QACD,WAAW,EAAE,CAAC,IAAU,EAAE,OAAO,EAAE,EAAE;YACnC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YAEhC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,YAAY,CACf,uBAAuB,GAAG,CAAC,WAAW,EAAE,EAAE,EAC1C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACJ,CAAC;QACH,CAAC;QACD,YAAY,EAAE,CAAC,IAAU,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;YACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;YACjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,YAAY,CACf,wBAAwB,GAAG,CAAC,WAAW,EAAE,EAAE,EAC3C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * PingopsSpanProcessor - OpenTelemetry SpanProcessor implementation
3
+ * Observes finished spans and sends eligible ones to PingOps backend
4
+ *
5
+ * This processor provides:
6
+ * - Automatic filtering of spans (CLIENT spans with HTTP/GenAI attributes only)
7
+ * - Domain and header filtering based on configuration
8
+ * - Batched or immediate export modes using OTLP exporters
9
+ * - Fire-and-forget transport (never blocks application)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { NodeSDK } from '@opentelemetry/sdk-node';
14
+ * import { PingopsSpanProcessor } from '@pingops/otel';
15
+ *
16
+ * const sdk = new NodeSDK({
17
+ * spanProcessors: [
18
+ * new PingopsSpanProcessor({
19
+ * apiKey: 'your-api-key',
20
+ * baseUrl: 'https://api.pingops.com',
21
+ * serviceName: 'my-service',
22
+ * exportMode: 'batched', // or 'immediate'
23
+ * domainAllowList: [
24
+ * { domain: 'api.example.com' }
25
+ * ]
26
+ * })
27
+ * ]
28
+ * });
29
+ *
30
+ * sdk.start();
31
+ * ```
32
+ */
33
+ import type { SpanProcessor, ReadableSpan, Span } from '@opentelemetry/sdk-trace-base';
34
+ import type { Context } from '@opentelemetry/api';
35
+ import type { PingopsProcessorConfig } from './config';
36
+ /**
37
+ * OpenTelemetry span processor for sending spans to PingOps backend.
38
+ *
39
+ * This processor wraps OpenTelemetry's built-in processors (BatchSpanProcessor or SimpleSpanProcessor)
40
+ * and applies filtering before passing spans to the OTLP exporter.
41
+ */
42
+ export declare class PingopsSpanProcessor implements SpanProcessor {
43
+ private processor;
44
+ private config;
45
+ /**
46
+ * Creates a new PingopsSpanProcessor instance.
47
+ *
48
+ * @param config - Configuration parameters for the processor
49
+ */
50
+ constructor(config: PingopsProcessorConfig);
51
+ /**
52
+ * Called when a span starts - extracts parent attributes from context and adds them to the span
53
+ */
54
+ onStart(span: Span, parentContext: Context): void;
55
+ /**
56
+ * Extracts custom attributes (non-HTTP, non-GenAI) from attributes.
57
+ * These are attributes that were added manually via updateActiveSpan.
58
+ */
59
+ private extractCustomAttributes;
60
+ /**
61
+ * Called when a span ends. Filters the span and passes it to the underlying processor if eligible.
62
+ *
63
+ * This method:
64
+ * 1. Checks if the span is eligible (CLIENT + HTTP/GenAI attributes)
65
+ * 2. Applies domain filtering
66
+ * 3. If eligible, passes span to underlying OTLP processor for export
67
+ */
68
+ onEnd(span: ReadableSpan): void;
69
+ /**
70
+ * Forces an immediate flush of all pending spans.
71
+ *
72
+ * @returns Promise that resolves when all pending operations are complete
73
+ */
74
+ forceFlush(): Promise<void>;
75
+ /**
76
+ * Gracefully shuts down the processor, ensuring all pending operations are completed.
77
+ *
78
+ * @returns Promise that resolves when shutdown is complete
79
+ */
80
+ shutdown(): Promise<void>;
81
+ }
82
+ //# sourceMappingURL=processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAGvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAOlD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAKvD;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACxD,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,MAAM,CAMZ;IAEF;;;;OAIG;gBACS,MAAM,EAAE,sBAAsB;IA8C1C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI;IA0CjD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqB/B;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IA4E/B;;;;OAIG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAYvC"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * PingopsSpanProcessor - OpenTelemetry SpanProcessor implementation
3
+ * Observes finished spans and sends eligible ones to PingOps backend
4
+ *
5
+ * This processor provides:
6
+ * - Automatic filtering of spans (CLIENT spans with HTTP/GenAI attributes only)
7
+ * - Domain and header filtering based on configuration
8
+ * - Batched or immediate export modes using OTLP exporters
9
+ * - Fire-and-forget transport (never blocks application)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { NodeSDK } from '@opentelemetry/sdk-node';
14
+ * import { PingopsSpanProcessor } from '@pingops/otel';
15
+ *
16
+ * const sdk = new NodeSDK({
17
+ * spanProcessors: [
18
+ * new PingopsSpanProcessor({
19
+ * apiKey: 'your-api-key',
20
+ * baseUrl: 'https://api.pingops.com',
21
+ * serviceName: 'my-service',
22
+ * exportMode: 'batched', // or 'immediate'
23
+ * domainAllowList: [
24
+ * { domain: 'api.example.com' }
25
+ * ]
26
+ * })
27
+ * ]
28
+ * });
29
+ *
30
+ * sdk.start();
31
+ * ```
32
+ */
33
+ import { BatchSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
34
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
35
+ import { isSpanEligible, shouldCaptureSpan, createLogger, } from '@pingops/core';
36
+ import { PINGOPS_PARENT_SPAN_ATTRIBUTES_KEY } from '@pingops/core';
37
+ const log = createLogger('[PingOps Processor]');
38
+ /**
39
+ * OpenTelemetry span processor for sending spans to PingOps backend.
40
+ *
41
+ * This processor wraps OpenTelemetry's built-in processors (BatchSpanProcessor or SimpleSpanProcessor)
42
+ * and applies filtering before passing spans to the OTLP exporter.
43
+ */
44
+ export class PingopsSpanProcessor {
45
+ processor;
46
+ config;
47
+ /**
48
+ * Creates a new PingopsSpanProcessor instance.
49
+ *
50
+ * @param config - Configuration parameters for the processor
51
+ */
52
+ constructor(config) {
53
+ const exportMode = config.exportMode ?? 'batched';
54
+ // Get API key from config or environment
55
+ const apiKey = config.apiKey || process.env.PINGOPS_API_KEY || '';
56
+ // Create OTLP exporter pointing to PingOps backend
57
+ const exporter = new OTLPTraceExporter({
58
+ url: `${config.baseUrl}/v1/traces`,
59
+ headers: {
60
+ 'Authorization': apiKey ? `Bearer ${apiKey}` : '',
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ timeoutMillis: 5000,
64
+ });
65
+ // Create underlying processor based on export mode
66
+ if (exportMode === 'immediate') {
67
+ this.processor = new SimpleSpanProcessor(exporter);
68
+ }
69
+ else {
70
+ this.processor = new BatchSpanProcessor(exporter, {
71
+ maxExportBatchSize: config.batchSize ?? 50,
72
+ scheduledDelayMillis: config.batchTimeout ?? 5000,
73
+ });
74
+ }
75
+ this.config = {
76
+ debug: config.debug ?? false,
77
+ headersAllowList: config.headersAllowList,
78
+ headersDenyList: config.headersDenyList,
79
+ domainAllowList: config.domainAllowList,
80
+ domainDenyList: config.domainDenyList,
81
+ };
82
+ log.info('Initialized PingopsSpanProcessor', {
83
+ baseUrl: config.baseUrl,
84
+ exportMode,
85
+ batchSize: config.batchSize,
86
+ batchTimeout: config.batchTimeout,
87
+ hasDomainAllowList: !!config.domainAllowList && config.domainAllowList.length > 0,
88
+ hasDomainDenyList: !!config.domainDenyList && config.domainDenyList.length > 0,
89
+ hasHeadersAllowList: !!config.headersAllowList && config.headersAllowList.length > 0,
90
+ hasHeadersDenyList: !!config.headersDenyList && config.headersDenyList.length > 0,
91
+ });
92
+ }
93
+ /**
94
+ * Called when a span starts - extracts parent attributes from context and adds them to the span
95
+ */
96
+ onStart(span, parentContext) {
97
+ const spanContext = span.spanContext();
98
+ log.debug('Span started', {
99
+ spanName: span.name,
100
+ spanId: spanContext.spanId,
101
+ traceId: spanContext.traceId,
102
+ });
103
+ // Extract parent span attributes from context and add them to this span
104
+ // This allows child spans (like HTTP spans) to inherit custom attributes from parent spans
105
+ const parentAttributes = parentContext.getValue(PINGOPS_PARENT_SPAN_ATTRIBUTES_KEY);
106
+ if (parentAttributes && Object.keys(parentAttributes).length > 0) {
107
+ // Filter out HTTP/GenAI attributes - we only want custom attributes
108
+ const customAttributes = this.extractCustomAttributes(parentAttributes);
109
+ if (Object.keys(customAttributes).length > 0) {
110
+ // Convert to Attributes type (string | number | boolean | string[] | undefined)
111
+ const attributesToSet = {};
112
+ for (const [key, value] of Object.entries(customAttributes)) {
113
+ // Only set valid attribute types
114
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array.isArray(value)) {
115
+ attributesToSet[key] = value;
116
+ }
117
+ else if (value !== null && value !== undefined) {
118
+ // Convert other types to string
119
+ attributesToSet[key] = String(value);
120
+ }
121
+ }
122
+ span.setAttributes(attributesToSet);
123
+ log.debug('Added parent span attributes to child span', {
124
+ spanName: span.name,
125
+ spanId: spanContext.spanId,
126
+ attributeCount: Object.keys(attributesToSet).length,
127
+ attributes: Object.keys(attributesToSet),
128
+ });
129
+ }
130
+ }
131
+ this.processor.onStart(span, parentContext);
132
+ }
133
+ /**
134
+ * Extracts custom attributes (non-HTTP, non-GenAI) from attributes.
135
+ * These are attributes that were added manually via updateActiveSpan.
136
+ */
137
+ extractCustomAttributes(attributes) {
138
+ const customAttributes = {};
139
+ // List of HTTP attribute prefixes to exclude
140
+ const httpPrefixes = ['http.', 'url.', 'server.', 'network.', 'user_agent.'];
141
+ // List of GenAI attribute prefixes to exclude
142
+ const genAiPrefixes = ['gen_ai.', 'genai.'];
143
+ for (const [key, value] of Object.entries(attributes)) {
144
+ // Skip HTTP and GenAI attributes
145
+ const isHttpAttribute = httpPrefixes.some(prefix => key.startsWith(prefix));
146
+ const isGenAiAttribute = genAiPrefixes.some(prefix => key.startsWith(prefix));
147
+ if (!isHttpAttribute && !isGenAiAttribute) {
148
+ customAttributes[key] = value;
149
+ }
150
+ }
151
+ return customAttributes;
152
+ }
153
+ /**
154
+ * Called when a span ends. Filters the span and passes it to the underlying processor if eligible.
155
+ *
156
+ * This method:
157
+ * 1. Checks if the span is eligible (CLIENT + HTTP/GenAI attributes)
158
+ * 2. Applies domain filtering
159
+ * 3. If eligible, passes span to underlying OTLP processor for export
160
+ */
161
+ onEnd(span) {
162
+ const spanContext = span.spanContext();
163
+ log.debug('Span ended, processing', {
164
+ spanName: span.name,
165
+ spanId: spanContext.spanId,
166
+ traceId: spanContext.traceId,
167
+ spanKind: span.kind,
168
+ });
169
+ try {
170
+ // Step 1: Check if span is eligible (CLIENT + HTTP/GenAI attributes)
171
+ if (!isSpanEligible(span)) {
172
+ log.debug('Span not eligible, skipping', {
173
+ spanName: span.name,
174
+ spanId: spanContext.spanId,
175
+ reason: 'not CLIENT or missing HTTP/GenAI attributes',
176
+ });
177
+ return;
178
+ }
179
+ // Step 2: Extract URL for domain filtering
180
+ const attributes = span.attributes;
181
+ const url = attributes['http.url'] ||
182
+ attributes['url.full'] ||
183
+ (attributes['server.address'] ? `https://${attributes['server.address']}` : '');
184
+ log.debug('Extracted URL for domain filtering', {
185
+ spanName: span.name,
186
+ url,
187
+ hasHttpUrl: !!attributes['http.url'],
188
+ hasUrlFull: !!attributes['url.full'],
189
+ hasServerAddress: !!attributes['server.address'],
190
+ });
191
+ // Step 3: Apply domain filtering
192
+ if (url) {
193
+ const shouldCapture = shouldCaptureSpan(url, this.config.domainAllowList, this.config.domainDenyList);
194
+ if (!shouldCapture) {
195
+ log.info('Span filtered out by domain rules', {
196
+ spanName: span.name,
197
+ spanId: spanContext.spanId,
198
+ url,
199
+ });
200
+ return;
201
+ }
202
+ }
203
+ else {
204
+ log.debug('No URL found for domain filtering, proceeding', {
205
+ spanName: span.name,
206
+ });
207
+ }
208
+ // Step 4: Span passed all filters, pass to underlying processor for export
209
+ this.processor.onEnd(span);
210
+ log.info('Span passed all filters and queued for export', {
211
+ spanName: span.name,
212
+ spanId: spanContext.spanId,
213
+ traceId: spanContext.traceId,
214
+ attributes: span.attributes,
215
+ url,
216
+ });
217
+ }
218
+ catch (error) {
219
+ // Defensive error handling - never crash the app
220
+ log.error('Error processing span', {
221
+ spanName: span.name,
222
+ spanId: spanContext.spanId,
223
+ error: error instanceof Error ? error.message : String(error),
224
+ });
225
+ }
226
+ }
227
+ /**
228
+ * Forces an immediate flush of all pending spans.
229
+ *
230
+ * @returns Promise that resolves when all pending operations are complete
231
+ */
232
+ async forceFlush() {
233
+ log.info('Force flushing spans');
234
+ try {
235
+ await this.processor.forceFlush();
236
+ log.info('Force flush complete');
237
+ }
238
+ catch (error) {
239
+ log.error('Error during force flush', {
240
+ error: error instanceof Error ? error.message : String(error),
241
+ });
242
+ throw error;
243
+ }
244
+ }
245
+ /**
246
+ * Gracefully shuts down the processor, ensuring all pending operations are completed.
247
+ *
248
+ * @returns Promise that resolves when shutdown is complete
249
+ */
250
+ async shutdown() {
251
+ log.info('Shutting down processor');
252
+ try {
253
+ await this.processor.shutdown();
254
+ log.info('Processor shutdown complete');
255
+ }
256
+ catch (error) {
257
+ log.error('Error during processor shutdown', {
258
+ error: error instanceof Error ? error.message : String(error),
259
+ });
260
+ throw error;
261
+ }
262
+ }
263
+ }
264
+ //# sourceMappingURL=processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.js","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * PingopsSpanProcessor - OpenTelemetry SpanProcessor implementation
3
+ * Observes finished spans and sends eligible ones to PingOps backend
4
+ *
5
+ * This processor provides:
6
+ * - Automatic filtering of spans (CLIENT spans with HTTP/GenAI attributes only)
7
+ * - Domain and header filtering based on configuration
8
+ * - Batched or immediate export modes using OTLP exporters
9
+ * - Fire-and-forget transport (never blocks application)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { NodeSDK } from '@opentelemetry/sdk-node';
14
+ * import { PingopsSpanProcessor } from '@pingops/otel';
15
+ *
16
+ * const sdk = new NodeSDK({
17
+ * spanProcessors: [
18
+ * new PingopsSpanProcessor({
19
+ * apiKey: 'your-api-key',
20
+ * baseUrl: 'https://api.pingops.com',
21
+ * serviceName: 'my-service',
22
+ * exportMode: 'batched', // or 'immediate'
23
+ * domainAllowList: [
24
+ * { domain: 'api.example.com' }
25
+ * ]
26
+ * })
27
+ * ]
28
+ * });
29
+ *
30
+ * sdk.start();
31
+ * ```
32
+ */
33
+ import type { SpanProcessor, ReadableSpan, Span } from "@opentelemetry/sdk-trace-base";
34
+ import type { Context } from "@opentelemetry/api";
35
+ import type { PingopsProcessorConfig } from "./config";
36
+ /**
37
+ * OpenTelemetry span processor for sending spans to PingOps backend.
38
+ *
39
+ * This processor wraps OpenTelemetry's built-in processors (BatchSpanProcessor or SimpleSpanProcessor)
40
+ * and applies filtering before passing spans to the OTLP exporter.
41
+ */
42
+ export declare class PingopsSpanProcessor implements SpanProcessor {
43
+ private processor;
44
+ private config;
45
+ /**
46
+ * Creates a new PingopsSpanProcessor instance.
47
+ *
48
+ * @param config - Configuration parameters for the processor
49
+ */
50
+ constructor(config: PingopsProcessorConfig);
51
+ /**
52
+ * Called when a span starts - extracts parent attributes from context and adds them to the span
53
+ */
54
+ onStart(span: Span, parentContext: Context): void;
55
+ /**
56
+ * Called when a span ends. Filters the span and passes it to the underlying processor if eligible.
57
+ *
58
+ * This method:
59
+ * 1. Checks if the span is eligible (CLIENT + HTTP/GenAI attributes)
60
+ * 2. Applies domain filtering (determines if span should be exported)
61
+ * 3. Applies header filtering via FilteredSpan wrapper (domain-specific and global rules)
62
+ * 4. If eligible, passes filtered span to underlying OTLP processor for export
63
+ */
64
+ onEnd(span: ReadableSpan): void;
65
+ /**
66
+ * Forces an immediate flush of all pending spans.
67
+ *
68
+ * @returns Promise that resolves when all pending operations are complete
69
+ */
70
+ forceFlush(): Promise<void>;
71
+ /**
72
+ * Gracefully shuts down the processor, ensuring all pending operations are completed.
73
+ *
74
+ * @returns Promise that resolves when shutdown is complete
75
+ */
76
+ shutdown(): Promise<void>;
77
+ }
78
+ //# sourceMappingURL=span-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span-processor.d.ts","sourceRoot":"","sources":["../src/span-processor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,IAAI,EACL,MAAM,+BAA+B,CAAC;AAMvC,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,oBAAoB,CAAC;AAS9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAwDvD;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACxD,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,MAAM,CAMZ;IAEF;;;;OAIG;gBACS,MAAM,EAAE,sBAAsB;IAkD1C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI;IA0BjD;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAyF/B;;;;OAIG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAYvC"}