Initial import of verae-middleware from zapier monorepo
This commit is contained in:
commit
a11a20128a
67 changed files with 6630 additions and 0 deletions
95
src/debug/trace.js
Normal file
95
src/debug/trace.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @fileoverview Correlation / trace IDs for failure tracing across HTTP and NATS.
|
||||
* @module debug/trace
|
||||
*/
|
||||
|
||||
import { createDebugger } from './logger.js';
|
||||
import {
|
||||
traceStorage,
|
||||
generateTraceId,
|
||||
getTraceId,
|
||||
getTraceContext,
|
||||
} from './trace-context.js';
|
||||
|
||||
export { generateTraceId, getTraceId, getTraceContext };
|
||||
|
||||
const log = createDebugger('trace');
|
||||
|
||||
/**
|
||||
* Run `fn` within a trace context. Nested calls inherit the same `traceId`
|
||||
* unless `traceId` is explicitly overridden.
|
||||
*
|
||||
* @template T
|
||||
* @param {object|(() => T|Promise<T>)} options - Options or the callback itself.
|
||||
* @param {string} [options.traceId] - Existing id (e.g. from `X-Trace-Id` header).
|
||||
* @param {string} [options.span] - Human label for this span (logged when debug on).
|
||||
* @param {() => T|Promise<T>} [fn] - Work to execute inside the context when options is an object.
|
||||
* @returns {T|Promise<T>} Return value of `fn`.
|
||||
*
|
||||
* @example
|
||||
* await withTrace({ span: 'timestamp.wait', traceId: req.headers['x-trace-id'] }, async () => {
|
||||
* // getTraceId() is stable here and in awaited children
|
||||
* });
|
||||
*/
|
||||
export function withTrace(options, fn) {
|
||||
const opts = typeof options === 'function' ? {} : options ?? {};
|
||||
const callback = typeof options === 'function' ? options : fn;
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
throw new TypeError('withTrace requires a function to execute');
|
||||
}
|
||||
|
||||
const parent = traceStorage.getStore();
|
||||
const traceId = opts.traceId || parent?.traceId || generateTraceId();
|
||||
const span = opts.span || parent?.span || 'root';
|
||||
|
||||
const ctx = { traceId, span };
|
||||
|
||||
return traceStorage.run(ctx, () => {
|
||||
log.debug('span enter', { span });
|
||||
try {
|
||||
const result = callback();
|
||||
if (result && typeof result.then === 'function') {
|
||||
return result.then(
|
||||
(value) => {
|
||||
log.debug('span exit', { span, ok: true });
|
||||
return value;
|
||||
},
|
||||
(err) => {
|
||||
log.debug('span exit', { span, ok: false, error: err?.message });
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
}
|
||||
log.debug('span exit', { span, ok: true });
|
||||
return result;
|
||||
} catch (err) {
|
||||
log.debug('span exit', { span, ok: false, error: err?.message });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Express middleware that establishes a trace context per request.
|
||||
*
|
||||
* Reads `X-Trace-Id` when present; otherwise generates a new id.
|
||||
* Sets `req.traceId` and response header `X-Trace-Id`.
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
* @param {import('express').NextFunction} next
|
||||
* @returns {void}
|
||||
*/
|
||||
export function traceMiddleware(req, res, next) {
|
||||
const incoming = req.headers['x-trace-id'];
|
||||
const traceId =
|
||||
typeof incoming === 'string' && incoming.trim() ? incoming.trim() : generateTraceId();
|
||||
|
||||
req.traceId = traceId;
|
||||
res.setHeader('X-Trace-Id', traceId);
|
||||
|
||||
withTrace({ traceId, span: `${req.method} ${req.path}` }, () => {
|
||||
next();
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue