@nextdog/node 1.0.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/LICENSE +21 -0
- package/dist/console-patch.d.ts +2 -0
- package/dist/console-patch.d.ts.map +1 -0
- package/dist/console-patch.js +145 -0
- package/dist/console-patch.js.map +1 -0
- package/dist/exporter.d.ts +10 -0
- package/dist/exporter.d.ts.map +1 -0
- package/dist/exporter.js +96 -0
- package/dist/exporter.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/request-capture.d.ts +15 -0
- package/dist/request-capture.d.ts.map +1 -0
- package/dist/request-capture.js +127 -0
- package/dist/request-capture.js.map +1 -0
- package/dist/request-context.d.ts +18 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +22 -0
- package/dist/request-context.js.map +1 -0
- package/dist/sidecar.d.ts +2 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +106 -0
- package/dist/sidecar.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christopher Scherban
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-patch.d.ts","sourceRoot":"","sources":["../src/console-patch.ts"],"names":[],"mappings":"AA4DA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QA4F5D"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { trace, context } from '@opentelemetry/api';
|
|
2
|
+
import { getRequestContext } from './request-context.js';
|
|
3
|
+
const LEVELS = ['debug', 'log', 'info', 'warn', 'error'];
|
|
4
|
+
const LEVEL_MAP = {
|
|
5
|
+
debug: 'debug',
|
|
6
|
+
log: 'info',
|
|
7
|
+
info: 'info',
|
|
8
|
+
warn: 'warn',
|
|
9
|
+
error: 'error',
|
|
10
|
+
};
|
|
11
|
+
function tryParseJson(str) {
|
|
12
|
+
if (typeof str !== 'string')
|
|
13
|
+
return null;
|
|
14
|
+
const trimmed = str.trim();
|
|
15
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
16
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(trimmed);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function flattenObject(obj, prefix = '') {
|
|
27
|
+
const result = {};
|
|
28
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
29
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
30
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
|
|
31
|
+
Object.assign(result, flattenObject(value, fullKey));
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
result[fullKey] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
function formatArg(arg) {
|
|
40
|
+
if (typeof arg === 'string')
|
|
41
|
+
return arg;
|
|
42
|
+
if (arg instanceof Error)
|
|
43
|
+
return `${arg.message}\n${arg.stack ?? ''}`;
|
|
44
|
+
try {
|
|
45
|
+
return JSON.stringify(arg);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return String(arg);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function extractAttributes(args) {
|
|
52
|
+
const attrs = {};
|
|
53
|
+
for (const arg of args) {
|
|
54
|
+
if (arg && typeof arg === 'object' && !(arg instanceof Error)) {
|
|
55
|
+
// Flatten nested objects into dot-notation keys
|
|
56
|
+
Object.assign(attrs, flattenObject(arg));
|
|
57
|
+
}
|
|
58
|
+
else if (typeof arg === 'string') {
|
|
59
|
+
// Try to parse JSON strings
|
|
60
|
+
const parsed = tryParseJson(arg);
|
|
61
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
62
|
+
Object.assign(attrs, flattenObject(parsed));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return attrs;
|
|
67
|
+
}
|
|
68
|
+
export function patchConsole(url, serviceName) {
|
|
69
|
+
const buffer = [];
|
|
70
|
+
let flushTimer;
|
|
71
|
+
function flush() {
|
|
72
|
+
if (buffer.length === 0)
|
|
73
|
+
return;
|
|
74
|
+
const logs = buffer.splice(0, buffer.length);
|
|
75
|
+
const body = JSON.stringify({
|
|
76
|
+
logs: logs.map((l) => ({
|
|
77
|
+
type: 'log',
|
|
78
|
+
timestamp: l.timestamp,
|
|
79
|
+
data: l,
|
|
80
|
+
})),
|
|
81
|
+
});
|
|
82
|
+
fetch(`${url}/v1/logs`, {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: { 'Content-Type': 'application/json' },
|
|
85
|
+
body,
|
|
86
|
+
}).catch(() => { });
|
|
87
|
+
}
|
|
88
|
+
flushTimer = setInterval(flush, 500);
|
|
89
|
+
if (flushTimer.unref)
|
|
90
|
+
flushTimer.unref();
|
|
91
|
+
for (const level of LEVELS) {
|
|
92
|
+
const original = console[level].bind(console);
|
|
93
|
+
console[level] = (...args) => {
|
|
94
|
+
original(...args);
|
|
95
|
+
const firstArg = args[0];
|
|
96
|
+
if (typeof firstArg === 'string' && firstArg.startsWith('[nextdog]'))
|
|
97
|
+
return;
|
|
98
|
+
// Skip Next.js internal OTel/RSC noise
|
|
99
|
+
if (typeof firstArg === 'string' && (firstArg.includes('Unexpected root span type') ||
|
|
100
|
+
firstArg.includes('Failed to fetch RSC payload')))
|
|
101
|
+
return;
|
|
102
|
+
// Try multiple approaches to get the active span context
|
|
103
|
+
// Next.js 14 has less reliable OTel context propagation
|
|
104
|
+
let traceId;
|
|
105
|
+
let spanId;
|
|
106
|
+
const activeSpan = trace.getActiveSpan();
|
|
107
|
+
if (activeSpan) {
|
|
108
|
+
const spanCtx = activeSpan.spanContext();
|
|
109
|
+
traceId = spanCtx.traceId;
|
|
110
|
+
spanId = spanCtx.spanId;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Fallback: try extracting from the active context directly
|
|
114
|
+
// This works in some Next.js 14 code paths where getActiveSpan() fails
|
|
115
|
+
const ctx = context.active();
|
|
116
|
+
const spanFromCtx = trace.getSpan(ctx);
|
|
117
|
+
if (spanFromCtx) {
|
|
118
|
+
const spanCtx = spanFromCtx.spanContext();
|
|
119
|
+
traceId = spanCtx.traceId;
|
|
120
|
+
spanId = spanCtx.spanId;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const message = args.map(formatArg).join(' ');
|
|
124
|
+
const attributes = extractAttributes(args);
|
|
125
|
+
// Enrich with request context from our own AsyncLocalStorage
|
|
126
|
+
// (reliable even when OTel context propagation fails in Next.js 14)
|
|
127
|
+
const reqCtx = getRequestContext();
|
|
128
|
+
if (reqCtx) {
|
|
129
|
+
attributes['http.method'] = reqCtx.method;
|
|
130
|
+
attributes['http.route'] = reqCtx.path;
|
|
131
|
+
attributes['request.id'] = reqCtx.requestId;
|
|
132
|
+
}
|
|
133
|
+
buffer.push({
|
|
134
|
+
timestamp: Date.now(),
|
|
135
|
+
level: LEVEL_MAP[level],
|
|
136
|
+
message,
|
|
137
|
+
attributes: { ...attributes, runtime: 'server' },
|
|
138
|
+
traceId,
|
|
139
|
+
spanId,
|
|
140
|
+
serviceName,
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=console-patch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-patch.js","sourceRoot":"","sources":["../src/console-patch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-node';
|
|
2
|
+
import type { ExportResult } from '@opentelemetry/core';
|
|
3
|
+
export declare class NextDogExporter implements SpanExporter {
|
|
4
|
+
private url;
|
|
5
|
+
constructor(url: string);
|
|
6
|
+
private isNextdogSpan;
|
|
7
|
+
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
|
|
8
|
+
shutdown(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=exporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exporter.d.ts","sourceRoot":"","sources":["../src/exporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAkFxD,qBAAa,eAAgB,YAAW,YAAY;IACtC,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAE/B,OAAO,CAAC,aAAa;IAKrB,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAgB7E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAChC"}
|
package/dist/exporter.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getRequestMetadata } from './request-capture.js';
|
|
2
|
+
const ExportResultCode = { SUCCESS: 0, FAILED: 1 };
|
|
3
|
+
/** Headers to never capture (security-sensitive but NOT cookies — we need those for replay) */
|
|
4
|
+
const SKIP_HEADERS = new Set([
|
|
5
|
+
'authorization', 'proxy-authorization', 'x-api-key', 'x-auth-token',
|
|
6
|
+
]);
|
|
7
|
+
const SPAN_KIND_MAP = {
|
|
8
|
+
0: 'INTERNAL',
|
|
9
|
+
1: 'SERVER',
|
|
10
|
+
2: 'CLIENT',
|
|
11
|
+
3: 'PRODUCER',
|
|
12
|
+
4: 'CONSUMER',
|
|
13
|
+
};
|
|
14
|
+
const STATUS_CODE_MAP = {
|
|
15
|
+
0: 'UNSET',
|
|
16
|
+
1: 'OK',
|
|
17
|
+
2: 'ERROR',
|
|
18
|
+
};
|
|
19
|
+
function hrtimeToNano(hrtime) {
|
|
20
|
+
const [seconds, nanos] = hrtime;
|
|
21
|
+
return String(BigInt(seconds) * 1000000000n + BigInt(nanos));
|
|
22
|
+
}
|
|
23
|
+
function convertSpan(span) {
|
|
24
|
+
const ctx = span.spanContext();
|
|
25
|
+
const serviceName = span.resource?.attributes?.['service.name'] ?? 'unknown';
|
|
26
|
+
const kind = SPAN_KIND_MAP[span.kind] ?? 'INTERNAL';
|
|
27
|
+
// Start with OTel's own attributes
|
|
28
|
+
const attributes = {
|
|
29
|
+
...span.attributes,
|
|
30
|
+
};
|
|
31
|
+
// Enrich SERVER spans with captured request metadata (headers, cookies, body)
|
|
32
|
+
// Correlate by method + URL path (traceId is not available at capture time)
|
|
33
|
+
if (kind === 'SERVER') {
|
|
34
|
+
const reqMethod = String(span.attributes['http.method'] ?? span.attributes['http.request.method'] ?? 'GET');
|
|
35
|
+
const reqUrl = String(span.attributes['http.target'] ?? span.attributes['url.path'] ?? span.name);
|
|
36
|
+
const metadata = getRequestMetadata(reqMethod, reqUrl);
|
|
37
|
+
if (metadata) {
|
|
38
|
+
// Add request headers as http.request.header.{name}
|
|
39
|
+
for (const [key, value] of Object.entries(metadata.headers)) {
|
|
40
|
+
if (SKIP_HEADERS.has(key.toLowerCase()))
|
|
41
|
+
continue;
|
|
42
|
+
attributes[`http.request.header.${key.toLowerCase()}`] = value;
|
|
43
|
+
}
|
|
44
|
+
// Add cookies explicitly (critical for replay)
|
|
45
|
+
if (metadata.cookies) {
|
|
46
|
+
attributes['http.request.cookies'] = metadata.cookies;
|
|
47
|
+
}
|
|
48
|
+
// Add request body if present
|
|
49
|
+
if (metadata.body) {
|
|
50
|
+
attributes['http.request.body'] = metadata.body;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
traceId: ctx.traceId,
|
|
56
|
+
spanId: ctx.spanId,
|
|
57
|
+
parentSpanId: span.parentSpanId ?? span.parentSpanContext?.spanId ?? undefined,
|
|
58
|
+
name: span.name,
|
|
59
|
+
kind,
|
|
60
|
+
startTimeUnixNano: hrtimeToNano(span.startTime),
|
|
61
|
+
endTimeUnixNano: hrtimeToNano(span.endTime),
|
|
62
|
+
attributes,
|
|
63
|
+
status: {
|
|
64
|
+
code: STATUS_CODE_MAP[span.status.code] ?? 'UNSET',
|
|
65
|
+
message: span.status.message,
|
|
66
|
+
},
|
|
67
|
+
statusCode: Number(span.attributes['http.status_code'] ?? span.attributes['http.response.status_code'] ?? 0) || undefined,
|
|
68
|
+
serviceName,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export class NextDogExporter {
|
|
72
|
+
url;
|
|
73
|
+
constructor(url) {
|
|
74
|
+
this.url = url;
|
|
75
|
+
}
|
|
76
|
+
isNextdogSpan(span) {
|
|
77
|
+
const url = String(span.attributes['http.url'] ?? span.attributes['url.full'] ?? '');
|
|
78
|
+
return url.startsWith(this.url);
|
|
79
|
+
}
|
|
80
|
+
export(spans, resultCallback) {
|
|
81
|
+
const filtered = spans.filter((s) => !this.isNextdogSpan(s));
|
|
82
|
+
if (filtered.length === 0) {
|
|
83
|
+
return resultCallback({ code: ExportResultCode.SUCCESS });
|
|
84
|
+
}
|
|
85
|
+
const converted = filtered.map(convertSpan);
|
|
86
|
+
fetch(`${this.url}/v1/spans`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
body: JSON.stringify({ spans: converted }),
|
|
90
|
+
})
|
|
91
|
+
.then(() => resultCallback({ code: ExportResultCode.SUCCESS }))
|
|
92
|
+
.catch(() => resultCallback({ code: ExportResultCode.FAILED }));
|
|
93
|
+
}
|
|
94
|
+
async shutdown() { }
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=exporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exporter.js","sourceRoot":"","sources":["../src/exporter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { NextDogExporter } from './exporter.js';
|
|
2
|
+
export { ensureSidecar } from './sidecar.js';
|
|
3
|
+
export { patchConsole } from './console-patch.js';
|
|
4
|
+
export { startRequestCapture, getRequestMetadata } from './request-capture.js';
|
|
5
|
+
export type { RequestMetadata } from './request-capture.js';
|
|
6
|
+
export { requestContextStorage, createRequestContext, getRequestContext, } from './request-context.js';
|
|
7
|
+
export type { RequestContext } from './request-context.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { NextDogExporter } from './exporter.js';
|
|
2
|
+
export { ensureSidecar } from './sidecar.js';
|
|
3
|
+
export { patchConsole } from './console-patch.js';
|
|
4
|
+
export { startRequestCapture, getRequestMetadata } from './request-capture.js';
|
|
5
|
+
export { requestContextStorage, createRequestContext, getRequestContext, } from './request-context.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/E,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface RequestMetadata {
|
|
2
|
+
method: string;
|
|
3
|
+
url: string;
|
|
4
|
+
headers: Record<string, string>;
|
|
5
|
+
cookies: string;
|
|
6
|
+
body?: string;
|
|
7
|
+
capturedAt: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Look up request metadata by method + URL path.
|
|
11
|
+
* Finds the most recent capture matching these fields.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getRequestMetadata(method: string, url: string): RequestMetadata | undefined;
|
|
14
|
+
export declare function startRequestCapture(): void;
|
|
15
|
+
//# sourceMappingURL=request-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-capture.d.ts","sourceRoot":"","sources":["../src/request-capture.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAkED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAM3F;AAcD,wBAAgB,mBAAmB,SAyClC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Captures HTTP request metadata (headers, cookies, body) and stores it
|
|
3
|
+
* so the span exporter can enrich spans with this data for replay.
|
|
4
|
+
*
|
|
5
|
+
* Works by monkey-patching Node's http.Server to intercept incoming requests.
|
|
6
|
+
* Metadata is stored keyed by "method url" for correlation with OTel spans
|
|
7
|
+
* in the exporter (since the OTel active span is not available at request time).
|
|
8
|
+
*/
|
|
9
|
+
import * as http from 'node:http';
|
|
10
|
+
import { requestContextStorage, createRequestContext } from './request-context.js';
|
|
11
|
+
// Store captured request metadata keyed by "METHOD url" for recent lookups
|
|
12
|
+
// Multiple requests to the same URL are stored as a stack (most recent first)
|
|
13
|
+
const requestStore = new Map();
|
|
14
|
+
// Max body size to capture (16KB — enough for API payloads, avoids memory issues)
|
|
15
|
+
const MAX_BODY_SIZE = 16 * 1024;
|
|
16
|
+
// Cleanup entries older than 60s to prevent memory leaks
|
|
17
|
+
const CLEANUP_INTERVAL = 30_000;
|
|
18
|
+
const MAX_AGE = 60_000;
|
|
19
|
+
function captureHeaders(req) {
|
|
20
|
+
const headers = {};
|
|
21
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
22
|
+
if (value && key !== 'host') {
|
|
23
|
+
headers[key] = Array.isArray(value) ? value.join(', ') : value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return headers;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Capture the request body WITHOUT adding 'data' listeners (which would put
|
|
30
|
+
* the stream into flowing mode and break Next.js 14's body parsing).
|
|
31
|
+
* Instead, we monkey-patch req.on so that when Next.js (or any other consumer)
|
|
32
|
+
* reads the body, we passively observe the chunks.
|
|
33
|
+
*/
|
|
34
|
+
function captureBody(req, metadata) {
|
|
35
|
+
const method = (req.method ?? 'GET').toUpperCase();
|
|
36
|
+
if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS')
|
|
37
|
+
return;
|
|
38
|
+
const chunks = [];
|
|
39
|
+
let size = 0;
|
|
40
|
+
const originalOn = req.on;
|
|
41
|
+
// Intercept listener registration to piggyback on whoever reads the body
|
|
42
|
+
req.on = function (event, listener) {
|
|
43
|
+
if (event === 'data') {
|
|
44
|
+
const self = this;
|
|
45
|
+
const wrappedListener = (chunk) => {
|
|
46
|
+
if (size < MAX_BODY_SIZE) {
|
|
47
|
+
chunks.push(chunk);
|
|
48
|
+
size += chunk.length;
|
|
49
|
+
}
|
|
50
|
+
return listener.call(self, chunk);
|
|
51
|
+
};
|
|
52
|
+
return originalOn.call(this, event, wrappedListener);
|
|
53
|
+
}
|
|
54
|
+
if (event === 'end') {
|
|
55
|
+
const self = this;
|
|
56
|
+
const wrappedListener = (...args) => {
|
|
57
|
+
if (chunks.length > 0) {
|
|
58
|
+
const body = Buffer.concat(chunks).toString('utf-8');
|
|
59
|
+
metadata.body = body.length > MAX_BODY_SIZE ? body.slice(0, MAX_BODY_SIZE) : body;
|
|
60
|
+
chunks.length = 0;
|
|
61
|
+
}
|
|
62
|
+
return listener.call(self, ...args);
|
|
63
|
+
};
|
|
64
|
+
return originalOn.call(this, event, wrappedListener);
|
|
65
|
+
}
|
|
66
|
+
return originalOn.call(this, event, listener);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Look up request metadata by method + URL path.
|
|
71
|
+
* Finds the most recent capture matching these fields.
|
|
72
|
+
*/
|
|
73
|
+
export function getRequestMetadata(method, url) {
|
|
74
|
+
const key = `${method} ${url}`;
|
|
75
|
+
const stack = requestStore.get(key);
|
|
76
|
+
if (!stack || stack.length === 0)
|
|
77
|
+
return undefined;
|
|
78
|
+
// Return and consume the oldest matching entry (FIFO — first request in, first matched)
|
|
79
|
+
return stack.shift();
|
|
80
|
+
}
|
|
81
|
+
function cleanup() {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
for (const [key, stack] of requestStore) {
|
|
84
|
+
const filtered = stack.filter((m) => now - m.capturedAt < MAX_AGE);
|
|
85
|
+
if (filtered.length === 0) {
|
|
86
|
+
requestStore.delete(key);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
requestStore.set(key, filtered);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function startRequestCapture() {
|
|
94
|
+
const timer = setInterval(cleanup, CLEANUP_INTERVAL);
|
|
95
|
+
timer.unref();
|
|
96
|
+
const originalEmit = http.Server.prototype.emit;
|
|
97
|
+
http.Server.prototype.emit = function (event, ...args) {
|
|
98
|
+
if (event === 'request') {
|
|
99
|
+
const req = args[0];
|
|
100
|
+
const headers = captureHeaders(req);
|
|
101
|
+
const metadata = {
|
|
102
|
+
method: req.method ?? 'GET',
|
|
103
|
+
url: req.url ?? '/',
|
|
104
|
+
headers,
|
|
105
|
+
cookies: headers['cookie'] ?? '',
|
|
106
|
+
capturedAt: Date.now(),
|
|
107
|
+
};
|
|
108
|
+
// Capture body asynchronously (mutates metadata.body when ready)
|
|
109
|
+
captureBody(req, metadata);
|
|
110
|
+
// Store keyed by method + url
|
|
111
|
+
const key = `${metadata.method} ${metadata.url}`;
|
|
112
|
+
const stack = requestStore.get(key);
|
|
113
|
+
if (stack) {
|
|
114
|
+
stack.push(metadata);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
requestStore.set(key, [metadata]);
|
|
118
|
+
}
|
|
119
|
+
// Run the rest of the request inside AsyncLocalStorage context
|
|
120
|
+
// so console.log calls have access to request info
|
|
121
|
+
const reqCtx = createRequestContext(metadata.method, metadata.url);
|
|
122
|
+
return requestContextStorage.run(reqCtx, () => originalEmit.apply(this, [event, ...args]));
|
|
123
|
+
}
|
|
124
|
+
return originalEmit.apply(this, [event, ...args]);
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=request-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-capture.js","sourceRoot":"","sources":["../src/request-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncLocalStorage-based request context that works independently of OTel.
|
|
3
|
+
* Provides reliable request correlation for logs even when OTel's async
|
|
4
|
+
* context propagation fails (common in Next.js 14).
|
|
5
|
+
*/
|
|
6
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
7
|
+
export interface RequestContext {
|
|
8
|
+
requestId: string;
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
startTime: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const requestContextStorage: AsyncLocalStorage<RequestContext>;
|
|
14
|
+
/** Create a new request context for the current request */
|
|
15
|
+
export declare function createRequestContext(method: string, url: string): RequestContext;
|
|
16
|
+
/** Get the current request context (if inside a request) */
|
|
17
|
+
export declare function getRequestContext(): RequestContext | undefined;
|
|
18
|
+
//# sourceMappingURL=request-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,qBAAqB,mCAA0C,CAAC;AAE7E,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,CAOhF;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncLocalStorage-based request context that works independently of OTel.
|
|
3
|
+
* Provides reliable request correlation for logs even when OTel's async
|
|
4
|
+
* context propagation fails (common in Next.js 14).
|
|
5
|
+
*/
|
|
6
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
export const requestContextStorage = new AsyncLocalStorage();
|
|
9
|
+
/** Create a new request context for the current request */
|
|
10
|
+
export function createRequestContext(method, url) {
|
|
11
|
+
return {
|
|
12
|
+
requestId: randomUUID().slice(0, 8),
|
|
13
|
+
method,
|
|
14
|
+
path: url.split('?')[0], // Strip query params for cleaner grouping
|
|
15
|
+
startTime: Date.now(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Get the current request context (if inside a request) */
|
|
19
|
+
export function getRequestContext() {
|
|
20
|
+
return requestContextStorage.getStore();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=request-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../src/request-context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAE7E,2DAA2D;AAC3D,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,GAAW;IAC9D,OAAO;QACL,SAAS,EAAE,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM;QACN,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,0CAA0C;QACnE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,iBAAiB;IAC/B,OAAO,qBAAqB,CAAC,QAAQ,EAAE,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AA0FA,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB9D"}
|
package/dist/sidecar.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { readFile, writeFile, mkdir, open } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
const NEXTDOG_DIR = join(homedir(), '.nextdog');
|
|
7
|
+
const PID_FILE = join(NEXTDOG_DIR, 'nextdog.pid');
|
|
8
|
+
const LOG_FILE = join(NEXTDOG_DIR, 'sidecar.log');
|
|
9
|
+
async function isHealthy(url) {
|
|
10
|
+
try {
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
13
|
+
const res = await fetch(`${url}/health`, { signal: controller.signal });
|
|
14
|
+
clearTimeout(timeout);
|
|
15
|
+
return res.ok;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function isProcessRunning(pid) {
|
|
22
|
+
try {
|
|
23
|
+
process.kill(pid, 0);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function readPid() {
|
|
31
|
+
try {
|
|
32
|
+
const content = await readFile(PID_FILE, 'utf-8');
|
|
33
|
+
const pid = Number(content.trim());
|
|
34
|
+
return Number.isFinite(pid) ? pid : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function resolveCoreCliPath() {
|
|
41
|
+
const require = createRequire(import.meta.url);
|
|
42
|
+
try {
|
|
43
|
+
const corePkgPath = require.resolve('@nextdog/core/package.json');
|
|
44
|
+
return join(dirname(corePkgPath), 'dist', 'cli.js');
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Fallback: try resolving the bin entry directly
|
|
48
|
+
try {
|
|
49
|
+
return require.resolve('@nextdog/core/dist/cli.js');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
throw new Error('@nextdog/core not found. Make sure it is installed: npm install @nextdog/core');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function spawnSidecar(url) {
|
|
57
|
+
const coreCliPath = resolveCoreCliPath();
|
|
58
|
+
await mkdir(NEXTDOG_DIR, { recursive: true });
|
|
59
|
+
// Write sidecar stdout/stderr to a log file for debugging
|
|
60
|
+
const logFd = await open(LOG_FILE, 'a');
|
|
61
|
+
const child = spawn('node', [coreCliPath], {
|
|
62
|
+
detached: true,
|
|
63
|
+
stdio: ['ignore', logFd.fd, logFd.fd],
|
|
64
|
+
env: { ...process.env, NEXTDOG_URL: url },
|
|
65
|
+
});
|
|
66
|
+
child.unref();
|
|
67
|
+
// Close our handle — the child process has its own fd now
|
|
68
|
+
await logFd.close();
|
|
69
|
+
if (child.pid) {
|
|
70
|
+
await writeFile(PID_FILE, String(child.pid), 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
// Wait for the sidecar to become healthy (up to 3 seconds)
|
|
73
|
+
for (let i = 0; i < 6; i++) {
|
|
74
|
+
await new Promise(r => setTimeout(r, 500));
|
|
75
|
+
if (await isHealthy(url))
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
console.warn(`[nextdog] sidecar spawned (PID ${child.pid}) but health check not passing yet`);
|
|
79
|
+
console.warn(`[nextdog] check ${LOG_FILE} for sidecar logs`);
|
|
80
|
+
}
|
|
81
|
+
export async function ensureSidecar(url) {
|
|
82
|
+
// Already running and healthy — fast path
|
|
83
|
+
if (await isHealthy(url))
|
|
84
|
+
return;
|
|
85
|
+
// PID file exists and process is alive — wait for it to become healthy
|
|
86
|
+
const pid = await readPid();
|
|
87
|
+
if (pid && await isProcessRunning(pid)) {
|
|
88
|
+
for (let i = 0; i < 4; i++) {
|
|
89
|
+
await new Promise(r => setTimeout(r, 500));
|
|
90
|
+
if (await isHealthy(url))
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.warn(`[nextdog] sidecar process ${pid} is running but not responding at ${url}`);
|
|
94
|
+
console.warn(`[nextdog] check ${LOG_FILE} for sidecar logs`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// No sidecar running — spawn one
|
|
98
|
+
try {
|
|
99
|
+
await spawnSidecar(url);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.warn('[nextdog] failed to spawn sidecar:', err.message);
|
|
103
|
+
console.warn('[nextdog] you can start it manually with: npx nextdog');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=sidecar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nextdog/node",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared Node.js instrumentation for NextDog — OTel exporter, sidecar, console capture",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./exporter": "./dist/exporter.js",
|
|
11
|
+
"./sidecar": "./dist/sidecar.js",
|
|
12
|
+
"./console-patch": "./dist/console-patch.js",
|
|
13
|
+
"./request-capture": "./dist/request-capture.js",
|
|
14
|
+
"./request-context": "./dist/request-context.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@opentelemetry/api": "^1",
|
|
24
|
+
"@opentelemetry/sdk-trace-node": "^1",
|
|
25
|
+
"@opentelemetry/resources": "^1",
|
|
26
|
+
"@opentelemetry/semantic-conventions": "^1",
|
|
27
|
+
"@nextdog/core": "1.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25",
|
|
31
|
+
"@opentelemetry/core": "^2",
|
|
32
|
+
"vitest": "^3"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/cscherban/NextDog"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"node",
|
|
41
|
+
"observability",
|
|
42
|
+
"tracing",
|
|
43
|
+
"opentelemetry",
|
|
44
|
+
"devtools",
|
|
45
|
+
"instrumentation"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"test": "vitest run --passWithNoTests"
|
|
50
|
+
}
|
|
51
|
+
}
|