@davidilie/telemetry-next 0.2.0 → 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 +22 -2
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/node.cjs +9 -3
- package/dist/node.mjs +11 -5
- package/dist/{request-error-CtYIz296.cjs → request-error-CEM6CkHw.cjs} +23 -6
- package/dist/{request-error-6cvJ4jpq.mjs → request-error-DbUeEPob.mjs} +23 -6
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -80,6 +80,25 @@ sampler strings. Either sampler is wrapped in a mutable provider-wide gate, so
|
|
|
80
80
|
new automatic and client-created spans, including children of sampled parents.
|
|
81
81
|
Already queued/exported work is not recalled.
|
|
82
82
|
|
|
83
|
+
Resource detectors default to an empty list, so `OTEL_RESOURCE_ATTRIBUTES` and
|
|
84
|
+
`OTEL_SERVICE_NAME` cannot override the configured identity or add unfiltered
|
|
85
|
+
attributes. Explicit `otel.resourceDetectors` opt into upstream detection;
|
|
86
|
+
review their output because it bypasses core redaction. Standard Vercel
|
|
87
|
+
deployment metadata from `@vercel/otel` remains available.
|
|
88
|
+
|
|
89
|
+
The default trace exporter sends OTLP/HTTP protobuf directly to Alloy. It
|
|
90
|
+
redacts completed span names, attributes, events, links, and status messages,
|
|
91
|
+
including automatic fetch spans and exceptions. URL paths are removed from
|
|
92
|
+
span names. A custom `otel.traceExporter` receives the same redacted copies.
|
|
93
|
+
Core attribute bounds apply; canonical service identity remains on the OTel
|
|
94
|
+
resource. Resource and instrumentation-scope metadata are trusted configuration.
|
|
95
|
+
|
|
96
|
+
Explicit `otel.spanProcessors` replace this default, so their exporters must
|
|
97
|
+
apply their own privacy policy (or use Node's `RedactingSpanExporter`). The
|
|
98
|
+
default does not use Vercel's hosted exporter or `VERCEL_OTEL_ENDPOINTS`.
|
|
99
|
+
Choose custom processors deliberately if a Vercel tracing integration is
|
|
100
|
+
required. Use a custom exporter for a protocol other than HTTP protobuf.
|
|
101
|
+
|
|
83
102
|
Provider registration owns trace sampling and the Node adapter applies
|
|
84
103
|
`sampleRate` once to logs; core does not make a second decision for those
|
|
85
104
|
signals. OTel histogram measurements remain aggregated instead of randomly
|
|
@@ -168,7 +187,8 @@ reaches VictoriaLogs with active `trace_id`, `span_id`, service, and release.
|
|
|
168
187
|
|
|
169
188
|
In long-running Next server processes the framework owns the provider
|
|
170
189
|
lifecycle. Do not call `shutdown()` during an ordinary request. Use `flush()`
|
|
171
|
-
only
|
|
172
|
-
|
|
190
|
+
only when queued telemetry must leave before returning or exiting. It drains
|
|
191
|
+
core sends and invokes public `forceFlush()` on registered trace, log, and
|
|
192
|
+
metric providers when supported. It does not shut down the framework provider.
|
|
173
193
|
|
|
174
194
|
See the [web adapter](https://www.npmjs.com/package/@davidilie/telemetry-web), [Node adapter](https://www.npmjs.com/package/@davidilie/telemetry-node), [project onboarding](https://github.com/DavidIlie/davidapps-telemetry/blob/main/docs/adding-a-project.md), and [troubleshooting](https://github.com/DavidIlie/davidapps-telemetry/blob/main/docs/troubleshooting.md).
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_request_error = require("./request-error-
|
|
2
|
+
const require_request_error = require("./request-error-CEM6CkHw.cjs");
|
|
3
3
|
exports.createNextRequestErrorHandler = require_request_error.createNextRequestErrorHandler;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createNextRequestErrorHandler } from "./request-error-
|
|
1
|
+
import { t as createNextRequestErrorHandler } from "./request-error-DbUeEPob.mjs";
|
|
2
2
|
export { createNextRequestErrorHandler };
|
package/dist/node.cjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_request_error = require("./request-error-
|
|
3
|
-
let _vercel_otel = require("@vercel/otel");
|
|
2
|
+
const require_request_error = require("./request-error-CEM6CkHw.cjs");
|
|
4
3
|
let _davidilie_telemetry_core = require("@davidilie/telemetry-core");
|
|
4
|
+
let _vercel_otel = require("@vercel/otel");
|
|
5
5
|
let _davidilie_telemetry_node = require("@davidilie/telemetry-node");
|
|
6
6
|
let _opentelemetry_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
7
|
+
let _opentelemetry_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
|
|
7
8
|
//#region src/node.ts
|
|
8
9
|
const REGISTRATION = Symbol.for("@davidilie/telemetry-next/registration");
|
|
9
10
|
function traceSampler(config) {
|
|
@@ -24,8 +25,13 @@ function registerNextTelemetry(config) {
|
|
|
24
25
|
enabled: config.enabled ?? true,
|
|
25
26
|
consent: config.consent ?? "granted"
|
|
26
27
|
});
|
|
28
|
+
const { spanProcessors, traceExporter, ...otel } = config.otel ?? {};
|
|
29
|
+
const exporter = traceExporter && traceExporter !== "auto" ? traceExporter : void 0;
|
|
27
30
|
(0, _vercel_otel.registerOTel)({
|
|
28
|
-
...
|
|
31
|
+
...otel,
|
|
32
|
+
spanProcessors: spanProcessors ?? [new _opentelemetry_sdk_trace_base.BatchSpanProcessor(new _davidilie_telemetry_node.RedactingSpanExporter(exporter ?? new _opentelemetry_exporter_trace_otlp_proto.OTLPTraceExporter()))],
|
|
33
|
+
...spanProcessors && exporter ? { traceExporter: new _davidilie_telemetry_node.RedactingSpanExporter(exporter) } : {},
|
|
34
|
+
resourceDetectors: config.otel?.resourceDetectors ?? [],
|
|
29
35
|
traceSampler: sampler,
|
|
30
36
|
serviceName: resource.serviceName,
|
|
31
37
|
attributes: (0, _davidilie_telemetry_node.telemetryResourceAttributes)(resource)
|
package/dist/node.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { t as createNextRequestErrorHandler } from "./request-error-
|
|
2
|
-
import { registerOTel } from "@vercel/otel";
|
|
1
|
+
import { t as createNextRequestErrorHandler } from "./request-error-DbUeEPob.mjs";
|
|
3
2
|
import { sanitizeResource } from "@davidilie/telemetry-core";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { registerOTel } from "@vercel/otel";
|
|
4
|
+
import { DynamicTelemetrySampler, NodeTelemetryClient, RedactingSpanExporter, telemetryResourceAttributes } from "@davidilie/telemetry-node";
|
|
5
|
+
import { BatchSpanProcessor, ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
|
|
6
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
6
7
|
//#region src/node.ts
|
|
7
8
|
const REGISTRATION = Symbol.for("@davidilie/telemetry-next/registration");
|
|
8
9
|
function traceSampler(config) {
|
|
@@ -23,8 +24,13 @@ function registerNextTelemetry(config) {
|
|
|
23
24
|
enabled: config.enabled ?? true,
|
|
24
25
|
consent: config.consent ?? "granted"
|
|
25
26
|
});
|
|
27
|
+
const { spanProcessors, traceExporter, ...otel } = config.otel ?? {};
|
|
28
|
+
const exporter = traceExporter && traceExporter !== "auto" ? traceExporter : void 0;
|
|
26
29
|
registerOTel({
|
|
27
|
-
...
|
|
30
|
+
...otel,
|
|
31
|
+
spanProcessors: spanProcessors ?? [new BatchSpanProcessor(new RedactingSpanExporter(exporter ?? new OTLPTraceExporter()))],
|
|
32
|
+
...spanProcessors && exporter ? { traceExporter: new RedactingSpanExporter(exporter) } : {},
|
|
33
|
+
resourceDetectors: config.otel?.resourceDetectors ?? [],
|
|
28
34
|
traceSampler: sampler,
|
|
29
35
|
serviceName: resource.serviceName,
|
|
30
36
|
attributes: telemetryResourceAttributes(resource)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
let _davidilie_telemetry_core = require("@davidilie/telemetry-core");
|
|
1
2
|
//#region src/request-error.ts
|
|
2
3
|
async function reportFailure(config, error, stage) {
|
|
3
4
|
try {
|
|
@@ -8,6 +9,22 @@ function errorDigest(error) {
|
|
|
8
9
|
if (typeof error === "object" && error !== null && "digest" in error && typeof error.digest === "string") return error.digest;
|
|
9
10
|
}
|
|
10
11
|
function createNextRequestErrorHandler(config) {
|
|
12
|
+
let pendingFlush;
|
|
13
|
+
let needsFlush = false;
|
|
14
|
+
function flushCaptured() {
|
|
15
|
+
needsFlush = true;
|
|
16
|
+
pendingFlush ??= Promise.resolve().then(async () => {
|
|
17
|
+
try {
|
|
18
|
+
do {
|
|
19
|
+
needsFlush = false;
|
|
20
|
+
await config.telemetry.flush?.();
|
|
21
|
+
} while (needsFlush);
|
|
22
|
+
} finally {
|
|
23
|
+
pendingFlush = void 0;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return pendingFlush;
|
|
27
|
+
}
|
|
11
28
|
return async (error, request, context) => {
|
|
12
29
|
if (config.beforeCapture) try {
|
|
13
30
|
if (!await config.beforeCapture(error, request, context)) return;
|
|
@@ -15,26 +32,26 @@ function createNextRequestErrorHandler(config) {
|
|
|
15
32
|
await reportFailure(config, hookError, "beforeCapture");
|
|
16
33
|
return;
|
|
17
34
|
}
|
|
18
|
-
const digest = errorDigest(error);
|
|
19
35
|
try {
|
|
20
|
-
|
|
36
|
+
const digest = errorDigest(error);
|
|
37
|
+
config.telemetry.captureException(error, (0, _davidilie_telemetry_core.sanitizeAttributes)({
|
|
21
38
|
...config.attributes,
|
|
22
39
|
"http.request.method": request.method,
|
|
23
|
-
"url.path": request.path,
|
|
40
|
+
"url.path": (0, _davidilie_telemetry_core.sanitizeUrl)(request.path),
|
|
24
41
|
"next.router.kind": context.routerKind,
|
|
25
|
-
"next.route.path": context.routePath,
|
|
42
|
+
"next.route.path": (0, _davidilie_telemetry_core.sanitizeUrl)(context.routePath),
|
|
26
43
|
"next.route.type": context.routeType,
|
|
27
44
|
...context.renderSource ? { "next.render.source": context.renderSource } : {},
|
|
28
45
|
...context.revalidateReason ? { "next.revalidate.reason": context.revalidateReason } : {},
|
|
29
46
|
...context.renderType ? { "next.render.type": context.renderType } : {},
|
|
30
47
|
...digest ? { "next.error.digest": digest } : {}
|
|
31
|
-
});
|
|
48
|
+
}));
|
|
32
49
|
} catch (captureError) {
|
|
33
50
|
await reportFailure(config, captureError, "capture");
|
|
34
51
|
return;
|
|
35
52
|
}
|
|
36
53
|
try {
|
|
37
|
-
await
|
|
54
|
+
await flushCaptured();
|
|
38
55
|
} catch (flushError) {
|
|
39
56
|
await reportFailure(config, flushError, "flush");
|
|
40
57
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeAttributes, sanitizeUrl } from "@davidilie/telemetry-core";
|
|
1
2
|
//#region src/request-error.ts
|
|
2
3
|
async function reportFailure(config, error, stage) {
|
|
3
4
|
try {
|
|
@@ -8,6 +9,22 @@ function errorDigest(error) {
|
|
|
8
9
|
if (typeof error === "object" && error !== null && "digest" in error && typeof error.digest === "string") return error.digest;
|
|
9
10
|
}
|
|
10
11
|
function createNextRequestErrorHandler(config) {
|
|
12
|
+
let pendingFlush;
|
|
13
|
+
let needsFlush = false;
|
|
14
|
+
function flushCaptured() {
|
|
15
|
+
needsFlush = true;
|
|
16
|
+
pendingFlush ??= Promise.resolve().then(async () => {
|
|
17
|
+
try {
|
|
18
|
+
do {
|
|
19
|
+
needsFlush = false;
|
|
20
|
+
await config.telemetry.flush?.();
|
|
21
|
+
} while (needsFlush);
|
|
22
|
+
} finally {
|
|
23
|
+
pendingFlush = void 0;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return pendingFlush;
|
|
27
|
+
}
|
|
11
28
|
return async (error, request, context) => {
|
|
12
29
|
if (config.beforeCapture) try {
|
|
13
30
|
if (!await config.beforeCapture(error, request, context)) return;
|
|
@@ -15,26 +32,26 @@ function createNextRequestErrorHandler(config) {
|
|
|
15
32
|
await reportFailure(config, hookError, "beforeCapture");
|
|
16
33
|
return;
|
|
17
34
|
}
|
|
18
|
-
const digest = errorDigest(error);
|
|
19
35
|
try {
|
|
20
|
-
|
|
36
|
+
const digest = errorDigest(error);
|
|
37
|
+
config.telemetry.captureException(error, sanitizeAttributes({
|
|
21
38
|
...config.attributes,
|
|
22
39
|
"http.request.method": request.method,
|
|
23
|
-
"url.path": request.path,
|
|
40
|
+
"url.path": sanitizeUrl(request.path),
|
|
24
41
|
"next.router.kind": context.routerKind,
|
|
25
|
-
"next.route.path": context.routePath,
|
|
42
|
+
"next.route.path": sanitizeUrl(context.routePath),
|
|
26
43
|
"next.route.type": context.routeType,
|
|
27
44
|
...context.renderSource ? { "next.render.source": context.renderSource } : {},
|
|
28
45
|
...context.revalidateReason ? { "next.revalidate.reason": context.revalidateReason } : {},
|
|
29
46
|
...context.renderType ? { "next.render.type": context.renderType } : {},
|
|
30
47
|
...digest ? { "next.error.digest": digest } : {}
|
|
31
|
-
});
|
|
48
|
+
}));
|
|
32
49
|
} catch (captureError) {
|
|
33
50
|
await reportFailure(config, captureError, "capture");
|
|
34
51
|
return;
|
|
35
52
|
}
|
|
36
53
|
try {
|
|
37
|
-
await
|
|
54
|
+
await flushCaptured();
|
|
38
55
|
} catch (flushError) {
|
|
39
56
|
await reportFailure(config, flushError, "flush");
|
|
40
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davidilie/telemetry-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Runtime-safe Next.js telemetry backed by @vercel/otel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analytics",
|
|
@@ -66,8 +66,9 @@
|
|
|
66
66
|
"clean": "rm -rf dist coverage"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@davidilie/telemetry-core": "0.
|
|
70
|
-
"@davidilie/telemetry-node": "0.
|
|
69
|
+
"@davidilie/telemetry-core": "0.4.0",
|
|
70
|
+
"@davidilie/telemetry-node": "0.4.0",
|
|
71
|
+
"@opentelemetry/exporter-trace-otlp-proto": "0.221.0",
|
|
71
72
|
"@opentelemetry/sdk-trace-base": "2.10.0",
|
|
72
73
|
"@vercel/otel": "2.1.3"
|
|
73
74
|
},
|
|
@@ -83,7 +84,7 @@
|
|
|
83
84
|
"devDependencies": {
|
|
84
85
|
"@opentelemetry/api": "1.9.0",
|
|
85
86
|
"@types/node": "25.0.10",
|
|
86
|
-
"next": "16.3.
|
|
87
|
+
"next": "16.3.5"
|
|
87
88
|
},
|
|
88
89
|
"publishConfig": {
|
|
89
90
|
"access": "public",
|