@pingops/sdk 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.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @pingops/sdk
2
+
3
+ PingOps SDK for Node.js. Provides a simple API for bootstrapping OpenTelemetry and capturing outgoing API and LLM calls.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @pingops/sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { initializePingops } from "@pingops/sdk";
15
+
16
+ initializePingops({
17
+ apiKey: "your-api-key", // or set PINGOPS_API_KEY env var
18
+ baseUrl: "https://api.pingops.com",
19
+ serviceName: "my-service",
20
+ });
21
+ ```
22
+
23
+ ## Features
24
+
25
+ - **Automatic Instrumentation**: Captures HTTP and fetch API calls automatically
26
+ - **Node.js Support**: Works in Node.js environments (including Node.js 20+ with native fetch)
27
+ - **GenAI Support**: Captures LLM calls using OpenTelemetry GenAI semantic conventions
28
+ - **Manual Instrumentation**: Create custom spans for specific operations
29
+ - **Zero Configuration**: Works out of the box with sensible defaults
30
+
31
+ ## API
32
+
33
+ ### `initializePingops(config)`
34
+
35
+ Initializes the PingOps SDK with OpenTelemetry.
36
+
37
+ **Configuration:**
38
+
39
+ ```typescript
40
+ interface PingopsInitConfig {
41
+ apiKey?: string; // Defaults to PINGOPS_API_KEY env var
42
+ baseUrl: string; // Required
43
+ serviceName: string; // Required
44
+ debug?: boolean;
45
+ headersAllowList?: string[];
46
+ headersDenyList?: string[];
47
+ domainAllowList?: DomainRule[];
48
+ domainDenyList?: DomainRule[];
49
+ batchSize?: number; // Default: 50
50
+ batchTimeout?: number; // Default: 5000ms
51
+ }
52
+ ```
53
+
54
+ ### `pingops.startSpan(name, attributes, fn)`
55
+
56
+ Creates a manual span for custom instrumentation.
57
+
58
+ ```typescript
59
+ import { pingops } from "@pingops/sdk";
60
+
61
+ await pingops.startSpan(
62
+ "external.api.call",
63
+ {
64
+ customer_id: "cust_123",
65
+ correlation_id: "req_456",
66
+ "custom_attributes.request_type": "webhook",
67
+ },
68
+ async (span) => {
69
+ // Your code here
70
+ const result = await fetch("https://api.example.com/data");
71
+ return result.json();
72
+ }
73
+ );
74
+ ```
75
+
76
+ The span is automatically ended when the function completes or throws an error.
77
+
78
+ ### `shutdownPingops()`
79
+
80
+ Gracefully shuts down the SDK and flushes remaining spans.
81
+
82
+ ```typescript
83
+ import { shutdownPingops } from "@pingops/sdk";
84
+
85
+ await shutdownPingops();
86
+ ```
87
+
88
+ ## Domain Filtering
89
+
90
+ Control which domains and paths are captured:
91
+
92
+ ```typescript
93
+ initializePingops({
94
+ // ... other config
95
+ domainAllowList: [
96
+ {
97
+ domain: "api.github.com",
98
+ paths: ["/repos"],
99
+ headersAllowList: ["authorization", "user-agent"],
100
+ },
101
+ {
102
+ domain: ".openai.com", // Suffix match
103
+ },
104
+ ],
105
+ domainDenyList: [
106
+ {
107
+ domain: "internal.service.local",
108
+ },
109
+ ],
110
+ });
111
+ ```
112
+
113
+ ## Header Filtering
114
+
115
+ Control which headers are captured:
116
+
117
+ ```typescript
118
+ initializePingops({
119
+ // ... other config
120
+ headersAllowList: ["user-agent", "x-request-id"],
121
+ headersDenyList: ["authorization", "cookie"],
122
+ });
123
+ ```
124
+
125
+ ## Integration with Existing OpenTelemetry
126
+
127
+ If you already have OpenTelemetry set up, you can use just the `PingopsSpanProcessor`:
128
+
129
+ ```typescript
130
+ import { PingopsSpanProcessor } from "@pingops/otel";
131
+ import { getTracerProvider } from "@opentelemetry/api";
132
+
133
+ const processor = new PingopsSpanProcessor({
134
+ apiKey: "your-api-key",
135
+ baseUrl: "https://api.pingops.com",
136
+ serviceName: "my-service",
137
+ });
138
+
139
+ const tracerProvider = getTracerProvider();
140
+ // Add processor to your existing tracer provider
141
+ ```
142
+
143
+ ## What Gets Captured
144
+
145
+ - **HTTP Requests**: All outgoing HTTP requests (via `http` module in Node.js)
146
+ - **Fetch API**: All `fetch()` calls (universal JS)
147
+ - **GenAI Calls**: LLM API calls that follow OpenTelemetry GenAI semantic conventions
148
+
149
+ ## What Doesn't Get Captured
150
+
151
+ - Incoming requests (server-side)
152
+ - Internal spans (non-CLIENT spans)
153
+ - Spans without HTTP or GenAI attributes
154
+
155
+ ## Requirements
156
+
157
+ - **Node.js**: Requires Node.js 20+ (for native fetch support) or Node.js 18+ with fetch polyfill
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Configuration types for @pingops/sdk
3
+ */
4
+ import type { PingopsProcessorConfig } from '@pingops/otel';
5
+ export interface PingopsInitConfig extends PingopsProcessorConfig {
6
+ }
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE5D,MAAM,WAAW,iBAAkB,SAAQ,sBAAsB;CAAG"}
package/dist/config.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Configuration types for @pingops/sdk
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @pingops/sdk - Public API
3
+ */
4
+ export { initializePingops, shutdownPingops, wrapHttp } from "./pingops";
5
+ export type { WrapHttpAttributes } from "@pingops/core";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACzE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @pingops/sdk - Public API
3
+ */
4
+ export { initializePingops, shutdownPingops, wrapHttp } from "./pingops";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared state for tracking SDK initialization
3
+ * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts
4
+ */
5
+ /**
6
+ * Sets the SDK initialization flag.
7
+ * Called by initializePingops when the SDK is initialized.
8
+ */
9
+ export declare function setSdkInitialized(initialized: boolean): void;
10
+ /**
11
+ * Checks if global instrumentation is enabled.
12
+ * This is used to determine instrumentation behavior:
13
+ * - If true: all HTTP requests are instrumented
14
+ * - If false: only requests within wrapHttp blocks are instrumented
15
+ */
16
+ export declare function isGlobalInstrumentationEnabled(): boolean;
17
+ //# sourceMappingURL=init-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-state.d.ts","sourceRoot":"","sources":["../src/init-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,IAAI,OAAO,CAExD"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared state for tracking SDK initialization
3
+ * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts
4
+ */
5
+ let isSdkInitializedFlag = false;
6
+ /**
7
+ * Sets the SDK initialization flag.
8
+ * Called by initializePingops when the SDK is initialized.
9
+ */
10
+ export function setSdkInitialized(initialized) {
11
+ isSdkInitializedFlag = initialized;
12
+ }
13
+ /**
14
+ * Checks if global instrumentation is enabled.
15
+ * This is used to determine instrumentation behavior:
16
+ * - If true: all HTTP requests are instrumented
17
+ * - If false: only requests within wrapHttp blocks are instrumented
18
+ */
19
+ export function isGlobalInstrumentationEnabled() {
20
+ return isSdkInitializedFlag;
21
+ }
22
+ //# sourceMappingURL=init-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-state.js","sourceRoot":"","sources":["../src/init-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,IAAI,oBAAoB,GAAG,KAAK,CAAC;AAEjC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAoB;IACpD,oBAAoB,GAAG,WAAW,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,8BAA8B;IAC5C,OAAO,oBAAoB,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Initializes PingOps SDK with OpenTelemetry
3
+ */
4
+ import type { PingopsProcessorConfig } from '@pingops/otel';
5
+ /**
6
+ * Initializes PingOps SDK
7
+ *
8
+ * This function:
9
+ * 1. Creates an OpenTelemetry NodeSDK instance
10
+ * 2. Configures Resource with service.name
11
+ * 3. Registers PingopsSpanProcessor
12
+ * 4. Enables HTTP/fetch/GenAI instrumentation
13
+ * 5. Starts the SDK
14
+ */
15
+ export declare function initializePingops(config: PingopsProcessorConfig): void;
16
+ /**
17
+ * Shuts down the SDK and flushes remaining spans
18
+ */
19
+ export declare function shutdownPingops(): Promise<void>;
20
+ //# sourceMappingURL=initialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../src/initialize.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAe5D;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CA4DtE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAWrD"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Initializes PingOps SDK with OpenTelemetry
3
+ */
4
+ import { NodeSDK } from '@opentelemetry/sdk-node';
5
+ import { resourceFromAttributes } from '@opentelemetry/resources';
6
+ import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
7
+ import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
8
+ import { getInstrumentations } from './instrumentation';
9
+ import { setPingopsTracerProvider, shutdownTracerProvider, PingopsSpanProcessor, } from '@pingops/otel';
10
+ import { createLogger } from '@pingops/core';
11
+ let sdkInstance = null;
12
+ let isInitialized = false;
13
+ const logger = createLogger('[PingOps Initialize]');
14
+ /**
15
+ * Initializes PingOps SDK
16
+ *
17
+ * This function:
18
+ * 1. Creates an OpenTelemetry NodeSDK instance
19
+ * 2. Configures Resource with service.name
20
+ * 3. Registers PingopsSpanProcessor
21
+ * 4. Enables HTTP/fetch/GenAI instrumentation
22
+ * 5. Starts the SDK
23
+ */
24
+ export function initializePingops(config) {
25
+ if (isInitialized) {
26
+ if (config.debug) {
27
+ logger.warn('[PingOps] SDK already initialized, skipping');
28
+ }
29
+ return;
30
+ }
31
+ // Create resource with service name
32
+ const resource = resourceFromAttributes({
33
+ [ATTR_SERVICE_NAME]: config.serviceName,
34
+ });
35
+ // Create PingopsSpanProcessor
36
+ const processor = new PingopsSpanProcessor(config);
37
+ // Get instrumentations only if autoInstrumentation is enabled (defaults to true)
38
+ const autoInstrumentation = config.autoInstrumentation !== false;
39
+ const instrumentations = autoInstrumentation ? getInstrumentations() : [];
40
+ // Node.js SDK
41
+ const nodeSdk = new NodeSDK({
42
+ resource,
43
+ spanProcessors: [processor],
44
+ instrumentations,
45
+ });
46
+ nodeSdk.start();
47
+ sdkInstance = nodeSdk;
48
+ // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts
49
+ // This ensures manual spans created via startSpan are processed by the same processor
50
+ // We register it after NodeSDK so it takes precedence as the global provider
51
+ try {
52
+ // In version 2.2.0, span processors are passed in the constructor
53
+ const isolatedProvider = new NodeTracerProvider({
54
+ resource,
55
+ spanProcessors: [processor],
56
+ });
57
+ // Register the provider globally
58
+ isolatedProvider.register();
59
+ // Set it in global state
60
+ setPingopsTracerProvider(isolatedProvider);
61
+ }
62
+ catch (error) {
63
+ if (config.debug) {
64
+ logger.error('[PingOps] Failed to create isolated TracerProvider:', error instanceof Error ? error.message : String(error));
65
+ }
66
+ // Continue without isolated provider - manual spans will use global provider
67
+ }
68
+ if (config.debug) {
69
+ logger.info('[PingOps] SDK initialized');
70
+ }
71
+ isInitialized = true;
72
+ }
73
+ /**
74
+ * Shuts down the SDK and flushes remaining spans
75
+ */
76
+ export async function shutdownPingops() {
77
+ // Shutdown isolated TracerProvider first
78
+ await shutdownTracerProvider();
79
+ if (!sdkInstance) {
80
+ return;
81
+ }
82
+ await sdkInstance.shutdown();
83
+ sdkInstance = null;
84
+ isInitialized = false;
85
+ }
86
+ //# sourceMappingURL=initialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize.js","sourceRoot":"","sources":["../src/initialize.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,IAAI,WAAW,GAAmB,IAAI,CAAC;AACvC,IAAI,aAAa,GAAG,KAAK,CAAC;AAE1B,MAAM,MAAM,GAAG,YAAY,CAAC,sBAAsB,CAAC,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA8B;IAC9D,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO;IACT,CAAC;IAED,oCAAoC;IACpC,MAAM,QAAQ,GAAG,sBAAsB,CAAC;QACpC,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,WAAW;KACxC,CAAC,CAAC;IAEL,8BAA8B;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAEnD,iFAAiF;IACjF,MAAM,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,KAAK,KAAK,CAAC;IACjE,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1E,cAAc;IACd,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;QAC1B,QAAQ;QACR,cAAc,EAAE,CAAC,SAAS,CAAC;QAC3B,gBAAgB;KACjB,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,WAAW,GAAG,OAAO,CAAC;IAEtB,2EAA2E;IAC3E,sFAAsF;IACtF,6EAA6E;IAC7E,IAAI,CAAC;QACH,kEAAkE;QAClE,MAAM,gBAAgB,GAAG,IAAI,kBAAkB,CAAC;YAC9C,QAAQ;YACR,cAAc,EAAE,CAAC,SAAgB,CAAC;SACnC,CAAC,CAAC;QAEH,iCAAiC;QACjC,gBAAgB,CAAC,QAAQ,EAAE,CAAC;QAE5B,yBAAyB;QACzB,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CACV,qDAAqD,EACrD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACJ,CAAC;QACD,6EAA6E;IAC/E,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAC3C,CAAC;IAED,aAAa,GAAG,IAAI,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,yCAAyC;IACzC,MAAM,sBAAsB,EAAE,CAAC;IAE/B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,MAAM,WAAW,CAAC,QAAQ,EAAE,CAAC;IAC7B,WAAW,GAAG,IAAI,CAAC;IACnB,aAAa,GAAG,KAAK,CAAC;AACxB,CAAC"}
@@ -0,0 +1,14 @@
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
+ export declare function getInstrumentations(): Instrumentation[];
14
+ //# sourceMappingURL=instrumentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAOtE;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,eAAe,EAAE,CAwEvD"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Instrumentation setup for HTTP, fetch, undici, and GenAI
3
+ */
4
+ import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
5
+ import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
6
+ import { registerInstrumentations } from '@opentelemetry/instrumentation';
7
+ import { context } from '@opentelemetry/api';
8
+ import { PINGOPS_HTTP_ENABLED, normalizeHeaders } from '@pingops/core';
9
+ import { isGlobalInstrumentationEnabled } from './init-state';
10
+ let installed = false;
11
+ /**
12
+ * Registers instrumentations for Node.js environment.
13
+ * This function is idempotent and can be called multiple times safely.
14
+ *
15
+ * Instrumentation behavior:
16
+ * - If global instrumentation is enabled: all HTTP requests are instrumented
17
+ * - If global instrumentation is NOT enabled: only requests within wrapHttp blocks are instrumented
18
+ */
19
+ export function getInstrumentations() {
20
+ if (installed) {
21
+ return [];
22
+ }
23
+ registerInstrumentations({
24
+ instrumentations: [
25
+ new HttpInstrumentation({
26
+ ignoreIncomingRequestHook: () => true, // Only instrument outgoing requests
27
+ ignoreOutgoingRequestHook: () => {
28
+ // If global instrumentation is enabled, instrument all outgoing requests
29
+ if (isGlobalInstrumentationEnabled()) {
30
+ return false;
31
+ }
32
+ // If global instrumentation is NOT enabled, only instrument when PINGOPS_HTTP_ENABLED is true
33
+ return context.active().getValue(PINGOPS_HTTP_ENABLED) !== true;
34
+ },
35
+ requestHook: (span, request) => {
36
+ const headers = normalizeHeaders(request.headers);
37
+ for (const [key, value] of Object.entries(headers)) {
38
+ span.setAttribute(`http.request.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(',') : String(value));
39
+ }
40
+ },
41
+ responseHook: (span, response) => {
42
+ const headers = normalizeHeaders(response.headers);
43
+ for (const [key, value] of Object.entries(headers)) {
44
+ span.setAttribute(`http.response.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(',') : String(value));
45
+ }
46
+ },
47
+ }),
48
+ new UndiciInstrumentation({
49
+ enabled: true,
50
+ ignoreRequestHook: () => {
51
+ // If global instrumentation is enabled, instrument all requests
52
+ if (isGlobalInstrumentationEnabled()) {
53
+ return false;
54
+ }
55
+ // If global instrumentation is NOT enabled, only instrument when PINGOPS_HTTP_ENABLED is true
56
+ return context.active().getValue(PINGOPS_HTTP_ENABLED) !== true;
57
+ },
58
+ requestHook: (span, request) => {
59
+ const headers = normalizeHeaders(request.headers);
60
+ for (const [key, value] of Object.entries(headers)) {
61
+ span.setAttribute(`http.request.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(',') : String(value));
62
+ }
63
+ },
64
+ responseHook: (span, { response }) => {
65
+ const headers = normalizeHeaders(response.headers);
66
+ for (const [key, value] of Object.entries(headers)) {
67
+ span.setAttribute(`http.response.header.${key.toLowerCase()}`, Array.isArray(value) ? value.join(',') : String(value));
68
+ }
69
+ },
70
+ }),
71
+ ],
72
+ });
73
+ installed = true;
74
+ return [];
75
+ }
76
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Global logger utility for PingOps SDK
3
+ *
4
+ * Provides consistent logging across all SDK components with support for
5
+ * different log levels and debug mode control via PINGOPS_DEBUG environment variable.
6
+ */
7
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
8
+ export interface Logger {
9
+ debug(message: string, ...args: unknown[]): void;
10
+ info(message: string, ...args: unknown[]): void;
11
+ warn(message: string, ...args: unknown[]): void;
12
+ error(message: string, ...args: unknown[]): void;
13
+ }
14
+ /**
15
+ * Creates a logger instance with a specific prefix
16
+ *
17
+ * @param prefix - Prefix to add to all log messages (e.g., '[PingOps TracerProvider]')
18
+ * @returns Logger instance
19
+ */
20
+ export declare function createLogger(prefix: string): Logger;
21
+ /**
22
+ * Default logger instance for general SDK logging
23
+ */
24
+ export declare const logger: Logger;
25
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAwBnD;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,QAAgC,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Global logger utility for PingOps SDK
3
+ *
4
+ * Provides consistent logging across all SDK components with support for
5
+ * different log levels and debug mode control via PINGOPS_DEBUG environment variable.
6
+ */
7
+ /**
8
+ * Creates a logger instance with a specific prefix
9
+ *
10
+ * @param prefix - Prefix to add to all log messages (e.g., '[PingOps TracerProvider]')
11
+ * @returns Logger instance
12
+ */
13
+ export function createLogger(prefix) {
14
+ const isDebugEnabled = true;
15
+ const formatMessage = (level, message) => {
16
+ const timestamp = new Date().toISOString();
17
+ return `[${timestamp}] ${prefix} [${level.toUpperCase()}] ${message}`;
18
+ };
19
+ return {
20
+ debug(message, ...args) {
21
+ if (isDebugEnabled) {
22
+ console.debug(formatMessage('debug', message), ...args);
23
+ }
24
+ },
25
+ info(message, ...args) {
26
+ console.log(formatMessage('info', message), ...args);
27
+ },
28
+ warn(message, ...args) {
29
+ console.warn(formatMessage('warn', message), ...args);
30
+ },
31
+ error(message, ...args) {
32
+ console.error(formatMessage('error', message), ...args);
33
+ },
34
+ };
35
+ }
36
+ /**
37
+ * Default logger instance for general SDK logging
38
+ */
39
+ export const logger = createLogger('[PingOps SDK]');
40
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,cAAc,GAAG,IAAI,CAAC;IAE5B,MAAM,aAAa,GAAG,CAAC,KAAe,EAAE,OAAe,EAAU,EAAE;QACjE,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,OAAO,IAAI,SAAS,KAAK,MAAM,KAAK,KAAK,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;IACxE,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;YACvC,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;YACtC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;YACtC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;YACvC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * PingOps SDK singleton for manual instrumentation
3
+ *
4
+ * Provides initializePingops and shutdownPingops functions.
5
+ * wrapHttp is available from @pingops/core and will auto-initialize
6
+ * from environment variables if needed.
7
+ */
8
+ import type { PingopsProcessorConfig } from "@pingops/otel";
9
+ import { type WrapHttpAttributes } from "@pingops/core";
10
+ /**
11
+ * Initializes PingOps SDK
12
+ *
13
+ * This function:
14
+ * 1. Creates an OpenTelemetry NodeSDK instance
15
+ * 2. Configures Resource with service.name
16
+ * 3. Registers PingopsSpanProcessor
17
+ * 4. Enables HTTP/fetch/GenAI instrumentation
18
+ * 5. Starts the SDK
19
+ *
20
+ * @param config - Configuration for the SDK
21
+ * @param explicit - Whether this is an explicit call (default: true).
22
+ * Set to false when called internally by wrapHttp auto-initialization.
23
+ */
24
+ export declare function initializePingops(config: PingopsProcessorConfig, explicit?: boolean): void;
25
+ /**
26
+ * Shuts down the SDK and flushes remaining spans
27
+ */
28
+ export declare function shutdownPingops(): Promise<void>;
29
+ /**
30
+ * Wraps a function to set attributes on HTTP spans created within the wrapped block.
31
+ *
32
+ * This function sets attributes (userId, sessionId, tags, metadata) in the OpenTelemetry
33
+ * context, which are automatically propagated to all spans created within the wrapped function.
34
+ *
35
+ * Instrumentation behavior:
36
+ * - If `initializePingops` was called: All HTTP requests are instrumented by default.
37
+ * `wrapHttp` only adds attributes to spans created within the wrapped block.
38
+ * - If `initializePingops` was NOT called: Only HTTP requests within `wrapHttp` blocks
39
+ * are instrumented. Requests outside `wrapHttp` are not instrumented.
40
+ *
41
+ * @param options - Options including attributes to propagate to spans
42
+ * @param fn - Function to execute within the attribute context
43
+ * @returns The result of the function
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { wrapHttp } from '@pingops/sdk';
48
+ *
49
+ * // Scenario 1: initializePingops was called
50
+ * initializePingops({ ... });
51
+ *
52
+ * // All HTTP requests are instrumented, but this block adds attributes
53
+ * const result = await wrapHttp({
54
+ * attributes: {
55
+ * userId: 'user-123',
56
+ * sessionId: 'session-456',
57
+ * tags: ['production', 'api'],
58
+ * metadata: { environment: 'prod', version: '1.0.0' }
59
+ * }
60
+ * }, async () => {
61
+ * // This HTTP request will be instrumented AND have the attributes set above
62
+ * const response = await fetch('https://api.example.com/users/123');
63
+ * return response.json();
64
+ * });
65
+ *
66
+ * // HTTP requests outside wrapHttp are still instrumented, just without the attributes
67
+ * const otherResponse = await fetch('https://api.example.com/other');
68
+ *
69
+ * // Scenario 2: initializePingops was NOT called
70
+ * // Only requests within wrapHttp are instrumented
71
+ * await wrapHttp({
72
+ * attributes: { userId: 'user-123' }
73
+ * }, async () => {
74
+ * // This request IS instrumented
75
+ * return fetch('https://api.example.com/data');
76
+ * });
77
+ *
78
+ * // This request is NOT instrumented (outside wrapHttp)
79
+ * await fetch('https://api.example.com/other');
80
+ * ```
81
+ */
82
+ export declare function wrapHttp<T>(options: {
83
+ attributes?: WrapHttpAttributes;
84
+ }, fn: () => T | Promise<T>): T | Promise<T>;
85
+ //# sourceMappingURL=pingops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pingops.d.ts","sourceRoot":"","sources":["../src/pingops.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAW5D,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAgBvB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,GAAE,OAAc,GACvB,IAAI,CA6DN;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAYrD;AA+GD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,kBAAkB,CAAA;CAAE,EAC5C,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAUhB"}