@twin.org/tracing-processors 0.9.2-next.7 → 0.9.3-next.1
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/dist/es/index.js +1 -0
- package/dist/es/index.js.map +1 -1
- package/dist/es/models/httpSpanAttributes.js +22 -0
- package/dist/es/models/httpSpanAttributes.js.map +1 -0
- package/dist/es/tracingRestClientProcessor.js +6 -2
- package/dist/es/tracingRestClientProcessor.js.map +1 -1
- package/dist/es/tracingRouteProcessor.js +4 -3
- package/dist/es/tracingRouteProcessor.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/models/httpSpanAttributes.d.ts +22 -0
- package/docs/changelog.md +25 -0
- package/docs/reference/index.md +8 -0
- package/docs/reference/type-aliases/HttpSpanAttributes.md +5 -0
- package/docs/reference/variables/HttpSpanAttributes.md +26 -0
- package/package.json +2 -2
- package/locales/.validate-ignore +0 -3
package/dist/es/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright 2026 IOTA Stiftung.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
export * from "./models/httpSpanAttributes.js";
|
|
3
4
|
export * from "./models/ITracingRestClientProcessorConfig.js";
|
|
4
5
|
export * from "./models/ITracingRestClientProcessorConstructorOptions.js";
|
|
5
6
|
export * from "./models/ITracingRouteProcessorConfig.js";
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2DAA2D,CAAC;AAC1E,cAAc,0CAA0C,CAAC;AACzD,cAAc,sDAAsD,CAAC;AACrE,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/ITracingRestClientProcessorConfig.js\";\nexport * from \"./models/ITracingRestClientProcessorConstructorOptions.js\";\nexport * from \"./models/ITracingRouteProcessorConfig.js\";\nexport * from \"./models/ITracingRouteProcessorConstructorOptions.js\";\nexport * from \"./tracingRestClientProcessor.js\";\nexport * from \"./tracingRouteProcessor.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2DAA2D,CAAC;AAC1E,cAAc,0CAA0C,CAAC;AACzD,cAAc,sDAAsD,CAAC;AACrE,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/httpSpanAttributes.js\";\nexport * from \"./models/ITracingRestClientProcessorConfig.js\";\nexport * from \"./models/ITracingRestClientProcessorConstructorOptions.js\";\nexport * from \"./models/ITracingRouteProcessorConfig.js\";\nexport * from \"./models/ITracingRouteProcessorConstructorOptions.js\";\nexport * from \"./tracingRestClientProcessor.js\";\nexport * from \"./tracingRouteProcessor.js\";\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
/**
|
|
4
|
+
* The span attribute keys which are not specific to any domain, following the OpenTelemetry
|
|
5
|
+
* semantic conventions. Domain specific keys belong with the domain, alongside its metric ids.
|
|
6
|
+
*/
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
8
|
+
export const HttpSpanAttributes = {
|
|
9
|
+
/**
|
|
10
|
+
* The HTTP method of the request the span covers.
|
|
11
|
+
*/
|
|
12
|
+
HttpMethod: "http.method",
|
|
13
|
+
/**
|
|
14
|
+
* The route of the request the span covers.
|
|
15
|
+
*/
|
|
16
|
+
HttpRoute: "http.route",
|
|
17
|
+
/**
|
|
18
|
+
* The HTTP status code the request completed with.
|
|
19
|
+
*/
|
|
20
|
+
HttpStatusCode: "http.status_code"
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=httpSpanAttributes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpSpanAttributes.js","sourceRoot":"","sources":["../../../src/models/httpSpanAttributes.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AAEvC;;;GAGG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC;;OAEG;IACH,UAAU,EAAE,aAAa;IAEzB;;OAEG;IACH,SAAS,EAAE,YAAY;IAEvB;;OAEG;IACH,cAAc,EAAE,kBAAkB;CACzB,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The span attribute keys which are not specific to any domain, following the OpenTelemetry\n * semantic conventions. Domain specific keys belong with the domain, alongside its metric ids.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const HttpSpanAttributes = {\n\t/**\n\t * The HTTP method of the request the span covers.\n\t */\n\tHttpMethod: \"http.method\",\n\n\t/**\n\t * The route of the request the span covers.\n\t */\n\tHttpRoute: \"http.route\",\n\n\t/**\n\t * The HTTP status code the request completed with.\n\t */\n\tHttpStatusCode: \"http.status_code\"\n} as const;\n\n/**\n * Union type of all non domain specific span attribute key string values.\n */\nexport type HttpSpanAttributes = (typeof HttpSpanAttributes)[keyof typeof HttpSpanAttributes];\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ComponentFactory, Is } from "@twin.org/core";
|
|
2
2
|
import { SpanKind, TraceparentHelper, TracingHelper } from "@twin.org/tracing-models";
|
|
3
3
|
import { HttpStatusCode } from "@twin.org/web";
|
|
4
|
+
import { HttpSpanAttributes } from "./models/httpSpanAttributes.js";
|
|
4
5
|
/**
|
|
5
6
|
* Records a span for each outbound REST request and carries the trace via traceparent header.
|
|
6
7
|
*/
|
|
@@ -47,7 +48,10 @@ export class TracingRestClientProcessor {
|
|
|
47
48
|
}
|
|
48
49
|
return TracingHelper.withSpan(this._tracing, `${context.restClientClassName}${context.route.startsWith("/") ? "" : "/"}${context.route}`, {
|
|
49
50
|
kind: SpanKind.Client,
|
|
50
|
-
attributes: {
|
|
51
|
+
attributes: {
|
|
52
|
+
[HttpSpanAttributes.HttpMethod]: context.method,
|
|
53
|
+
[HttpSpanAttributes.HttpRoute]: context.route
|
|
54
|
+
}
|
|
51
55
|
}, async (span) => {
|
|
52
56
|
if (!Is.empty(span)) {
|
|
53
57
|
context.headers[TraceparentHelper.HEADER_NAME] = TraceparentHelper.format(span.context);
|
|
@@ -56,7 +60,7 @@ export class TracingRestClientProcessor {
|
|
|
56
60
|
if (!Is.empty(span)) {
|
|
57
61
|
span.attributes = {
|
|
58
62
|
...span.attributes,
|
|
59
|
-
|
|
63
|
+
[HttpSpanAttributes.HttpStatusCode]: response.status ?? HttpStatusCode.ok
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
66
|
return response;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracingRestClientProcessor.js","sourceRoot":"","sources":["../../src/tracingRestClientProcessor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EACN,QAAQ,EACR,iBAAiB,EACjB,aAAa,EAEb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"tracingRestClientProcessor.js","sourceRoot":"","sources":["../../src/tracingRestClientProcessor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EACN,QAAQ,EACR,iBAAiB,EACjB,aAAa,EAEb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE;;GAEG;AACH,MAAM,OAAO,0BAA0B;IACtC;;OAEG;IACI,MAAM,CAAU,UAAU,gCAAgD;IAEjF;;;OAGG;IACc,QAAQ,CAAqB;IAE9C;;;OAGG;IACc,sBAAsB,CAAW;IAElD;;;OAGG;IACH,YAAY,OAAuD;QAClE,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,WAAW,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAC5E,IAAI,CAAC,sBAAsB,GAAG,OAAO,EAAE,MAAM,EAAE,YAAY,IAAI,EAAE,CAAC;IACnE,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,0BAA0B,CAAC,UAAU,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,GAAG,CACf,OAAoC,EACpC,IAA6B;QAE7B,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YAC5F,OAAO,IAAI,EAAE,CAAC;QACf,CAAC;QAED,OAAO,aAAa,CAAC,QAAQ,CAC5B,IAAI,CAAC,QAAQ,EACb,GAAG,OAAO,CAAC,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,EAC3F;YACC,IAAI,EAAE,QAAQ,CAAC,MAAM;YACrB,UAAU,EAAE;gBACX,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM;gBAC/C,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK;aAC7C;SACD,EACD,KAAK,EAAC,IAAI,EAAC,EAAE;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,WAAW,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzF,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAC;YAE9B,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,IAAI,CAAC,UAAU,GAAG;oBACjB,GAAG,IAAI,CAAC,UAAU;oBAClB,CAAC,kBAAkB,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,IAAI,cAAc,CAAC,EAAE;iBACzE,CAAC;YACH,CAAC;YAED,OAAO,QAAQ,CAAC;QACjB,CAAC,CACD,CAAC;IACH,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestClientProcessor, IRestClientProcessorContext } from \"@twin.org/api-models\";\nimport { ComponentFactory, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tSpanKind,\n\tTraceparentHelper,\n\tTracingHelper,\n\ttype ITracingComponent\n} from \"@twin.org/tracing-models\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport { HttpSpanAttributes } from \"./models/httpSpanAttributes.js\";\nimport type { ITracingRestClientProcessorConstructorOptions } from \"./models/ITracingRestClientProcessorConstructorOptions.js\";\n\n/**\n * Records a span for each outbound REST request and carries the trace via traceparent header.\n */\nexport class TracingRestClientProcessor implements IRestClientProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TracingRestClientProcessor>();\n\n\t/**\n\t * The component recording the spans.\n\t * @internal\n\t */\n\tprivate readonly _tracing?: ITracingComponent;\n\n\t/**\n\t * Route templates to skip tracing.\n\t * @internal\n\t */\n\tprivate readonly _excludeRouteTemplates: string[];\n\n\t/**\n\t * Create a new instance of TracingRestClientProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: ITracingRestClientProcessorConstructorOptions) {\n\t\tthis._tracing = ComponentFactory.getIfExists(options?.tracingComponentType);\n\t\tthis._excludeRouteTemplates = options?.config?.excludePaths ?? [];\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn TracingRestClientProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Wrap the request in a client span, sending the current span as the traceparent so the\n\t * receiving service continues this trace.\n\t * @param context The details of the request being made.\n\t * @param next Performs the request.\n\t * @returns The response.\n\t */\n\tpublic async pre(\n\t\tcontext: IRestClientProcessorContext,\n\t\tnext: () => Promise<Response>\n\t): Promise<Response> {\n\t\tif (Is.empty(this._tracing) || this._excludeRouteTemplates.includes(context.routeTemplate)) {\n\t\t\treturn next();\n\t\t}\n\n\t\treturn TracingHelper.withSpan(\n\t\t\tthis._tracing,\n\t\t\t`${context.restClientClassName}${context.route.startsWith(\"/\") ? \"\" : \"/\"}${context.route}`,\n\t\t\t{\n\t\t\t\tkind: SpanKind.Client,\n\t\t\t\tattributes: {\n\t\t\t\t\t[HttpSpanAttributes.HttpMethod]: context.method,\n\t\t\t\t\t[HttpSpanAttributes.HttpRoute]: context.route\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync span => {\n\t\t\t\tif (!Is.empty(span)) {\n\t\t\t\t\tcontext.headers[TraceparentHelper.HEADER_NAME] = TraceparentHelper.format(span.context);\n\t\t\t\t}\n\n\t\t\t\tconst response = await next();\n\n\t\t\t\tif (!Is.empty(span)) {\n\t\t\t\t\tspan.attributes = {\n\t\t\t\t\t\t...span.attributes,\n\t\t\t\t\t\t[HttpSpanAttributes.HttpStatusCode]: response.status ?? HttpStatusCode.ok\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn response;\n\t\t\t}\n\t\t);\n\t}\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseError, Coerce, ComponentFactory, Is } from "@twin.org/core";
|
|
2
2
|
import { SpanKind, SpanStatus, TraceparentHelper, TracingHelper } from "@twin.org/tracing-models";
|
|
3
3
|
import { HttpStatusCode } from "@twin.org/web";
|
|
4
|
+
import { HttpSpanAttributes } from "./models/httpSpanAttributes.js";
|
|
4
5
|
/**
|
|
5
6
|
* Process the REST request and record it as a span.
|
|
6
7
|
*/
|
|
@@ -114,7 +115,7 @@ export class TracingRouteProcessor {
|
|
|
114
115
|
delete processorState[TracingRouteProcessor._STATE_KEY];
|
|
115
116
|
try {
|
|
116
117
|
const statusCode = Is.number(response.statusCode) ? response.statusCode : HttpStatusCode.ok;
|
|
117
|
-
span.attributes = { ...span.attributes,
|
|
118
|
+
span.attributes = { ...span.attributes, [HttpSpanAttributes.HttpStatusCode]: statusCode };
|
|
118
119
|
await this._tracing.endSpan(span, statusCode >= HttpStatusCode.badRequest ? SpanStatus.Error : SpanStatus.Ok);
|
|
119
120
|
}
|
|
120
121
|
catch (err) {
|
|
@@ -150,10 +151,10 @@ export class TracingRouteProcessor {
|
|
|
150
151
|
requestAttributes(request, route) {
|
|
151
152
|
const attributes = {};
|
|
152
153
|
if (Is.stringValue(request.method)) {
|
|
153
|
-
attributes[
|
|
154
|
+
attributes[HttpSpanAttributes.HttpMethod] = request.method;
|
|
154
155
|
}
|
|
155
156
|
if (Is.stringValue(route?.path)) {
|
|
156
|
-
attributes[
|
|
157
|
+
attributes[HttpSpanAttributes.HttpRoute] = route.path;
|
|
157
158
|
}
|
|
158
159
|
return attributes;
|
|
159
160
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracingRouteProcessor.js","sourceRoot":"","sources":["../../src/tracingRouteProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIBaseRoute,\n\tIBaseRouteProcessor,\n\tIHttpResponse,\n\tIHttpServerRequest\n} from \"@twin.org/api-models\";\nimport type { IContextIds } from \"@twin.org/context\";\nimport { BaseError, Coerce, ComponentFactory, Is } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tSpanKind,\n\tSpanStatus,\n\tTraceparentHelper,\n\tTracingHelper,\n\ttype ISpan,\n\ttype ITracingComponent\n} from \"@twin.org/tracing-models\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport type { ITracingRouteProcessorConstructorOptions } from \"./models/ITracingRouteProcessorConstructorOptions.js\";\n\n/**\n * Process the REST request and record it as a span.\n */\nexport class TracingRouteProcessor implements IBaseRouteProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TracingRouteProcessor>();\n\n\t/**\n\t * The key the span is stored under while it is handed between the processor phases.\n\t * @internal\n\t */\n\tprivate static readonly _STATE_KEY: string = \"tracingProcessorSpan\";\n\n\t/**\n\t * The component for recording the spans.\n\t * @internal\n\t */\n\tprivate readonly _tracing?: ITracingComponent;\n\n\t/**\n\t * The component for logging failures.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * Request URL path prefixes to skip tracing, defaults to [\"/tracing\"].\n\t * @internal\n\t */\n\tprivate readonly _excludePaths: string[];\n\n\t/**\n\t * Route operation IDs to skip tracing.\n\t * @internal\n\t */\n\tprivate readonly _excludeOperationIds: string[];\n\n\t/**\n\t * Create a new instance of TracingRouteProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: ITracingRouteProcessorConstructorOptions) {\n\t\tthis._tracing = ComponentFactory.getIfExists(options?.tracingComponentType);\n\t\tthis._logging = ComponentFactory.getIfExists(options?.loggingComponentType);\n\t\tthis._excludePaths = options?.config?.excludePaths ?? [\"/tracing\"];\n\t\tthis._excludeOperationIds = options?.config?.excludeOperationIds ?? [];\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn TracingRouteProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route, opening the span for the request.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the span has been started.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (Is.empty(this._tracing)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Don't want to run tracing on routes without auth as we have no\n\t\t// tenancy context to record the span against, and it will just create noise in the traces.\n\t\t// this is usually endpoints like /health, /metrics, etc. that are not part of the business logic of the service.\n\t\tif (route?.skipAuth ?? false) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (Is.stringValue(route?.path) && this._excludePaths.includes(route.path)) {\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tIs.stringValue(route?.operationId) &&\n\t\t\tthis._excludeOperationIds.includes(route.operationId)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst parentContext = TraceparentHelper.parse(\n\t\t\tCoerce.string(request.headers?.[TraceparentHelper.HEADER_NAME])\n\t\t);\n\n\t\ttry {\n\t\t\tconst span = await this._tracing.startSpan(this.spanName(request, route), {\n\t\t\t\tkind: SpanKind.Server,\n\t\t\t\tparentContext,\n\t\t\t\tattributes: this.requestAttributes(request, route)\n\t\t\t});\n\n\t\t\tprocessorState[TracingRouteProcessor._STATE_KEY] = span;\n\n\t\t\tObject.assign(contextIds, TracingHelper.spanContextToContextIds(span.context));\n\t\t} catch (err) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: TracingRouteProcessor.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"startSpanFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Post process the REST request for the specified route, ending the span for the request.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the span has been ended.\n\t */\n\tpublic async post(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tconst span = processorState[TracingRouteProcessor._STATE_KEY];\n\n\t\tif (Is.empty(this._tracing) || !Is.object<ISpan>(span)) {\n\t\t\treturn;\n\t\t}\n\n\t\tdelete processorState[TracingRouteProcessor._STATE_KEY];\n\n\t\ttry {\n\t\t\tconst statusCode = Is.number(response.statusCode) ? response.statusCode : HttpStatusCode.ok;\n\n\t\t\tspan.attributes = { ...span.attributes, \"http.status_code\": statusCode };\n\n\t\t\tawait this._tracing.endSpan(\n\t\t\t\tspan,\n\t\t\t\tstatusCode >= HttpStatusCode.badRequest ? SpanStatus.Error : SpanStatus.Ok\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: TracingRouteProcessor.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"endSpanFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Build the name for the span.\n\t * @param request The incoming request.\n\t * @param route The route to process.\n\t * @returns The span name.\n\t * @internal\n\t */\n\tprivate spanName(request: IHttpServerRequest, route: IBaseRoute | undefined): string {\n\t\tconst routeName = route?.operationId ?? route?.path;\n\n\t\treturn Is.stringValue(routeName)\n\t\t\t? routeName\n\t\t\t: `${request.method ?? \"UNKNOWN\"} ${TracingRouteProcessor.CLASS_NAME}`;\n\t}\n\n\t/**\n\t * Build the attributes describing the request.\n\t * @param request The incoming request.\n\t * @param route The route to process.\n\t * @returns The attributes.\n\t * @internal\n\t */\n\tprivate requestAttributes(\n\t\trequest: IHttpServerRequest,\n\t\troute: IBaseRoute | undefined\n\t): { [key: string]: unknown } {\n\t\tconst attributes: { [key: string]: unknown } = {};\n\n\t\tif (Is.stringValue(request.method)) {\n\t\t\tattributes[\"http.method\"] = request.method;\n\t\t}\n\t\tif (Is.stringValue(route?.path)) {\n\t\t\tattributes[\"http.route\"] = route.path;\n\t\t}\n\n\t\treturn attributes;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tracingRouteProcessor.js","sourceRoot":"","sources":["../../src/tracingRouteProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIBaseRoute,\n\tIBaseRouteProcessor,\n\tIHttpResponse,\n\tIHttpServerRequest\n} from \"@twin.org/api-models\";\nimport type { IContextIds } from \"@twin.org/context\";\nimport { BaseError, Coerce, ComponentFactory, Is } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tSpanKind,\n\tSpanStatus,\n\tTraceparentHelper,\n\tTracingHelper,\n\ttype ISpan,\n\ttype ITracingComponent\n} from \"@twin.org/tracing-models\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport { HttpSpanAttributes } from \"./models/httpSpanAttributes.js\";\nimport type { ITracingRouteProcessorConstructorOptions } from \"./models/ITracingRouteProcessorConstructorOptions.js\";\n\n/**\n * Process the REST request and record it as a span.\n */\nexport class TracingRouteProcessor implements IBaseRouteProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TracingRouteProcessor>();\n\n\t/**\n\t * The key the span is stored under while it is handed between the processor phases.\n\t * @internal\n\t */\n\tprivate static readonly _STATE_KEY: string = \"tracingProcessorSpan\";\n\n\t/**\n\t * The component for recording the spans.\n\t * @internal\n\t */\n\tprivate readonly _tracing?: ITracingComponent;\n\n\t/**\n\t * The component for logging failures.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * Request URL path prefixes to skip tracing, defaults to [\"/tracing\"].\n\t * @internal\n\t */\n\tprivate readonly _excludePaths: string[];\n\n\t/**\n\t * Route operation IDs to skip tracing.\n\t * @internal\n\t */\n\tprivate readonly _excludeOperationIds: string[];\n\n\t/**\n\t * Create a new instance of TracingRouteProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: ITracingRouteProcessorConstructorOptions) {\n\t\tthis._tracing = ComponentFactory.getIfExists(options?.tracingComponentType);\n\t\tthis._logging = ComponentFactory.getIfExists(options?.loggingComponentType);\n\t\tthis._excludePaths = options?.config?.excludePaths ?? [\"/tracing\"];\n\t\tthis._excludeOperationIds = options?.config?.excludeOperationIds ?? [];\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn TracingRouteProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route, opening the span for the request.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the span has been started.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (Is.empty(this._tracing)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Don't want to run tracing on routes without auth as we have no\n\t\t// tenancy context to record the span against, and it will just create noise in the traces.\n\t\t// this is usually endpoints like /health, /metrics, etc. that are not part of the business logic of the service.\n\t\tif (route?.skipAuth ?? false) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (Is.stringValue(route?.path) && this._excludePaths.includes(route.path)) {\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tIs.stringValue(route?.operationId) &&\n\t\t\tthis._excludeOperationIds.includes(route.operationId)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst parentContext = TraceparentHelper.parse(\n\t\t\tCoerce.string(request.headers?.[TraceparentHelper.HEADER_NAME])\n\t\t);\n\n\t\ttry {\n\t\t\tconst span = await this._tracing.startSpan(this.spanName(request, route), {\n\t\t\t\tkind: SpanKind.Server,\n\t\t\t\tparentContext,\n\t\t\t\tattributes: this.requestAttributes(request, route)\n\t\t\t});\n\n\t\t\tprocessorState[TracingRouteProcessor._STATE_KEY] = span;\n\n\t\t\tObject.assign(contextIds, TracingHelper.spanContextToContextIds(span.context));\n\t\t} catch (err) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: TracingRouteProcessor.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"startSpanFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Post process the REST request for the specified route, ending the span for the request.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the span has been ended.\n\t */\n\tpublic async post(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tconst span = processorState[TracingRouteProcessor._STATE_KEY];\n\n\t\tif (Is.empty(this._tracing) || !Is.object<ISpan>(span)) {\n\t\t\treturn;\n\t\t}\n\n\t\tdelete processorState[TracingRouteProcessor._STATE_KEY];\n\n\t\ttry {\n\t\t\tconst statusCode = Is.number(response.statusCode) ? response.statusCode : HttpStatusCode.ok;\n\n\t\t\tspan.attributes = { ...span.attributes, [HttpSpanAttributes.HttpStatusCode]: statusCode };\n\n\t\t\tawait this._tracing.endSpan(\n\t\t\t\tspan,\n\t\t\t\tstatusCode >= HttpStatusCode.badRequest ? SpanStatus.Error : SpanStatus.Ok\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: TracingRouteProcessor.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"endSpanFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Build the name for the span.\n\t * @param request The incoming request.\n\t * @param route The route to process.\n\t * @returns The span name.\n\t * @internal\n\t */\n\tprivate spanName(request: IHttpServerRequest, route: IBaseRoute | undefined): string {\n\t\tconst routeName = route?.operationId ?? route?.path;\n\n\t\treturn Is.stringValue(routeName)\n\t\t\t? routeName\n\t\t\t: `${request.method ?? \"UNKNOWN\"} ${TracingRouteProcessor.CLASS_NAME}`;\n\t}\n\n\t/**\n\t * Build the attributes describing the request.\n\t * @param request The incoming request.\n\t * @param route The route to process.\n\t * @returns The attributes.\n\t * @internal\n\t */\n\tprivate requestAttributes(\n\t\trequest: IHttpServerRequest,\n\t\troute: IBaseRoute | undefined\n\t): { [key: string]: unknown } {\n\t\tconst attributes: { [key: string]: unknown } = {};\n\n\t\tif (Is.stringValue(request.method)) {\n\t\t\tattributes[HttpSpanAttributes.HttpMethod] = request.method;\n\t\t}\n\t\tif (Is.stringValue(route?.path)) {\n\t\t\tattributes[HttpSpanAttributes.HttpRoute] = route.path;\n\t\t}\n\n\t\treturn attributes;\n\t}\n}\n"]}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The span attribute keys which are not specific to any domain, following the OpenTelemetry
|
|
3
|
+
* semantic conventions. Domain specific keys belong with the domain, alongside its metric ids.
|
|
4
|
+
*/
|
|
5
|
+
export declare const HttpSpanAttributes: {
|
|
6
|
+
/**
|
|
7
|
+
* The HTTP method of the request the span covers.
|
|
8
|
+
*/
|
|
9
|
+
readonly HttpMethod: "http.method";
|
|
10
|
+
/**
|
|
11
|
+
* The route of the request the span covers.
|
|
12
|
+
*/
|
|
13
|
+
readonly HttpRoute: "http.route";
|
|
14
|
+
/**
|
|
15
|
+
* The HTTP status code the request completed with.
|
|
16
|
+
*/
|
|
17
|
+
readonly HttpStatusCode: "http.status_code";
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Union type of all non domain specific span attribute key string values.
|
|
21
|
+
*/
|
|
22
|
+
export type HttpSpanAttributes = (typeof HttpSpanAttributes)[keyof typeof HttpSpanAttributes];
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.3-next.1](https://github.com/iotaledger/twin-tracing/compare/tracing-processors-v0.9.3-next.0...tracing-processors-v0.9.3-next.1) (2026-08-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* console tracing connector ([#31](https://github.com/iotaledger/twin-tracing/issues/31)) ([9393573](https://github.com/iotaledger/twin-tracing/commit/939357302fe7c900b46d96618615128d8f2f5fa4))
|
|
9
|
+
* missing locale ([9353f8f](https://github.com/iotaledger/twin-tracing/commit/9353f8fb5a25564299616942c21c31715ea23ad3))
|
|
10
|
+
* rename SpanEntity to Span ([5dcca6c](https://github.com/iotaledger/twin-tracing/commit/5dcca6cdb7d24eb047eb9334bd0691b6069df82b))
|
|
11
|
+
* tenant aware entity storage ([#17](https://github.com/iotaledger/twin-tracing/issues/17)) ([ae11c56](https://github.com/iotaledger/twin-tracing/commit/ae11c56edf472bc1842997600238c2309ef7147d))
|
|
12
|
+
* tracing processors ([#9](https://github.com/iotaledger/twin-tracing/issues/9)) ([e5f5d16](https://github.com/iotaledger/twin-tracing/commit/e5f5d1616992440d067830965372d32fc34a939d))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Dependencies
|
|
16
|
+
|
|
17
|
+
* The following workspace dependencies were updated
|
|
18
|
+
* dependencies
|
|
19
|
+
* @twin.org/tracing-models bumped from 0.9.3-next.0 to 0.9.3-next.1
|
|
20
|
+
|
|
21
|
+
## [0.9.2](https://github.com/iotaledger/twin-tracing/compare/tracing-processors-v0.9.2...tracing-processors-v0.9.2) (2026-08-24)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* release to production ([#27](https://github.com/iotaledger/twin-tracing/issues/27)) ([d1ed9dd](https://github.com/iotaledger/twin-tracing/commit/d1ed9dde0b0d582dbbefb623e003042f5f36f051))
|
|
27
|
+
|
|
3
28
|
## [0.9.2-next.7](https://github.com/iotaledger/twin-tracing/compare/tracing-processors-v0.9.2-next.6...tracing-processors-v0.9.2-next.7) (2026-08-21)
|
|
4
29
|
|
|
5
30
|
|
package/docs/reference/index.md
CHANGED
|
@@ -11,3 +11,11 @@
|
|
|
11
11
|
- [ITracingRestClientProcessorConstructorOptions](interfaces/ITracingRestClientProcessorConstructorOptions.md)
|
|
12
12
|
- [ITracingRouteProcessorConfig](interfaces/ITracingRouteProcessorConfig.md)
|
|
13
13
|
- [ITracingRouteProcessorConstructorOptions](interfaces/ITracingRouteProcessorConstructorOptions.md)
|
|
14
|
+
|
|
15
|
+
## Type Aliases
|
|
16
|
+
|
|
17
|
+
- [HttpSpanAttributes](type-aliases/HttpSpanAttributes.md)
|
|
18
|
+
|
|
19
|
+
## Variables
|
|
20
|
+
|
|
21
|
+
- [HttpSpanAttributes](variables/HttpSpanAttributes.md)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Type Alias: HttpSpanAttributes
|
|
2
|
+
|
|
3
|
+
> **HttpSpanAttributes** = *typeof* [`HttpSpanAttributes`](../variables/HttpSpanAttributes.md)\[keyof *typeof* [`HttpSpanAttributes`](../variables/HttpSpanAttributes.md)\]
|
|
4
|
+
|
|
5
|
+
Union type of all non domain specific span attribute key string values.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Variable: HttpSpanAttributes
|
|
2
|
+
|
|
3
|
+
> `const` **HttpSpanAttributes**: `object`
|
|
4
|
+
|
|
5
|
+
The span attribute keys which are not specific to any domain, following the OpenTelemetry
|
|
6
|
+
semantic conventions. Domain specific keys belong with the domain, alongside its metric ids.
|
|
7
|
+
|
|
8
|
+
## Type Declaration
|
|
9
|
+
|
|
10
|
+
### HttpMethod {#httpmethod}
|
|
11
|
+
|
|
12
|
+
> `readonly` **HttpMethod**: `"http.method"` = `"http.method"`
|
|
13
|
+
|
|
14
|
+
The HTTP method of the request the span covers.
|
|
15
|
+
|
|
16
|
+
### HttpRoute {#httproute}
|
|
17
|
+
|
|
18
|
+
> `readonly` **HttpRoute**: `"http.route"` = `"http.route"`
|
|
19
|
+
|
|
20
|
+
The route of the request the span covers.
|
|
21
|
+
|
|
22
|
+
### HttpStatusCode {#httpstatuscode}
|
|
23
|
+
|
|
24
|
+
> `readonly` **HttpStatusCode**: `"http.status_code"` = `"http.status_code"`
|
|
25
|
+
|
|
26
|
+
The HTTP status code the request completed with.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/tracing-processors",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3-next.1",
|
|
4
4
|
"description": "Processors which record spans for inbound and outbound REST requests.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@twin.org/core": "next",
|
|
20
20
|
"@twin.org/logging-models": "next",
|
|
21
21
|
"@twin.org/nameof": "next",
|
|
22
|
-
"@twin.org/tracing-models": "0.9.
|
|
22
|
+
"@twin.org/tracing-models": "0.9.3-next.1",
|
|
23
23
|
"@twin.org/web": "next"
|
|
24
24
|
},
|
|
25
25
|
"main": "./dist/es/index.js",
|
package/locales/.validate-ignore
DELETED