@pingops/sdk 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -85
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +25 -40
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +25 -40
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{pingops-Cb6UV5D8.cjs → pingops-BBXegVtL.cjs} +89 -58
- package/dist/pingops-BBXegVtL.cjs.map +1 -0
- package/dist/{pingops-DlQdUQEU.mjs → pingops-D4xrGLxy.mjs} +77 -58
- package/dist/pingops-D4xrGLxy.mjs.map +1 -0
- package/dist/register.cjs +1 -1
- package/dist/register.mjs +1 -1
- package/package.json +3 -3
- package/dist/pingops-Cb6UV5D8.cjs.map +0 -1
- package/dist/pingops-DlQdUQEU.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
# @pingops/sdk
|
|
2
2
|
|
|
3
|
-
PingOps SDK for Node.js
|
|
3
|
+
**PingOps SDK for Node.js** — Bootstrap OpenTelemetry and capture outgoing HTTP and fetch API calls with minimal code. Built for observability of external API usage, AI/LLM calls, and third-party integrations.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#overview)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [API Reference](#api-reference)
|
|
14
|
+
- [Tracing](#tracing)
|
|
15
|
+
- [Filtering & Privacy](#filtering--privacy)
|
|
16
|
+
- [Integration with Existing OpenTelemetry](#integration-with-existing-opentelemetry)
|
|
17
|
+
- [What Gets Captured](#what-gets-captured)
|
|
18
|
+
- [Requirements](#requirements)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Overview
|
|
23
|
+
|
|
24
|
+
The PingOps SDK gives you:
|
|
25
|
+
|
|
26
|
+
- **Automatic instrumentation** — Outgoing HTTP (Node.js `http` module) and `fetch` (via Undici) are instrumented without wrapping your code.
|
|
27
|
+
- **Structured traces** — Start traces with `userId`, `sessionId`, tags, and metadata so every span is tied to your business context.
|
|
28
|
+
- **Control over what is captured** — Domain allow/deny lists, header filtering, and optional request/response body capture with size limits.
|
|
29
|
+
- **Flexible setup** — Use environment variables, a config file (JSON or YAML), or pass config programmatically. Auto-initialize via `--require` or import `@pingops/sdk/register` first.
|
|
30
|
+
|
|
31
|
+
You initialize once (at process startup or before any HTTP clients load); after that, outgoing requests are captured and sent to your PingOps backend in batches or immediately.
|
|
32
|
+
|
|
33
|
+
---
|
|
4
34
|
|
|
5
35
|
## Installation
|
|
6
36
|
|
|
@@ -8,17 +38,29 @@ PingOps SDK for Node.js. Provides a simple API for bootstrapping OpenTelemetry a
|
|
|
8
38
|
pnpm add @pingops/sdk
|
|
9
39
|
```
|
|
10
40
|
|
|
41
|
+
Or with npm:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @pingops/sdk
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Requirement:** Node.js **20 or later** (for native `fetch` and modern APIs).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
11
51
|
## Quick Start
|
|
12
52
|
|
|
13
|
-
### Option 1: Auto-initialization (
|
|
53
|
+
### Option 1: Auto-initialization (recommended)
|
|
54
|
+
|
|
55
|
+
**Best for:** Getting started quickly and for production when config comes from environment or a config file.
|
|
14
56
|
|
|
15
|
-
**
|
|
57
|
+
**A. Using Node.js `--require`** (runs before any application code):
|
|
16
58
|
|
|
17
59
|
```bash
|
|
18
60
|
node --require @pingops/sdk/register your-app.js
|
|
19
61
|
```
|
|
20
62
|
|
|
21
|
-
Set environment variables:
|
|
63
|
+
Set required environment variables:
|
|
22
64
|
|
|
23
65
|
```bash
|
|
24
66
|
export PINGOPS_API_KEY="your-api-key"
|
|
@@ -26,168 +68,409 @@ export PINGOPS_BASE_URL="https://api.pingops.com"
|
|
|
26
68
|
export PINGOPS_SERVICE_NAME="my-service"
|
|
27
69
|
```
|
|
28
70
|
|
|
29
|
-
**
|
|
71
|
+
**B. Importing the register entry first** (must be before any HTTP client imports):
|
|
30
72
|
|
|
31
73
|
```typescript
|
|
32
|
-
//
|
|
74
|
+
// Must be first — before axios, node-fetch, or any code that makes HTTP requests
|
|
33
75
|
import "@pingops/sdk/register";
|
|
34
76
|
|
|
35
77
|
import axios from "axios";
|
|
36
|
-
// ... rest of your
|
|
78
|
+
// ... rest of your application
|
|
37
79
|
```
|
|
38
80
|
|
|
81
|
+
With a config file, set `PINGOPS_CONFIG_FILE` to the path to your JSON or YAML file; environment variables override values from the file.
|
|
82
|
+
|
|
39
83
|
### Option 2: Manual initialization
|
|
40
84
|
|
|
85
|
+
**Best for:** Config from code, feature flags, or when you need to ensure initialization order explicitly.
|
|
86
|
+
|
|
41
87
|
```typescript
|
|
42
88
|
import { initializePingops } from "@pingops/sdk";
|
|
43
89
|
|
|
44
|
-
//
|
|
90
|
+
// Before importing or using any HTTP clients
|
|
45
91
|
initializePingops({
|
|
46
|
-
apiKey:
|
|
92
|
+
apiKey: process.env.PINGOPS_API_KEY,
|
|
47
93
|
baseUrl: "https://api.pingops.com",
|
|
48
94
|
serviceName: "my-service",
|
|
49
95
|
});
|
|
50
96
|
|
|
51
|
-
|
|
97
|
+
import axios from "axios";
|
|
98
|
+
// ... rest of your application
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You can also initialize from a config file path (environment variables still override file values):
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
52
104
|
initializePingops("./pingops.config.yaml");
|
|
53
|
-
// or
|
|
105
|
+
// or
|
|
54
106
|
initializePingops({ configFile: "./pingops.config.json" });
|
|
55
107
|
```
|
|
56
108
|
|
|
57
|
-
**Important
|
|
109
|
+
**Important:** Call `initializePingops` before any HTTP client is loaded or used so instrumentation is applied correctly.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Configuration
|
|
114
|
+
|
|
115
|
+
Configuration can be provided via:
|
|
116
|
+
|
|
117
|
+
1. **Programmatic config** — Object passed to `initializePingops(...)`
|
|
118
|
+
2. **Config file** — Path as first argument or `{ configFile: "path" }`; supports JSON and YAML
|
|
119
|
+
3. **Environment variables** — Always override file and can supply all required fields for auto-init
|
|
120
|
+
|
|
121
|
+
### Required fields
|
|
122
|
+
|
|
123
|
+
| Field | Env var | Description |
|
|
124
|
+
| ------------- | ---------------------- | ------------------------- |
|
|
125
|
+
| `baseUrl` | `PINGOPS_BASE_URL` | PingOps backend base URL |
|
|
126
|
+
| `serviceName` | `PINGOPS_SERVICE_NAME` | Service name for resource |
|
|
127
|
+
|
|
128
|
+
`apiKey` is optional at config level; if your backend requires it, set `apiKey` or `PINGOPS_API_KEY`.
|
|
129
|
+
|
|
130
|
+
### Full configuration reference
|
|
131
|
+
|
|
132
|
+
| Option | Type | Default | Description |
|
|
133
|
+
| --------------------- | ---------------------------- | ------------ | ---------------------------------------------- |
|
|
134
|
+
| `apiKey` | `string` | — | API key (or `PINGOPS_API_KEY`) |
|
|
135
|
+
| `baseUrl` | `string` | **required** | Backend base URL |
|
|
136
|
+
| `serviceName` | `string` | **required** | Service name |
|
|
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
|
+
| `captureRequestBody` | `boolean` | `false` | Capture request bodies (global) |
|
|
141
|
+
| `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
|
+
| `domainAllowList` | `DomainRule[]` | — | Domains (and optional rules) to allow |
|
|
145
|
+
| `domainDenyList` | `DomainRule[]` | — | Domains to exclude |
|
|
146
|
+
| `headerRedaction` | `HeaderRedactionConfig` | — | Custom header redaction |
|
|
147
|
+
| `batchSize` | `number` | `50` | Spans per batch (`PINGOPS_BATCH_SIZE`) |
|
|
148
|
+
| `batchTimeout` | `number` | `5000` | Flush interval in ms (`PINGOPS_BATCH_TIMEOUT`) |
|
|
149
|
+
| `exportMode` | `"batched"` \| `"immediate"` | `"batched"` | `PINGOPS_EXPORT_MODE` |
|
|
150
|
+
|
|
151
|
+
**Config file path:** Set `PINGOPS_CONFIG_FILE` to the path of your JSON or YAML file when using the register entry.
|
|
152
|
+
|
|
153
|
+
**Export mode:**
|
|
154
|
+
|
|
155
|
+
- **`batched`** — Best for long-running processes; spans are sent in batches (default).
|
|
156
|
+
- **`immediate`** — Best for serverless/short-lived processes; each span is sent as it finishes to reduce loss on freeze/exit.
|
|
157
|
+
|
|
158
|
+
### Config file examples
|
|
159
|
+
|
|
160
|
+
**JSON (`pingops.config.json`):**
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"apiKey": "your-api-key",
|
|
165
|
+
"baseUrl": "https://api.pingops.com",
|
|
166
|
+
"serviceName": "my-service",
|
|
167
|
+
"debug": false,
|
|
168
|
+
"exportMode": "batched",
|
|
169
|
+
"batchSize": 50,
|
|
170
|
+
"batchTimeout": 5000,
|
|
171
|
+
"captureRequestBody": false,
|
|
172
|
+
"captureResponseBody": false
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**YAML (`pingops.config.yaml`):**
|
|
58
177
|
|
|
59
|
-
|
|
178
|
+
```yaml
|
|
179
|
+
apiKey: your-api-key
|
|
180
|
+
baseUrl: https://api.pingops.com
|
|
181
|
+
serviceName: my-service
|
|
182
|
+
debug: false
|
|
183
|
+
exportMode: batched
|
|
184
|
+
batchSize: 50
|
|
185
|
+
batchTimeout: 5000
|
|
186
|
+
```
|
|
60
187
|
|
|
61
|
-
|
|
62
|
-
- **Node.js Support**: Works in Node.js environments (including Node.js 20+ with native fetch)
|
|
63
|
-
- **GenAI Support**: Captures LLM calls using OpenTelemetry GenAI semantic conventions
|
|
64
|
-
- **Manual Instrumentation**: Create custom spans for specific operations
|
|
65
|
-
- **Zero Configuration**: Works out of the box with sensible defaults
|
|
188
|
+
---
|
|
66
189
|
|
|
67
|
-
## API
|
|
190
|
+
## API Reference
|
|
68
191
|
|
|
69
192
|
### `initializePingops(config)`
|
|
70
193
|
|
|
71
|
-
Initializes the PingOps SDK
|
|
194
|
+
Initializes the PingOps SDK: sets up OpenTelemetry `NodeSDK`, registers the PingOps span processor, and enables HTTP and Undici (fetch) instrumentation.
|
|
195
|
+
|
|
196
|
+
**Overloads:**
|
|
197
|
+
|
|
198
|
+
- `initializePingops(config: PingopsProcessorConfig): void`
|
|
199
|
+
- `initializePingops(configFilePath: string): void`
|
|
200
|
+
- `initializePingops({ configFile: string }): void`
|
|
72
201
|
|
|
73
|
-
**
|
|
202
|
+
**Example:**
|
|
74
203
|
|
|
75
204
|
```typescript
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
domainDenyList?: DomainRule[];
|
|
85
|
-
batchSize?: number; // Default: 50
|
|
86
|
-
batchTimeout?: number; // Default: 5000ms
|
|
87
|
-
}
|
|
205
|
+
import { initializePingops } from "@pingops/sdk";
|
|
206
|
+
|
|
207
|
+
initializePingops({
|
|
208
|
+
baseUrl: "https://api.pingops.com",
|
|
209
|
+
serviceName: "my-service",
|
|
210
|
+
apiKey: process.env.PINGOPS_API_KEY,
|
|
211
|
+
exportMode: "immediate", // e.g. for serverless
|
|
212
|
+
});
|
|
88
213
|
```
|
|
89
214
|
|
|
90
|
-
|
|
215
|
+
Calling `initializePingops` again after the first successful call is a no-op (idempotent).
|
|
216
|
+
|
|
217
|
+
---
|
|
91
218
|
|
|
92
|
-
|
|
219
|
+
### `shutdownPingops()`
|
|
220
|
+
|
|
221
|
+
Gracefully shuts down the SDK and flushes remaining spans. Returns a `Promise<void>`.
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
93
224
|
|
|
94
225
|
```typescript
|
|
95
|
-
import {
|
|
226
|
+
import { shutdownPingops } from "@pingops/sdk";
|
|
227
|
+
|
|
228
|
+
process.on("SIGTERM", async () => {
|
|
229
|
+
await shutdownPingops();
|
|
230
|
+
process.exit(0);
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### `startTrace(options, fn)`
|
|
237
|
+
|
|
238
|
+
Starts a new trace, sets PingOps attributes (e.g. `userId`, `sessionId`, tags, metadata) in context, runs the given function inside that context, and returns the function’s result. Any spans created inside the function (including automatic HTTP/fetch spans) are part of this trace and carry the same context.
|
|
239
|
+
|
|
240
|
+
**Parameters:**
|
|
241
|
+
|
|
242
|
+
- `options.attributes` — Optional [PingopsTraceAttributes](#pingopstraceattributes) to attach to the trace and propagate to spans.
|
|
243
|
+
- `options.seed` — Optional string; when provided, a deterministic trace ID is derived from it (useful for idempotency or correlation with external systems).
|
|
244
|
+
- `fn` — `() => T | Promise<T>`. Your code; runs inside the new trace and attribute context.
|
|
96
245
|
|
|
97
|
-
|
|
98
|
-
|
|
246
|
+
**Returns:** `Promise<T>` — The result of `fn`.
|
|
247
|
+
|
|
248
|
+
**Example:**
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { startTrace, initializePingops } from "@pingops/sdk";
|
|
252
|
+
|
|
253
|
+
initializePingops({ baseUrl: "...", serviceName: "my-api" });
|
|
254
|
+
|
|
255
|
+
const data = await startTrace(
|
|
99
256
|
{
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
257
|
+
attributes: {
|
|
258
|
+
userId: "user-123",
|
|
259
|
+
sessionId: "sess-456",
|
|
260
|
+
tags: ["checkout", "v2"],
|
|
261
|
+
metadata: { plan: "pro", region: "us" },
|
|
262
|
+
captureRequestBody: true,
|
|
263
|
+
captureResponseBody: true,
|
|
264
|
+
},
|
|
265
|
+
seed: "order-789", // optional: stable trace ID for this order
|
|
103
266
|
},
|
|
104
|
-
async (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return result.json();
|
|
267
|
+
async () => {
|
|
268
|
+
const res = await fetch("https://api.stripe.com/v1/charges", { ... });
|
|
269
|
+
return res.json();
|
|
108
270
|
}
|
|
109
271
|
);
|
|
110
272
|
```
|
|
111
273
|
|
|
112
|
-
|
|
274
|
+
---
|
|
113
275
|
|
|
114
|
-
### `
|
|
276
|
+
### `getActiveTraceId()`
|
|
277
|
+
|
|
278
|
+
Returns the trace ID of the currently active span, or `undefined` if there is none.
|
|
115
279
|
|
|
116
|
-
|
|
280
|
+
**Example:**
|
|
117
281
|
|
|
118
282
|
```typescript
|
|
119
|
-
import {
|
|
283
|
+
import { getActiveTraceId } from "@pingops/sdk";
|
|
120
284
|
|
|
121
|
-
|
|
285
|
+
const traceId = getActiveTraceId();
|
|
286
|
+
console.log("Current trace:", traceId);
|
|
122
287
|
```
|
|
123
288
|
|
|
124
|
-
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### `getActiveSpanId()`
|
|
125
292
|
|
|
126
|
-
|
|
293
|
+
Returns the span ID of the currently active span, or `undefined` if there is none.
|
|
294
|
+
|
|
295
|
+
**Example:**
|
|
127
296
|
|
|
128
297
|
```typescript
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
298
|
+
import { getActiveSpanId } from "@pingops/sdk";
|
|
299
|
+
|
|
300
|
+
const spanId = getActiveSpanId();
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
### `PingopsTraceAttributes`
|
|
306
|
+
|
|
307
|
+
Type for attributes you can pass into `startTrace({ attributes })`:
|
|
308
|
+
|
|
309
|
+
| Field | Type | Description |
|
|
310
|
+
| --------------------- | ------------------------ | --------------------------------------------------------------------- |
|
|
311
|
+
| `traceId` | `string` | Override trace ID (otherwise one is generated or derived from `seed`) |
|
|
312
|
+
| `userId` | `string` | User identifier |
|
|
313
|
+
| `sessionId` | `string` | Session identifier |
|
|
314
|
+
| `tags` | `string[]` | Tags for the trace |
|
|
315
|
+
| `metadata` | `Record<string, string>` | Key-value metadata |
|
|
316
|
+
| `captureRequestBody` | `boolean` | Override request body capture for spans in this trace |
|
|
317
|
+
| `captureResponseBody` | `boolean` | Override response body capture for spans in this trace |
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Tracing
|
|
322
|
+
|
|
323
|
+
### Why use `startTrace`?
|
|
324
|
+
|
|
325
|
+
- **Correlation** — Tie all outgoing calls in a request (or job) to one trace and to a user/session.
|
|
326
|
+
- **Stable IDs** — Use `seed` (e.g. request ID or order ID) to get a deterministic trace ID for logging or external systems.
|
|
327
|
+
- **Scoped body capture** — Enable `captureRequestBody` / `captureResponseBody` only for specific traces (e.g. a single webhook or LLM call) instead of globally.
|
|
328
|
+
|
|
329
|
+
### Auto-initialization when using `startTrace`
|
|
330
|
+
|
|
331
|
+
If you call `startTrace` before calling `initializePingops`, the SDK will try to auto-initialize from environment variables (`PINGOPS_API_KEY`, `PINGOPS_BASE_URL`, `PINGOPS_SERVICE_NAME`). If any of these are missing, `startTrace` throws. For predictable behavior, prefer initializing explicitly at startup.
|
|
332
|
+
|
|
333
|
+
### Example: request-scoped trace
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { startTrace, getActiveTraceId, initializePingops } from "@pingops/sdk";
|
|
337
|
+
|
|
338
|
+
initializePingops({ baseUrl: "...", serviceName: "my-api" });
|
|
339
|
+
|
|
340
|
+
app.post("/webhook", async (req, res) => {
|
|
341
|
+
const result = await startTrace(
|
|
137
342
|
{
|
|
138
|
-
|
|
343
|
+
attributes: {
|
|
344
|
+
userId: req.user?.id,
|
|
345
|
+
sessionId: req.sessionId,
|
|
346
|
+
tags: ["webhook"],
|
|
347
|
+
metadata: { provider: req.body.provider },
|
|
348
|
+
},
|
|
349
|
+
seed: req.headers["x-request-id"] ?? undefined,
|
|
139
350
|
},
|
|
140
|
-
|
|
141
|
-
|
|
351
|
+
async () => {
|
|
352
|
+
await callExternalApi(req.body);
|
|
353
|
+
return { ok: true };
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const traceId = getActiveTraceId();
|
|
358
|
+
res.setHeader("X-Trace-Id", traceId ?? "");
|
|
359
|
+
res.json(result);
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Filtering & Privacy
|
|
366
|
+
|
|
367
|
+
### Domain allow/deny lists
|
|
368
|
+
|
|
369
|
+
Restrict which domains (and optionally paths) are captured:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
initializePingops({
|
|
373
|
+
baseUrl: "https://api.pingops.com",
|
|
374
|
+
serviceName: "my-service",
|
|
375
|
+
domainAllowList: [
|
|
376
|
+
{ domain: "api.github.com", paths: ["/repos"] },
|
|
377
|
+
{ domain: ".openai.com" }, // suffix match
|
|
142
378
|
{
|
|
143
|
-
domain: "
|
|
379
|
+
domain: "generativelanguage.googleapis.com",
|
|
380
|
+
captureRequestBody: true,
|
|
381
|
+
captureResponseBody: true,
|
|
144
382
|
},
|
|
145
383
|
],
|
|
384
|
+
domainDenyList: [{ domain: "internal.corp.local" }],
|
|
146
385
|
});
|
|
147
386
|
```
|
|
148
387
|
|
|
149
|
-
|
|
388
|
+
Each rule in `domainAllowList` / `domainDenyList` can include:
|
|
389
|
+
|
|
390
|
+
- `domain` — Exact or suffix (e.g. `.openai.com`) match.
|
|
391
|
+
- `paths` — Optional path prefixes to allow/deny.
|
|
392
|
+
- `headersAllowList` / `headersDenyList` — Header rules for that domain.
|
|
393
|
+
- `captureRequestBody` / `captureResponseBody` — Override body capture for that domain.
|
|
150
394
|
|
|
151
|
-
|
|
395
|
+
### Header allow/deny lists
|
|
396
|
+
|
|
397
|
+
Control which headers are included on captured spans (global default; domain rules can refine):
|
|
152
398
|
|
|
153
399
|
```typescript
|
|
154
400
|
initializePingops({
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
401
|
+
baseUrl: "https://api.pingops.com",
|
|
402
|
+
serviceName: "my-service",
|
|
403
|
+
headersAllowList: ["user-agent", "x-request-id", "content-type"],
|
|
404
|
+
headersDenyList: ["authorization", "cookie", "x-api-key"],
|
|
158
405
|
});
|
|
159
406
|
```
|
|
160
407
|
|
|
408
|
+
Deny list takes precedence over allow list. Sensitive headers are redacted by default; use `headerRedaction` in config for custom behavior.
|
|
409
|
+
|
|
410
|
+
### Request/response body capture
|
|
411
|
+
|
|
412
|
+
- **Global:** `captureRequestBody` and `captureResponseBody` in config.
|
|
413
|
+
- **Per-domain:** Same flags on a [DomainRule](#domain-allowdeny-lists).
|
|
414
|
+
- **Per-trace:** `captureRequestBody` / `captureResponseBody` in [PingopsTraceAttributes](#pingopstraceattributes) in `startTrace`.
|
|
415
|
+
|
|
416
|
+
Body size is capped by `maxRequestBodySize` and `maxResponseBodySize` (default 4096 bytes each). Larger bodies are truncated.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
161
420
|
## Integration with Existing OpenTelemetry
|
|
162
421
|
|
|
163
|
-
If you already
|
|
422
|
+
If you already use OpenTelemetry and only want the PingOps exporter and filtering, use `PingopsSpanProcessor` from `@pingops/otel` and add it to your existing `TracerProvider`:
|
|
164
423
|
|
|
165
424
|
```typescript
|
|
425
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
166
426
|
import { PingopsSpanProcessor } from "@pingops/otel";
|
|
167
|
-
import { getTracerProvider } from "@opentelemetry/api";
|
|
168
427
|
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
428
|
+
const sdk = new NodeSDK({
|
|
429
|
+
spanProcessors: [
|
|
430
|
+
new PingopsSpanProcessor({
|
|
431
|
+
apiKey: "your-api-key",
|
|
432
|
+
baseUrl: "https://api.pingops.com",
|
|
433
|
+
serviceName: "my-service",
|
|
434
|
+
exportMode: "batched",
|
|
435
|
+
domainAllowList: [{ domain: "api.example.com" }],
|
|
436
|
+
}),
|
|
437
|
+
],
|
|
438
|
+
// your existing instrumentations, resource, etc.
|
|
173
439
|
});
|
|
174
440
|
|
|
175
|
-
|
|
176
|
-
// Add processor to your existing tracer provider
|
|
441
|
+
sdk.start();
|
|
177
442
|
```
|
|
178
443
|
|
|
444
|
+
You can still use `@pingops/sdk` for `startTrace`, `getActiveTraceId`, and `getActiveSpanId`; ensure your tracer provider is the one that uses `PingopsSpanProcessor` (or is bridged to it) so those spans are exported to PingOps.
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
179
448
|
## What Gets Captured
|
|
180
449
|
|
|
181
|
-
- **HTTP Requests
|
|
182
|
-
- **
|
|
183
|
-
- **GenAI Calls**: LLM API calls that follow OpenTelemetry GenAI semantic conventions
|
|
450
|
+
- **Outgoing HTTP** — Requests made with Node’s `http` / `https` (e.g. many HTTP clients under the hood).
|
|
451
|
+
- **Outgoing fetch** — Requests made with the global `fetch` (in Node.js 18+ this is implemented by Undici; both are instrumented).
|
|
184
452
|
|
|
185
|
-
|
|
453
|
+
Only **CLIENT** spans with HTTP (or supported semantic) attributes are exported to PingOps; server-side and internal spans are filtered out.
|
|
186
454
|
|
|
187
|
-
|
|
188
|
-
- Internal spans (non-CLIENT spans)
|
|
189
|
-
- Spans without HTTP or GenAI attributes
|
|
455
|
+
---
|
|
190
456
|
|
|
191
457
|
## Requirements
|
|
192
458
|
|
|
193
|
-
- **Node.js
|
|
459
|
+
- **Node.js** ≥ **20**
|
|
460
|
+
- **ESM** — The package is published as ES modules; use `import` and, if needed, `"type": "module"` or `.mjs`.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## Summary
|
|
465
|
+
|
|
466
|
+
| Goal | What to do |
|
|
467
|
+
| ------------------ | ----------------------------------------------------------------------------------------------- |
|
|
468
|
+
| Install | `pnpm add @pingops/sdk` |
|
|
469
|
+
| Auto-init from env | `node --require @pingops/sdk/register your-app.js` or `import "@pingops/sdk/register"` first |
|
|
470
|
+
| Manual init | `initializePingops({ baseUrl, serviceName, ... })` before any HTTP usage |
|
|
471
|
+
| Config from file | `PINGOPS_CONFIG_FILE=./pingops.config.yaml` or `initializePingops("./pingops.config.json")` |
|
|
472
|
+
| Trace with context | `startTrace({ attributes: { userId, sessionId, tags, metadata }, seed? }, async () => { ... })` |
|
|
473
|
+
| Get current IDs | `getActiveTraceId()`, `getActiveSpanId()` |
|
|
474
|
+
| Graceful shutdown | `await shutdownPingops()` |
|
|
475
|
+
|
|
476
|
+
For more detail on types and options, see the [Configuration](#configuration) and [API Reference](#api-reference) sections above.
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
const require_pingops = require('./pingops-
|
|
1
|
+
const require_pingops = require('./pingops-BBXegVtL.cjs');
|
|
2
2
|
|
|
3
|
+
exports.getActiveSpanId = require_pingops.getActiveSpanId;
|
|
4
|
+
exports.getActiveTraceId = require_pingops.getActiveTraceId;
|
|
3
5
|
exports.initializePingops = require_pingops.initializePingops;
|
|
4
6
|
exports.shutdownPingops = require_pingops.shutdownPingops;
|
|
5
|
-
exports.
|
|
7
|
+
exports.startTrace = require_pingops.startTrace;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PingopsProcessorConfig } from "@pingops/otel";
|
|
2
|
-
import {
|
|
2
|
+
import { PingopsTraceAttributes, PingopsTraceAttributes as PingopsTraceAttributes$1 } from "@pingops/core";
|
|
3
3
|
|
|
4
4
|
//#region src/pingops.d.ts
|
|
5
5
|
|
|
@@ -15,7 +15,7 @@ import { WrapHttpAttributes, WrapHttpAttributes as WrapHttpAttributes$1 } from "
|
|
|
15
15
|
*
|
|
16
16
|
* @param config - Configuration object, config file path, or config file wrapper
|
|
17
17
|
* @param explicit - Whether this is an explicit call (default: true).
|
|
18
|
-
* Set to false when called internally by
|
|
18
|
+
* Set to false when called internally by startTrace auto-initialization.
|
|
19
19
|
*/
|
|
20
20
|
declare function initializePingops(config: PingopsProcessorConfig, explicit?: boolean): void;
|
|
21
21
|
declare function initializePingops(configFilePath: string, explicit?: boolean): void;
|
|
@@ -27,61 +27,46 @@ declare function initializePingops(config: {
|
|
|
27
27
|
*/
|
|
28
28
|
declare function shutdownPingops(): Promise<void>;
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
*
|
|
30
|
+
* Returns the trace ID of the currently active span, if any.
|
|
31
|
+
*/
|
|
32
|
+
declare function getActiveTraceId(): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Returns the span ID of the currently active span, if any.
|
|
35
|
+
*/
|
|
36
|
+
declare function getActiveSpanId(): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Starts a new trace using the PingOps tracer provider and runs the callback within that trace.
|
|
39
|
+
* Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are
|
|
40
|
+
* propagated to spans created within the callback.
|
|
40
41
|
*
|
|
41
|
-
* @param options - Options including attributes
|
|
42
|
-
* @param fn - Function to execute within the attribute context
|
|
43
|
-
* @returns
|
|
42
|
+
* @param options - Options including optional attributes and optional seed for deterministic traceId
|
|
43
|
+
* @param fn - Function to execute within the trace and attribute context
|
|
44
|
+
* @returns Promise resolving to the result of the function
|
|
44
45
|
*
|
|
45
46
|
* @example
|
|
46
47
|
* ```typescript
|
|
47
|
-
* import {
|
|
48
|
+
* import { startTrace, initializePingops } from '@pingops/sdk';
|
|
48
49
|
*
|
|
49
|
-
* // Scenario 1: initializePingops was called
|
|
50
50
|
* initializePingops({ ... });
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
* const result = await wrapHttp({
|
|
52
|
+
* const result = await startTrace({
|
|
54
53
|
* attributes: {
|
|
55
54
|
* userId: 'user-123',
|
|
56
55
|
* sessionId: 'session-456',
|
|
57
56
|
* tags: ['production', 'api'],
|
|
58
57
|
* metadata: { environment: 'prod', version: '1.0.0' }
|
|
59
|
-
* }
|
|
58
|
+
* },
|
|
59
|
+
* seed: 'request-123' // optional: deterministic traceId from this seed
|
|
60
60
|
* }, async () => {
|
|
61
|
-
* // This HTTP request will be instrumented AND have the attributes set above
|
|
62
61
|
* const response = await fetch('https://api.example.com/users/123');
|
|
63
62
|
* return response.json();
|
|
64
63
|
* });
|
|
65
|
-
*
|
|
66
|
-
* // HTTP requests outside wrapHttp are still instrumented, just without the attributes
|
|
67
|
-
* const otherResponse = await fetch('https://api.example.com/other');
|
|
68
|
-
*
|
|
69
|
-
* // Scenario 2: initializePingops was NOT called
|
|
70
|
-
* // Only requests within wrapHttp are instrumented
|
|
71
|
-
* await wrapHttp({
|
|
72
|
-
* attributes: { userId: 'user-123' }
|
|
73
|
-
* }, async () => {
|
|
74
|
-
* // This request IS instrumented
|
|
75
|
-
* return fetch('https://api.example.com/data');
|
|
76
|
-
* });
|
|
77
|
-
*
|
|
78
|
-
* // This request is NOT instrumented (outside wrapHttp)
|
|
79
|
-
* await fetch('https://api.example.com/other');
|
|
80
64
|
* ```
|
|
81
65
|
*/
|
|
82
|
-
declare function
|
|
83
|
-
attributes?:
|
|
84
|
-
|
|
66
|
+
declare function startTrace<T>(options: {
|
|
67
|
+
attributes?: PingopsTraceAttributes$1;
|
|
68
|
+
seed?: string;
|
|
69
|
+
}, fn: () => T | Promise<T>): Promise<T>;
|
|
85
70
|
//#endregion
|
|
86
|
-
export { type
|
|
71
|
+
export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace };
|
|
87
72
|
//# sourceMappingURL=index.d.cts.map
|