apitally 1.0.0-beta.5 → 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/README.md +12 -8
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/activation.cjs +30 -0
- package/dist/activation.cjs.map +1 -1
- package/dist/activation.d.cts +1 -0
- package/dist/activation.d.ts +1 -0
- package/dist/activation.js +28 -1
- package/dist/activation.js.map +1 -1
- package/dist/adonisjs/middleware.cjs +1 -1
- package/dist/adonisjs/middleware.cjs.map +1 -1
- package/dist/adonisjs/middleware.js +1 -1
- package/dist/adonisjs/middleware.js.map +1 -1
- package/dist/elysia/index.cjs.map +1 -1
- package/dist/elysia/index.js.map +1 -1
- package/dist/elysia/middleware.cjs +8 -10
- package/dist/elysia/middleware.cjs.map +1 -1
- package/dist/elysia/middleware.js +8 -10
- package/dist/elysia/middleware.js.map +1 -1
- package/dist/elysia/routes.cjs +2 -1
- package/dist/elysia/routes.cjs.map +1 -1
- package/dist/elysia/routes.js +2 -1
- package/dist/elysia/routes.js.map +1 -1
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js.map +1 -1
- package/dist/express/install.cjs +1 -0
- package/dist/express/install.cjs.map +1 -1
- package/dist/express/install.js +2 -1
- package/dist/express/install.js.map +1 -1
- package/dist/express/middleware.cjs +1 -1
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.js +1 -1
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/middleware.cjs +1 -1
- package/dist/fastify/middleware.cjs.map +1 -1
- package/dist/fastify/middleware.js +1 -1
- package/dist/fastify/middleware.js.map +1 -1
- package/dist/h3/index.cjs +1 -0
- package/dist/h3/index.cjs.map +1 -1
- package/dist/h3/index.js +2 -1
- package/dist/h3/index.js.map +1 -1
- package/dist/h3/middleware.cjs +1 -1
- package/dist/h3/middleware.cjs.map +1 -1
- package/dist/h3/middleware.js +1 -1
- package/dist/h3/middleware.js.map +1 -1
- package/dist/hapi/middleware.cjs +1 -1
- package/dist/hapi/middleware.cjs.map +1 -1
- package/dist/hapi/middleware.js +1 -1
- package/dist/hapi/middleware.js.map +1 -1
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +2 -1
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.js +3 -2
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +1 -0
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.js +2 -1
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +1 -1
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.js +1 -1
- package/dist/koa/middleware.js.map +1 -1
- package/dist/requestAttributes.cjs +7 -1
- package/dist/requestAttributes.cjs.map +1 -1
- package/dist/requestAttributes.js +7 -1
- package/dist/requestAttributes.js.map +1 -1
- package/dist/requestObservation.cjs +2 -1
- package/dist/requestObservation.cjs.map +1 -1
- package/dist/requestObservation.js +2 -1
- package/dist/requestObservation.js.map +1 -1
- package/dist/tracing.cjs +2 -1
- package/dist/tracing.cjs.map +1 -1
- package/dist/tracing.js +2 -1
- package/dist/tracing.js.map +1 -1
- package/package.json +14 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/koa/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\n\nimport type Koa = require(\"koa\");\n\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst MIDDLEWARE_MARKER = Symbol.for(\"apitally.koaMiddleware\");\nconst TRACER_NAME = \"apitally
|
|
1
|
+
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/koa/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\n\nimport type Koa = require(\"koa\");\n\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst MIDDLEWARE_MARKER = Symbol.for(\"apitally.koaMiddleware\");\nconst TRACER_NAME = \"apitally/koa\";\n\ninterface KoaRequestObservation extends NodeRequestObservation {\n route?: string;\n}\n\ninterface ObservedRequestStart {\n observation: KoaRequestObservation;\n requestContext: Context;\n}\n\nexport function installKoaMiddleware(app: Koa): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[MIDDLEWARE_MARKER] === true) {\n return;\n }\n markedApp[MIDDLEWARE_MARKER] = true;\n warnIfMiddlewareAlreadyRegistered(app);\n\n let isErrorListenerRegistered = false;\n app.use(async (ctx, next) => {\n if (!isErrorListenerRegistered) {\n // Register after callback() so our listener does not suppress Koa's default error logger.\n app.on(\"error\", (error) => {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n captureException(error);\n }\n });\n isErrorListenerRegistered = true;\n }\n let started: ObservedRequestStart | undefined;\n try {\n started = observeRequest(ctx);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!started) {\n await next();\n return;\n }\n\n try {\n await context.with(started.requestContext, next);\n } catch (error) {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n context.with(started.requestContext, () => captureException(error));\n }\n throw error;\n } finally {\n started.observation.route = resolveMatchedRoute(ctx);\n }\n });\n}\n\nfunction observeRequest(ctx: Koa.Context): ObservedRequestStart | undefined {\n activate();\n if (!isActivated()) {\n return undefined;\n }\n registerServerCloseFlush(ctx.req);\n const started = startNodeRequestObservation({ request: ctx.req, tracerName: TRACER_NAME });\n context.bind(started.requestContext, ctx.req);\n context.bind(started.requestContext, ctx.res);\n const observation: KoaRequestObservation = started.observation;\n captureNodeResponse(\n ctx.res,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: ctx.res.statusCode,\n route: observation.route ?? resolveMatchedRoute(ctx),\n clientAddress: ctx.ip,\n requestHeaders: ctx.req.headers,\n responseHeaders: ctx.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return { observation, requestContext: started.requestContext };\n}\n\nfunction warnIfMiddlewareAlreadyRegistered(app: Koa): void {\n try {\n if (app.middleware.length > 0) {\n logWarning(\n \"useApitally() was called after middleware was registered on the Koa app, so earlier middleware may handle or consume requests before Apitally observes them. To resolve this, call useApitally() immediately after creating the app, before registering middleware and routes.\",\n );\n }\n } catch (error) {\n logDebug(`Error inspecting the koa app's middleware: ${String(error)}`);\n }\n}\n"],"mappings":";;;;;;;;;AAiBA,MAAM,oBAAoB,OAAO,IAAI,wBAAwB;AAC7D,MAAM,cAAc;AAWpB,SAAgB,qBAAqB,KAAgB;CACnD,MAAM,YAAY;CAClB,IAAI,UAAU,uBAAuB,MACnC;CAEF,UAAU,qBAAqB;CAC/B,kCAAkC,GAAG;CAErC,IAAI,4BAA4B;CAChC,IAAI,IAAI,OAAO,KAAK,SAAS;EAC3B,IAAI,CAAC,2BAA2B;GAE9B,IAAI,GAAG,UAAU,UAAU;IACzB,MAAM,SAAS,mBAAmB,KAAK;IACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,iBAAiB,KAAK;GAE1B,CAAC;GACD,4BAA4B;EAC9B;EACA,IAAI;EACJ,IAAI;GACF,UAAU,eAAe,GAAG;EAC9B,SAAS,OAAO;GACd,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,SAAS;GACZ,MAAM,KAAK;GACX;EACF;EAEA,IAAI;GACF,MAAM,QAAQ,KAAK,QAAQ,gBAAgB,IAAI;EACjD,SAAS,OAAO;GACd,MAAM,SAAS,mBAAmB,KAAK;GACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,QAAQ,KAAK,QAAQ,sBAAsB,iBAAiB,KAAK,CAAC;GAEpE,MAAM;EACR,UAAU;GACR,QAAQ,YAAY,QAAQ,oBAAoB,GAAG;EACrD;CACF,CAAC;AACH;AAEA,SAAS,eAAe,KAAoD;CAC1E,SAAS;CACT,IAAI,CAAC,YAAY,GACf;CAEF,yBAAyB,IAAI,GAAG;CAChC,MAAM,UAAU,4BAA4B;EAAE,SAAS,IAAI;EAAK,YAAY;CAAY,CAAC;CACzF,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,GAAG;CAC5C,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,GAAG;CAC5C,MAAM,cAAqC,QAAQ;CACnD,oBACE,IAAI,KACJ,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA,GAC5E,YAAY,kBACd,CAAC,CACE,MAAM,eAAe;EACpB,2BAA2B;GACzB;GACA,mBAAmB,WAAW;GAC9B,YAAY,IAAI,IAAI;GACpB,OAAO,YAAY,SAAS,oBAAoB,GAAG;GACnD,eAAe,IAAI;GACnB,gBAAgB,IAAI,IAAI;GACxB,iBAAiB,IAAI,IAAI,WAAW;GACpC,qBAAqB,WAAW;GAChC,sBAAsB;EACxB,CAAC;CACH,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,WAAW,qCAAqC,OAAO,KAAK,GAAG;CACjE,CAAC;CACH,OAAO;EAAE;EAAa,gBAAgB,QAAQ;CAAe;AAC/D;AAEA,SAAS,kCAAkC,KAAgB;CACzD,IAAI;EACF,IAAI,IAAI,WAAW,SAAS,GAC1B,WACE,gRACF;CAEJ,SAAS,OAAO;EACd,SAAS,8CAA8C,OAAO,KAAK,GAAG;CACxE;AACF"}
|
|
@@ -2,9 +2,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_logger = require("./logger.cjs");
|
|
3
3
|
const require_context = require("./context.cjs");
|
|
4
4
|
//#region src/requestAttributes.ts
|
|
5
|
+
const CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE = "apitally.request.custom_attribute_keys";
|
|
5
6
|
function setRequestAttribute(key, value) {
|
|
6
7
|
try {
|
|
7
|
-
|
|
8
|
+
const record = require_context.getRequestRecord();
|
|
9
|
+
writeRequestAttribute(require_context.getServerSpan(), record, key, value);
|
|
10
|
+
if (record) {
|
|
11
|
+
const keys = record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] ?? [];
|
|
12
|
+
if (!keys.includes(key)) record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] = [...keys, key];
|
|
13
|
+
}
|
|
8
14
|
} catch (error) {
|
|
9
15
|
require_logger.logDebug(`Error setting request attribute: ${String(error)}`);
|
|
10
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestAttributes.cjs","names":["
|
|
1
|
+
{"version":3,"file":"requestAttributes.cjs","names":["getRequestRecord","getServerSpan"],"sources":["../src/requestAttributes.ts"],"sourcesContent":["import type { AttributeValue, Span } from \"@opentelemetry/api\";\nimport { getRequestRecord, getServerSpan, type RequestRecord } from \"./context.js\";\nimport { logDebug } from \"./logger.js\";\n\nconst CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE = \"apitally.request.custom_attribute_keys\";\n\nexport function setRequestAttribute(key: string, value: AttributeValue): void {\n try {\n const record = getRequestRecord();\n writeRequestAttribute(getServerSpan(), record, key, value);\n if (record) {\n const keys =\n (record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] as string[] | undefined) ?? [];\n if (!keys.includes(key)) {\n // A new array leaves already-built export copies unchanged\n record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] = [...keys, key];\n }\n }\n } catch (error) {\n logDebug(`Error setting request attribute: ${String(error)}`);\n }\n}\n\n// Mirroring attributes into the request record preserves values learned after\n// the live span stops recording.\nexport function writeRequestAttribute(\n span: Span | undefined,\n record: RequestRecord | undefined,\n key: string,\n value: AttributeValue,\n): void {\n if (span?.isRecording()) {\n span.setAttribute(key, value);\n }\n if (record) {\n record.attributes[key] = value;\n }\n}\n"],"mappings":";;;;AAIA,MAAM,kCAAkC;AAExC,SAAgB,oBAAoB,KAAa,OAA6B;CAC5E,IAAI;EACF,MAAM,SAASA,gBAAAA,iBAAiB;EAChC,sBAAsBC,gBAAAA,cAAc,GAAG,QAAQ,KAAK,KAAK;EACzD,IAAI,QAAQ;GACV,MAAM,OACH,OAAO,WAAW,oCAA6D,CAAC;GACnF,IAAI,CAAC,KAAK,SAAS,GAAG,GAEpB,OAAO,WAAW,mCAAmC,CAAC,GAAG,MAAM,GAAG;EAEtE;CACF,SAAS,OAAO;EACd,eAAA,SAAS,oCAAoC,OAAO,KAAK,GAAG;CAC9D;AACF;AAIA,SAAgB,sBACd,MACA,QACA,KACA,OACM;CACN,IAAI,MAAM,YAAY,GACpB,KAAK,aAAa,KAAK,KAAK;CAE9B,IAAI,QACF,OAAO,WAAW,OAAO;AAE7B"}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { logDebug } from "./logger.js";
|
|
2
2
|
import { getRequestRecord, getServerSpan } from "./context.js";
|
|
3
3
|
//#region src/requestAttributes.ts
|
|
4
|
+
const CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE = "apitally.request.custom_attribute_keys";
|
|
4
5
|
function setRequestAttribute(key, value) {
|
|
5
6
|
try {
|
|
6
|
-
|
|
7
|
+
const record = getRequestRecord();
|
|
8
|
+
writeRequestAttribute(getServerSpan(), record, key, value);
|
|
9
|
+
if (record) {
|
|
10
|
+
const keys = record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] ?? [];
|
|
11
|
+
if (!keys.includes(key)) record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] = [...keys, key];
|
|
12
|
+
}
|
|
7
13
|
} catch (error) {
|
|
8
14
|
logDebug(`Error setting request attribute: ${String(error)}`);
|
|
9
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestAttributes.js","names":[],"sources":["../src/requestAttributes.ts"],"sourcesContent":["import type { AttributeValue, Span } from \"@opentelemetry/api\";\nimport { getRequestRecord, getServerSpan, type RequestRecord } from \"./context.js\";\nimport { logDebug } from \"./logger.js\";\n\nexport function setRequestAttribute(key: string, value: AttributeValue): void {\n try {\n writeRequestAttribute(getServerSpan(),
|
|
1
|
+
{"version":3,"file":"requestAttributes.js","names":[],"sources":["../src/requestAttributes.ts"],"sourcesContent":["import type { AttributeValue, Span } from \"@opentelemetry/api\";\nimport { getRequestRecord, getServerSpan, type RequestRecord } from \"./context.js\";\nimport { logDebug } from \"./logger.js\";\n\nconst CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE = \"apitally.request.custom_attribute_keys\";\n\nexport function setRequestAttribute(key: string, value: AttributeValue): void {\n try {\n const record = getRequestRecord();\n writeRequestAttribute(getServerSpan(), record, key, value);\n if (record) {\n const keys =\n (record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] as string[] | undefined) ?? [];\n if (!keys.includes(key)) {\n // A new array leaves already-built export copies unchanged\n record.attributes[CUSTOM_ATTRIBUTE_KEYS_ATTRIBUTE] = [...keys, key];\n }\n }\n } catch (error) {\n logDebug(`Error setting request attribute: ${String(error)}`);\n }\n}\n\n// Mirroring attributes into the request record preserves values learned after\n// the live span stops recording.\nexport function writeRequestAttribute(\n span: Span | undefined,\n record: RequestRecord | undefined,\n key: string,\n value: AttributeValue,\n): void {\n if (span?.isRecording()) {\n span.setAttribute(key, value);\n }\n if (record) {\n record.attributes[key] = value;\n }\n}\n"],"mappings":";;;AAIA,MAAM,kCAAkC;AAExC,SAAgB,oBAAoB,KAAa,OAA6B;CAC5E,IAAI;EACF,MAAM,SAAS,iBAAiB;EAChC,sBAAsB,cAAc,GAAG,QAAQ,KAAK,KAAK;EACzD,IAAI,QAAQ;GACV,MAAM,OACH,OAAO,WAAW,oCAA6D,CAAC;GACnF,IAAI,CAAC,KAAK,SAAS,GAAG,GAEpB,OAAO,WAAW,mCAAmC,CAAC,GAAG,MAAM,GAAG;EAEtE;CACF,SAAS,OAAO;EACd,SAAS,oCAAoC,OAAO,KAAK,GAAG;CAC9D;AACF;AAIA,SAAgB,sBACd,MACA,QACA,KACA,OACM;CACN,IAAI,MAAM,YAAY,GACpB,KAAK,aAAa,KAAK,KAAK;CAE9B,IAAI,QACF,OAAO,WAAW,OAAO;AAE7B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_logger = require("./logger.cjs");
|
|
3
3
|
const require_config = require("./config.cjs");
|
|
4
|
+
const require_packageVersion = require("./packageVersion.cjs");
|
|
4
5
|
const require_context = require("./context.cjs");
|
|
5
6
|
const require_spanProcessor = require("./spanProcessor.cjs");
|
|
6
7
|
const require_requestAttributes = require("./requestAttributes.cjs");
|
|
@@ -44,7 +45,7 @@ function startRequestObservation(options) {
|
|
|
44
45
|
requestContext = require_context.withRequestHolders(activeContext, spanHandle, requestRecord);
|
|
45
46
|
} else {
|
|
46
47
|
requestContext = require_context.withRequestHolders(extractedContext, spanHandle, requestRecord);
|
|
47
|
-
const ownSpan = _opentelemetry_api.trace.getTracer(tracerName).startSpan(method, {
|
|
48
|
+
const ownSpan = _opentelemetry_api.trace.getTracer(tracerName, require_packageVersion.getDistroVersion()).startSpan(method, {
|
|
48
49
|
kind: _opentelemetry_api.SpanKind.SERVER,
|
|
49
50
|
attributes: startAttributes
|
|
50
51
|
}, requestContext);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestObservation.cjs","names":["trace","getActiveSpanPipeline","SpanKind","withRequestHolders","isApitallySpanProcessorDeclared","getRPCMetadata","RPCType","setRPCMetadata","SpanStatusCode","isValidationResponseStatus","getConfig","coerceToException","getActivationHandles"],"sources":["../src/requestObservation.ts"],"sourcesContent":["import { type Attributes, type Context, SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport { getRPCMetadata, type RPCMetadata, RPCType, setRPCMetadata } from \"@opentelemetry/core\";\nimport { getActivationHandles } from \"./activation.js\";\nimport type { BodyCapture, CapturedBody } from \"./bodyCapture.js\";\nimport { getConfig } from \"./config.js\";\nimport { emitConsumerUpdateIfChanged } from \"./consumers.js\";\nimport {\n attachRequestRecordToRpcMetadata,\n type RequestRecord,\n type SpanHandle,\n withRequestHolders,\n} from \"./context.js\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logWarning } from \"./logger.js\";\nimport { writeRequestAttribute } from \"./requestAttributes.js\";\nimport { addServerError } from \"./serverErrors.js\";\nimport {\n getActiveSpanPipeline,\n isApitallySpanProcessorDeclared,\n type RequestStash,\n} from \"./spanProcessor.js\";\nimport { addValidationErrors, isValidationResponseStatus } from \"./validationErrors.js\";\n\ninterface StartRequestObservationOptions {\n activeContext: Context;\n extractedContext: Context;\n tracerName: string;\n method: string;\n startAttributes: Attributes;\n requestBodyCapture: BodyCapture;\n}\n\nexport interface StartedRequestObservation {\n requestRecord: RequestRecord;\n requestContext: Context;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n}\n\nexport interface HttpRequestStartAttributeInput {\n method: string;\n path?: string;\n query?: string;\n scheme?: string;\n serverAddress?: string;\n fullUrl?: string;\n clientAddress?: string;\n userAgent?: string;\n requestBodySize?: number;\n}\n\nexport function resolveHttpRequestStartAttributes(\n input: HttpRequestStartAttributeInput,\n): Attributes {\n const attributes: Attributes = { \"http.request.method\": input.method };\n if (input.path !== undefined) {\n attributes[\"url.path\"] = input.path;\n }\n if (input.query !== undefined) {\n attributes[\"url.query\"] = input.query;\n }\n if (input.scheme !== undefined) {\n attributes[\"url.scheme\"] = input.scheme;\n }\n if (input.fullUrl !== undefined) {\n attributes[\"url.full\"] = input.fullUrl;\n }\n if (input.serverAddress !== undefined) {\n attributes[\"server.address\"] = input.serverAddress;\n }\n if (input.clientAddress !== undefined) {\n attributes[\"client.address\"] = input.clientAddress;\n }\n if (input.userAgent !== undefined) {\n attributes[\"user_agent.original\"] = input.userAgent;\n }\n if (input.requestBodySize !== undefined) {\n attributes[\"http.request.body.size\"] = input.requestBodySize;\n }\n return attributes;\n}\n\nexport function startRequestObservation(\n options: StartRequestObservationOptions,\n): StartedRequestObservation {\n const {\n activeContext,\n extractedContext,\n tracerName,\n method,\n startAttributes,\n requestBodyCapture,\n } = options;\n const requestRecord: RequestRecord = { attributes: {} };\n const spanHandle: SpanHandle = {};\n // Metrics and the exported span copy read from the request record, so the\n // start attributes are mirrored into it on every path, span or no span.\n Object.assign(requestRecord.attributes, startAttributes);\n const activeSpan = trace.getSpan(activeContext);\n const resolveUnavailableSpanDropReason = (): RequestRecord[\"dropReason\"] =>\n getActiveSpanPipeline()?.resolveRequestDropReasonBeforeSampling(startAttributes) ??\n \"sampled-out\";\n let requestContext: Context;\n // Span kind is not part of the OTel API surface, so read the SDK-level\n // property from whichever package copy produced the span.\n if (activeSpan?.isRecording() && (activeSpan as { kind?: unknown }).kind === SpanKind.SERVER) {\n // A SERVER span produced by the user's own instrumentation is adopted:\n // no second span, and the request runs under the user's context.\n spanHandle.span = activeSpan;\n requestRecord.serverSpanId = activeSpan.spanContext().spanId;\n getActiveSpanPipeline()?.attachRequestRecord(requestRecord.serverSpanId, requestRecord);\n if (getActiveSpanPipeline()?.isRequestInFlight(requestRecord.serverSpanId) !== true) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else if (activeSpan && !activeSpan.isRecording() && !activeSpan.spanContext().isRemote) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else {\n requestContext = withRequestHolders(extractedContext, spanHandle, requestRecord);\n const ownSpan = trace\n .getTracer(tracerName)\n .startSpan(method, { kind: SpanKind.SERVER, attributes: startAttributes }, requestContext);\n if (!ownSpan.isRecording()) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n } else {\n spanHandle.span = ownSpan;\n spanHandle.ownSpan = ownSpan;\n if (isApitallySpanProcessorDeclared() && requestRecord.serverSpanId === undefined) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnAboutUnattachedSpanProcessor();\n }\n }\n }\n // A non-recording span still carries a valid span context, so user child\n // spans parent to it and parent-based samplers drop them consistently.\n requestContext = trace.setSpan(requestContext, ownSpan);\n }\n\n // Middleware-based span producers inspect OTel RPC metadata to demote duplicate\n // transport spans; completion adds the route.\n let rpcMetadata = getRPCMetadata(requestContext);\n if (!rpcMetadata && spanHandle.span) {\n rpcMetadata = { type: RPCType.HTTP, span: spanHandle.span };\n requestContext = setRPCMetadata(requestContext, rpcMetadata);\n }\n attachRequestRecordToRpcMetadata(rpcMetadata, requestRecord);\n if (requestRecord.dropReason !== undefined) {\n requestBodyCapture.stopBuffering();\n }\n return { requestRecord, requestContext, spanHandle, rpcMetadata };\n}\n\nexport interface RequestObservation {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n method: string;\n startTimeMillis: number;\n}\n\nexport interface FinalizeRequestObservationOptions {\n observation: RequestObservation;\n completedAtMillis: number;\n statusCode: number;\n route?: string;\n clientAddress?: string;\n requestHeaders: Headers | Record<string, string | string[] | undefined>;\n responseHeaders: Headers | Record<string, string | number | string[] | undefined>;\n capturedRequestBody?: CapturedBody;\n capturedResponseBody?: CapturedBody;\n}\n\nexport function finalizeRequestObservation(options: FinalizeRequestObservationOptions): void {\n const { observation, statusCode, route } = options;\n const { requestRecord, spanHandle, rpcMetadata, method } = observation;\n requestRecord.durationSeconds = (options.completedAtMillis - observation.startTimeMillis) / 1000;\n const { span, ownSpan } = spanHandle;\n if (options.clientAddress !== undefined) {\n writeRequestAttribute(span, requestRecord, \"client.address\", options.clientAddress);\n }\n writeRequestAttribute(span, requestRecord, \"http.response.status_code\", statusCode);\n const requestBodySize = options.capturedRequestBody?.size;\n if (requestBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.request.body.size\", requestBodySize);\n }\n const responseBodySize = options.capturedResponseBody?.size;\n if (responseBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.response.body.size\", responseBodySize);\n }\n if (route !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.route\", route);\n if (rpcMetadata) {\n rpcMetadata.route = route;\n }\n if (ownSpan?.isRecording()) {\n ownSpan.updateName(`${method} ${route}`);\n }\n } else {\n // An empty route clears one set by the producing instrumentation; request\n // metrics omit empty routes.\n requestRecord.attributes[\"http.route\"] = \"\";\n }\n if (ownSpan && statusCode >= 500) {\n ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n }\n // Validation and server errors are counted for every routed request,\n // including requests dropped from tracing.\n const consumer = requestRecord.attributes[\"apitally.consumer.identifier\"];\n const consumerIdentifier = typeof consumer === \"string\" ? consumer : undefined;\n if (requestRecord.validationErrors && isValidationResponseStatus(statusCode)) {\n addValidationErrors(consumerIdentifier, method, route ?? \"\", requestRecord.validationErrors);\n }\n if (statusCode === 500 && requestRecord.exception !== undefined) {\n const sentryEventId = requestRecord.attributes[\"apitally.exception.sentry_event_id\"];\n addServerError(\n consumerIdentifier,\n method,\n route ?? \"\",\n requestRecord.exception,\n typeof sentryEventId === \"string\" ? sentryEventId : undefined,\n );\n }\n // A dropped request's spans are never released, so a stash entry for it\n // would sit unconsumed until the cap evicts it.\n if (requestRecord.serverSpanId !== undefined && requestRecord.dropReason === undefined) {\n const config = getConfig();\n const stash: RequestStash = {};\n if (config.captureRequestHeaders) {\n stash.requestHeaders = normalizeHeaders(options.requestHeaders);\n }\n if (config.captureResponseHeaders) {\n stash.responseHeaders = normalizeHeaders(options.responseHeaders);\n }\n const requestBody = options.capturedRequestBody?.body;\n if (requestBody) {\n stash.requestBody = requestBody;\n stash.requestContentEncoding = getHeaderValue(options.requestHeaders, \"content-encoding\");\n }\n const responseBody = options.capturedResponseBody?.body;\n if (responseBody && config.captureResponseBody) {\n stash.responseBody = responseBody;\n stash.responseContentEncoding = getHeaderValue(options.responseHeaders, \"content-encoding\");\n }\n if (Object.keys(stash).length > 0) {\n getActiveSpanPipeline()?.updateStash(requestRecord.serverSpanId, stash);\n }\n }\n ownSpan?.end(options.completedAtMillis);\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nexport interface FinalizeRequestObservationWithErrorOptions {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n error: unknown;\n durationSeconds: number;\n}\n\nexport function finalizeRequestObservationWithError(\n options: FinalizeRequestObservationWithErrorOptions,\n): void {\n const { requestRecord, spanHandle, error, durationSeconds } = options;\n requestRecord.durationSeconds = durationSeconds;\n if (spanHandle.span?.isRecording()) {\n spanHandle.span.recordException(coerceToException(error));\n }\n if (spanHandle.ownSpan) {\n spanHandle.ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n spanHandle.ownSpan.end();\n }\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nfunction getHeaderValue(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n name: string,\n): string | undefined {\n if (isWebHeaders(headers)) {\n return headers.get(name) ?? undefined;\n }\n for (const headerName of Object.keys(headers)) {\n if (headerName.toLowerCase() === name) {\n const value = headers[headerName];\n return Array.isArray(value) ? value.join(\",\") : value?.toString();\n }\n }\n return undefined;\n}\n\n// Values remain raw so all redaction happens at the export boundary.\nfunction normalizeHeaders(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n): Record<string, string | string[]> {\n const normalized: Record<string, string | string[]> = {};\n if (isWebHeaders(headers)) {\n // The Headers API combines repeated values except Set-Cookie.\n for (const [name, value] of headers) {\n const existing = normalized[name];\n if (existing === undefined) {\n normalized[name] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n normalized[name] = [existing, value];\n }\n }\n return normalized;\n }\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (value !== undefined) {\n normalized[name.toLowerCase()] = Array.isArray(value) ? value : String(value);\n }\n }\n return normalized;\n}\n\n// Duck-typed on iterability: a plain header record has no Symbol.iterator, and\n// the Headers instance may come from another realm's implementation.\nfunction isWebHeaders(headers: object): headers is Headers {\n return typeof (headers as Headers)[Symbol.iterator] === \"function\";\n}\n\n// The SDK-owned tracer provider's sampler leaves a SERVER span unrecorded only\n// when sampleRate samples it out, so there is no coverage loss to warn about.\nfunction warnIfUserSamplerDroppedServerSpan(): void {\n if (getActivationHandles()?.tracerProvider) {\n return;\n }\n logWarning(\n \"The OpenTelemetry sampler did not sample the SERVER span of a request. Only sampled requests are exported to Apitally as traces and request logs. Request metrics include all requests.\",\n );\n}\n\nfunction warnAboutUnattachedSpanProcessor(): void {\n logWarning(\n \"The recording OpenTelemetry SERVER span created for a request did not reach ApitallySpanProcessor. Traces and request logs are unavailable while request metrics continue. To resolve this, add the ApitallySpanProcessor instance to your tracer provider's spanProcessors constructor option or the NodeSDK spanProcessors option.\",\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"requestObservation.cjs","names":["trace","getActiveSpanPipeline","SpanKind","withRequestHolders","getDistroVersion","isApitallySpanProcessorDeclared","getRPCMetadata","RPCType","setRPCMetadata","SpanStatusCode","isValidationResponseStatus","getConfig","coerceToException","getActivationHandles"],"sources":["../src/requestObservation.ts"],"sourcesContent":["import { type Attributes, type Context, SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport { getRPCMetadata, type RPCMetadata, RPCType, setRPCMetadata } from \"@opentelemetry/core\";\nimport { getActivationHandles } from \"./activation.js\";\nimport type { BodyCapture, CapturedBody } from \"./bodyCapture.js\";\nimport { getConfig } from \"./config.js\";\nimport { emitConsumerUpdateIfChanged } from \"./consumers.js\";\nimport {\n attachRequestRecordToRpcMetadata,\n type RequestRecord,\n type SpanHandle,\n withRequestHolders,\n} from \"./context.js\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logWarning } from \"./logger.js\";\nimport { getDistroVersion } from \"./packageVersion.js\";\nimport { writeRequestAttribute } from \"./requestAttributes.js\";\nimport { addServerError } from \"./serverErrors.js\";\nimport {\n getActiveSpanPipeline,\n isApitallySpanProcessorDeclared,\n type RequestStash,\n} from \"./spanProcessor.js\";\nimport { addValidationErrors, isValidationResponseStatus } from \"./validationErrors.js\";\n\ninterface StartRequestObservationOptions {\n activeContext: Context;\n extractedContext: Context;\n tracerName: string;\n method: string;\n startAttributes: Attributes;\n requestBodyCapture: BodyCapture;\n}\n\nexport interface StartedRequestObservation {\n requestRecord: RequestRecord;\n requestContext: Context;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n}\n\nexport interface HttpRequestStartAttributeInput {\n method: string;\n path?: string;\n query?: string;\n scheme?: string;\n serverAddress?: string;\n fullUrl?: string;\n clientAddress?: string;\n userAgent?: string;\n requestBodySize?: number;\n}\n\nexport function resolveHttpRequestStartAttributes(\n input: HttpRequestStartAttributeInput,\n): Attributes {\n const attributes: Attributes = { \"http.request.method\": input.method };\n if (input.path !== undefined) {\n attributes[\"url.path\"] = input.path;\n }\n if (input.query !== undefined) {\n attributes[\"url.query\"] = input.query;\n }\n if (input.scheme !== undefined) {\n attributes[\"url.scheme\"] = input.scheme;\n }\n if (input.fullUrl !== undefined) {\n attributes[\"url.full\"] = input.fullUrl;\n }\n if (input.serverAddress !== undefined) {\n attributes[\"server.address\"] = input.serverAddress;\n }\n if (input.clientAddress !== undefined) {\n attributes[\"client.address\"] = input.clientAddress;\n }\n if (input.userAgent !== undefined) {\n attributes[\"user_agent.original\"] = input.userAgent;\n }\n if (input.requestBodySize !== undefined) {\n attributes[\"http.request.body.size\"] = input.requestBodySize;\n }\n return attributes;\n}\n\nexport function startRequestObservation(\n options: StartRequestObservationOptions,\n): StartedRequestObservation {\n const {\n activeContext,\n extractedContext,\n tracerName,\n method,\n startAttributes,\n requestBodyCapture,\n } = options;\n const requestRecord: RequestRecord = { attributes: {} };\n const spanHandle: SpanHandle = {};\n // Metrics and the exported span copy read from the request record, so the\n // start attributes are mirrored into it on every path, span or no span.\n Object.assign(requestRecord.attributes, startAttributes);\n const activeSpan = trace.getSpan(activeContext);\n const resolveUnavailableSpanDropReason = (): RequestRecord[\"dropReason\"] =>\n getActiveSpanPipeline()?.resolveRequestDropReasonBeforeSampling(startAttributes) ??\n \"sampled-out\";\n let requestContext: Context;\n // Span kind is not part of the OTel API surface, so read the SDK-level\n // property from whichever package copy produced the span.\n if (activeSpan?.isRecording() && (activeSpan as { kind?: unknown }).kind === SpanKind.SERVER) {\n // A SERVER span produced by the user's own instrumentation is adopted:\n // no second span, and the request runs under the user's context.\n spanHandle.span = activeSpan;\n requestRecord.serverSpanId = activeSpan.spanContext().spanId;\n getActiveSpanPipeline()?.attachRequestRecord(requestRecord.serverSpanId, requestRecord);\n if (getActiveSpanPipeline()?.isRequestInFlight(requestRecord.serverSpanId) !== true) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else if (activeSpan && !activeSpan.isRecording() && !activeSpan.spanContext().isRemote) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else {\n requestContext = withRequestHolders(extractedContext, spanHandle, requestRecord);\n const ownSpan = trace\n .getTracer(tracerName, getDistroVersion())\n .startSpan(method, { kind: SpanKind.SERVER, attributes: startAttributes }, requestContext);\n if (!ownSpan.isRecording()) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n } else {\n spanHandle.span = ownSpan;\n spanHandle.ownSpan = ownSpan;\n if (isApitallySpanProcessorDeclared() && requestRecord.serverSpanId === undefined) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnAboutUnattachedSpanProcessor();\n }\n }\n }\n // A non-recording span still carries a valid span context, so user child\n // spans parent to it and parent-based samplers drop them consistently.\n requestContext = trace.setSpan(requestContext, ownSpan);\n }\n\n // Middleware-based span producers inspect OTel RPC metadata to demote duplicate\n // transport spans; completion adds the route.\n let rpcMetadata = getRPCMetadata(requestContext);\n if (!rpcMetadata && spanHandle.span) {\n rpcMetadata = { type: RPCType.HTTP, span: spanHandle.span };\n requestContext = setRPCMetadata(requestContext, rpcMetadata);\n }\n attachRequestRecordToRpcMetadata(rpcMetadata, requestRecord);\n if (requestRecord.dropReason !== undefined) {\n requestBodyCapture.stopBuffering();\n }\n return { requestRecord, requestContext, spanHandle, rpcMetadata };\n}\n\nexport interface RequestObservation {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n method: string;\n startTimeMillis: number;\n}\n\nexport interface FinalizeRequestObservationOptions {\n observation: RequestObservation;\n completedAtMillis: number;\n statusCode: number;\n route?: string;\n clientAddress?: string;\n requestHeaders: Headers | Record<string, string | string[] | undefined>;\n responseHeaders: Headers | Record<string, string | number | string[] | undefined>;\n capturedRequestBody?: CapturedBody;\n capturedResponseBody?: CapturedBody;\n}\n\nexport function finalizeRequestObservation(options: FinalizeRequestObservationOptions): void {\n const { observation, statusCode, route } = options;\n const { requestRecord, spanHandle, rpcMetadata, method } = observation;\n requestRecord.durationSeconds = (options.completedAtMillis - observation.startTimeMillis) / 1000;\n const { span, ownSpan } = spanHandle;\n if (options.clientAddress !== undefined) {\n writeRequestAttribute(span, requestRecord, \"client.address\", options.clientAddress);\n }\n writeRequestAttribute(span, requestRecord, \"http.response.status_code\", statusCode);\n const requestBodySize = options.capturedRequestBody?.size;\n if (requestBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.request.body.size\", requestBodySize);\n }\n const responseBodySize = options.capturedResponseBody?.size;\n if (responseBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.response.body.size\", responseBodySize);\n }\n if (route !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.route\", route);\n if (rpcMetadata) {\n rpcMetadata.route = route;\n }\n if (ownSpan?.isRecording()) {\n ownSpan.updateName(`${method} ${route}`);\n }\n } else {\n // An empty route clears one set by the producing instrumentation; request\n // metrics omit empty routes.\n requestRecord.attributes[\"http.route\"] = \"\";\n }\n if (ownSpan && statusCode >= 500) {\n ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n }\n // Validation and server errors are counted for every routed request,\n // including requests dropped from tracing.\n const consumer = requestRecord.attributes[\"apitally.consumer.identifier\"];\n const consumerIdentifier = typeof consumer === \"string\" ? consumer : undefined;\n if (requestRecord.validationErrors && isValidationResponseStatus(statusCode)) {\n addValidationErrors(consumerIdentifier, method, route ?? \"\", requestRecord.validationErrors);\n }\n if (statusCode === 500 && requestRecord.exception !== undefined) {\n const sentryEventId = requestRecord.attributes[\"apitally.exception.sentry_event_id\"];\n addServerError(\n consumerIdentifier,\n method,\n route ?? \"\",\n requestRecord.exception,\n typeof sentryEventId === \"string\" ? sentryEventId : undefined,\n );\n }\n // A dropped request's spans are never released, so a stash entry for it\n // would sit unconsumed until the cap evicts it.\n if (requestRecord.serverSpanId !== undefined && requestRecord.dropReason === undefined) {\n const config = getConfig();\n const stash: RequestStash = {};\n if (config.captureRequestHeaders) {\n stash.requestHeaders = normalizeHeaders(options.requestHeaders);\n }\n if (config.captureResponseHeaders) {\n stash.responseHeaders = normalizeHeaders(options.responseHeaders);\n }\n const requestBody = options.capturedRequestBody?.body;\n if (requestBody) {\n stash.requestBody = requestBody;\n stash.requestContentEncoding = getHeaderValue(options.requestHeaders, \"content-encoding\");\n }\n const responseBody = options.capturedResponseBody?.body;\n if (responseBody && config.captureResponseBody) {\n stash.responseBody = responseBody;\n stash.responseContentEncoding = getHeaderValue(options.responseHeaders, \"content-encoding\");\n }\n if (Object.keys(stash).length > 0) {\n getActiveSpanPipeline()?.updateStash(requestRecord.serverSpanId, stash);\n }\n }\n ownSpan?.end(options.completedAtMillis);\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nexport interface FinalizeRequestObservationWithErrorOptions {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n error: unknown;\n durationSeconds: number;\n}\n\nexport function finalizeRequestObservationWithError(\n options: FinalizeRequestObservationWithErrorOptions,\n): void {\n const { requestRecord, spanHandle, error, durationSeconds } = options;\n requestRecord.durationSeconds = durationSeconds;\n if (spanHandle.span?.isRecording()) {\n spanHandle.span.recordException(coerceToException(error));\n }\n if (spanHandle.ownSpan) {\n spanHandle.ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n spanHandle.ownSpan.end();\n }\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nfunction getHeaderValue(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n name: string,\n): string | undefined {\n if (isWebHeaders(headers)) {\n return headers.get(name) ?? undefined;\n }\n for (const headerName of Object.keys(headers)) {\n if (headerName.toLowerCase() === name) {\n const value = headers[headerName];\n return Array.isArray(value) ? value.join(\",\") : value?.toString();\n }\n }\n return undefined;\n}\n\n// Values remain raw so all redaction happens at the export boundary.\nfunction normalizeHeaders(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n): Record<string, string | string[]> {\n const normalized: Record<string, string | string[]> = {};\n if (isWebHeaders(headers)) {\n // The Headers API combines repeated values except Set-Cookie.\n for (const [name, value] of headers) {\n const existing = normalized[name];\n if (existing === undefined) {\n normalized[name] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n normalized[name] = [existing, value];\n }\n }\n return normalized;\n }\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (value !== undefined) {\n normalized[name.toLowerCase()] = Array.isArray(value) ? value : String(value);\n }\n }\n return normalized;\n}\n\n// Duck-typed on iterability: a plain header record has no Symbol.iterator, and\n// the Headers instance may come from another realm's implementation.\nfunction isWebHeaders(headers: object): headers is Headers {\n return typeof (headers as Headers)[Symbol.iterator] === \"function\";\n}\n\n// The SDK-owned tracer provider's sampler leaves a SERVER span unrecorded only\n// when sampleRate samples it out, so there is no coverage loss to warn about.\nfunction warnIfUserSamplerDroppedServerSpan(): void {\n if (getActivationHandles()?.tracerProvider) {\n return;\n }\n logWarning(\n \"The OpenTelemetry sampler did not sample the SERVER span of a request. Only sampled requests are exported to Apitally as traces and request logs. Request metrics include all requests.\",\n );\n}\n\nfunction warnAboutUnattachedSpanProcessor(): void {\n logWarning(\n \"The recording OpenTelemetry SERVER span created for a request did not reach ApitallySpanProcessor. Traces and request logs are unavailable while request metrics continue. To resolve this, add the ApitallySpanProcessor instance to your tracer provider's spanProcessors constructor option or the NodeSDK spanProcessors option.\",\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logWarning } from "./logger.js";
|
|
2
2
|
import { getConfig } from "./config.js";
|
|
3
|
+
import { getDistroVersion } from "./packageVersion.js";
|
|
3
4
|
import { attachRequestRecordToRpcMetadata, withRequestHolders } from "./context.js";
|
|
4
5
|
import { getActiveSpanPipeline, isApitallySpanProcessorDeclared } from "./spanProcessor.js";
|
|
5
6
|
import { writeRequestAttribute } from "./requestAttributes.js";
|
|
@@ -43,7 +44,7 @@ function startRequestObservation(options) {
|
|
|
43
44
|
requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);
|
|
44
45
|
} else {
|
|
45
46
|
requestContext = withRequestHolders(extractedContext, spanHandle, requestRecord);
|
|
46
|
-
const ownSpan = trace.getTracer(tracerName).startSpan(method, {
|
|
47
|
+
const ownSpan = trace.getTracer(tracerName, getDistroVersion()).startSpan(method, {
|
|
47
48
|
kind: SpanKind.SERVER,
|
|
48
49
|
attributes: startAttributes
|
|
49
50
|
}, requestContext);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestObservation.js","names":[],"sources":["../src/requestObservation.ts"],"sourcesContent":["import { type Attributes, type Context, SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport { getRPCMetadata, type RPCMetadata, RPCType, setRPCMetadata } from \"@opentelemetry/core\";\nimport { getActivationHandles } from \"./activation.js\";\nimport type { BodyCapture, CapturedBody } from \"./bodyCapture.js\";\nimport { getConfig } from \"./config.js\";\nimport { emitConsumerUpdateIfChanged } from \"./consumers.js\";\nimport {\n attachRequestRecordToRpcMetadata,\n type RequestRecord,\n type SpanHandle,\n withRequestHolders,\n} from \"./context.js\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logWarning } from \"./logger.js\";\nimport { writeRequestAttribute } from \"./requestAttributes.js\";\nimport { addServerError } from \"./serverErrors.js\";\nimport {\n getActiveSpanPipeline,\n isApitallySpanProcessorDeclared,\n type RequestStash,\n} from \"./spanProcessor.js\";\nimport { addValidationErrors, isValidationResponseStatus } from \"./validationErrors.js\";\n\ninterface StartRequestObservationOptions {\n activeContext: Context;\n extractedContext: Context;\n tracerName: string;\n method: string;\n startAttributes: Attributes;\n requestBodyCapture: BodyCapture;\n}\n\nexport interface StartedRequestObservation {\n requestRecord: RequestRecord;\n requestContext: Context;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n}\n\nexport interface HttpRequestStartAttributeInput {\n method: string;\n path?: string;\n query?: string;\n scheme?: string;\n serverAddress?: string;\n fullUrl?: string;\n clientAddress?: string;\n userAgent?: string;\n requestBodySize?: number;\n}\n\nexport function resolveHttpRequestStartAttributes(\n input: HttpRequestStartAttributeInput,\n): Attributes {\n const attributes: Attributes = { \"http.request.method\": input.method };\n if (input.path !== undefined) {\n attributes[\"url.path\"] = input.path;\n }\n if (input.query !== undefined) {\n attributes[\"url.query\"] = input.query;\n }\n if (input.scheme !== undefined) {\n attributes[\"url.scheme\"] = input.scheme;\n }\n if (input.fullUrl !== undefined) {\n attributes[\"url.full\"] = input.fullUrl;\n }\n if (input.serverAddress !== undefined) {\n attributes[\"server.address\"] = input.serverAddress;\n }\n if (input.clientAddress !== undefined) {\n attributes[\"client.address\"] = input.clientAddress;\n }\n if (input.userAgent !== undefined) {\n attributes[\"user_agent.original\"] = input.userAgent;\n }\n if (input.requestBodySize !== undefined) {\n attributes[\"http.request.body.size\"] = input.requestBodySize;\n }\n return attributes;\n}\n\nexport function startRequestObservation(\n options: StartRequestObservationOptions,\n): StartedRequestObservation {\n const {\n activeContext,\n extractedContext,\n tracerName,\n method,\n startAttributes,\n requestBodyCapture,\n } = options;\n const requestRecord: RequestRecord = { attributes: {} };\n const spanHandle: SpanHandle = {};\n // Metrics and the exported span copy read from the request record, so the\n // start attributes are mirrored into it on every path, span or no span.\n Object.assign(requestRecord.attributes, startAttributes);\n const activeSpan = trace.getSpan(activeContext);\n const resolveUnavailableSpanDropReason = (): RequestRecord[\"dropReason\"] =>\n getActiveSpanPipeline()?.resolveRequestDropReasonBeforeSampling(startAttributes) ??\n \"sampled-out\";\n let requestContext: Context;\n // Span kind is not part of the OTel API surface, so read the SDK-level\n // property from whichever package copy produced the span.\n if (activeSpan?.isRecording() && (activeSpan as { kind?: unknown }).kind === SpanKind.SERVER) {\n // A SERVER span produced by the user's own instrumentation is adopted:\n // no second span, and the request runs under the user's context.\n spanHandle.span = activeSpan;\n requestRecord.serverSpanId = activeSpan.spanContext().spanId;\n getActiveSpanPipeline()?.attachRequestRecord(requestRecord.serverSpanId, requestRecord);\n if (getActiveSpanPipeline()?.isRequestInFlight(requestRecord.serverSpanId) !== true) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else if (activeSpan && !activeSpan.isRecording() && !activeSpan.spanContext().isRemote) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else {\n requestContext = withRequestHolders(extractedContext, spanHandle, requestRecord);\n const ownSpan = trace\n .getTracer(tracerName)\n .startSpan(method, { kind: SpanKind.SERVER, attributes: startAttributes }, requestContext);\n if (!ownSpan.isRecording()) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n } else {\n spanHandle.span = ownSpan;\n spanHandle.ownSpan = ownSpan;\n if (isApitallySpanProcessorDeclared() && requestRecord.serverSpanId === undefined) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnAboutUnattachedSpanProcessor();\n }\n }\n }\n // A non-recording span still carries a valid span context, so user child\n // spans parent to it and parent-based samplers drop them consistently.\n requestContext = trace.setSpan(requestContext, ownSpan);\n }\n\n // Middleware-based span producers inspect OTel RPC metadata to demote duplicate\n // transport spans; completion adds the route.\n let rpcMetadata = getRPCMetadata(requestContext);\n if (!rpcMetadata && spanHandle.span) {\n rpcMetadata = { type: RPCType.HTTP, span: spanHandle.span };\n requestContext = setRPCMetadata(requestContext, rpcMetadata);\n }\n attachRequestRecordToRpcMetadata(rpcMetadata, requestRecord);\n if (requestRecord.dropReason !== undefined) {\n requestBodyCapture.stopBuffering();\n }\n return { requestRecord, requestContext, spanHandle, rpcMetadata };\n}\n\nexport interface RequestObservation {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n method: string;\n startTimeMillis: number;\n}\n\nexport interface FinalizeRequestObservationOptions {\n observation: RequestObservation;\n completedAtMillis: number;\n statusCode: number;\n route?: string;\n clientAddress?: string;\n requestHeaders: Headers | Record<string, string | string[] | undefined>;\n responseHeaders: Headers | Record<string, string | number | string[] | undefined>;\n capturedRequestBody?: CapturedBody;\n capturedResponseBody?: CapturedBody;\n}\n\nexport function finalizeRequestObservation(options: FinalizeRequestObservationOptions): void {\n const { observation, statusCode, route } = options;\n const { requestRecord, spanHandle, rpcMetadata, method } = observation;\n requestRecord.durationSeconds = (options.completedAtMillis - observation.startTimeMillis) / 1000;\n const { span, ownSpan } = spanHandle;\n if (options.clientAddress !== undefined) {\n writeRequestAttribute(span, requestRecord, \"client.address\", options.clientAddress);\n }\n writeRequestAttribute(span, requestRecord, \"http.response.status_code\", statusCode);\n const requestBodySize = options.capturedRequestBody?.size;\n if (requestBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.request.body.size\", requestBodySize);\n }\n const responseBodySize = options.capturedResponseBody?.size;\n if (responseBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.response.body.size\", responseBodySize);\n }\n if (route !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.route\", route);\n if (rpcMetadata) {\n rpcMetadata.route = route;\n }\n if (ownSpan?.isRecording()) {\n ownSpan.updateName(`${method} ${route}`);\n }\n } else {\n // An empty route clears one set by the producing instrumentation; request\n // metrics omit empty routes.\n requestRecord.attributes[\"http.route\"] = \"\";\n }\n if (ownSpan && statusCode >= 500) {\n ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n }\n // Validation and server errors are counted for every routed request,\n // including requests dropped from tracing.\n const consumer = requestRecord.attributes[\"apitally.consumer.identifier\"];\n const consumerIdentifier = typeof consumer === \"string\" ? consumer : undefined;\n if (requestRecord.validationErrors && isValidationResponseStatus(statusCode)) {\n addValidationErrors(consumerIdentifier, method, route ?? \"\", requestRecord.validationErrors);\n }\n if (statusCode === 500 && requestRecord.exception !== undefined) {\n const sentryEventId = requestRecord.attributes[\"apitally.exception.sentry_event_id\"];\n addServerError(\n consumerIdentifier,\n method,\n route ?? \"\",\n requestRecord.exception,\n typeof sentryEventId === \"string\" ? sentryEventId : undefined,\n );\n }\n // A dropped request's spans are never released, so a stash entry for it\n // would sit unconsumed until the cap evicts it.\n if (requestRecord.serverSpanId !== undefined && requestRecord.dropReason === undefined) {\n const config = getConfig();\n const stash: RequestStash = {};\n if (config.captureRequestHeaders) {\n stash.requestHeaders = normalizeHeaders(options.requestHeaders);\n }\n if (config.captureResponseHeaders) {\n stash.responseHeaders = normalizeHeaders(options.responseHeaders);\n }\n const requestBody = options.capturedRequestBody?.body;\n if (requestBody) {\n stash.requestBody = requestBody;\n stash.requestContentEncoding = getHeaderValue(options.requestHeaders, \"content-encoding\");\n }\n const responseBody = options.capturedResponseBody?.body;\n if (responseBody && config.captureResponseBody) {\n stash.responseBody = responseBody;\n stash.responseContentEncoding = getHeaderValue(options.responseHeaders, \"content-encoding\");\n }\n if (Object.keys(stash).length > 0) {\n getActiveSpanPipeline()?.updateStash(requestRecord.serverSpanId, stash);\n }\n }\n ownSpan?.end(options.completedAtMillis);\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nexport interface FinalizeRequestObservationWithErrorOptions {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n error: unknown;\n durationSeconds: number;\n}\n\nexport function finalizeRequestObservationWithError(\n options: FinalizeRequestObservationWithErrorOptions,\n): void {\n const { requestRecord, spanHandle, error, durationSeconds } = options;\n requestRecord.durationSeconds = durationSeconds;\n if (spanHandle.span?.isRecording()) {\n spanHandle.span.recordException(coerceToException(error));\n }\n if (spanHandle.ownSpan) {\n spanHandle.ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n spanHandle.ownSpan.end();\n }\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nfunction getHeaderValue(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n name: string,\n): string | undefined {\n if (isWebHeaders(headers)) {\n return headers.get(name) ?? undefined;\n }\n for (const headerName of Object.keys(headers)) {\n if (headerName.toLowerCase() === name) {\n const value = headers[headerName];\n return Array.isArray(value) ? value.join(\",\") : value?.toString();\n }\n }\n return undefined;\n}\n\n// Values remain raw so all redaction happens at the export boundary.\nfunction normalizeHeaders(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n): Record<string, string | string[]> {\n const normalized: Record<string, string | string[]> = {};\n if (isWebHeaders(headers)) {\n // The Headers API combines repeated values except Set-Cookie.\n for (const [name, value] of headers) {\n const existing = normalized[name];\n if (existing === undefined) {\n normalized[name] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n normalized[name] = [existing, value];\n }\n }\n return normalized;\n }\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (value !== undefined) {\n normalized[name.toLowerCase()] = Array.isArray(value) ? value : String(value);\n }\n }\n return normalized;\n}\n\n// Duck-typed on iterability: a plain header record has no Symbol.iterator, and\n// the Headers instance may come from another realm's implementation.\nfunction isWebHeaders(headers: object): headers is Headers {\n return typeof (headers as Headers)[Symbol.iterator] === \"function\";\n}\n\n// The SDK-owned tracer provider's sampler leaves a SERVER span unrecorded only\n// when sampleRate samples it out, so there is no coverage loss to warn about.\nfunction warnIfUserSamplerDroppedServerSpan(): void {\n if (getActivationHandles()?.tracerProvider) {\n return;\n }\n logWarning(\n \"The OpenTelemetry sampler did not sample the SERVER span of a request. Only sampled requests are exported to Apitally as traces and request logs. Request metrics include all requests.\",\n );\n}\n\nfunction warnAboutUnattachedSpanProcessor(): void {\n logWarning(\n \"The recording OpenTelemetry SERVER span created for a request did not reach ApitallySpanProcessor. Traces and request logs are unavailable while request metrics continue. To resolve this, add the ApitallySpanProcessor instance to your tracer provider's spanProcessors constructor option or the NodeSDK spanProcessors option.\",\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"requestObservation.js","names":[],"sources":["../src/requestObservation.ts"],"sourcesContent":["import { type Attributes, type Context, SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport { getRPCMetadata, type RPCMetadata, RPCType, setRPCMetadata } from \"@opentelemetry/core\";\nimport { getActivationHandles } from \"./activation.js\";\nimport type { BodyCapture, CapturedBody } from \"./bodyCapture.js\";\nimport { getConfig } from \"./config.js\";\nimport { emitConsumerUpdateIfChanged } from \"./consumers.js\";\nimport {\n attachRequestRecordToRpcMetadata,\n type RequestRecord,\n type SpanHandle,\n withRequestHolders,\n} from \"./context.js\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logWarning } from \"./logger.js\";\nimport { getDistroVersion } from \"./packageVersion.js\";\nimport { writeRequestAttribute } from \"./requestAttributes.js\";\nimport { addServerError } from \"./serverErrors.js\";\nimport {\n getActiveSpanPipeline,\n isApitallySpanProcessorDeclared,\n type RequestStash,\n} from \"./spanProcessor.js\";\nimport { addValidationErrors, isValidationResponseStatus } from \"./validationErrors.js\";\n\ninterface StartRequestObservationOptions {\n activeContext: Context;\n extractedContext: Context;\n tracerName: string;\n method: string;\n startAttributes: Attributes;\n requestBodyCapture: BodyCapture;\n}\n\nexport interface StartedRequestObservation {\n requestRecord: RequestRecord;\n requestContext: Context;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n}\n\nexport interface HttpRequestStartAttributeInput {\n method: string;\n path?: string;\n query?: string;\n scheme?: string;\n serverAddress?: string;\n fullUrl?: string;\n clientAddress?: string;\n userAgent?: string;\n requestBodySize?: number;\n}\n\nexport function resolveHttpRequestStartAttributes(\n input: HttpRequestStartAttributeInput,\n): Attributes {\n const attributes: Attributes = { \"http.request.method\": input.method };\n if (input.path !== undefined) {\n attributes[\"url.path\"] = input.path;\n }\n if (input.query !== undefined) {\n attributes[\"url.query\"] = input.query;\n }\n if (input.scheme !== undefined) {\n attributes[\"url.scheme\"] = input.scheme;\n }\n if (input.fullUrl !== undefined) {\n attributes[\"url.full\"] = input.fullUrl;\n }\n if (input.serverAddress !== undefined) {\n attributes[\"server.address\"] = input.serverAddress;\n }\n if (input.clientAddress !== undefined) {\n attributes[\"client.address\"] = input.clientAddress;\n }\n if (input.userAgent !== undefined) {\n attributes[\"user_agent.original\"] = input.userAgent;\n }\n if (input.requestBodySize !== undefined) {\n attributes[\"http.request.body.size\"] = input.requestBodySize;\n }\n return attributes;\n}\n\nexport function startRequestObservation(\n options: StartRequestObservationOptions,\n): StartedRequestObservation {\n const {\n activeContext,\n extractedContext,\n tracerName,\n method,\n startAttributes,\n requestBodyCapture,\n } = options;\n const requestRecord: RequestRecord = { attributes: {} };\n const spanHandle: SpanHandle = {};\n // Metrics and the exported span copy read from the request record, so the\n // start attributes are mirrored into it on every path, span or no span.\n Object.assign(requestRecord.attributes, startAttributes);\n const activeSpan = trace.getSpan(activeContext);\n const resolveUnavailableSpanDropReason = (): RequestRecord[\"dropReason\"] =>\n getActiveSpanPipeline()?.resolveRequestDropReasonBeforeSampling(startAttributes) ??\n \"sampled-out\";\n let requestContext: Context;\n // Span kind is not part of the OTel API surface, so read the SDK-level\n // property from whichever package copy produced the span.\n if (activeSpan?.isRecording() && (activeSpan as { kind?: unknown }).kind === SpanKind.SERVER) {\n // A SERVER span produced by the user's own instrumentation is adopted:\n // no second span, and the request runs under the user's context.\n spanHandle.span = activeSpan;\n requestRecord.serverSpanId = activeSpan.spanContext().spanId;\n getActiveSpanPipeline()?.attachRequestRecord(requestRecord.serverSpanId, requestRecord);\n if (getActiveSpanPipeline()?.isRequestInFlight(requestRecord.serverSpanId) !== true) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else if (activeSpan && !activeSpan.isRecording() && !activeSpan.spanContext().isRemote) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n requestContext = withRequestHolders(activeContext, spanHandle, requestRecord);\n } else {\n requestContext = withRequestHolders(extractedContext, spanHandle, requestRecord);\n const ownSpan = trace\n .getTracer(tracerName, getDistroVersion())\n .startSpan(method, { kind: SpanKind.SERVER, attributes: startAttributes }, requestContext);\n if (!ownSpan.isRecording()) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnIfUserSamplerDroppedServerSpan();\n }\n } else {\n spanHandle.span = ownSpan;\n spanHandle.ownSpan = ownSpan;\n if (isApitallySpanProcessorDeclared() && requestRecord.serverSpanId === undefined) {\n requestRecord.dropReason = resolveUnavailableSpanDropReason();\n if (requestRecord.dropReason === \"sampled-out\") {\n warnAboutUnattachedSpanProcessor();\n }\n }\n }\n // A non-recording span still carries a valid span context, so user child\n // spans parent to it and parent-based samplers drop them consistently.\n requestContext = trace.setSpan(requestContext, ownSpan);\n }\n\n // Middleware-based span producers inspect OTel RPC metadata to demote duplicate\n // transport spans; completion adds the route.\n let rpcMetadata = getRPCMetadata(requestContext);\n if (!rpcMetadata && spanHandle.span) {\n rpcMetadata = { type: RPCType.HTTP, span: spanHandle.span };\n requestContext = setRPCMetadata(requestContext, rpcMetadata);\n }\n attachRequestRecordToRpcMetadata(rpcMetadata, requestRecord);\n if (requestRecord.dropReason !== undefined) {\n requestBodyCapture.stopBuffering();\n }\n return { requestRecord, requestContext, spanHandle, rpcMetadata };\n}\n\nexport interface RequestObservation {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n rpcMetadata?: RPCMetadata;\n method: string;\n startTimeMillis: number;\n}\n\nexport interface FinalizeRequestObservationOptions {\n observation: RequestObservation;\n completedAtMillis: number;\n statusCode: number;\n route?: string;\n clientAddress?: string;\n requestHeaders: Headers | Record<string, string | string[] | undefined>;\n responseHeaders: Headers | Record<string, string | number | string[] | undefined>;\n capturedRequestBody?: CapturedBody;\n capturedResponseBody?: CapturedBody;\n}\n\nexport function finalizeRequestObservation(options: FinalizeRequestObservationOptions): void {\n const { observation, statusCode, route } = options;\n const { requestRecord, spanHandle, rpcMetadata, method } = observation;\n requestRecord.durationSeconds = (options.completedAtMillis - observation.startTimeMillis) / 1000;\n const { span, ownSpan } = spanHandle;\n if (options.clientAddress !== undefined) {\n writeRequestAttribute(span, requestRecord, \"client.address\", options.clientAddress);\n }\n writeRequestAttribute(span, requestRecord, \"http.response.status_code\", statusCode);\n const requestBodySize = options.capturedRequestBody?.size;\n if (requestBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.request.body.size\", requestBodySize);\n }\n const responseBodySize = options.capturedResponseBody?.size;\n if (responseBodySize !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.response.body.size\", responseBodySize);\n }\n if (route !== undefined) {\n writeRequestAttribute(span, requestRecord, \"http.route\", route);\n if (rpcMetadata) {\n rpcMetadata.route = route;\n }\n if (ownSpan?.isRecording()) {\n ownSpan.updateName(`${method} ${route}`);\n }\n } else {\n // An empty route clears one set by the producing instrumentation; request\n // metrics omit empty routes.\n requestRecord.attributes[\"http.route\"] = \"\";\n }\n if (ownSpan && statusCode >= 500) {\n ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n }\n // Validation and server errors are counted for every routed request,\n // including requests dropped from tracing.\n const consumer = requestRecord.attributes[\"apitally.consumer.identifier\"];\n const consumerIdentifier = typeof consumer === \"string\" ? consumer : undefined;\n if (requestRecord.validationErrors && isValidationResponseStatus(statusCode)) {\n addValidationErrors(consumerIdentifier, method, route ?? \"\", requestRecord.validationErrors);\n }\n if (statusCode === 500 && requestRecord.exception !== undefined) {\n const sentryEventId = requestRecord.attributes[\"apitally.exception.sentry_event_id\"];\n addServerError(\n consumerIdentifier,\n method,\n route ?? \"\",\n requestRecord.exception,\n typeof sentryEventId === \"string\" ? sentryEventId : undefined,\n );\n }\n // A dropped request's spans are never released, so a stash entry for it\n // would sit unconsumed until the cap evicts it.\n if (requestRecord.serverSpanId !== undefined && requestRecord.dropReason === undefined) {\n const config = getConfig();\n const stash: RequestStash = {};\n if (config.captureRequestHeaders) {\n stash.requestHeaders = normalizeHeaders(options.requestHeaders);\n }\n if (config.captureResponseHeaders) {\n stash.responseHeaders = normalizeHeaders(options.responseHeaders);\n }\n const requestBody = options.capturedRequestBody?.body;\n if (requestBody) {\n stash.requestBody = requestBody;\n stash.requestContentEncoding = getHeaderValue(options.requestHeaders, \"content-encoding\");\n }\n const responseBody = options.capturedResponseBody?.body;\n if (responseBody && config.captureResponseBody) {\n stash.responseBody = responseBody;\n stash.responseContentEncoding = getHeaderValue(options.responseHeaders, \"content-encoding\");\n }\n if (Object.keys(stash).length > 0) {\n getActiveSpanPipeline()?.updateStash(requestRecord.serverSpanId, stash);\n }\n }\n ownSpan?.end(options.completedAtMillis);\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nexport interface FinalizeRequestObservationWithErrorOptions {\n requestRecord: RequestRecord;\n spanHandle: SpanHandle;\n error: unknown;\n durationSeconds: number;\n}\n\nexport function finalizeRequestObservationWithError(\n options: FinalizeRequestObservationWithErrorOptions,\n): void {\n const { requestRecord, spanHandle, error, durationSeconds } = options;\n requestRecord.durationSeconds = durationSeconds;\n if (spanHandle.span?.isRecording()) {\n spanHandle.span.recordException(coerceToException(error));\n }\n if (spanHandle.ownSpan) {\n spanHandle.ownSpan.setStatus({ code: SpanStatusCode.ERROR });\n spanHandle.ownSpan.end();\n }\n emitConsumerUpdateIfChanged(requestRecord);\n getActiveSpanPipeline()?.handleTransportCompletion(requestRecord);\n}\n\nfunction getHeaderValue(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n name: string,\n): string | undefined {\n if (isWebHeaders(headers)) {\n return headers.get(name) ?? undefined;\n }\n for (const headerName of Object.keys(headers)) {\n if (headerName.toLowerCase() === name) {\n const value = headers[headerName];\n return Array.isArray(value) ? value.join(\",\") : value?.toString();\n }\n }\n return undefined;\n}\n\n// Values remain raw so all redaction happens at the export boundary.\nfunction normalizeHeaders(\n headers: Headers | Record<string, string | number | string[] | undefined>,\n): Record<string, string | string[]> {\n const normalized: Record<string, string | string[]> = {};\n if (isWebHeaders(headers)) {\n // The Headers API combines repeated values except Set-Cookie.\n for (const [name, value] of headers) {\n const existing = normalized[name];\n if (existing === undefined) {\n normalized[name] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n normalized[name] = [existing, value];\n }\n }\n return normalized;\n }\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (value !== undefined) {\n normalized[name.toLowerCase()] = Array.isArray(value) ? value : String(value);\n }\n }\n return normalized;\n}\n\n// Duck-typed on iterability: a plain header record has no Symbol.iterator, and\n// the Headers instance may come from another realm's implementation.\nfunction isWebHeaders(headers: object): headers is Headers {\n return typeof (headers as Headers)[Symbol.iterator] === \"function\";\n}\n\n// The SDK-owned tracer provider's sampler leaves a SERVER span unrecorded only\n// when sampleRate samples it out, so there is no coverage loss to warn about.\nfunction warnIfUserSamplerDroppedServerSpan(): void {\n if (getActivationHandles()?.tracerProvider) {\n return;\n }\n logWarning(\n \"The OpenTelemetry sampler did not sample the SERVER span of a request. Only sampled requests are exported to Apitally as traces and request logs. Request metrics include all requests.\",\n );\n}\n\nfunction warnAboutUnattachedSpanProcessor(): void {\n logWarning(\n \"The recording OpenTelemetry SERVER span created for a request did not reach ApitallySpanProcessor. Traces and request logs are unavailable while request metrics continue. To resolve this, add the ApitallySpanProcessor instance to your tracer provider's spanProcessors constructor option or the NodeSDK spanProcessors option.\",\n );\n}\n"],"mappings":"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"}
|
package/dist/tracing.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_logger = require("./logger.cjs");
|
|
3
|
+
const require_packageVersion = require("./packageVersion.cjs");
|
|
3
4
|
const require_exceptions = require("./exceptions.cjs");
|
|
4
5
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
5
6
|
let node_url = require("node:url");
|
|
@@ -25,7 +26,7 @@ function span(name, fn) {
|
|
|
25
26
|
function runInsideSpan(name, attributes, fn) {
|
|
26
27
|
let internalSpan;
|
|
27
28
|
try {
|
|
28
|
-
internalSpan = _opentelemetry_api.trace.getTracer("apitally.
|
|
29
|
+
internalSpan = _opentelemetry_api.trace.getTracer("apitally", require_packageVersion.getDistroVersion()).startSpan(name, {
|
|
29
30
|
kind: _opentelemetry_api.SpanKind.INTERNAL,
|
|
30
31
|
attributes
|
|
31
32
|
});
|
package/dist/tracing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.cjs","names":["trace","SpanKind","context","fileURLToPath","coerceToException","SpanStatusCode"],"sources":["../src/tracing.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport {\n type Attributes,\n context,\n type Span,\n SpanKind,\n SpanStatusCode,\n trace,\n} from \"@opentelemetry/api\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logDebug } from \"./logger.js\";\n\nconst STACK_FRAME_PATTERN = /at (?:.*\\()?(.*?):(\\d+):\\d+\\)?$/;\n\nexport function instrument<Fn extends (...args: never[]) => unknown>(fn: Fn): Fn;\nexport function instrument<Fn extends (...args: never[]) => unknown>(name: string, fn: Fn): Fn;\nexport function instrument(\n nameOrFn: string | ((...args: unknown[]) => unknown),\n fnWhenNamed?: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n const fn =\n typeof nameOrFn === \"function\" ? nameOrFn : (fnWhenNamed as (...args: unknown[]) => unknown);\n const spanName = typeof nameOrFn === \"string\" ? nameOrFn : fn.name || \"anonymous\";\n const attributes: Attributes = {};\n if (fn.name) {\n attributes[\"code.function.name\"] = fn.name;\n }\n const wrapSite = resolveWrapSite();\n if (wrapSite) {\n attributes[\"code.file.path\"] = wrapSite.filePath;\n attributes[\"code.line.number\"] = wrapSite.lineNumber;\n }\n return function (this: unknown, ...args: unknown[]): unknown {\n return runInsideSpan(spanName, attributes, () => fn.apply(this, args));\n };\n}\n\nexport function span<Result>(name: string, fn: () => Result): Result {\n return runInsideSpan(name, undefined, fn);\n}\n\n// Function errors propagate unchanged. If span creation fails, the function\n// runs without a span.\nfunction runInsideSpan<Result>(\n name: string,\n attributes: Attributes | undefined,\n fn: () => Result,\n): Result {\n let internalSpan: Span;\n try {\n internalSpan = trace\n .getTracer(\"apitally
|
|
1
|
+
{"version":3,"file":"tracing.cjs","names":["trace","getDistroVersion","SpanKind","context","fileURLToPath","coerceToException","SpanStatusCode"],"sources":["../src/tracing.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport {\n type Attributes,\n context,\n type Span,\n SpanKind,\n SpanStatusCode,\n trace,\n} from \"@opentelemetry/api\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logDebug } from \"./logger.js\";\nimport { getDistroVersion } from \"./packageVersion.js\";\n\nconst STACK_FRAME_PATTERN = /at (?:.*\\()?(.*?):(\\d+):\\d+\\)?$/;\n\nexport function instrument<Fn extends (...args: never[]) => unknown>(fn: Fn): Fn;\nexport function instrument<Fn extends (...args: never[]) => unknown>(name: string, fn: Fn): Fn;\nexport function instrument(\n nameOrFn: string | ((...args: unknown[]) => unknown),\n fnWhenNamed?: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n const fn =\n typeof nameOrFn === \"function\" ? nameOrFn : (fnWhenNamed as (...args: unknown[]) => unknown);\n const spanName = typeof nameOrFn === \"string\" ? nameOrFn : fn.name || \"anonymous\";\n const attributes: Attributes = {};\n if (fn.name) {\n attributes[\"code.function.name\"] = fn.name;\n }\n const wrapSite = resolveWrapSite();\n if (wrapSite) {\n attributes[\"code.file.path\"] = wrapSite.filePath;\n attributes[\"code.line.number\"] = wrapSite.lineNumber;\n }\n return function (this: unknown, ...args: unknown[]): unknown {\n return runInsideSpan(spanName, attributes, () => fn.apply(this, args));\n };\n}\n\nexport function span<Result>(name: string, fn: () => Result): Result {\n return runInsideSpan(name, undefined, fn);\n}\n\n// Function errors propagate unchanged. If span creation fails, the function\n// runs without a span.\nfunction runInsideSpan<Result>(\n name: string,\n attributes: Attributes | undefined,\n fn: () => Result,\n): Result {\n let internalSpan: Span;\n try {\n internalSpan = trace\n .getTracer(\"apitally\", getDistroVersion())\n .startSpan(name, { kind: SpanKind.INTERNAL, attributes });\n } catch (error) {\n logDebug(`Error starting a manual span: ${String(error)}`);\n return fn();\n }\n let result: Result;\n try {\n result = context.with(trace.setSpan(context.active(), internalSpan), fn);\n } catch (error) {\n endSpanWithError(internalSpan, error);\n throw error;\n }\n if (isPromiseLike(result)) {\n // Side listener on the function's own promise, which passes through\n // unchanged; neither handler rethrows, so this chain never rejects.\n result.then(\n () => endSpan(internalSpan),\n (error: unknown) => endSpanWithError(internalSpan, error),\n );\n return result;\n }\n endSpan(internalSpan);\n return result;\n}\n\n// The wrap-time call site, taken from a stack capture that starts at\n// instrument's caller. An unparseable stack yields no location attributes.\nfunction resolveWrapSite(): { filePath: string; lineNumber: number } | undefined {\n const holder: { stack?: string } = {};\n Error.captureStackTrace(holder, instrument);\n for (const frame of (holder.stack ?? \"\").split(\"\\n\")) {\n const match = STACK_FRAME_PATTERN.exec(frame);\n if (match) {\n return {\n filePath: match[1].startsWith(\"file://\") ? fileURLToPath(match[1]) : match[1],\n lineNumber: Number(match[2]),\n };\n }\n }\n return undefined;\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as PromiseLike<unknown>).then === \"function\"\n );\n}\n\nfunction endSpan(internalSpan: Span): void {\n try {\n internalSpan.end();\n } catch (error) {\n logDebug(`Error ending a manual span: ${String(error)}`);\n }\n}\n\nfunction endSpanWithError(internalSpan: Span, error: unknown): void {\n try {\n const exception = coerceToException(error);\n internalSpan.recordException(exception);\n internalSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: typeof exception === \"string\" ? exception : exception.message,\n });\n internalSpan.end();\n } catch (endError) {\n logDebug(`Error ending a manual span: ${String(endError)}`);\n }\n}\n"],"mappings":";;;;;;;AAaA,MAAM,sBAAsB;AAI5B,SAAgB,WACd,UACA,aACiC;CACjC,MAAM,KACJ,OAAO,aAAa,aAAa,WAAY;CAC/C,MAAM,WAAW,OAAO,aAAa,WAAW,WAAW,GAAG,QAAQ;CACtE,MAAM,aAAyB,CAAC;CAChC,IAAI,GAAG,MACL,WAAW,wBAAwB,GAAG;CAExC,MAAM,WAAW,gBAAgB;CACjC,IAAI,UAAU;EACZ,WAAW,oBAAoB,SAAS;EACxC,WAAW,sBAAsB,SAAS;CAC5C;CACA,OAAO,SAAyB,GAAG,MAA0B;EAC3D,OAAO,cAAc,UAAU,kBAAkB,GAAG,MAAM,MAAM,IAAI,CAAC;CACvE;AACF;AAEA,SAAgB,KAAa,MAAc,IAA0B;CACnE,OAAO,cAAc,MAAM,KAAA,GAAW,EAAE;AAC1C;AAIA,SAAS,cACP,MACA,YACA,IACQ;CACR,IAAI;CACJ,IAAI;EACF,eAAeA,mBAAAA,MACZ,UAAU,YAAYC,uBAAAA,iBAAiB,CAAC,CAAC,CACzC,UAAU,MAAM;GAAE,MAAMC,mBAAAA,SAAS;GAAU;EAAW,CAAC;CAC5D,SAAS,OAAO;EACd,eAAA,SAAS,iCAAiC,OAAO,KAAK,GAAG;EACzD,OAAO,GAAG;CACZ;CACA,IAAI;CACJ,IAAI;EACF,SAASC,mBAAAA,QAAQ,KAAKH,mBAAAA,MAAM,QAAQG,mBAAAA,QAAQ,OAAO,GAAG,YAAY,GAAG,EAAE;CACzE,SAAS,OAAO;EACd,iBAAiB,cAAc,KAAK;EACpC,MAAM;CACR;CACA,IAAI,cAAc,MAAM,GAAG;EAGzB,OAAO,WACC,QAAQ,YAAY,IACzB,UAAmB,iBAAiB,cAAc,KAAK,CAC1D;EACA,OAAO;CACT;CACA,QAAQ,YAAY;CACpB,OAAO;AACT;AAIA,SAAS,kBAAwE;CAC/E,MAAM,SAA6B,CAAC;CACpC,MAAM,kBAAkB,QAAQ,UAAU;CAC1C,KAAK,MAAM,UAAU,OAAO,SAAS,GAAA,CAAI,MAAM,IAAI,GAAG;EACpD,MAAM,QAAQ,oBAAoB,KAAK,KAAK;EAC5C,IAAI,OACF,OAAO;GACL,UAAU,MAAM,EAAE,CAAC,WAAW,SAAS,KAAA,GAAIC,SAAAA,cAAAA,CAAc,MAAM,EAAE,IAAI,MAAM;GAC3E,YAAY,OAAO,MAAM,EAAE;EAC7B;CAEJ;AAEF;AAEA,SAAS,cAAc,OAA+C;CACpE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA+B,SAAS;AAEpD;AAEA,SAAS,QAAQ,cAA0B;CACzC,IAAI;EACF,aAAa,IAAI;CACnB,SAAS,OAAO;EACd,eAAA,SAAS,+BAA+B,OAAO,KAAK,GAAG;CACzD;AACF;AAEA,SAAS,iBAAiB,cAAoB,OAAsB;CAClE,IAAI;EACF,MAAM,YAAYC,mBAAAA,kBAAkB,KAAK;EACzC,aAAa,gBAAgB,SAAS;EACtC,aAAa,UAAU;GACrB,MAAMC,mBAAAA,eAAe;GACrB,SAAS,OAAO,cAAc,WAAW,YAAY,UAAU;EACjE,CAAC;EACD,aAAa,IAAI;CACnB,SAAS,UAAU;EACjB,eAAA,SAAS,+BAA+B,OAAO,QAAQ,GAAG;CAC5D;AACF"}
|
package/dist/tracing.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logDebug } from "./logger.js";
|
|
2
|
+
import { getDistroVersion } from "./packageVersion.js";
|
|
2
3
|
import { coerceToException } from "./exceptions.js";
|
|
3
4
|
import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -24,7 +25,7 @@ function span(name, fn) {
|
|
|
24
25
|
function runInsideSpan(name, attributes, fn) {
|
|
25
26
|
let internalSpan;
|
|
26
27
|
try {
|
|
27
|
-
internalSpan = trace.getTracer("apitally
|
|
28
|
+
internalSpan = trace.getTracer("apitally", getDistroVersion()).startSpan(name, {
|
|
28
29
|
kind: SpanKind.INTERNAL,
|
|
29
30
|
attributes
|
|
30
31
|
});
|
package/dist/tracing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.js","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport {\n type Attributes,\n context,\n type Span,\n SpanKind,\n SpanStatusCode,\n trace,\n} from \"@opentelemetry/api\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logDebug } from \"./logger.js\";\n\nconst STACK_FRAME_PATTERN = /at (?:.*\\()?(.*?):(\\d+):\\d+\\)?$/;\n\nexport function instrument<Fn extends (...args: never[]) => unknown>(fn: Fn): Fn;\nexport function instrument<Fn extends (...args: never[]) => unknown>(name: string, fn: Fn): Fn;\nexport function instrument(\n nameOrFn: string | ((...args: unknown[]) => unknown),\n fnWhenNamed?: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n const fn =\n typeof nameOrFn === \"function\" ? nameOrFn : (fnWhenNamed as (...args: unknown[]) => unknown);\n const spanName = typeof nameOrFn === \"string\" ? nameOrFn : fn.name || \"anonymous\";\n const attributes: Attributes = {};\n if (fn.name) {\n attributes[\"code.function.name\"] = fn.name;\n }\n const wrapSite = resolveWrapSite();\n if (wrapSite) {\n attributes[\"code.file.path\"] = wrapSite.filePath;\n attributes[\"code.line.number\"] = wrapSite.lineNumber;\n }\n return function (this: unknown, ...args: unknown[]): unknown {\n return runInsideSpan(spanName, attributes, () => fn.apply(this, args));\n };\n}\n\nexport function span<Result>(name: string, fn: () => Result): Result {\n return runInsideSpan(name, undefined, fn);\n}\n\n// Function errors propagate unchanged. If span creation fails, the function\n// runs without a span.\nfunction runInsideSpan<Result>(\n name: string,\n attributes: Attributes | undefined,\n fn: () => Result,\n): Result {\n let internalSpan: Span;\n try {\n internalSpan = trace\n .getTracer(\"apitally
|
|
1
|
+
{"version":3,"file":"tracing.js","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport {\n type Attributes,\n context,\n type Span,\n SpanKind,\n SpanStatusCode,\n trace,\n} from \"@opentelemetry/api\";\nimport { coerceToException } from \"./exceptions.js\";\nimport { logDebug } from \"./logger.js\";\nimport { getDistroVersion } from \"./packageVersion.js\";\n\nconst STACK_FRAME_PATTERN = /at (?:.*\\()?(.*?):(\\d+):\\d+\\)?$/;\n\nexport function instrument<Fn extends (...args: never[]) => unknown>(fn: Fn): Fn;\nexport function instrument<Fn extends (...args: never[]) => unknown>(name: string, fn: Fn): Fn;\nexport function instrument(\n nameOrFn: string | ((...args: unknown[]) => unknown),\n fnWhenNamed?: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => unknown {\n const fn =\n typeof nameOrFn === \"function\" ? nameOrFn : (fnWhenNamed as (...args: unknown[]) => unknown);\n const spanName = typeof nameOrFn === \"string\" ? nameOrFn : fn.name || \"anonymous\";\n const attributes: Attributes = {};\n if (fn.name) {\n attributes[\"code.function.name\"] = fn.name;\n }\n const wrapSite = resolveWrapSite();\n if (wrapSite) {\n attributes[\"code.file.path\"] = wrapSite.filePath;\n attributes[\"code.line.number\"] = wrapSite.lineNumber;\n }\n return function (this: unknown, ...args: unknown[]): unknown {\n return runInsideSpan(spanName, attributes, () => fn.apply(this, args));\n };\n}\n\nexport function span<Result>(name: string, fn: () => Result): Result {\n return runInsideSpan(name, undefined, fn);\n}\n\n// Function errors propagate unchanged. If span creation fails, the function\n// runs without a span.\nfunction runInsideSpan<Result>(\n name: string,\n attributes: Attributes | undefined,\n fn: () => Result,\n): Result {\n let internalSpan: Span;\n try {\n internalSpan = trace\n .getTracer(\"apitally\", getDistroVersion())\n .startSpan(name, { kind: SpanKind.INTERNAL, attributes });\n } catch (error) {\n logDebug(`Error starting a manual span: ${String(error)}`);\n return fn();\n }\n let result: Result;\n try {\n result = context.with(trace.setSpan(context.active(), internalSpan), fn);\n } catch (error) {\n endSpanWithError(internalSpan, error);\n throw error;\n }\n if (isPromiseLike(result)) {\n // Side listener on the function's own promise, which passes through\n // unchanged; neither handler rethrows, so this chain never rejects.\n result.then(\n () => endSpan(internalSpan),\n (error: unknown) => endSpanWithError(internalSpan, error),\n );\n return result;\n }\n endSpan(internalSpan);\n return result;\n}\n\n// The wrap-time call site, taken from a stack capture that starts at\n// instrument's caller. An unparseable stack yields no location attributes.\nfunction resolveWrapSite(): { filePath: string; lineNumber: number } | undefined {\n const holder: { stack?: string } = {};\n Error.captureStackTrace(holder, instrument);\n for (const frame of (holder.stack ?? \"\").split(\"\\n\")) {\n const match = STACK_FRAME_PATTERN.exec(frame);\n if (match) {\n return {\n filePath: match[1].startsWith(\"file://\") ? fileURLToPath(match[1]) : match[1],\n lineNumber: Number(match[2]),\n };\n }\n }\n return undefined;\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as PromiseLike<unknown>).then === \"function\"\n );\n}\n\nfunction endSpan(internalSpan: Span): void {\n try {\n internalSpan.end();\n } catch (error) {\n logDebug(`Error ending a manual span: ${String(error)}`);\n }\n}\n\nfunction endSpanWithError(internalSpan: Span, error: unknown): void {\n try {\n const exception = coerceToException(error);\n internalSpan.recordException(exception);\n internalSpan.setStatus({\n code: SpanStatusCode.ERROR,\n message: typeof exception === \"string\" ? exception : exception.message,\n });\n internalSpan.end();\n } catch (endError) {\n logDebug(`Error ending a manual span: ${String(endError)}`);\n }\n}\n"],"mappings":";;;;;;AAaA,MAAM,sBAAsB;AAI5B,SAAgB,WACd,UACA,aACiC;CACjC,MAAM,KACJ,OAAO,aAAa,aAAa,WAAY;CAC/C,MAAM,WAAW,OAAO,aAAa,WAAW,WAAW,GAAG,QAAQ;CACtE,MAAM,aAAyB,CAAC;CAChC,IAAI,GAAG,MACL,WAAW,wBAAwB,GAAG;CAExC,MAAM,WAAW,gBAAgB;CACjC,IAAI,UAAU;EACZ,WAAW,oBAAoB,SAAS;EACxC,WAAW,sBAAsB,SAAS;CAC5C;CACA,OAAO,SAAyB,GAAG,MAA0B;EAC3D,OAAO,cAAc,UAAU,kBAAkB,GAAG,MAAM,MAAM,IAAI,CAAC;CACvE;AACF;AAEA,SAAgB,KAAa,MAAc,IAA0B;CACnE,OAAO,cAAc,MAAM,KAAA,GAAW,EAAE;AAC1C;AAIA,SAAS,cACP,MACA,YACA,IACQ;CACR,IAAI;CACJ,IAAI;EACF,eAAe,MACZ,UAAU,YAAY,iBAAiB,CAAC,CAAC,CACzC,UAAU,MAAM;GAAE,MAAM,SAAS;GAAU;EAAW,CAAC;CAC5D,SAAS,OAAO;EACd,SAAS,iCAAiC,OAAO,KAAK,GAAG;EACzD,OAAO,GAAG;CACZ;CACA,IAAI;CACJ,IAAI;EACF,SAAS,QAAQ,KAAK,MAAM,QAAQ,QAAQ,OAAO,GAAG,YAAY,GAAG,EAAE;CACzE,SAAS,OAAO;EACd,iBAAiB,cAAc,KAAK;EACpC,MAAM;CACR;CACA,IAAI,cAAc,MAAM,GAAG;EAGzB,OAAO,WACC,QAAQ,YAAY,IACzB,UAAmB,iBAAiB,cAAc,KAAK,CAC1D;EACA,OAAO;CACT;CACA,QAAQ,YAAY;CACpB,OAAO;AACT;AAIA,SAAS,kBAAwE;CAC/E,MAAM,SAA6B,CAAC;CACpC,MAAM,kBAAkB,QAAQ,UAAU;CAC1C,KAAK,MAAM,UAAU,OAAO,SAAS,GAAA,CAAI,MAAM,IAAI,GAAG;EACpD,MAAM,QAAQ,oBAAoB,KAAK,KAAK;EAC5C,IAAI,OACF,OAAO;GACL,UAAU,MAAM,EAAE,CAAC,WAAW,SAAS,IAAI,cAAc,MAAM,EAAE,IAAI,MAAM;GAC3E,YAAY,OAAO,MAAM,EAAE;EAC7B;CAEJ;AAEF;AAEA,SAAS,cAAc,OAA+C;CACpE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA+B,SAAS;AAEpD;AAEA,SAAS,QAAQ,cAA0B;CACzC,IAAI;EACF,aAAa,IAAI;CACnB,SAAS,OAAO;EACd,SAAS,+BAA+B,OAAO,KAAK,GAAG;CACzD;AACF;AAEA,SAAS,iBAAiB,cAAoB,OAAsB;CAClE,IAAI;EACF,MAAM,YAAY,kBAAkB,KAAK;EACzC,aAAa,gBAAgB,SAAS;EACtC,aAAa,UAAU;GACrB,MAAM,eAAe;GACrB,SAAS,OAAO,cAAc,WAAW,YAAY,UAAU;EACjE,CAAC;EACD,aAAa,IAAI;CACnB,SAAS,UAAU;EACjB,SAAS,+BAA+B,OAAO,QAAQ,GAAG;CAC5D;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apitally",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Simple API monitoring, analytics and request logging for REST APIs built with AdonisJS, Elysia, Express, Fastify, H3, Hapi, Hono, Koa and NestJS.",
|
|
5
5
|
"author": "Apitally <hello@apitally.io>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"homepage": "https://apitally.io",
|
|
41
41
|
"type": "module",
|
|
42
42
|
"engines": {
|
|
43
|
-
"node": "
|
|
43
|
+
"node": "^20.16.0 || >=22.3.0",
|
|
44
44
|
"bun": ">=1.2.0"
|
|
45
45
|
},
|
|
46
46
|
"sideEffects": [
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"elysia": ">=1.1 <2",
|
|
204
204
|
"express": ">=4.18.2 <6",
|
|
205
205
|
"fastify": ">=4.10.2 <6",
|
|
206
|
-
"h3": ">=2.0.1
|
|
206
|
+
"h3": ">=2.0.1 <3",
|
|
207
207
|
"hono": ">=4.8.4 <5",
|
|
208
208
|
"koa": ">=2 <4",
|
|
209
209
|
"pino": ">=9.6.0 <11",
|
|
@@ -251,16 +251,16 @@
|
|
|
251
251
|
}
|
|
252
252
|
},
|
|
253
253
|
"dependencies": {
|
|
254
|
-
"@opentelemetry/api-logs": "0.
|
|
255
|
-
"@opentelemetry/context-async-hooks": "2.
|
|
256
|
-
"@opentelemetry/core": "2.
|
|
257
|
-
"@opentelemetry/instrumentation-undici": "0.
|
|
258
|
-
"@opentelemetry/otlp-transformer": "0.
|
|
259
|
-
"@opentelemetry/resources": "2.
|
|
260
|
-
"@opentelemetry/sdk-logs": "0.
|
|
261
|
-
"@opentelemetry/sdk-metrics": "2.
|
|
262
|
-
"@opentelemetry/sdk-trace-base": "2.
|
|
263
|
-
"@opentelemetry/sdk-trace-node": "2.
|
|
254
|
+
"@opentelemetry/api-logs": "0.223.0",
|
|
255
|
+
"@opentelemetry/context-async-hooks": "2.12.0",
|
|
256
|
+
"@opentelemetry/core": "2.12.0",
|
|
257
|
+
"@opentelemetry/instrumentation-undici": "0.33.0",
|
|
258
|
+
"@opentelemetry/otlp-transformer": "0.223.0",
|
|
259
|
+
"@opentelemetry/resources": "2.12.0",
|
|
260
|
+
"@opentelemetry/sdk-logs": "0.223.0",
|
|
261
|
+
"@opentelemetry/sdk-metrics": "2.12.0",
|
|
262
|
+
"@opentelemetry/sdk-trace-base": "2.12.0",
|
|
263
|
+
"@opentelemetry/sdk-trace-node": "2.12.0",
|
|
264
264
|
"undici": "^6.27.0"
|
|
265
265
|
},
|
|
266
266
|
"devDependencies": {
|
|
@@ -289,7 +289,7 @@
|
|
|
289
289
|
"elysia": "^1.4.29",
|
|
290
290
|
"express": "^5.2.1",
|
|
291
291
|
"fastify": "^5.11.0",
|
|
292
|
-
"h3": "2.0.
|
|
292
|
+
"h3": "2.0.2",
|
|
293
293
|
"hono": "^4.12.31",
|
|
294
294
|
"knip": "^6.29.0",
|
|
295
295
|
"koa": "^3.2.1",
|