@provide-io/telemetry 0.2.2
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 +247 -0
- package/dist/backpressure.d.ts +19 -0
- package/dist/backpressure.d.ts.map +1 -0
- package/dist/backpressure.js +51 -0
- package/dist/cardinality.d.ts +15 -0
- package/dist/cardinality.d.ts.map +1 -0
- package/dist/cardinality.js +69 -0
- package/dist/classification.d.ts +29 -0
- package/dist/classification.d.ts.map +1 -0
- package/dist/classification.js +58 -0
- package/dist/config.d.ts +156 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +350 -0
- package/dist/consent.d.ts +11 -0
- package/dist/consent.d.ts.map +1 -0
- package/dist/consent.js +50 -0
- package/dist/context.d.ts +60 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +127 -0
- package/dist/exceptions.d.ts +14 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +21 -0
- package/dist/fingerprint.d.ts +5 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +50 -0
- package/dist/hash.d.ts +8 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +102 -0
- package/dist/health.d.ts +54 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +102 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/logger.d.ts +28 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +254 -0
- package/dist/metrics.d.ts +78 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +238 -0
- package/dist/otel-logs.d.ts +29 -0
- package/dist/otel-logs.d.ts.map +1 -0
- package/dist/otel-logs.js +127 -0
- package/dist/otel-noop.d.ts +13 -0
- package/dist/otel-noop.d.ts.map +1 -0
- package/dist/otel-noop.js +5 -0
- package/dist/otel.d.ts +20 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +80 -0
- package/dist/pii.d.ts +43 -0
- package/dist/pii.d.ts.map +1 -0
- package/dist/pii.js +278 -0
- package/dist/pretty.d.ts +12 -0
- package/dist/pretty.d.ts.map +1 -0
- package/dist/pretty.js +85 -0
- package/dist/propagation.d.ts +52 -0
- package/dist/propagation.d.ts.map +1 -0
- package/dist/propagation.js +183 -0
- package/dist/react.d.ts +38 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +72 -0
- package/dist/receipts.d.ts +26 -0
- package/dist/receipts.d.ts.map +1 -0
- package/dist/receipts.js +69 -0
- package/dist/resilience.d.ts +26 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +183 -0
- package/dist/runtime.d.ts +33 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +133 -0
- package/dist/sampling.d.ts +9 -0
- package/dist/sampling.d.ts.map +1 -0
- package/dist/sampling.js +53 -0
- package/dist/sanitize.d.ts +6 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +7 -0
- package/dist/schema.d.ts +41 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +109 -0
- package/dist/shutdown.d.ts +2 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +15 -0
- package/dist/slo.d.ts +25 -0
- package/dist/slo.d.ts.map +1 -0
- package/dist/slo.js +115 -0
- package/dist/testing.d.ts +10 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +51 -0
- package/dist/tracing.d.ts +51 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +181 -0
- package/package.json +139 -0
- package/src/backpressure.ts +68 -0
- package/src/cardinality.ts +83 -0
- package/src/classification.ts +87 -0
- package/src/config.ts +589 -0
- package/src/consent.ts +61 -0
- package/src/context.ts +157 -0
- package/src/exceptions.ts +24 -0
- package/src/fingerprint.ts +53 -0
- package/src/hash.ts +118 -0
- package/src/health.ts +175 -0
- package/src/index.ts +183 -0
- package/src/logger.ts +287 -0
- package/src/metrics.ts +204 -0
- package/src/otel-logs.ts +161 -0
- package/src/otel-noop.ts +19 -0
- package/src/otel.ts +112 -0
- package/src/pii.ts +358 -0
- package/src/pretty.ts +93 -0
- package/src/propagation.ts +222 -0
- package/src/react.ts +98 -0
- package/src/receipts.ts +97 -0
- package/src/resilience.ts +220 -0
- package/src/runtime.ts +171 -0
- package/src/sampling.ts +68 -0
- package/src/sanitize.ts +8 -0
- package/src/schema.ts +135 -0
- package/src/shutdown.ts +18 -0
- package/src/slo.ts +156 -0
- package/src/testing.ts +56 -0
- package/src/tracing.ts +211 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @provide-io/telemetry
|
|
2
|
+
|
|
3
|
+
Structured logging + OpenTelemetry traces and metrics for TypeScript — feature parity with the [`provide-telemetry`](https://pypi.org/p/provide-telemetry) Python package.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @provide-io/telemetry
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Optional OTEL peer dependencies
|
|
12
|
+
|
|
13
|
+
To export traces and metrics to an OTLP endpoint (e.g. OpenObserve, Jaeger, Tempo):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install \
|
|
17
|
+
@opentelemetry/sdk-trace-base \
|
|
18
|
+
@opentelemetry/sdk-metrics \
|
|
19
|
+
@opentelemetry/resources \
|
|
20
|
+
@opentelemetry/exporter-trace-otlp-http \
|
|
21
|
+
@opentelemetry/exporter-metrics-otlp-http
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
All five are optional — the library degrades gracefully to no-op providers when they are absent.
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { setupTelemetry, getConfig, getLogger, registerOtelProviders, shutdownTelemetry } from '@provide-io/telemetry';
|
|
30
|
+
|
|
31
|
+
// Call once at app startup.
|
|
32
|
+
setupTelemetry({
|
|
33
|
+
serviceName: 'my-app',
|
|
34
|
+
environment: 'production',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
logLevel: 'info',
|
|
37
|
+
logFormat: 'json',
|
|
38
|
+
otelEnabled: true,
|
|
39
|
+
otlpEndpoint: 'http://localhost:4318',
|
|
40
|
+
otlpHeaders: { Authorization: 'Basic ...' },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Activate OTLP exporters (requires peer deps above).
|
|
44
|
+
await registerOtelProviders(getConfig());
|
|
45
|
+
|
|
46
|
+
const log = getLogger('api');
|
|
47
|
+
log.info({ event: 'request.received.ok', method: 'GET', path: '/health', status: 200 });
|
|
48
|
+
|
|
49
|
+
// On shutdown — flushes and drains all OTel providers.
|
|
50
|
+
await shutdownTelemetry();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API reference
|
|
54
|
+
|
|
55
|
+
### Setup
|
|
56
|
+
|
|
57
|
+
| Export | Description |
|
|
58
|
+
|--------|-------------|
|
|
59
|
+
| `setupTelemetry(config)` | Configure the library. Idempotent — safe to call multiple times. |
|
|
60
|
+
| `getConfig()` | Return the current `TelemetryConfig`. |
|
|
61
|
+
| `configFromEnv()` | Build config from environment variables (see [Configuration](#configuration)). |
|
|
62
|
+
| `registerOtelProviders(cfg)` | Wire OTLP trace + metrics exporters. Call after `setupTelemetry`. |
|
|
63
|
+
| `shutdownTelemetry()` | Flush and shut down all registered OTel providers. |
|
|
64
|
+
|
|
65
|
+
### Logging
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { getLogger } from '@provide-io/telemetry';
|
|
69
|
+
|
|
70
|
+
const log = getLogger('my-module');
|
|
71
|
+
log.debug({ event: 'cache.miss.ok', key: 'user:42' });
|
|
72
|
+
log.info({ event: 'request.complete.ok', status: 200, duration_ms: 14 });
|
|
73
|
+
log.warn({ event: 'retry.attempt.warn', attempt: 2 });
|
|
74
|
+
log.error({ event: 'db.query.error', error: err.message });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Event names follow the DA(R)S pattern: 3 segments (`domain.action.status`) or 4 segments (`domain.action.resource.status`).
|
|
78
|
+
|
|
79
|
+
### Tracing
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { withTrace, getActiveTraceIds, setTraceContext } from '@provide-io/telemetry';
|
|
83
|
+
|
|
84
|
+
const result = await withTrace('my.operation.ok', async () => {
|
|
85
|
+
const { trace_id, span_id } = getActiveTraceIds();
|
|
86
|
+
// trace_id / span_id are available here and in any log emitted inside
|
|
87
|
+
return doWork();
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Metrics
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { counter, gauge, histogram } from '@provide-io/telemetry';
|
|
95
|
+
|
|
96
|
+
const requests = counter('http.requests', { unit: '1', description: 'Total HTTP requests' });
|
|
97
|
+
requests.add(1, { method: 'GET', status: '200' });
|
|
98
|
+
|
|
99
|
+
const latency = histogram('http.duration', { unit: 'ms' });
|
|
100
|
+
latency.record(42, { route: '/api/users' });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Context binding
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { bindContext, runWithContext, clearContext } from '@provide-io/telemetry';
|
|
107
|
+
|
|
108
|
+
bindContext({ request_id: 'req-abc', user_id: 7 });
|
|
109
|
+
// All log calls in this async context will include these fields automatically.
|
|
110
|
+
clearContext();
|
|
111
|
+
|
|
112
|
+
// Scoped — context is automatically cleaned up after fn resolves.
|
|
113
|
+
await runWithContext({ trace_id: '...' }, async () => { /* ... */ });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Session correlation
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { bindSessionContext, getSessionId, clearSessionContext } from '@provide-io/telemetry';
|
|
120
|
+
|
|
121
|
+
bindSessionContext('sess-abc-123');
|
|
122
|
+
// All logs and traces now include session_id automatically.
|
|
123
|
+
const sid = getSessionId(); // 'sess-abc-123'
|
|
124
|
+
clearSessionContext();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Error fingerprinting
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { computeErrorFingerprint } from '@provide-io/telemetry';
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
throw new Error('connection refused');
|
|
134
|
+
} catch (e) {
|
|
135
|
+
const err = e as Error;
|
|
136
|
+
const fp = computeErrorFingerprint(err.constructor.name, err.stack);
|
|
137
|
+
// fp: 12-char hex digest, stable across deploys — use for dedup and alert grouping.
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### W3C trace propagation
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { extractW3cContext, bindPropagationContext } from '@provide-io/telemetry';
|
|
145
|
+
|
|
146
|
+
// In an HTTP handler — extract incoming traceparent/tracestate.
|
|
147
|
+
const ctx = extractW3cContext(req.headers);
|
|
148
|
+
bindPropagationContext(ctx);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### PII sanitization
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { sanitize, registerPiiRule } from '@provide-io/telemetry';
|
|
155
|
+
|
|
156
|
+
// Built-in: redacts password, token, secret, authorization, api_key, ...
|
|
157
|
+
const obj = { user: 'alice', password: 'hunter2' }; // pragma: allowlist secret
|
|
158
|
+
sanitize(obj);
|
|
159
|
+
// obj is now { user: 'alice', password: '[REDACTED]' }
|
|
160
|
+
|
|
161
|
+
// Custom rule (dot-separated path; '*' as wildcard segment)
|
|
162
|
+
registerPiiRule({ path: 'user.ssn', mode: 'redact' });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Health snapshot
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { getHealthSnapshot } from '@provide-io/telemetry';
|
|
169
|
+
|
|
170
|
+
const snap = getHealthSnapshot();
|
|
171
|
+
// snap.export_failures_logs, snap.queue_depth_traces, ...
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## React integration
|
|
175
|
+
|
|
176
|
+
Requires React 18+ as a peer dependency.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { useTelemetryContext, TelemetryErrorBoundary } from '@provide-io/telemetry/react';
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### `useTelemetryContext(values)`
|
|
183
|
+
|
|
184
|
+
Binds key/value pairs into telemetry context for the lifetime of a component. Automatically cleans up on unmount and re-runs when values change (compared by content, not reference).
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
function UserDashboard({ userId }: { userId: string }) {
|
|
188
|
+
useTelemetryContext({ user_id: userId, page: 'dashboard' });
|
|
189
|
+
return <Dashboard />;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `TelemetryErrorBoundary`
|
|
194
|
+
|
|
195
|
+
React error boundary that auto-logs caught render errors via `getLogger('react.error_boundary')`. Accepts a static fallback or a render-prop receiving the error and a reset callback.
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
<TelemetryErrorBoundary
|
|
199
|
+
fallback={(error, reset) => <ErrorPage error={error} onRetry={reset} />}
|
|
200
|
+
onError={(error, info) => reportToSentry(error, info)}
|
|
201
|
+
>
|
|
202
|
+
<App />
|
|
203
|
+
</TelemetryErrorBoundary>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Browser support
|
|
207
|
+
|
|
208
|
+
The library is browser-compatible via conditional exports. OpenTelemetry providers become no-ops in browser environments, so no server-only imports leak into client bundles. No polyfills are required for modern browsers (ES2020+).
|
|
209
|
+
|
|
210
|
+
Browser-specific options in `setupTelemetry()`:
|
|
211
|
+
|
|
212
|
+
| Option | Effect |
|
|
213
|
+
|--------|--------|
|
|
214
|
+
| `captureToWindow: true` | Buffers structured logs to `window.__pinoLogs` for devtools inspection |
|
|
215
|
+
| `consoleOutput: true` | Mirrors log output to `console.debug` / `console.log` / `console.warn` / `console.error` |
|
|
216
|
+
|
|
217
|
+
## Configuration
|
|
218
|
+
|
|
219
|
+
All options can be set programmatically via `setupTelemetry()` or via environment variables:
|
|
220
|
+
|
|
221
|
+
| Env var | Default | Description |
|
|
222
|
+
|---------|---------|-------------|
|
|
223
|
+
| `PROVIDE_TELEMETRY_SERVICE_NAME` | `provide-service` | Service identity |
|
|
224
|
+
| `PROVIDE_ENV` | `development` | Deployment environment |
|
|
225
|
+
| `PROVIDE_VERSION` | `unknown` | Service version |
|
|
226
|
+
| `PROVIDE_LOG_LEVEL` | `info` | Log level: `debug` / `info` / `warn` / `error` |
|
|
227
|
+
| `PROVIDE_LOG_FORMAT` | `json` | Output format: `json` / `pretty` |
|
|
228
|
+
| `PROVIDE_TRACE_ENABLED` | `false` | Enable OTLP export |
|
|
229
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318` | OTLP base endpoint |
|
|
230
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | — | Comma-separated `key=value` auth headers |
|
|
231
|
+
|
|
232
|
+
### Pretty renderer
|
|
233
|
+
|
|
234
|
+
Set `logFormat: 'pretty'` (or `PROVIDE_LOG_FORMAT=pretty`) for human-readable colored output during local development. Color support respects `FORCE_COLOR` and `NO_COLOR` environment variables.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
setupTelemetry({ logFormat: 'pretty' });
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Requirements
|
|
241
|
+
|
|
242
|
+
- Node.js ≥ 18
|
|
243
|
+
- TypeScript ≥ 5 (built with TypeScript 6)
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
Apache-2.0. See [LICENSE](../LICENSES/Apache-2.0.txt).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded queue controls for telemetry signal paths.
|
|
3
|
+
* Mirrors Python provide.telemetry.backpressure.
|
|
4
|
+
*/
|
|
5
|
+
export interface QueuePolicy {
|
|
6
|
+
maxLogs: number;
|
|
7
|
+
maxTraces: number;
|
|
8
|
+
maxMetrics: number;
|
|
9
|
+
}
|
|
10
|
+
export interface QueueTicket {
|
|
11
|
+
signal: 'logs' | 'traces' | 'metrics';
|
|
12
|
+
token: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function setQueuePolicy(policy: Partial<QueuePolicy>): void;
|
|
15
|
+
export declare function getQueuePolicy(): QueuePolicy;
|
|
16
|
+
export declare function tryAcquire(signal: QueueTicket['signal']): QueueTicket | null;
|
|
17
|
+
export declare function release(ticket: QueueTicket): void;
|
|
18
|
+
export declare function _resetBackpressureForTests(): void;
|
|
19
|
+
//# sourceMappingURL=backpressure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backpressure.d.ts","sourceRoot":"","sources":["../src/backpressure.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;CACf;AAYD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAEjE;AAED,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAQD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,WAAW,GAAG,IAAI,CAW5E;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAKjD;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAIjD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 provide.io llc. All rights reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
const DEFAULT_POLICY = { maxLogs: 0, maxTraces: 0, maxMetrics: 0 };
|
|
4
|
+
let _policy = { ...DEFAULT_POLICY };
|
|
5
|
+
let _tokenCounter = 1;
|
|
6
|
+
const _acquired = new Map([
|
|
7
|
+
['logs', new Set()],
|
|
8
|
+
['traces', new Set()],
|
|
9
|
+
['metrics', new Set()],
|
|
10
|
+
]);
|
|
11
|
+
export function setQueuePolicy(policy) {
|
|
12
|
+
_policy = { ..._policy, ...policy };
|
|
13
|
+
}
|
|
14
|
+
export function getQueuePolicy() {
|
|
15
|
+
return { ..._policy };
|
|
16
|
+
}
|
|
17
|
+
function _maxFor(signal) {
|
|
18
|
+
if (signal === 'logs')
|
|
19
|
+
return _policy.maxLogs;
|
|
20
|
+
if (signal === 'traces')
|
|
21
|
+
return _policy.maxTraces;
|
|
22
|
+
return _policy.maxMetrics;
|
|
23
|
+
}
|
|
24
|
+
export function tryAcquire(signal) {
|
|
25
|
+
const max = _maxFor(signal);
|
|
26
|
+
if (max <= 0)
|
|
27
|
+
return { signal, token: 0 };
|
|
28
|
+
const set = _acquired.get(signal);
|
|
29
|
+
// Stryker disable next-line ConditionalExpression: defensive guard — _acquired is initialized with all three signal keys, so get() never returns undefined
|
|
30
|
+
/* v8 ignore next */
|
|
31
|
+
if (!set)
|
|
32
|
+
return null;
|
|
33
|
+
if (set.size >= max)
|
|
34
|
+
return null;
|
|
35
|
+
const token = _tokenCounter++;
|
|
36
|
+
set.add(token);
|
|
37
|
+
return { signal, token };
|
|
38
|
+
}
|
|
39
|
+
export function release(ticket) {
|
|
40
|
+
// Stryker disable next-line ConditionalExpression: token=0 is the unlimited-queue sentinel; delete(0) from set is always a no-op
|
|
41
|
+
if (ticket.token === 0)
|
|
42
|
+
return;
|
|
43
|
+
// Stryker disable next-line OptionalChaining: signal is always a key in _acquired (initialized in the Map constructor)
|
|
44
|
+
_acquired.get(ticket.signal)?.delete(ticket.token);
|
|
45
|
+
}
|
|
46
|
+
export function _resetBackpressureForTests() {
|
|
47
|
+
_policy = { ...DEFAULT_POLICY };
|
|
48
|
+
_tokenCounter = 1;
|
|
49
|
+
for (const set of _acquired.values())
|
|
50
|
+
set.clear();
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribute cardinality guardrails with TTL pruning.
|
|
3
|
+
* Mirrors Python provide.telemetry.cardinality.
|
|
4
|
+
*/
|
|
5
|
+
export interface CardinalityLimit {
|
|
6
|
+
maxValues: number;
|
|
7
|
+
ttlSeconds: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const OVERFLOW_VALUE = "__overflow__";
|
|
10
|
+
export declare function registerCardinalityLimit(key: string, limit: CardinalityLimit): void;
|
|
11
|
+
export declare function getCardinalityLimits(): Map<string, CardinalityLimit>;
|
|
12
|
+
export declare function clearCardinalityLimits(): void;
|
|
13
|
+
export declare function guardAttributes(attrs: Record<string, string>): Record<string, string>;
|
|
14
|
+
export declare function _resetCardinalityForTests(): void;
|
|
15
|
+
//# sourceMappingURL=cardinality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cardinality.d.ts","sourceRoot":"","sources":["../src/cardinality.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAQ7C,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAOnF;AAED,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAEpE;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAI7C;AAcD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0BrF;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 provide.io llc. All rights reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export const OVERFLOW_VALUE = '__overflow__';
|
|
4
|
+
const _limits = new Map();
|
|
5
|
+
/** key → (value → expiresAt timestamp ms) */
|
|
6
|
+
const _seen = new Map();
|
|
7
|
+
const _lastPrune = new Map();
|
|
8
|
+
const PRUNE_INTERVAL_MS = 5000;
|
|
9
|
+
export function registerCardinalityLimit(key, limit) {
|
|
10
|
+
_limits.set(key, {
|
|
11
|
+
maxValues: Math.max(1, limit.maxValues),
|
|
12
|
+
ttlSeconds: Math.max(1, limit.ttlSeconds),
|
|
13
|
+
});
|
|
14
|
+
// Stryker disable next-line ConditionalExpression: equivalent mutant — guardAttributes line 64 has a ?? fallback that compensates if _seen entry is absent
|
|
15
|
+
if (!_seen.has(key))
|
|
16
|
+
_seen.set(key, new Map());
|
|
17
|
+
}
|
|
18
|
+
export function getCardinalityLimits() {
|
|
19
|
+
return new Map(_limits);
|
|
20
|
+
}
|
|
21
|
+
export function clearCardinalityLimits() {
|
|
22
|
+
_limits.clear();
|
|
23
|
+
_seen.clear();
|
|
24
|
+
_lastPrune.clear();
|
|
25
|
+
}
|
|
26
|
+
function _pruneExpired(key, now) {
|
|
27
|
+
const limit = _limits.get(key);
|
|
28
|
+
const seen = _seen.get(key);
|
|
29
|
+
// Stryker disable next-line ConditionalExpression,LogicalOperator: defensive guard — _pruneExpired is only called from guardAttributes after _limits.get(key) succeeds
|
|
30
|
+
/* v8 ignore next 2 */
|
|
31
|
+
if (!limit || !seen)
|
|
32
|
+
return;
|
|
33
|
+
const threshold = now - limit.ttlSeconds * 1000;
|
|
34
|
+
for (const [value, seenAt] of seen) {
|
|
35
|
+
if (seenAt < threshold)
|
|
36
|
+
seen.delete(value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function guardAttributes(attrs) {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
const result = { ...attrs };
|
|
42
|
+
for (const [key, value] of Object.entries(result)) {
|
|
43
|
+
const limit = _limits.get(key);
|
|
44
|
+
if (!limit)
|
|
45
|
+
continue;
|
|
46
|
+
const lastPrune = _lastPrune.get(key) ?? 0;
|
|
47
|
+
// Stryker disable next-line ConditionalExpression,ArithmeticOperator: early prune is a no-op (only deletes expired values); now+lastPrune equivalent because fresh values are always within TTL
|
|
48
|
+
if (now - lastPrune >= PRUNE_INTERVAL_MS) {
|
|
49
|
+
_pruneExpired(key, now);
|
|
50
|
+
_lastPrune.set(key, now);
|
|
51
|
+
}
|
|
52
|
+
/* v8 ignore next */
|
|
53
|
+
const seen = _seen.get(key) ?? new Map();
|
|
54
|
+
if (seen.has(value)) {
|
|
55
|
+
seen.set(value, now);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (seen.size >= limit.maxValues) {
|
|
59
|
+
result[key] = OVERFLOW_VALUE;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
seen.set(value, now);
|
|
63
|
+
_seen.set(key, seen);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
export function _resetCardinalityForTests() {
|
|
68
|
+
clearCardinalityLimits();
|
|
69
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Data classification labels. */
|
|
2
|
+
export type DataClass = 'PUBLIC' | 'INTERNAL' | 'PII' | 'PHI' | 'PCI' | 'SECRET';
|
|
3
|
+
/** Maps a glob pattern to a DataClass label. */
|
|
4
|
+
export interface ClassificationRule {
|
|
5
|
+
pattern: string;
|
|
6
|
+
classification: DataClass;
|
|
7
|
+
}
|
|
8
|
+
/** Defines the action to take per DataClass. */
|
|
9
|
+
export interface ClassificationPolicy {
|
|
10
|
+
PUBLIC: string;
|
|
11
|
+
INTERNAL: string;
|
|
12
|
+
PII: string;
|
|
13
|
+
PHI: string;
|
|
14
|
+
PCI: string;
|
|
15
|
+
SECRET: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Register classification rules and install the classification hook on the PII engine.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerClassificationRules(rules: ClassificationRule[]): void;
|
|
21
|
+
/** Replace the current classification policy. */
|
|
22
|
+
export declare function setClassificationPolicy(policy: ClassificationPolicy): void;
|
|
23
|
+
/** Return the current classification policy. */
|
|
24
|
+
export declare function getClassificationPolicy(): ClassificationPolicy;
|
|
25
|
+
/** Return the DataClass label for a key if a rule matches, else null. */
|
|
26
|
+
export declare function _classifyField(key: string, _value: unknown): string | null;
|
|
27
|
+
/** Reset all classification state and remove the hook (test helper). */
|
|
28
|
+
export declare function resetClassificationForTests(): void;
|
|
29
|
+
//# sourceMappingURL=classification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classification.d.ts","sourceRoot":"","sources":["../src/classification.ts"],"names":[],"mappings":"AAYA,kCAAkC;AAClC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEjF,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,SAAS,CAAC;CAC3B;AAED,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAG7E;AAED,iDAAiD;AACjD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAE1E;AAED,gDAAgD;AAChD,wBAAgB,uBAAuB,IAAI,oBAAoB,CAE9D;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAO1E;AAED,wEAAwE;AACxE,wBAAgB,2BAA2B,IAAI,IAAI,CAIlD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 provide.io llc. All rights reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Data classification engine — strippable governance module.
|
|
5
|
+
*
|
|
6
|
+
* Registers a classification hook on the PII engine when rules are configured.
|
|
7
|
+
* If this file is deleted, the PII engine runs unchanged (hook stays null).
|
|
8
|
+
*/
|
|
9
|
+
import { setClassificationHook } from './pii';
|
|
10
|
+
const _DEFAULT_POLICY = {
|
|
11
|
+
PUBLIC: 'pass',
|
|
12
|
+
INTERNAL: 'pass',
|
|
13
|
+
PII: 'redact',
|
|
14
|
+
PHI: 'drop',
|
|
15
|
+
PCI: 'hash',
|
|
16
|
+
SECRET: 'drop', // pragma: allowlist secret
|
|
17
|
+
};
|
|
18
|
+
// Stryker disable next-line ArrayDeclaration
|
|
19
|
+
const _rules = [];
|
|
20
|
+
let _policy = { ..._DEFAULT_POLICY };
|
|
21
|
+
/**
|
|
22
|
+
* Convert a glob pattern (supporting * wildcards) to a RegExp.
|
|
23
|
+
* Only * is treated as a wildcard; all other regex special chars are escaped.
|
|
24
|
+
*/
|
|
25
|
+
function matchGlob(pattern, key) {
|
|
26
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
|
|
27
|
+
return new RegExp(`^${escaped}$`).test(key);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Register classification rules and install the classification hook on the PII engine.
|
|
31
|
+
*/
|
|
32
|
+
export function registerClassificationRules(rules) {
|
|
33
|
+
_rules.push(...rules);
|
|
34
|
+
setClassificationHook(_classifyField);
|
|
35
|
+
}
|
|
36
|
+
/** Replace the current classification policy. */
|
|
37
|
+
export function setClassificationPolicy(policy) {
|
|
38
|
+
_policy = { ...policy };
|
|
39
|
+
}
|
|
40
|
+
/** Return the current classification policy. */
|
|
41
|
+
export function getClassificationPolicy() {
|
|
42
|
+
return { ..._policy };
|
|
43
|
+
}
|
|
44
|
+
/** Return the DataClass label for a key if a rule matches, else null. */
|
|
45
|
+
export function _classifyField(key, _value) {
|
|
46
|
+
for (const rule of _rules) {
|
|
47
|
+
if (matchGlob(rule.pattern, key)) {
|
|
48
|
+
return rule.classification;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/** Reset all classification state and remove the hook (test helper). */
|
|
54
|
+
export function resetClassificationForTests() {
|
|
55
|
+
_rules.length = 0;
|
|
56
|
+
_policy = { ..._DEFAULT_POLICY };
|
|
57
|
+
setClassificationHook(null);
|
|
58
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
export interface TelemetryConfig {
|
|
2
|
+
/** Service name injected into every log record. */
|
|
3
|
+
serviceName: string;
|
|
4
|
+
/** Deployment environment (e.g. "development", "production"). */
|
|
5
|
+
environment: string;
|
|
6
|
+
/** Application version injected into every log record. */
|
|
7
|
+
version: string;
|
|
8
|
+
/** Pino log level: trace | debug | info | warn | error. */
|
|
9
|
+
logLevel: string;
|
|
10
|
+
/** Output format: "json" (default) or "pretty". */
|
|
11
|
+
logFormat: 'json' | 'pretty';
|
|
12
|
+
/** Enable OTEL SDK registration on setupTelemetry(). */
|
|
13
|
+
otelEnabled: boolean;
|
|
14
|
+
/** OTLP export endpoint (e.g. "http://localhost:4318"). */
|
|
15
|
+
otlpEndpoint?: string;
|
|
16
|
+
/** OTLP headers as key=value pairs. */
|
|
17
|
+
otlpHeaders?: Record<string, string>;
|
|
18
|
+
/** Fields whose values are replaced with "[REDACTED]". */
|
|
19
|
+
sanitizeFields: string[];
|
|
20
|
+
/** Push every log object into window.__pinoLogs (browser only). */
|
|
21
|
+
captureToWindow: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Emit logs to browser console via console.debug/log/warn/error.
|
|
24
|
+
* Default false — use captureToWindow + window.__pinoLogs or OTEL export instead.
|
|
25
|
+
* Set true during local development for live devtools inspection.
|
|
26
|
+
*/
|
|
27
|
+
consoleOutput: boolean;
|
|
28
|
+
/** Enforce strict event name validation (3-5 dot-separated segments). */
|
|
29
|
+
strictSchema: boolean;
|
|
30
|
+
/** Keys required on every log record when strictSchema is enabled. */
|
|
31
|
+
requiredLogKeys: string[];
|
|
32
|
+
/** Include timestamp in log output. */
|
|
33
|
+
logIncludeTimestamp: boolean;
|
|
34
|
+
/** Include caller info in log output. */
|
|
35
|
+
logIncludeCaller: boolean;
|
|
36
|
+
/** Enable PII/secret sanitization in logs. */
|
|
37
|
+
logSanitize: boolean;
|
|
38
|
+
/** Attach code.filepath / code.lineno attributes to log records. */
|
|
39
|
+
logCodeAttributes: boolean;
|
|
40
|
+
/** Per-module log level overrides (e.g. {"provide.server": "DEBUG"}). */
|
|
41
|
+
logModuleLevels: Record<string, string>;
|
|
42
|
+
/** Trace sampling rate (0.0–1.0). */
|
|
43
|
+
traceSampleRate: number;
|
|
44
|
+
/** Enable metrics collection. */
|
|
45
|
+
metricsEnabled: boolean;
|
|
46
|
+
/** Probabilistic sampling rate for logs (0.0–1.0). */
|
|
47
|
+
samplingLogsRate: number;
|
|
48
|
+
/** Probabilistic sampling rate for traces (0.0–1.0). */
|
|
49
|
+
samplingTracesRate: number;
|
|
50
|
+
/** Probabilistic sampling rate for metrics (0.0–1.0). */
|
|
51
|
+
samplingMetricsRate: number;
|
|
52
|
+
/** Max queue size for log export (0 = unbounded). */
|
|
53
|
+
backpressureLogsMaxsize: number;
|
|
54
|
+
/** Max queue size for trace export (0 = unbounded). */
|
|
55
|
+
backpressureTracesMaxsize: number;
|
|
56
|
+
/** Max queue size for metric export (0 = unbounded). */
|
|
57
|
+
backpressureMetricsMaxsize: number;
|
|
58
|
+
/** Max retries for log export. */
|
|
59
|
+
exporterLogsRetries: number;
|
|
60
|
+
/** Backoff between log export retries (ms). */
|
|
61
|
+
exporterLogsBackoffMs: number;
|
|
62
|
+
/** Timeout for log export (ms). */
|
|
63
|
+
exporterLogsTimeoutMs: number;
|
|
64
|
+
/** If true, drop telemetry on export failure instead of crashing. */
|
|
65
|
+
exporterLogsFailOpen: boolean;
|
|
66
|
+
/** Max retries for trace export. */
|
|
67
|
+
exporterTracesRetries: number;
|
|
68
|
+
/** Backoff between trace export retries (ms). */
|
|
69
|
+
exporterTracesBackoffMs: number;
|
|
70
|
+
/** Timeout for trace export (ms). */
|
|
71
|
+
exporterTracesTimeoutMs: number;
|
|
72
|
+
/** If true, drop telemetry on export failure instead of crashing. */
|
|
73
|
+
exporterTracesFailOpen: boolean;
|
|
74
|
+
/** Max retries for metric export. */
|
|
75
|
+
exporterMetricsRetries: number;
|
|
76
|
+
/** Backoff between metric export retries (ms). */
|
|
77
|
+
exporterMetricsBackoffMs: number;
|
|
78
|
+
/** Timeout for metric export (ms). */
|
|
79
|
+
exporterMetricsTimeoutMs: number;
|
|
80
|
+
/** If true, drop telemetry on export failure instead of crashing. */
|
|
81
|
+
exporterMetricsFailOpen: boolean;
|
|
82
|
+
/** Enable RED (Rate/Error/Duration) metrics. */
|
|
83
|
+
sloEnableRedMetrics: boolean;
|
|
84
|
+
/** Enable USE (Utilization/Saturation/Errors) metrics. */
|
|
85
|
+
sloEnableUseMetrics: boolean;
|
|
86
|
+
/** Maximum recursion depth for PII sanitization of nested objects. */
|
|
87
|
+
piiMaxDepth: number;
|
|
88
|
+
/** Max length for any single attribute value. */
|
|
89
|
+
securityMaxAttrValueLength: number;
|
|
90
|
+
/** Max number of attributes on a single span/log/metric point. */
|
|
91
|
+
securityMaxAttrCount: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Hot-reloadable config subset. Only fields that can be changed at runtime
|
|
95
|
+
* without restarting providers. All fields are optional.
|
|
96
|
+
*/
|
|
97
|
+
export interface RuntimeOverrides {
|
|
98
|
+
samplingLogsRate?: number;
|
|
99
|
+
samplingTracesRate?: number;
|
|
100
|
+
samplingMetricsRate?: number;
|
|
101
|
+
backpressureLogsMaxsize?: number;
|
|
102
|
+
backpressureTracesMaxsize?: number;
|
|
103
|
+
backpressureMetricsMaxsize?: number;
|
|
104
|
+
exporterLogsRetries?: number;
|
|
105
|
+
exporterLogsBackoffMs?: number;
|
|
106
|
+
exporterLogsTimeoutMs?: number;
|
|
107
|
+
exporterLogsFailOpen?: boolean;
|
|
108
|
+
exporterTracesRetries?: number;
|
|
109
|
+
exporterTracesBackoffMs?: number;
|
|
110
|
+
exporterTracesTimeoutMs?: number;
|
|
111
|
+
exporterTracesFailOpen?: boolean;
|
|
112
|
+
exporterMetricsRetries?: number;
|
|
113
|
+
exporterMetricsBackoffMs?: number;
|
|
114
|
+
exporterMetricsTimeoutMs?: number;
|
|
115
|
+
exporterMetricsFailOpen?: boolean;
|
|
116
|
+
securityMaxAttrValueLength?: number;
|
|
117
|
+
securityMaxAttrCount?: number;
|
|
118
|
+
sloEnableRedMetrics?: boolean;
|
|
119
|
+
sloEnableUseMetrics?: boolean;
|
|
120
|
+
piiMaxDepth?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Build a TelemetryConfig from environment variables.
|
|
124
|
+
* Uses the same env var names as the Python package.
|
|
125
|
+
* Explicit values passed to setupTelemetry() override env vars.
|
|
126
|
+
*/
|
|
127
|
+
export declare function configFromEnv(): TelemetryConfig;
|
|
128
|
+
/** Return the active TelemetryConfig. */
|
|
129
|
+
export declare function getConfig(): TelemetryConfig;
|
|
130
|
+
/**
|
|
131
|
+
* Apply parsed config fields to the runtime policy engines (sampling, backpressure, resilience).
|
|
132
|
+
* Mirrors Python provide.telemetry.runtime.apply_runtime_config.
|
|
133
|
+
*/
|
|
134
|
+
export declare function applyConfigPolicies(cfg: TelemetryConfig): void;
|
|
135
|
+
/**
|
|
136
|
+
* Configure telemetry. Call once at app startup.
|
|
137
|
+
* Merges explicit values over the current config (which may include env-derived values).
|
|
138
|
+
*/
|
|
139
|
+
export declare function setupTelemetry(overrides?: Partial<TelemetryConfig>): void;
|
|
140
|
+
/** Reset to defaults (used in tests). */
|
|
141
|
+
export declare function _resetConfig(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Parse OTLP-style header string "key=value,key2=value2" into a Record.
|
|
144
|
+
* Keys and values are URL-decoded. Malformed pairs (no '=') and empty keys are skipped.
|
|
145
|
+
* Values may contain '=' characters (only the first '=' splits key from value).
|
|
146
|
+
*/
|
|
147
|
+
export declare function parseOtlpHeaders(raw: string): Record<string, string>;
|
|
148
|
+
/**
|
|
149
|
+
* Return a copy of the config with OTLP secrets masked.
|
|
150
|
+
* Safe to log or serialize — never leaks header values or endpoint credentials.
|
|
151
|
+
*/
|
|
152
|
+
export declare function redactConfig(config: TelemetryConfig): Record<string, unknown>;
|
|
153
|
+
/** Package version — mirrors Python __version__. */
|
|
154
|
+
export declare const version = "0.2.0";
|
|
155
|
+
export declare const __version__ = "0.2.0";
|
|
156
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|