@pingops/sdk 0.1.2 → 0.2.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 +394 -73
- package/dist/index.cjs +6 -247
- package/dist/index.d.cts +30 -41
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +30 -41
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -245
- package/dist/pingops-Bbm0Xd0z.cjs +343 -0
- package/dist/pingops-Bbm0Xd0z.cjs.map +1 -0
- package/dist/pingops-DJsItvUR.mjs +302 -0
- package/dist/pingops-DJsItvUR.mjs.map +1 -0
- package/dist/register.cjs +73 -0
- package/dist/register.cjs.map +1 -0
- package/dist/register.d.cts +1 -0
- package/dist/register.d.mts +1 -0
- package/dist/register.mjs +74 -0
- package/dist/register.mjs.map +1 -0
- package/package.json +12 -3
- package/dist/index.cjs.map +0 -1
- package/dist/index.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,150 +38,441 @@ 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
|
|
|
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.
|
|
56
|
+
|
|
57
|
+
**A. Using Node.js `--require`** (runs before any application code):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node --require @pingops/sdk/register your-app.js
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Set required environment variables:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export PINGOPS_API_KEY="your-api-key"
|
|
67
|
+
export PINGOPS_BASE_URL="https://api.pingops.com"
|
|
68
|
+
export PINGOPS_SERVICE_NAME="my-service"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**B. Importing the register entry first** (must be before any HTTP client imports):
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Must be first — before axios, node-fetch, or any code that makes HTTP requests
|
|
75
|
+
import "@pingops/sdk/register";
|
|
76
|
+
|
|
77
|
+
import axios from "axios";
|
|
78
|
+
// ... rest of your application
|
|
79
|
+
```
|
|
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
|
+
|
|
83
|
+
### Option 2: Manual initialization
|
|
84
|
+
|
|
85
|
+
**Best for:** Config from code, feature flags, or when you need to ensure initialization order explicitly.
|
|
86
|
+
|
|
13
87
|
```typescript
|
|
14
88
|
import { initializePingops } from "@pingops/sdk";
|
|
15
89
|
|
|
90
|
+
// Before importing or using any HTTP clients
|
|
16
91
|
initializePingops({
|
|
17
|
-
apiKey:
|
|
92
|
+
apiKey: process.env.PINGOPS_API_KEY,
|
|
18
93
|
baseUrl: "https://api.pingops.com",
|
|
19
94
|
serviceName: "my-service",
|
|
20
95
|
});
|
|
96
|
+
|
|
97
|
+
import axios from "axios";
|
|
98
|
+
// ... rest of your application
|
|
21
99
|
```
|
|
22
100
|
|
|
23
|
-
|
|
101
|
+
You can also initialize from a config file path (environment variables still override file values):
|
|
24
102
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
103
|
+
```typescript
|
|
104
|
+
initializePingops("./pingops.config.yaml");
|
|
105
|
+
// or
|
|
106
|
+
initializePingops({ configFile: "./pingops.config.json" });
|
|
107
|
+
```
|
|
30
108
|
|
|
31
|
-
|
|
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`):**
|
|
177
|
+
|
|
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
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## API Reference
|
|
32
191
|
|
|
33
192
|
### `initializePingops(config)`
|
|
34
193
|
|
|
35
|
-
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:**
|
|
36
197
|
|
|
37
|
-
|
|
198
|
+
- `initializePingops(config: PingopsProcessorConfig): void`
|
|
199
|
+
- `initializePingops(configFilePath: string): void`
|
|
200
|
+
- `initializePingops({ configFile: string }): void`
|
|
201
|
+
|
|
202
|
+
**Example:**
|
|
38
203
|
|
|
39
204
|
```typescript
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Calling `initializePingops` again after the first successful call is a no-op (idempotent).
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### `shutdownPingops()`
|
|
220
|
+
|
|
221
|
+
Gracefully shuts down the SDK and flushes remaining spans. Returns a `Promise<void>`.
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { shutdownPingops } from "@pingops/sdk";
|
|
227
|
+
|
|
228
|
+
process.on("SIGTERM", async () => {
|
|
229
|
+
await shutdownPingops();
|
|
230
|
+
process.exit(0);
|
|
231
|
+
});
|
|
52
232
|
```
|
|
53
233
|
|
|
54
|
-
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### `startTrace(options, fn)`
|
|
55
237
|
|
|
56
|
-
|
|
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.
|
|
245
|
+
|
|
246
|
+
**Returns:** `Promise<T>` — The result of `fn`.
|
|
247
|
+
|
|
248
|
+
**Example:**
|
|
57
249
|
|
|
58
250
|
```typescript
|
|
59
|
-
import {
|
|
251
|
+
import { startTrace, initializePingops } from "@pingops/sdk";
|
|
252
|
+
|
|
253
|
+
initializePingops({ baseUrl: "...", serviceName: "my-api" });
|
|
60
254
|
|
|
61
|
-
await
|
|
62
|
-
"external.api.call",
|
|
255
|
+
const data = await startTrace(
|
|
63
256
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
67
266
|
},
|
|
68
|
-
async (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return result.json();
|
|
267
|
+
async () => {
|
|
268
|
+
const res = await fetch("https://api.stripe.com/v1/charges", { ... });
|
|
269
|
+
return res.json();
|
|
72
270
|
}
|
|
73
271
|
);
|
|
74
272
|
```
|
|
75
273
|
|
|
76
|
-
|
|
274
|
+
---
|
|
77
275
|
|
|
78
|
-
### `
|
|
276
|
+
### `getActiveTraceId()`
|
|
79
277
|
|
|
80
|
-
|
|
278
|
+
Returns the trace ID of the currently active span, or `undefined` if there is none.
|
|
279
|
+
|
|
280
|
+
**Example:**
|
|
81
281
|
|
|
82
282
|
```typescript
|
|
83
|
-
import {
|
|
283
|
+
import { getActiveTraceId } from "@pingops/sdk";
|
|
84
284
|
|
|
85
|
-
|
|
285
|
+
const traceId = getActiveTraceId();
|
|
286
|
+
console.log("Current trace:", traceId);
|
|
86
287
|
```
|
|
87
288
|
|
|
88
|
-
|
|
289
|
+
---
|
|
89
290
|
|
|
90
|
-
|
|
291
|
+
### `getActiveSpanId()`
|
|
292
|
+
|
|
293
|
+
Returns the span ID of the currently active span, or `undefined` if there is none.
|
|
294
|
+
|
|
295
|
+
**Example:**
|
|
91
296
|
|
|
92
297
|
```typescript
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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(
|
|
96
342
|
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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,
|
|
100
350
|
},
|
|
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
|
|
101
378
|
{
|
|
102
|
-
domain: ".
|
|
379
|
+
domain: "generativelanguage.googleapis.com",
|
|
380
|
+
captureRequestBody: true,
|
|
381
|
+
captureResponseBody: true,
|
|
103
382
|
},
|
|
104
383
|
],
|
|
105
384
|
domainDenyList: [
|
|
106
|
-
{
|
|
107
|
-
domain: "internal.service.local",
|
|
108
|
-
},
|
|
385
|
+
{ domain: "internal.corp.local" },
|
|
109
386
|
],
|
|
110
387
|
});
|
|
111
388
|
```
|
|
112
389
|
|
|
113
|
-
|
|
390
|
+
Each rule in `domainAllowList` / `domainDenyList` can include:
|
|
391
|
+
|
|
392
|
+
- `domain` — Exact or suffix (e.g. `.openai.com`) match.
|
|
393
|
+
- `paths` — Optional path prefixes to allow/deny.
|
|
394
|
+
- `headersAllowList` / `headersDenyList` — Header rules for that domain.
|
|
395
|
+
- `captureRequestBody` / `captureResponseBody` — Override body capture for that domain.
|
|
114
396
|
|
|
115
|
-
|
|
397
|
+
### Header allow/deny lists
|
|
398
|
+
|
|
399
|
+
Control which headers are included on captured spans (global default; domain rules can refine):
|
|
116
400
|
|
|
117
401
|
```typescript
|
|
118
402
|
initializePingops({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
403
|
+
baseUrl: "https://api.pingops.com",
|
|
404
|
+
serviceName: "my-service",
|
|
405
|
+
headersAllowList: ["user-agent", "x-request-id", "content-type"],
|
|
406
|
+
headersDenyList: ["authorization", "cookie", "x-api-key"],
|
|
122
407
|
});
|
|
123
408
|
```
|
|
124
409
|
|
|
410
|
+
Deny list takes precedence over allow list. Sensitive headers are redacted by default; use `headerRedaction` in config for custom behavior.
|
|
411
|
+
|
|
412
|
+
### Request/response body capture
|
|
413
|
+
|
|
414
|
+
- **Global:** `captureRequestBody` and `captureResponseBody` in config.
|
|
415
|
+
- **Per-domain:** Same flags on a [DomainRule](#domain-allowdeny-lists).
|
|
416
|
+
- **Per-trace:** `captureRequestBody` / `captureResponseBody` in [PingopsTraceAttributes](#pingopstraceattributes) in `startTrace`.
|
|
417
|
+
|
|
418
|
+
Body size is capped by `maxRequestBodySize` and `maxResponseBodySize` (default 4096 bytes each). Larger bodies are truncated.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
125
422
|
## Integration with Existing OpenTelemetry
|
|
126
423
|
|
|
127
|
-
If you already
|
|
424
|
+
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`:
|
|
128
425
|
|
|
129
426
|
```typescript
|
|
427
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
130
428
|
import { PingopsSpanProcessor } from "@pingops/otel";
|
|
131
|
-
import { getTracerProvider } from "@opentelemetry/api";
|
|
132
429
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
430
|
+
const sdk = new NodeSDK({
|
|
431
|
+
spanProcessors: [
|
|
432
|
+
new PingopsSpanProcessor({
|
|
433
|
+
apiKey: "your-api-key",
|
|
434
|
+
baseUrl: "https://api.pingops.com",
|
|
435
|
+
serviceName: "my-service",
|
|
436
|
+
exportMode: "batched",
|
|
437
|
+
domainAllowList: [{ domain: "api.example.com" }],
|
|
438
|
+
}),
|
|
439
|
+
],
|
|
440
|
+
// your existing instrumentations, resource, etc.
|
|
137
441
|
});
|
|
138
442
|
|
|
139
|
-
|
|
140
|
-
// Add processor to your existing tracer provider
|
|
443
|
+
sdk.start();
|
|
141
444
|
```
|
|
142
445
|
|
|
446
|
+
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.
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
143
450
|
## What Gets Captured
|
|
144
451
|
|
|
145
|
-
- **HTTP Requests
|
|
146
|
-
- **
|
|
147
|
-
- **GenAI Calls**: LLM API calls that follow OpenTelemetry GenAI semantic conventions
|
|
452
|
+
- **Outgoing HTTP** — Requests made with Node’s `http` / `https` (e.g. many HTTP clients under the hood).
|
|
453
|
+
- **Outgoing fetch** — Requests made with the global `fetch` (in Node.js 18+ this is implemented by Undici; both are instrumented).
|
|
148
454
|
|
|
149
|
-
|
|
455
|
+
Only **CLIENT** spans with HTTP (or supported semantic) attributes are exported to PingOps; server-side and internal spans are filtered out.
|
|
150
456
|
|
|
151
|
-
|
|
152
|
-
- Internal spans (non-CLIENT spans)
|
|
153
|
-
- Spans without HTTP or GenAI attributes
|
|
457
|
+
---
|
|
154
458
|
|
|
155
459
|
## Requirements
|
|
156
460
|
|
|
157
|
-
- **Node.js
|
|
461
|
+
- **Node.js** ≥ **20**
|
|
462
|
+
- **ESM** — The package is published as ES modules; use `import` and, if needed, `"type": "module"` or `.mjs`.
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Summary
|
|
467
|
+
|
|
468
|
+
| Goal | What to do |
|
|
469
|
+
|------|------------|
|
|
470
|
+
| Install | `pnpm add @pingops/sdk` |
|
|
471
|
+
| Auto-init from env | `node --require @pingops/sdk/register your-app.js` or `import "@pingops/sdk/register"` first |
|
|
472
|
+
| Manual init | `initializePingops({ baseUrl, serviceName, ... })` before any HTTP usage |
|
|
473
|
+
| Config from file | `PINGOPS_CONFIG_FILE=./pingops.config.yaml` or `initializePingops("./pingops.config.json")` |
|
|
474
|
+
| Trace with context | `startTrace({ attributes: { userId, sessionId, tags, metadata }, seed? }, async () => { ... })` |
|
|
475
|
+
| Get current IDs | `getActiveTraceId()`, `getActiveSpanId()` |
|
|
476
|
+
| Graceful shutdown | `await shutdownPingops()` |
|
|
477
|
+
|
|
478
|
+
For more detail on types and options, see the [Configuration](#configuration) and [API Reference](#api-reference) sections above.
|