@pingops/sdk 0.2.6 → 0.4.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 +23 -31
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +9 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +9 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{pingops-BM2jW6Bi.cjs → pingops-D7n9pxch.cjs} +45 -43
- package/dist/pingops-D7n9pxch.cjs.map +1 -0
- package/dist/{pingops-SV21E4bp.mjs → pingops-DDeCBa2d.mjs} +42 -40
- package/dist/pingops-DDeCBa2d.mjs.map +1 -0
- package/dist/register.cjs +1 -1
- package/dist/register.mjs +1 -1
- package/package.json +7 -7
- package/dist/pingops-BM2jW6Bi.cjs.map +0 -1
- package/dist/pingops-SV21E4bp.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -135,15 +135,12 @@ Configuration can be provided via:
|
|
|
135
135
|
| `baseUrl` | `string` | **required** | Backend base URL |
|
|
136
136
|
| `serviceName` | `string` | **required** | Service name |
|
|
137
137
|
| `debug` | `boolean` | `false` | Enable debug logs (`PINGOPS_DEBUG=true`) |
|
|
138
|
-
| `headersAllowList` | `string[]` | — | Headers to include (case-insensitive) |
|
|
139
|
-
| `headersDenyList` | `string[]` | — | Headers to exclude (overrides allow) |
|
|
140
138
|
| `captureRequestBody` | `boolean` | `false` | Capture request bodies (global) |
|
|
141
139
|
| `captureResponseBody` | `boolean` | `false` | Capture response bodies (global) |
|
|
142
|
-
| `maxRequestBodySize` | `number` | `4096` | Max request body size in bytes |
|
|
143
|
-
| `maxResponseBodySize` | `number` | `4096` | Max response body size in bytes |
|
|
144
140
|
| `domainAllowList` | `DomainRule[]` | — | Domains (and optional rules) to allow |
|
|
145
141
|
| `domainDenyList` | `DomainRule[]` | — | Domains to exclude |
|
|
146
|
-
| `
|
|
142
|
+
| `llmMonitoring` | `LlmMonitoringConfig` | disabled | Opt-in GenAI token enrichment |
|
|
143
|
+
| `transforms` | `HttpTransformConfig` | — | Outbound transform rules for headers/body/query |
|
|
147
144
|
| `batchSize` | `number` | `50` | Spans per batch (`PINGOPS_BATCH_SIZE`) |
|
|
148
145
|
| `batchTimeout` | `number` | `5000` | Flush interval in ms (`PINGOPS_BATCH_TIMEOUT`) |
|
|
149
146
|
| `exportMode` | `"batched"` \| `"immediate"` | `"batched"` | `PINGOPS_EXPORT_MODE` |
|
|
@@ -155,6 +152,12 @@ Configuration can be provided via:
|
|
|
155
152
|
- **`batched`** — Best for long-running processes; spans are sent in batches (default).
|
|
156
153
|
- **`immediate`** — Best for serverless/short-lived processes; each span is sent as it finishes to reduce loss on freeze/exit.
|
|
157
154
|
|
|
155
|
+
**LLM monitoring (opt-in):**
|
|
156
|
+
|
|
157
|
+
- Enable with `llmMonitoring.enabled: true`.
|
|
158
|
+
- Supported providers auto-detected in v1: `openai`, `anthropic`, `gemini`, `xai`.
|
|
159
|
+
- Captures GenAI token attributes even when request/response body capture is disabled. Costs are computed on backend.
|
|
160
|
+
|
|
158
161
|
### Config file examples
|
|
159
162
|
|
|
160
163
|
**JSON (`pingops.config.json`):**
|
|
@@ -277,22 +280,29 @@ const data = await startTrace(
|
|
|
277
280
|
|
|
278
281
|
---
|
|
279
282
|
|
|
280
|
-
### `
|
|
283
|
+
### `suppressInstrumentation(fn)`
|
|
284
|
+
|
|
285
|
+
Runs `fn` in an intentionally tracing-suppressed context.
|
|
286
|
+
|
|
287
|
+
Use this to exclude a specific code block from telemetry capture. Any spans started inside `fn` (including automatic outbound HTTP/fetch spans) are suppressed and not exported.
|
|
288
|
+
|
|
289
|
+
**Parameters:**
|
|
290
|
+
|
|
291
|
+
- `fn` — `() => T | Promise<T>`. Callback to run without tracing capture.
|
|
281
292
|
|
|
282
|
-
|
|
293
|
+
**Returns:** `T | Promise<T>` — The result of `fn`.
|
|
283
294
|
|
|
284
295
|
**Example:**
|
|
285
296
|
|
|
286
297
|
```typescript
|
|
287
|
-
import {
|
|
298
|
+
import { suppressInstrumentation } from "@pingops/sdk";
|
|
288
299
|
|
|
289
|
-
await
|
|
290
|
-
await fetch("https://
|
|
300
|
+
await suppressInstrumentation(async () => {
|
|
301
|
+
await fetch("https://internal.example.com/health");
|
|
302
|
+
// any outbound API calls here are intentionally not captured
|
|
291
303
|
});
|
|
292
304
|
```
|
|
293
305
|
|
|
294
|
-
This helper is scoped to the callback only and does not globally disable OpenTelemetry suppression.
|
|
295
|
-
|
|
296
306
|
---
|
|
297
307
|
|
|
298
308
|
### `getActiveTraceId()`
|
|
@@ -411,23 +421,8 @@ Each rule in `domainAllowList` / `domainDenyList` can include:
|
|
|
411
421
|
|
|
412
422
|
- `domain` — Exact or suffix (e.g. `.openai.com`) match.
|
|
413
423
|
- `paths` — Optional path prefixes to allow/deny.
|
|
414
|
-
- `headersAllowList` / `headersDenyList` — Header rules for that domain.
|
|
415
424
|
- `captureRequestBody` / `captureResponseBody` — Override body capture for that domain.
|
|
416
|
-
|
|
417
|
-
### Header allow/deny lists
|
|
418
|
-
|
|
419
|
-
Control which headers are included on captured spans (global default; domain rules can refine):
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
initializePingops({
|
|
423
|
-
baseUrl: "https://api.pingops.com",
|
|
424
|
-
serviceName: "my-service",
|
|
425
|
-
headersAllowList: ["user-agent", "x-request-id", "content-type"],
|
|
426
|
-
headersDenyList: ["authorization", "cookie", "x-api-key"],
|
|
427
|
-
});
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
Deny list takes precedence over allow list. Sensitive headers are redacted by default; use `headerRedaction` in config for custom behavior.
|
|
425
|
+
- `transforms` — Domain-specific transform configuration (overrides global `transforms` for matching spans).
|
|
431
426
|
|
|
432
427
|
### Request/response body capture
|
|
433
428
|
|
|
@@ -435,8 +430,6 @@ Deny list takes precedence over allow list. Sensitive headers are redacted by de
|
|
|
435
430
|
- **Per-domain:** Same flags on a [DomainRule](#domain-allowdeny-lists).
|
|
436
431
|
- **Per-trace:** `captureRequestBody` / `captureResponseBody` in [PingopsTraceAttributes](#pingopstraceattributes) in `startTrace`.
|
|
437
432
|
|
|
438
|
-
Body size is capped by `maxRequestBodySize` and `maxResponseBodySize` (default 4096 bytes each). Larger bodies are truncated.
|
|
439
|
-
|
|
440
433
|
---
|
|
441
434
|
|
|
442
435
|
## Integration with Existing OpenTelemetry
|
|
@@ -492,7 +485,6 @@ Only **CLIENT** spans with HTTP (or supported semantic) attributes are exported
|
|
|
492
485
|
| Manual init | `initializePingops({ baseUrl, serviceName, ... })` before any HTTP usage |
|
|
493
486
|
| Config from file | `PINGOPS_CONFIG_FILE=./pingops.config.yaml` or `initializePingops("./pingops.config.json")` |
|
|
494
487
|
| Trace with context | `startTrace({ attributes: { userId, sessionId, tags, metadata }, seed? }, async () => { ... })` |
|
|
495
|
-
| Unsuppress a scope | `runUnsuppressed(async () => { ... })` |
|
|
496
488
|
| Get current IDs | `getActiveTraceId()`, `getActiveSpanId()` |
|
|
497
489
|
| Graceful shutdown | `await shutdownPingops()` |
|
|
498
490
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const require_pingops = require('./pingops-
|
|
1
|
+
const require_pingops = require('./pingops-D7n9pxch.cjs');
|
|
2
2
|
|
|
3
3
|
exports.getActiveSpanId = require_pingops.getActiveSpanId;
|
|
4
4
|
exports.getActiveTraceId = require_pingops.getActiveTraceId;
|
|
5
5
|
exports.initializePingops = require_pingops.initializePingops;
|
|
6
|
-
exports.runUnsuppressed = require_pingops.runUnsuppressed;
|
|
7
6
|
exports.shutdownPingops = require_pingops.shutdownPingops;
|
|
8
|
-
exports.startTrace = require_pingops.startTrace;
|
|
7
|
+
exports.startTrace = require_pingops.startTrace;
|
|
8
|
+
exports.suppressInstrumentation = require_pingops.suppressInstrumentation;
|
package/dist/index.d.cts
CHANGED
|
@@ -34,6 +34,14 @@ declare function getActiveTraceId(): string | undefined;
|
|
|
34
34
|
* Returns the span ID of the currently active span, if any.
|
|
35
35
|
*/
|
|
36
36
|
declare function getActiveSpanId(): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Runs the callback in an intentionally tracing-suppressed context.
|
|
39
|
+
*
|
|
40
|
+
* Any spans started in this callback (including auto-instrumented outbound HTTP/fetch spans)
|
|
41
|
+
* are intentionally dropped and not exported.
|
|
42
|
+
*/
|
|
43
|
+
declare function suppressInstrumentation<T>(fn: () => T): T;
|
|
44
|
+
declare function suppressInstrumentation<T>(fn: () => Promise<T>): Promise<T>;
|
|
37
45
|
/**
|
|
38
46
|
* Starts a new trace using the PingOps tracer provider and runs the callback within that trace.
|
|
39
47
|
* Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are
|
|
@@ -67,11 +75,6 @@ declare function startTrace<T>(options: {
|
|
|
67
75
|
attributes?: PingopsTraceAttributes$1;
|
|
68
76
|
seed?: string;
|
|
69
77
|
}, fn: () => T | Promise<T>): Promise<T>;
|
|
70
|
-
/**
|
|
71
|
-
* Runs a callback in a context that is guaranteed to be unsuppressed.
|
|
72
|
-
* Useful for task/job boundaries where suppression may have leaked.
|
|
73
|
-
*/
|
|
74
|
-
declare function runUnsuppressed<T>(fn: () => T): T;
|
|
75
78
|
//#endregion
|
|
76
|
-
export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops,
|
|
79
|
+
export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace, suppressInstrumentation };
|
|
77
80
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;AAsTA;AAOA;AAUA;AACA;;;;;;AAwCA;;;;;AAMW,iBAhTK,iBAAA,CAgTL,MAAA,EA/SD,sBA+SC,EAAA,QAAA,CAAA,EAAA,OAAA,CAAA,EAAA,IAAA;AAAR,iBA5Sa,iBAAA,CA4Sb,cAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,OAAA,CAAA,EAAA,IAAA;AAAO,iBAxSM,iBAAA,CAwSN,MAAA,EAAA;;;;;;iBA9LY,eAAA,CAAA,GAAmB;;;;iBA8HzB,gBAAA,CAAA;;;;iBAOA,eAAA,CAAA;;;;;;;iBAUA,qCAAqC,IAAI;iBACzC,qCAAqC,QAAQ,KAAK,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCpD;eAEL;;aAGL,IAAI,QAAQ,KACrB,QAAQ"}
|
package/dist/index.d.mts
CHANGED
|
@@ -34,6 +34,14 @@ declare function getActiveTraceId(): string | undefined;
|
|
|
34
34
|
* Returns the span ID of the currently active span, if any.
|
|
35
35
|
*/
|
|
36
36
|
declare function getActiveSpanId(): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Runs the callback in an intentionally tracing-suppressed context.
|
|
39
|
+
*
|
|
40
|
+
* Any spans started in this callback (including auto-instrumented outbound HTTP/fetch spans)
|
|
41
|
+
* are intentionally dropped and not exported.
|
|
42
|
+
*/
|
|
43
|
+
declare function suppressInstrumentation<T>(fn: () => T): T;
|
|
44
|
+
declare function suppressInstrumentation<T>(fn: () => Promise<T>): Promise<T>;
|
|
37
45
|
/**
|
|
38
46
|
* Starts a new trace using the PingOps tracer provider and runs the callback within that trace.
|
|
39
47
|
* Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are
|
|
@@ -67,11 +75,6 @@ declare function startTrace<T>(options: {
|
|
|
67
75
|
attributes?: PingopsTraceAttributes$1;
|
|
68
76
|
seed?: string;
|
|
69
77
|
}, fn: () => T | Promise<T>): Promise<T>;
|
|
70
|
-
/**
|
|
71
|
-
* Runs a callback in a context that is guaranteed to be unsuppressed.
|
|
72
|
-
* Useful for task/job boundaries where suppression may have leaked.
|
|
73
|
-
*/
|
|
74
|
-
declare function runUnsuppressed<T>(fn: () => T): T;
|
|
75
78
|
//#endregion
|
|
76
|
-
export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops,
|
|
79
|
+
export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace, suppressInstrumentation };
|
|
77
80
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;AAsTA;AAOA;AAUA;AACA;;;;;;AAwCA;;;;;AAMW,iBAhTK,iBAAA,CAgTL,MAAA,EA/SD,sBA+SC,EAAA,QAAA,CAAA,EAAA,OAAA,CAAA,EAAA,IAAA;AAAR,iBA5Sa,iBAAA,CA4Sb,cAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,OAAA,CAAA,EAAA,IAAA;AAAO,iBAxSM,iBAAA,CAwSN,MAAA,EAAA;;;;;;iBA9LY,eAAA,CAAA,GAAmB;;;;iBA8HzB,gBAAA,CAAA;;;;iBAOA,eAAA,CAAA;;;;;;;iBAUA,qCAAqC,IAAI;iBACzC,qCAAqC,QAAQ,KAAK,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCpD;eAEL;;aAGL,IAAI,QAAQ,KACrB,QAAQ"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as startTrace, i as shutdownPingops, n as getActiveTraceId, o as suppressInstrumentation, r as initializePingops, t as getActiveSpanId } from "./pingops-DDeCBa2d.mjs";
|
|
2
2
|
|
|
3
|
-
export { getActiveSpanId, getActiveTraceId, initializePingops,
|
|
3
|
+
export { getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace, suppressInstrumentation };
|
|
@@ -70,6 +70,15 @@ function setSdkInitialized(initialized) {
|
|
|
70
70
|
isSdkInitializedFlag$1 = initialized;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region package.json
|
|
75
|
+
var version = "0.4.0";
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/sdk-version.ts
|
|
79
|
+
const packageVersion = version;
|
|
80
|
+
const SDK_VERSION = typeof packageVersion === "string" ? packageVersion : "unknown";
|
|
81
|
+
|
|
73
82
|
//#endregion
|
|
74
83
|
//#region src/pingops.ts
|
|
75
84
|
/**
|
|
@@ -96,7 +105,10 @@ function initializePingops(config, explicit = true) {
|
|
|
96
105
|
return;
|
|
97
106
|
}
|
|
98
107
|
const resource = (0, _opentelemetry_resources.resourceFromAttributes)({ [_opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: resolvedConfig.serviceName });
|
|
99
|
-
const processor = new _pingops_otel.PingopsSpanProcessor(
|
|
108
|
+
const processor = new _pingops_otel.PingopsSpanProcessor({
|
|
109
|
+
...resolvedConfig,
|
|
110
|
+
sdkVersion: SDK_VERSION
|
|
111
|
+
});
|
|
100
112
|
const instrumentations = (0, _pingops_otel.getInstrumentations)();
|
|
101
113
|
const nodeSdk = new _opentelemetry_sdk_node.NodeSDK({
|
|
102
114
|
resource,
|
|
@@ -229,6 +241,10 @@ function getActiveTraceId() {
|
|
|
229
241
|
function getActiveSpanId() {
|
|
230
242
|
return _opentelemetry_api.trace.getActiveSpan()?.spanContext().spanId;
|
|
231
243
|
}
|
|
244
|
+
function suppressInstrumentation(fn) {
|
|
245
|
+
const untrackedContext = (0, _opentelemetry_core.suppressTracing)(_opentelemetry_api.context.active()).setValue(_pingops_core.PINGOPS_INTENTIONAL_SUPPRESSION, true);
|
|
246
|
+
return _opentelemetry_api.context.with(untrackedContext, fn);
|
|
247
|
+
}
|
|
232
248
|
/**
|
|
233
249
|
* Starts a new trace using the PingOps tracer provider and runs the callback within that trace.
|
|
234
250
|
* Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are
|
|
@@ -266,49 +282,35 @@ async function startTrace(options, fn) {
|
|
|
266
282
|
spanId: (0, _pingops_core.uint8ArrayToHex)(crypto.getRandomValues(new Uint8Array(8))),
|
|
267
283
|
traceFlags: TRACE_FLAG_SAMPLED
|
|
268
284
|
};
|
|
269
|
-
const
|
|
270
|
-
const traceExecutionBaseContext = (0, _opentelemetry_core.isTracingSuppressed)(activeContext) ? _opentelemetry_api.ROOT_CONTEXT : activeContext;
|
|
285
|
+
const traceExecutionBaseContext = getUnsuppressedContext();
|
|
271
286
|
if (traceExecutionBaseContext === _opentelemetry_api.ROOT_CONTEXT) if (!hasLoggedSuppressedStartTraceWarning) {
|
|
272
287
|
logger.warn("startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation");
|
|
273
288
|
hasLoggedSuppressedStartTraceWarning = true;
|
|
274
289
|
} else logger.debug("startTrace received a suppressed active context; running trace on ROOT_CONTEXT");
|
|
275
|
-
const
|
|
276
|
-
|
|
290
|
+
const runWithAttributes = () => {
|
|
291
|
+
let contextWithAttributes = _opentelemetry_api.context.active();
|
|
292
|
+
const attrs = options.attributes;
|
|
293
|
+
if (attrs) contextWithAttributes = setAttributesInContext(contextWithAttributes, attrs);
|
|
294
|
+
contextWithAttributes = contextWithAttributes.setValue(_pingops_core.PINGOPS_TRACE_ID, traceId);
|
|
295
|
+
return _opentelemetry_api.context.with(contextWithAttributes, fn);
|
|
296
|
+
};
|
|
277
297
|
return new Promise((resolve$1, reject) => {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
resolve$1(v);
|
|
289
|
-
}).catch((err) => {
|
|
290
|
-
span.end();
|
|
291
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
292
|
-
});
|
|
293
|
-
else {
|
|
294
|
-
span.end();
|
|
295
|
-
resolve$1(result);
|
|
296
|
-
}
|
|
297
|
-
} catch (err) {
|
|
298
|
-
span.end();
|
|
299
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
300
|
-
}
|
|
301
|
-
});
|
|
298
|
+
const nonRecordingParent = _opentelemetry_api.trace.wrapSpanContext(spanContext);
|
|
299
|
+
const contextWithParentSpan = _opentelemetry_api.trace.setSpan(traceExecutionBaseContext, nonRecordingParent);
|
|
300
|
+
const contextWithTrace = _opentelemetry_api.trace.setSpanContext(contextWithParentSpan, spanContext);
|
|
301
|
+
try {
|
|
302
|
+
const result = _opentelemetry_api.context.with(contextWithTrace, runWithAttributes);
|
|
303
|
+
if (result instanceof Promise) result.then((v) => resolve$1(v)).catch((err) => reject(err instanceof Error ? err : new Error(String(err))));
|
|
304
|
+
else resolve$1(result);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
307
|
+
}
|
|
302
308
|
});
|
|
303
309
|
}
|
|
304
|
-
|
|
305
|
-
* Runs a callback in a context that is guaranteed to be unsuppressed.
|
|
306
|
-
* Useful for task/job boundaries where suppression may have leaked.
|
|
307
|
-
*/
|
|
308
|
-
function runUnsuppressed(fn) {
|
|
310
|
+
function getUnsuppressedContext() {
|
|
309
311
|
const activeContext = _opentelemetry_api.context.active();
|
|
310
|
-
|
|
311
|
-
return
|
|
312
|
+
if (activeContext.getValue(_pingops_core.PINGOPS_INTENTIONAL_SUPPRESSION) === true) return activeContext;
|
|
313
|
+
return (0, _opentelemetry_core.isTracingSuppressed)(activeContext) ? _opentelemetry_api.ROOT_CONTEXT : activeContext;
|
|
312
314
|
}
|
|
313
315
|
function setAttributesInContext(ctx, attrs) {
|
|
314
316
|
if (attrs.userId !== void 0) ctx = ctx.setValue(_pingops_core.PINGOPS_USER_ID, attrs.userId);
|
|
@@ -351,12 +353,6 @@ Object.defineProperty(exports, 'mergeConfigWithEnv', {
|
|
|
351
353
|
return mergeConfigWithEnv;
|
|
352
354
|
}
|
|
353
355
|
});
|
|
354
|
-
Object.defineProperty(exports, 'runUnsuppressed', {
|
|
355
|
-
enumerable: true,
|
|
356
|
-
get: function () {
|
|
357
|
-
return runUnsuppressed;
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
356
|
Object.defineProperty(exports, 'shutdownPingops', {
|
|
361
357
|
enumerable: true,
|
|
362
358
|
get: function () {
|
|
@@ -369,4 +365,10 @@ Object.defineProperty(exports, 'startTrace', {
|
|
|
369
365
|
return startTrace;
|
|
370
366
|
}
|
|
371
367
|
});
|
|
372
|
-
|
|
368
|
+
Object.defineProperty(exports, 'suppressInstrumentation', {
|
|
369
|
+
enumerable: true,
|
|
370
|
+
get: function () {
|
|
371
|
+
return suppressInstrumentation;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
//# sourceMappingURL=pingops-D7n9pxch.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pingops-D7n9pxch.cjs","names":["isSdkInitializedFlag","(sdkPackageJson as PackageJsonWithVersion).version","ATTR_SERVICE_NAME","PingopsSpanProcessor","NodeSDK","NodeTracerProvider","trace","context","PINGOPS_INTENTIONAL_SUPPRESSION","ROOT_CONTEXT","PINGOPS_TRACE_ID","resolve","PINGOPS_USER_ID","PINGOPS_SESSION_ID","PINGOPS_TAGS","PINGOPS_METADATA","PINGOPS_CAPTURE_REQUEST_BODY","PINGOPS_CAPTURE_RESPONSE_BODY"],"sources":["../src/config-loader.ts","../src/init-state.ts","../package.json","../src/sdk-version.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Returns whether the SDK has been initialized.\n */\nexport function isSdkInitialized(): boolean {\n return isSdkInitializedFlag;\n}\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","","import sdkPackageJson from \"../package.json\";\n\ntype PackageJsonWithVersion = {\n version?: string;\n};\n\nconst packageVersion = (sdkPackageJson as PackageJsonWithVersion).version;\n\nexport const SDK_VERSION =\n typeof packageVersion === \"string\" ? packageVersion : \"unknown\";\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { ROOT_CONTEXT, context, trace } from \"@opentelemetry/api\";\nimport { isTracingSuppressed, suppressTracing } from \"@opentelemetry/core\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n PINGOPS_INTENTIONAL_SUPPRESSION,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\nimport { SDK_VERSION } from \"./sdk-version\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\nlet hasLoggedSuppressedStartTraceWarning = false;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor({\n ...resolvedConfig,\n sdkVersion: SDK_VERSION,\n });\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Runs the callback in an intentionally tracing-suppressed context.\n *\n * Any spans started in this callback (including auto-instrumented outbound HTTP/fetch spans)\n * are intentionally dropped and not exported.\n */\nexport function suppressInstrumentation<T>(fn: () => T): T;\nexport function suppressInstrumentation<T>(fn: () => Promise<T>): Promise<T>;\nexport function suppressInstrumentation<T>(\n fn: () => T | Promise<T>\n): T | Promise<T> {\n const untrackedContext = suppressTracing(context.active()).setValue(\n PINGOPS_INTENTIONAL_SUPPRESSION,\n true\n );\n return context.with(untrackedContext, fn);\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: {\n attributes?: PingopsTraceAttributes;\n seed?: string;\n },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const traceExecutionBaseContext = getUnsuppressedContext();\n if (traceExecutionBaseContext === ROOT_CONTEXT) {\n if (!hasLoggedSuppressedStartTraceWarning) {\n logger.warn(\n \"startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation\"\n );\n hasLoggedSuppressedStartTraceWarning = true;\n } else {\n logger.debug(\n \"startTrace received a suppressed active context; running trace on ROOT_CONTEXT\"\n );\n }\n }\n const runWithAttributes = (): T | Promise<T> => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n return context.with(contextWithAttributes, fn);\n };\n\n return new Promise((resolve, reject) => {\n const nonRecordingParent = trace.wrapSpanContext(spanContext);\n const contextWithParentSpan = trace.setSpan(\n traceExecutionBaseContext,\n nonRecordingParent\n );\n const contextWithTrace = trace.setSpanContext(\n contextWithParentSpan,\n spanContext\n );\n try {\n const result = context.with(contextWithTrace, runWithAttributes);\n if (result instanceof Promise) {\n result\n .then((v) => resolve(v))\n .catch((err) =>\n reject(err instanceof Error ? err : new Error(String(err)))\n );\n } else {\n resolve(result);\n }\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n}\n\nfunction getUnsuppressedContext(): ReturnType<typeof context.active> {\n const activeContext = context.active();\n if (activeContext.getValue(PINGOPS_INTENTIONAL_SUPPRESSION) === true) {\n return activeContext;\n }\n return isTracingSuppressed(activeContext) ? ROOT_CONTEXT : activeContext;\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ROOT_CONTEXT, context, trace } from "@opentelemetry/api";
|
|
2
|
-
import { isTracingSuppressed } from "@opentelemetry/core";
|
|
2
|
+
import { isTracingSuppressed, suppressTracing } from "@opentelemetry/core";
|
|
3
3
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
4
4
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
5
5
|
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
6
6
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
7
7
|
import { PingopsSpanProcessor, getInstrumentations, getPingopsTracerProvider, setPingopsTracerProvider, shutdownTracerProvider } from "@pingops/otel";
|
|
8
|
-
import { PINGOPS_CAPTURE_REQUEST_BODY, PINGOPS_CAPTURE_RESPONSE_BODY, PINGOPS_METADATA, PINGOPS_SESSION_ID, PINGOPS_TAGS, PINGOPS_TRACE_ID, PINGOPS_USER_ID, createLogger, createTraceId, uint8ArrayToHex } from "@pingops/core";
|
|
8
|
+
import { PINGOPS_CAPTURE_REQUEST_BODY, PINGOPS_CAPTURE_RESPONSE_BODY, PINGOPS_INTENTIONAL_SUPPRESSION, PINGOPS_METADATA, PINGOPS_SESSION_ID, PINGOPS_TAGS, PINGOPS_TRACE_ID, PINGOPS_USER_ID, createLogger, createTraceId, uint8ArrayToHex } from "@pingops/core";
|
|
9
9
|
import { readFileSync } from "node:fs";
|
|
10
10
|
import { resolve } from "node:path";
|
|
11
11
|
import { load } from "js-yaml";
|
|
@@ -70,6 +70,15 @@ function setSdkInitialized(initialized) {
|
|
|
70
70
|
isSdkInitializedFlag$1 = initialized;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region package.json
|
|
75
|
+
var version = "0.4.0";
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/sdk-version.ts
|
|
79
|
+
const packageVersion = version;
|
|
80
|
+
const SDK_VERSION = typeof packageVersion === "string" ? packageVersion : "unknown";
|
|
81
|
+
|
|
73
82
|
//#endregion
|
|
74
83
|
//#region src/pingops.ts
|
|
75
84
|
/**
|
|
@@ -96,7 +105,10 @@ function initializePingops(config, explicit = true) {
|
|
|
96
105
|
return;
|
|
97
106
|
}
|
|
98
107
|
const resource = resourceFromAttributes({ [ATTR_SERVICE_NAME]: resolvedConfig.serviceName });
|
|
99
|
-
const processor = new PingopsSpanProcessor(
|
|
108
|
+
const processor = new PingopsSpanProcessor({
|
|
109
|
+
...resolvedConfig,
|
|
110
|
+
sdkVersion: SDK_VERSION
|
|
111
|
+
});
|
|
100
112
|
const instrumentations = getInstrumentations();
|
|
101
113
|
const nodeSdk = new NodeSDK({
|
|
102
114
|
resource,
|
|
@@ -229,6 +241,10 @@ function getActiveTraceId() {
|
|
|
229
241
|
function getActiveSpanId() {
|
|
230
242
|
return trace.getActiveSpan()?.spanContext().spanId;
|
|
231
243
|
}
|
|
244
|
+
function suppressInstrumentation(fn) {
|
|
245
|
+
const untrackedContext = suppressTracing(context.active()).setValue(PINGOPS_INTENTIONAL_SUPPRESSION, true);
|
|
246
|
+
return context.with(untrackedContext, fn);
|
|
247
|
+
}
|
|
232
248
|
/**
|
|
233
249
|
* Starts a new trace using the PingOps tracer provider and runs the callback within that trace.
|
|
234
250
|
* Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are
|
|
@@ -266,49 +282,35 @@ async function startTrace(options, fn) {
|
|
|
266
282
|
spanId: uint8ArrayToHex(crypto.getRandomValues(new Uint8Array(8))),
|
|
267
283
|
traceFlags: TRACE_FLAG_SAMPLED
|
|
268
284
|
};
|
|
269
|
-
const
|
|
270
|
-
const traceExecutionBaseContext = isTracingSuppressed(activeContext) ? ROOT_CONTEXT : activeContext;
|
|
285
|
+
const traceExecutionBaseContext = getUnsuppressedContext();
|
|
271
286
|
if (traceExecutionBaseContext === ROOT_CONTEXT) if (!hasLoggedSuppressedStartTraceWarning) {
|
|
272
287
|
logger.warn("startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation");
|
|
273
288
|
hasLoggedSuppressedStartTraceWarning = true;
|
|
274
289
|
} else logger.debug("startTrace received a suppressed active context; running trace on ROOT_CONTEXT");
|
|
275
|
-
const
|
|
276
|
-
|
|
290
|
+
const runWithAttributes = () => {
|
|
291
|
+
let contextWithAttributes = context.active();
|
|
292
|
+
const attrs = options.attributes;
|
|
293
|
+
if (attrs) contextWithAttributes = setAttributesInContext(contextWithAttributes, attrs);
|
|
294
|
+
contextWithAttributes = contextWithAttributes.setValue(PINGOPS_TRACE_ID, traceId);
|
|
295
|
+
return context.with(contextWithAttributes, fn);
|
|
296
|
+
};
|
|
277
297
|
return new Promise((resolve$1, reject) => {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
resolve$1(v);
|
|
289
|
-
}).catch((err) => {
|
|
290
|
-
span.end();
|
|
291
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
292
|
-
});
|
|
293
|
-
else {
|
|
294
|
-
span.end();
|
|
295
|
-
resolve$1(result);
|
|
296
|
-
}
|
|
297
|
-
} catch (err) {
|
|
298
|
-
span.end();
|
|
299
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
300
|
-
}
|
|
301
|
-
});
|
|
298
|
+
const nonRecordingParent = trace.wrapSpanContext(spanContext);
|
|
299
|
+
const contextWithParentSpan = trace.setSpan(traceExecutionBaseContext, nonRecordingParent);
|
|
300
|
+
const contextWithTrace = trace.setSpanContext(contextWithParentSpan, spanContext);
|
|
301
|
+
try {
|
|
302
|
+
const result = context.with(contextWithTrace, runWithAttributes);
|
|
303
|
+
if (result instanceof Promise) result.then((v) => resolve$1(v)).catch((err) => reject(err instanceof Error ? err : new Error(String(err))));
|
|
304
|
+
else resolve$1(result);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
307
|
+
}
|
|
302
308
|
});
|
|
303
309
|
}
|
|
304
|
-
|
|
305
|
-
* Runs a callback in a context that is guaranteed to be unsuppressed.
|
|
306
|
-
* Useful for task/job boundaries where suppression may have leaked.
|
|
307
|
-
*/
|
|
308
|
-
function runUnsuppressed(fn) {
|
|
310
|
+
function getUnsuppressedContext() {
|
|
309
311
|
const activeContext = context.active();
|
|
310
|
-
|
|
311
|
-
return
|
|
312
|
+
if (activeContext.getValue(PINGOPS_INTENTIONAL_SUPPRESSION) === true) return activeContext;
|
|
313
|
+
return isTracingSuppressed(activeContext) ? ROOT_CONTEXT : activeContext;
|
|
312
314
|
}
|
|
313
315
|
function setAttributesInContext(ctx, attrs) {
|
|
314
316
|
if (attrs.userId !== void 0) ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);
|
|
@@ -321,5 +323,5 @@ function setAttributesInContext(ctx, attrs) {
|
|
|
321
323
|
}
|
|
322
324
|
|
|
323
325
|
//#endregion
|
|
324
|
-
export {
|
|
325
|
-
//# sourceMappingURL=pingops-
|
|
326
|
+
export { startTrace as a, mergeConfigWithEnv as c, shutdownPingops as i, getActiveTraceId as n, suppressInstrumentation as o, initializePingops as r, loadConfigFromFile as s, getActiveSpanId as t };
|
|
327
|
+
//# sourceMappingURL=pingops-DDeCBa2d.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pingops-DDeCBa2d.mjs","names":["loadYaml","isSdkInitializedFlag","(sdkPackageJson as PackageJsonWithVersion).version","resolve"],"sources":["../src/config-loader.ts","../src/init-state.ts","../package.json","../src/sdk-version.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Returns whether the SDK has been initialized.\n */\nexport function isSdkInitialized(): boolean {\n return isSdkInitializedFlag;\n}\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","","import sdkPackageJson from \"../package.json\";\n\ntype PackageJsonWithVersion = {\n version?: string;\n};\n\nconst packageVersion = (sdkPackageJson as PackageJsonWithVersion).version;\n\nexport const SDK_VERSION =\n typeof packageVersion === \"string\" ? packageVersion : \"unknown\";\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { ROOT_CONTEXT, context, trace } from \"@opentelemetry/api\";\nimport { isTracingSuppressed, suppressTracing } from \"@opentelemetry/core\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n PINGOPS_INTENTIONAL_SUPPRESSION,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\nimport { SDK_VERSION } from \"./sdk-version\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\nlet hasLoggedSuppressedStartTraceWarning = false;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor({\n ...resolvedConfig,\n sdkVersion: SDK_VERSION,\n });\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Runs the callback in an intentionally tracing-suppressed context.\n *\n * Any spans started in this callback (including auto-instrumented outbound HTTP/fetch spans)\n * are intentionally dropped and not exported.\n */\nexport function suppressInstrumentation<T>(fn: () => T): T;\nexport function suppressInstrumentation<T>(fn: () => Promise<T>): Promise<T>;\nexport function suppressInstrumentation<T>(\n fn: () => T | Promise<T>\n): T | Promise<T> {\n const untrackedContext = suppressTracing(context.active()).setValue(\n PINGOPS_INTENTIONAL_SUPPRESSION,\n true\n );\n return context.with(untrackedContext, fn);\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: {\n attributes?: PingopsTraceAttributes;\n seed?: string;\n },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const traceExecutionBaseContext = getUnsuppressedContext();\n if (traceExecutionBaseContext === ROOT_CONTEXT) {\n if (!hasLoggedSuppressedStartTraceWarning) {\n logger.warn(\n \"startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation\"\n );\n hasLoggedSuppressedStartTraceWarning = true;\n } else {\n logger.debug(\n \"startTrace received a suppressed active context; running trace on ROOT_CONTEXT\"\n );\n }\n }\n const runWithAttributes = (): T | Promise<T> => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n return context.with(contextWithAttributes, fn);\n };\n\n return new Promise((resolve, reject) => {\n const nonRecordingParent = trace.wrapSpanContext(spanContext);\n const contextWithParentSpan = trace.setSpan(\n traceExecutionBaseContext,\n nonRecordingParent\n );\n const contextWithTrace = trace.setSpanContext(\n contextWithParentSpan,\n spanContext\n );\n try {\n const result = context.with(contextWithTrace, runWithAttributes);\n if (result instanceof Promise) {\n result\n .then((v) => resolve(v))\n .catch((err) =>\n reject(err instanceof Error ? err : new Error(String(err)))\n );\n } else {\n resolve(result);\n }\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n}\n\nfunction getUnsuppressedContext(): ReturnType<typeof context.active> {\n const activeContext = context.active();\n if (activeContext.getValue(PINGOPS_INTENTIONAL_SUPPRESSION) === true) {\n return activeContext;\n }\n return isTracingSuppressed(activeContext) ? ROOT_CONTEXT : activeContext;\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
|
package/dist/register.cjs
CHANGED
package/dist/register.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pingops/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20"
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
|
46
46
|
"@opentelemetry/semantic-conventions": "^1.38.0",
|
|
47
47
|
"js-yaml": "^4.1.0",
|
|
48
|
-
"@pingops/
|
|
49
|
-
"@pingops/
|
|
48
|
+
"@pingops/core": "^0.4.0",
|
|
49
|
+
"@pingops/otel": "^0.4.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@types/node": "^20.11.0",
|
|
53
52
|
"@types/js-yaml": "^4.0.9",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
53
|
+
"@types/node": "^20.11.0",
|
|
54
|
+
"axios": "^1.13.5",
|
|
56
55
|
"tsx": "^4.7.0",
|
|
57
|
-
"typescript": "^5.6.0"
|
|
56
|
+
"typescript": "^5.6.0",
|
|
57
|
+
"vitest": "^3.2.4"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsdown",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pingops-BM2jW6Bi.cjs","names":["isSdkInitializedFlag","ATTR_SERVICE_NAME","PingopsSpanProcessor","NodeSDK","NodeTracerProvider","trace","context","ROOT_CONTEXT","PINGOPS_TRACE_ID","PINGOPS_USER_ID","PINGOPS_SESSION_ID","PINGOPS_TAGS","PINGOPS_METADATA","PINGOPS_CAPTURE_REQUEST_BODY","PINGOPS_CAPTURE_RESPONSE_BODY"],"sources":["../src/config-loader.ts","../src/init-state.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Returns whether the SDK has been initialized.\n */\nexport function isSdkInitialized(): boolean {\n return isSdkInitializedFlag;\n}\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { ROOT_CONTEXT, context, trace } from \"@opentelemetry/api\";\nimport { isTracingSuppressed } from \"@opentelemetry/core\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\nlet hasLoggedSuppressedStartTraceWarning = false;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor(resolvedConfig);\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: { attributes?: PingopsTraceAttributes; seed?: string },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const activeContext = context.active();\n const traceExecutionBaseContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n if (traceExecutionBaseContext === ROOT_CONTEXT) {\n if (!hasLoggedSuppressedStartTraceWarning) {\n logger.warn(\n \"startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation\"\n );\n hasLoggedSuppressedStartTraceWarning = true;\n } else {\n logger.debug(\n \"startTrace received a suppressed active context; running trace on ROOT_CONTEXT\"\n );\n }\n }\n const contextWithSpanContext = trace.setSpanContext(\n traceExecutionBaseContext,\n spanContext\n );\n\n const tracer = getPingopsTracerProvider().getTracer(\"pingops-sdk\", \"1.0.0\");\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(\n \"pingops-trace\",\n {},\n contextWithSpanContext,\n (span) => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n\n const run = () => fn();\n\n try {\n const result = context.with(contextWithAttributes, run);\n if (result instanceof Promise) {\n result\n .then((v) => {\n span.end();\n resolve(v);\n })\n .catch((err) => {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n } else {\n span.end();\n resolve(result);\n }\n } catch (err) {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n );\n });\n}\n\n/**\n * Runs a callback in a context that is guaranteed to be unsuppressed.\n * Useful for task/job boundaries where suppression may have leaked.\n */\nexport function runUnsuppressed<T>(fn: () => T): T {\n const activeContext = context.active();\n const unsuppressedContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n return context.with(unsuppressedContext, fn);\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pingops-SV21E4bp.mjs","names":["loadYaml","isSdkInitializedFlag"],"sources":["../src/config-loader.ts","../src/init-state.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Returns whether the SDK has been initialized.\n */\nexport function isSdkInitialized(): boolean {\n return isSdkInitializedFlag;\n}\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { ROOT_CONTEXT, context, trace } from \"@opentelemetry/api\";\nimport { isTracingSuppressed } from \"@opentelemetry/core\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\nlet hasLoggedSuppressedStartTraceWarning = false;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor(resolvedConfig);\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: { attributes?: PingopsTraceAttributes; seed?: string },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const activeContext = context.active();\n const traceExecutionBaseContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n if (traceExecutionBaseContext === ROOT_CONTEXT) {\n if (!hasLoggedSuppressedStartTraceWarning) {\n logger.warn(\n \"startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation\"\n );\n hasLoggedSuppressedStartTraceWarning = true;\n } else {\n logger.debug(\n \"startTrace received a suppressed active context; running trace on ROOT_CONTEXT\"\n );\n }\n }\n const contextWithSpanContext = trace.setSpanContext(\n traceExecutionBaseContext,\n spanContext\n );\n\n const tracer = getPingopsTracerProvider().getTracer(\"pingops-sdk\", \"1.0.0\");\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(\n \"pingops-trace\",\n {},\n contextWithSpanContext,\n (span) => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n\n const run = () => fn();\n\n try {\n const result = context.with(contextWithAttributes, run);\n if (result instanceof Promise) {\n result\n .then((v) => {\n span.end();\n resolve(v);\n })\n .catch((err) => {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n } else {\n span.end();\n resolve(result);\n }\n } catch (err) {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n );\n });\n}\n\n/**\n * Runs a callback in a context that is guaranteed to be unsuppressed.\n * Useful for task/job boundaries where suppression may have leaked.\n */\nexport function runUnsuppressed<T>(fn: () => T): T {\n const activeContext = context.active();\n const unsuppressedContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n return context.with(unsuppressedContext, fn);\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
|