@shrub/express-tracing 0.5.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/middleware.js +57 -0
- package/dist/esm/module.js +20 -0
- package/dist/esm/service.js +85 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +18 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.js +62 -0
- package/dist/module.d.ts +9 -0
- package/dist/module.js +24 -0
- package/dist/service.d.ts +30 -0
- package/dist/service.js +88 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 jjvainav
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from "./middleware";
|
|
2
|
+
export * from "./module";
|
|
3
|
+
export { TraceHeaders } from "./service";
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxVQUFVLENBQUM7QUFDekIsT0FBTyxFQUEwQixZQUFZLEVBQUUsTUFBTSxXQUFXLENBQUMifQ==
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { IExpressTracingService } from "./service";
|
|
2
|
+
attachErrorListener();
|
|
3
|
+
/** @internal */
|
|
4
|
+
export const addSpanRequestBuilder = (context, span) => ({ ...context, span });
|
|
5
|
+
/** Request tracing middleware that will start a new span for a request. */
|
|
6
|
+
export const useRequestTracing = (options) => {
|
|
7
|
+
return (req, res, next) => {
|
|
8
|
+
const service = req.context.services.tryGet(IExpressTracingService);
|
|
9
|
+
if (!service) {
|
|
10
|
+
return next(new Error("express-tracing-service not registered, make sure the ExpressTracingModule is loaded when using request tracing."));
|
|
11
|
+
}
|
|
12
|
+
const span = service.startSpan(req, options);
|
|
13
|
+
req.contextBuilder.addSpan(span);
|
|
14
|
+
// if a request connection is 'keep-alive' the response will never finish;
|
|
15
|
+
// so instead, listen for the close event for when the connection has been closed
|
|
16
|
+
// https://nodejs.org/api/http.html#http_event_close_1
|
|
17
|
+
res.once("close", () => service.endSpan(span, req, res));
|
|
18
|
+
// https://nodejs.org/api/http.html#http_event_finish
|
|
19
|
+
res.once("finish", () => {
|
|
20
|
+
if (res.statusCode >= 300 && res.statusCode < 400) {
|
|
21
|
+
const location = res.getHeader("location");
|
|
22
|
+
if (location) {
|
|
23
|
+
span.tag("http.location", location);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
service.endSpan(span, req, res);
|
|
27
|
+
});
|
|
28
|
+
const ref = res.writeHead;
|
|
29
|
+
res.writeHead = function writeHead() {
|
|
30
|
+
// gets invoked just before writing headers
|
|
31
|
+
if (res.getHeader("connection") === "keep-alive") {
|
|
32
|
+
span.tag("http.keepAlive", true);
|
|
33
|
+
}
|
|
34
|
+
return ref.apply(res, arguments);
|
|
35
|
+
};
|
|
36
|
+
next();
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Overrides the express internal handle_error function that gets invoked when an error has occurred.
|
|
41
|
+
* There are no global error events exposed by express and the only way to handle errors is via express middleware.
|
|
42
|
+
* By monkey patching the internal handle_error function we can 'listen' for errors and not have to worry
|
|
43
|
+
* about error handling middleware not passing the error down to next.
|
|
44
|
+
*
|
|
45
|
+
* Note: the node http request/response define an error event (e.g. req.on("error")) but these do not seem to get invoked.
|
|
46
|
+
*
|
|
47
|
+
* https://github.com/expressjs/express/blob/3ed5090ca91f6a387e66370d57ead94d886275e1/lib/router/layer.js#L62
|
|
48
|
+
*/
|
|
49
|
+
function attachErrorListener() {
|
|
50
|
+
const Layer = require("express/lib/router/layer");
|
|
51
|
+
const ref = Layer.prototype.handle_error;
|
|
52
|
+
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
|
|
53
|
+
req.context.bag.__error = error;
|
|
54
|
+
return ref.call(this, error, req, res, next);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBLE9BQU8sRUFBRSxzQkFBc0IsRUFBMEIsTUFBTSxXQUFXLENBQUM7QUFZM0UsbUJBQW1CLEVBQUUsQ0FBQztBQUV0QixnQkFBZ0I7QUFDaEIsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxPQUF3QixFQUFFLElBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLEdBQUcsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFFdkcsMkVBQTJFO0FBQzNFLE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLENBQUMsT0FBZ0MsRUFBa0IsRUFBRTtJQUNsRixPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtRQUN0QixNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ1YsT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsa0hBQWtILENBQUMsQ0FBQyxDQUFDO1NBQzlJO1FBRUQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDN0MsR0FBRyxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFakMsMEVBQTBFO1FBQzFFLGlGQUFpRjtRQUVqRixzREFBc0Q7UUFDdEQsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFFekQscURBQXFEO1FBQ3JELEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtZQUNwQixJQUFJLEdBQUcsQ0FBQyxVQUFVLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxVQUFVLEdBQUcsR0FBRyxFQUFFO2dCQUMvQyxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUMzQyxJQUFJLFFBQVEsRUFBRTtvQkFDVixJQUFJLENBQUMsR0FBRyxDQUFDLGVBQWUsRUFBVSxRQUFRLENBQUMsQ0FBQztpQkFDL0M7YUFDSjtZQUVELE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNwQyxDQUFDLENBQUMsQ0FBQztRQUVILE1BQU0sR0FBRyxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDMUIsR0FBRyxDQUFDLFNBQVMsR0FBRyxTQUFTLFNBQVM7WUFDOUIsMkNBQTJDO1lBQzNDLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsS0FBSyxZQUFZLEVBQUU7Z0JBQzlDLElBQUksQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDcEM7WUFFRCxPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFNBQWdCLENBQUMsQ0FBQztRQUM1QyxDQUFDLENBQUM7UUFFRixJQUFJLEVBQUUsQ0FBQztJQUNYLENBQUMsQ0FBQztBQUNOLENBQUMsQ0FBQztBQUVGOzs7Ozs7Ozs7R0FTRztBQUNILFNBQVMsbUJBQW1CO0lBQ3hCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0lBQ2xELE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDO0lBQ3pDLEtBQUssQ0FBQyxTQUFTLENBQUMsWUFBWSxHQUFHLFNBQVMsWUFBWSxDQUFDLEtBQVksRUFBRSxHQUFZLEVBQUUsR0FBYSxFQUFFLElBQWtCO1FBQzlHLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFDaEMsT0FBTyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNqRCxDQUFDLENBQUE7QUFDTCxDQUFDIn0=
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ExpressModule, IExpressConfiguration } from "@shrub/express";
|
|
2
|
+
import { TracingModule } from "@shrub/tracing";
|
|
3
|
+
import { addSpanRequestBuilder } from "./middleware";
|
|
4
|
+
import { ExpressTracingService, IExpressTracingService } from "./service";
|
|
5
|
+
export class ExpressTracingModule {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = "express-tracing";
|
|
8
|
+
this.dependencies = [
|
|
9
|
+
ExpressModule,
|
|
10
|
+
TracingModule
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
configureServices(registration) {
|
|
14
|
+
registration.register(IExpressTracingService, ExpressTracingService);
|
|
15
|
+
}
|
|
16
|
+
configure({ config }) {
|
|
17
|
+
config.get(IExpressConfiguration).useRequestBuilder("addSpan", addSpanRequestBuilder);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsYUFBYSxFQUFFLHFCQUFxQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFMUUsTUFBTSxPQUFPLG9CQUFvQjtJQUFqQztRQUNhLFNBQUksR0FBRyxpQkFBaUIsQ0FBQztRQUN6QixpQkFBWSxHQUFHO1lBQ3BCLGFBQWE7WUFDYixhQUFhO1NBQ2hCLENBQUM7SUFTTixDQUFDO0lBUEcsaUJBQWlCLENBQUMsWUFBa0M7UUFDaEQsWUFBWSxDQUFDLFFBQVEsQ0FBQyxzQkFBc0IsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7SUFFRCxTQUFTLENBQUMsRUFBRSxNQUFNLEVBQXVCO1FBQ3JDLE1BQU0sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUscUJBQXFCLENBQUMsQ0FBQztJQUMxRixDQUFDO0NBQ0oifQ==
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { createService, Singleton } from "@shrub/core";
|
|
11
|
+
import { ITracingService } from "@shrub/tracing";
|
|
12
|
+
export var TraceHeaders;
|
|
13
|
+
(function (TraceHeaders) {
|
|
14
|
+
TraceHeaders["traceId"] = "X-Trace-ID";
|
|
15
|
+
TraceHeaders["spanId"] = "X-Span-ID";
|
|
16
|
+
})(TraceHeaders || (TraceHeaders = {}));
|
|
17
|
+
export const IExpressTracingService = createService("express-tracing-service");
|
|
18
|
+
function isRequestContextScope(scope) {
|
|
19
|
+
return scope.bag !== undefined && scope.services !== undefined;
|
|
20
|
+
}
|
|
21
|
+
function isRequestScope(scope) {
|
|
22
|
+
return scope.url !== undefined && scope.method !== undefined;
|
|
23
|
+
}
|
|
24
|
+
let ExpressTracingService = class ExpressTracingService {
|
|
25
|
+
constructor(tracingService) {
|
|
26
|
+
this.tracingService = tracingService;
|
|
27
|
+
}
|
|
28
|
+
startSpan(req, options) {
|
|
29
|
+
const builder = options && options.external
|
|
30
|
+
? this.getExternalBuilder()
|
|
31
|
+
: this.getInternalBuilder();
|
|
32
|
+
const tags = {
|
|
33
|
+
"http.url": req.originalUrl,
|
|
34
|
+
"http.method": req.method
|
|
35
|
+
};
|
|
36
|
+
this.addHeaderFieldTag(tags, req, "http.id", "X-Request-ID");
|
|
37
|
+
return builder.build(req).startSpan("http.request", tags);
|
|
38
|
+
}
|
|
39
|
+
endSpan(span, req, res) {
|
|
40
|
+
span.tag("http.status", res.statusCode);
|
|
41
|
+
span.done(req.context.bag.__error);
|
|
42
|
+
}
|
|
43
|
+
getExternalBuilder() {
|
|
44
|
+
this.externalBuilder = this.externalBuilder || this.tracingService.getBuilder();
|
|
45
|
+
return this.externalBuilder;
|
|
46
|
+
}
|
|
47
|
+
getInternalBuilder() {
|
|
48
|
+
this.internalBuilder = this.internalBuilder || this.tracingService.getBuilder().useContextProvider({
|
|
49
|
+
getSpanContext: scope => {
|
|
50
|
+
if (isRequestScope(scope)) {
|
|
51
|
+
const traceId = scope.get(TraceHeaders.traceId);
|
|
52
|
+
const scopeId = scope.get(TraceHeaders.spanId);
|
|
53
|
+
if (traceId && scopeId) {
|
|
54
|
+
return {
|
|
55
|
+
getParentSpanId: () => scopeId,
|
|
56
|
+
getTraceId: () => traceId
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (isRequestContextScope(scope) && scope.span) {
|
|
61
|
+
const scopeId = scope.span.id;
|
|
62
|
+
const traceId = scope.span.traceId;
|
|
63
|
+
return {
|
|
64
|
+
getParentSpanId: () => scopeId,
|
|
65
|
+
getTraceId: () => traceId
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return this.internalBuilder;
|
|
72
|
+
}
|
|
73
|
+
addHeaderFieldTag(tags, req, tagName, headerName) {
|
|
74
|
+
const value = req.get(headerName);
|
|
75
|
+
if (value !== undefined) {
|
|
76
|
+
tags[tagName] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
ExpressTracingService = __decorate([
|
|
81
|
+
Singleton,
|
|
82
|
+
__param(0, ITracingService)
|
|
83
|
+
], ExpressTracingService);
|
|
84
|
+
export { ExpressTracingService };
|
|
85
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUNBLE9BQU8sRUFBRSxhQUFhLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRXZELE9BQU8sRUFBZ0MsZUFBZSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFrQi9FLE1BQU0sQ0FBTixJQUFZLFlBR1g7QUFIRCxXQUFZLFlBQVk7SUFDcEIsc0NBQXNCLENBQUE7SUFDdEIsb0NBQW9CLENBQUE7QUFDeEIsQ0FBQyxFQUhXLFlBQVksS0FBWixZQUFZLFFBR3ZCO0FBRUQsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsYUFBYSxDQUF5Qix5QkFBeUIsQ0FBQyxDQUFDO0FBRXZHLFNBQVMscUJBQXFCLENBQUMsS0FBVTtJQUNyQyxPQUF5QixLQUFNLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBc0IsS0FBTSxDQUFDLFFBQVEsS0FBSyxTQUFTLENBQUM7QUFDekcsQ0FBQztBQUVELFNBQVMsY0FBYyxDQUFDLEtBQVU7SUFDOUIsT0FBaUIsS0FBTSxDQUFDLEdBQUcsS0FBSyxTQUFTLElBQWMsS0FBTSxDQUFDLE1BQU0sS0FBSyxTQUFTLENBQUM7QUFDdkYsQ0FBQztBQUdELElBQWEscUJBQXFCLEdBQWxDLE1BQWEscUJBQXFCO0lBSTlCLFlBQThDLGNBQStCO1FBQS9CLG1CQUFjLEdBQWQsY0FBYyxDQUFpQjtJQUM3RSxDQUFDO0lBRUQsU0FBUyxDQUFDLEdBQVksRUFBRSxPQUFnQztRQUNwRCxNQUFNLE9BQU8sR0FBRyxPQUFPLElBQUksT0FBTyxDQUFDLFFBQVE7WUFDdkMsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRTtZQUMzQixDQUFDLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7UUFFaEMsTUFBTSxJQUFJLEdBQVU7WUFDaEIsVUFBVSxFQUFFLEdBQUcsQ0FBQyxXQUFXO1lBQzNCLGFBQWEsRUFBRSxHQUFHLENBQUMsTUFBTTtTQUM1QixDQUFDO1FBRUYsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1FBQzdELE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxTQUFTLENBQUMsY0FBYyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRCxPQUFPLENBQUMsSUFBVyxFQUFFLEdBQVksRUFBRSxHQUFhO1FBQzVDLElBQUksQ0FBQyxHQUFHLENBQUMsYUFBYSxFQUFFLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN4QyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFFTyxrQkFBa0I7UUFDdEIsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsZUFBZSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDaEYsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDO0lBQ2hDLENBQUM7SUFFTyxrQkFBa0I7UUFDdEIsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsZUFBZSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsVUFBVSxFQUFFLENBQUMsa0JBQWtCLENBQUM7WUFDL0YsY0FBYyxFQUFFLEtBQUssQ0FBQyxFQUFFO2dCQUNwQixJQUFJLGNBQWMsQ0FBQyxLQUFLLENBQUMsRUFBRTtvQkFDdkIsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ2hELE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDO29CQUUvQyxJQUFJLE9BQU8sSUFBSSxPQUFPLEVBQUU7d0JBQ3BCLE9BQU87NEJBQ0gsZUFBZSxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU87NEJBQzlCLFVBQVUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPO3lCQUM1QixDQUFBO3FCQUNKO2lCQUNKO3FCQUNJLElBQUkscUJBQXFCLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksRUFBRTtvQkFDakQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7b0JBQzlCLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO29CQUNuQyxPQUFPO3dCQUNILGVBQWUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPO3dCQUM5QixVQUFVLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTztxQkFDNUIsQ0FBQTtpQkFDSjtnQkFFRCxPQUFPLFNBQVMsQ0FBQztZQUNyQixDQUFDO1NBQ0osQ0FBQyxDQUFDO1FBRUgsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDO0lBQ2hDLENBQUM7SUFFTyxpQkFBaUIsQ0FBQyxJQUFvQixFQUFFLEdBQVksRUFBRSxPQUFlLEVBQUUsVUFBa0I7UUFDN0YsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNsQyxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUU7WUFDckIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEtBQUssQ0FBQztTQUN6QjtJQUNMLENBQUM7Q0FDSixDQUFBO0FBbkVZLHFCQUFxQjtJQURqQyxTQUFTO0lBS08sV0FBQSxlQUFlLENBQUE7R0FKbkIscUJBQXFCLENBbUVqQztTQW5FWSxxQkFBcUIifQ==
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.TraceHeaders = void 0;
|
|
14
|
+
__exportStar(require("./middleware"), exports);
|
|
15
|
+
__exportStar(require("./module"), exports);
|
|
16
|
+
var service_1 = require("./service");
|
|
17
|
+
Object.defineProperty(exports, "TraceHeaders", { enumerable: true, get: function () { return service_1.TraceHeaders; } });
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7OztBQUFBLCtDQUE2QjtBQUM3QiwyQ0FBeUI7QUFDekIscUNBQWlFO0FBQWhDLHVHQUFBLFlBQVksT0FBQSJ9
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ISpan } from "@shrub/tracing";
|
|
2
|
+
import { RequestHandler } from "express";
|
|
3
|
+
import { IRequestTracingOptions } from "./service";
|
|
4
|
+
declare module "@shrub/express/dist/request-context" {
|
|
5
|
+
interface IRequestContext {
|
|
6
|
+
readonly span?: ISpan;
|
|
7
|
+
}
|
|
8
|
+
interface IRequestContextBuilder {
|
|
9
|
+
addSpan(span: ISpan): IRequestContextBuilder;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Request tracing middleware that will start a new span for a request. */
|
|
13
|
+
export declare const useRequestTracing: (options?: IRequestTracingOptions | undefined) => RequestHandler;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useRequestTracing = exports.addSpanRequestBuilder = void 0;
|
|
4
|
+
const service_1 = require("./service");
|
|
5
|
+
attachErrorListener();
|
|
6
|
+
/** @internal */
|
|
7
|
+
const addSpanRequestBuilder = (context, span) => ({ ...context, span });
|
|
8
|
+
exports.addSpanRequestBuilder = addSpanRequestBuilder;
|
|
9
|
+
/** Request tracing middleware that will start a new span for a request. */
|
|
10
|
+
const useRequestTracing = (options) => {
|
|
11
|
+
return (req, res, next) => {
|
|
12
|
+
const service = req.context.services.tryGet(service_1.IExpressTracingService);
|
|
13
|
+
if (!service) {
|
|
14
|
+
return next(new Error("express-tracing-service not registered, make sure the ExpressTracingModule is loaded when using request tracing."));
|
|
15
|
+
}
|
|
16
|
+
const span = service.startSpan(req, options);
|
|
17
|
+
req.contextBuilder.addSpan(span);
|
|
18
|
+
// if a request connection is 'keep-alive' the response will never finish;
|
|
19
|
+
// so instead, listen for the close event for when the connection has been closed
|
|
20
|
+
// https://nodejs.org/api/http.html#http_event_close_1
|
|
21
|
+
res.once("close", () => service.endSpan(span, req, res));
|
|
22
|
+
// https://nodejs.org/api/http.html#http_event_finish
|
|
23
|
+
res.once("finish", () => {
|
|
24
|
+
if (res.statusCode >= 300 && res.statusCode < 400) {
|
|
25
|
+
const location = res.getHeader("location");
|
|
26
|
+
if (location) {
|
|
27
|
+
span.tag("http.location", location);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
service.endSpan(span, req, res);
|
|
31
|
+
});
|
|
32
|
+
const ref = res.writeHead;
|
|
33
|
+
res.writeHead = function writeHead() {
|
|
34
|
+
// gets invoked just before writing headers
|
|
35
|
+
if (res.getHeader("connection") === "keep-alive") {
|
|
36
|
+
span.tag("http.keepAlive", true);
|
|
37
|
+
}
|
|
38
|
+
return ref.apply(res, arguments);
|
|
39
|
+
};
|
|
40
|
+
next();
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
exports.useRequestTracing = useRequestTracing;
|
|
44
|
+
/**
|
|
45
|
+
* Overrides the express internal handle_error function that gets invoked when an error has occurred.
|
|
46
|
+
* There are no global error events exposed by express and the only way to handle errors is via express middleware.
|
|
47
|
+
* By monkey patching the internal handle_error function we can 'listen' for errors and not have to worry
|
|
48
|
+
* about error handling middleware not passing the error down to next.
|
|
49
|
+
*
|
|
50
|
+
* Note: the node http request/response define an error event (e.g. req.on("error")) but these do not seem to get invoked.
|
|
51
|
+
*
|
|
52
|
+
* https://github.com/expressjs/express/blob/3ed5090ca91f6a387e66370d57ead94d886275e1/lib/router/layer.js#L62
|
|
53
|
+
*/
|
|
54
|
+
function attachErrorListener() {
|
|
55
|
+
const Layer = require("express/lib/router/layer");
|
|
56
|
+
const ref = Layer.prototype.handle_error;
|
|
57
|
+
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
|
|
58
|
+
req.context.bag.__error = error;
|
|
59
|
+
return ref.call(this, error, req, res, next);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUdBLHVDQUEyRTtBQVkzRSxtQkFBbUIsRUFBRSxDQUFDO0FBRXRCLGdCQUFnQjtBQUNULE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxPQUF3QixFQUFFLElBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLEdBQUcsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFBMUYsUUFBQSxxQkFBcUIseUJBQXFFO0FBRXZHLDJFQUEyRTtBQUNwRSxNQUFNLGlCQUFpQixHQUFHLENBQUMsT0FBZ0MsRUFBa0IsRUFBRTtJQUNsRixPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtRQUN0QixNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsZ0NBQXNCLENBQUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ1YsT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsa0hBQWtILENBQUMsQ0FBQyxDQUFDO1NBQzlJO1FBRUQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDN0MsR0FBRyxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFakMsMEVBQTBFO1FBQzFFLGlGQUFpRjtRQUVqRixzREFBc0Q7UUFDdEQsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFFekQscURBQXFEO1FBQ3JELEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtZQUNwQixJQUFJLEdBQUcsQ0FBQyxVQUFVLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxVQUFVLEdBQUcsR0FBRyxFQUFFO2dCQUMvQyxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUMzQyxJQUFJLFFBQVEsRUFBRTtvQkFDVixJQUFJLENBQUMsR0FBRyxDQUFDLGVBQWUsRUFBVSxRQUFRLENBQUMsQ0FBQztpQkFDL0M7YUFDSjtZQUVELE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNwQyxDQUFDLENBQUMsQ0FBQztRQUVILE1BQU0sR0FBRyxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDMUIsR0FBRyxDQUFDLFNBQVMsR0FBRyxTQUFTLFNBQVM7WUFDOUIsMkNBQTJDO1lBQzNDLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsS0FBSyxZQUFZLEVBQUU7Z0JBQzlDLElBQUksQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDcEM7WUFFRCxPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFNBQWdCLENBQUMsQ0FBQztRQUM1QyxDQUFDLENBQUM7UUFFRixJQUFJLEVBQUUsQ0FBQztJQUNYLENBQUMsQ0FBQztBQUNOLENBQUMsQ0FBQztBQXhDVyxRQUFBLGlCQUFpQixxQkF3QzVCO0FBRUY7Ozs7Ozs7OztHQVNHO0FBQ0gsU0FBUyxtQkFBbUI7SUFDeEIsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLDBCQUEwQixDQUFDLENBQUM7SUFDbEQsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUM7SUFDekMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxZQUFZLEdBQUcsU0FBUyxZQUFZLENBQUMsS0FBWSxFQUFFLEdBQVksRUFBRSxHQUFhLEVBQUUsSUFBa0I7UUFDOUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztRQUNoQyxPQUFPLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2pELENBQUMsQ0FBQTtBQUNMLENBQUMifQ==
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IModule, IModuleConfigurator, IServiceRegistration } from "@shrub/core";
|
|
2
|
+
import { ExpressModule } from "@shrub/express";
|
|
3
|
+
import { TracingModule } from "@shrub/tracing";
|
|
4
|
+
export declare class ExpressTracingModule implements IModule {
|
|
5
|
+
readonly name = "express-tracing";
|
|
6
|
+
readonly dependencies: (typeof ExpressModule | typeof TracingModule)[];
|
|
7
|
+
configureServices(registration: IServiceRegistration): void;
|
|
8
|
+
configure({ config }: IModuleConfigurator): void;
|
|
9
|
+
}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExpressTracingModule = void 0;
|
|
4
|
+
const express_1 = require("@shrub/express");
|
|
5
|
+
const tracing_1 = require("@shrub/tracing");
|
|
6
|
+
const middleware_1 = require("./middleware");
|
|
7
|
+
const service_1 = require("./service");
|
|
8
|
+
class ExpressTracingModule {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.name = "express-tracing";
|
|
11
|
+
this.dependencies = [
|
|
12
|
+
express_1.ExpressModule,
|
|
13
|
+
tracing_1.TracingModule
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
configureServices(registration) {
|
|
17
|
+
registration.register(service_1.IExpressTracingService, service_1.ExpressTracingService);
|
|
18
|
+
}
|
|
19
|
+
configure({ config }) {
|
|
20
|
+
config.get(express_1.IExpressConfiguration).useRequestBuilder("addSpan", middleware_1.addSpanRequestBuilder);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.ExpressTracingModule = ExpressTracingModule;
|
|
24
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL21vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSw0Q0FBc0U7QUFDdEUsNENBQStDO0FBQy9DLDZDQUFxRDtBQUNyRCx1Q0FBMEU7QUFFMUUsTUFBYSxvQkFBb0I7SUFBakM7UUFDYSxTQUFJLEdBQUcsaUJBQWlCLENBQUM7UUFDekIsaUJBQVksR0FBRztZQUNwQix1QkFBYTtZQUNiLHVCQUFhO1NBQ2hCLENBQUM7SUFTTixDQUFDO0lBUEcsaUJBQWlCLENBQUMsWUFBa0M7UUFDaEQsWUFBWSxDQUFDLFFBQVEsQ0FBQyxnQ0FBc0IsRUFBRSwrQkFBcUIsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7SUFFRCxTQUFTLENBQUMsRUFBRSxNQUFNLEVBQXVCO1FBQ3JDLE1BQU0sQ0FBQyxHQUFHLENBQUMsK0JBQXFCLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsa0NBQXFCLENBQUMsQ0FBQztJQUMxRixDQUFDO0NBQ0o7QUFkRCxvREFjQyJ9
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { ISpan, ITracingService } from "@shrub/tracing";
|
|
3
|
+
/** Internal service used by the middleware for starting and ending spans. */
|
|
4
|
+
export interface IExpressTracingService {
|
|
5
|
+
endSpan(span: ISpan, req: Request, res: Response): void;
|
|
6
|
+
startSpan(req: Request, options?: IRequestTracingOptions): ISpan;
|
|
7
|
+
}
|
|
8
|
+
export interface IRequestTracingOptions {
|
|
9
|
+
/**
|
|
10
|
+
* True if the requests are expected to be from an external client and will disable searching the
|
|
11
|
+
* request for the X-Trace-ID and X-Span-ID headers; the default is false.
|
|
12
|
+
*/
|
|
13
|
+
readonly external?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare enum TraceHeaders {
|
|
16
|
+
traceId = "X-Trace-ID",
|
|
17
|
+
spanId = "X-Span-ID"
|
|
18
|
+
}
|
|
19
|
+
export declare const IExpressTracingService: import("@shrub/core").IService<IExpressTracingService>;
|
|
20
|
+
export declare class ExpressTracingService implements IExpressTracingService {
|
|
21
|
+
private readonly tracingService;
|
|
22
|
+
private externalBuilder?;
|
|
23
|
+
private internalBuilder?;
|
|
24
|
+
constructor(tracingService: ITracingService);
|
|
25
|
+
startSpan(req: Request, options?: IRequestTracingOptions): ISpan;
|
|
26
|
+
endSpan(span: ISpan, req: Request, res: Response): void;
|
|
27
|
+
private getExternalBuilder;
|
|
28
|
+
private getInternalBuilder;
|
|
29
|
+
private addHeaderFieldTag;
|
|
30
|
+
}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
9
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ExpressTracingService = exports.IExpressTracingService = exports.TraceHeaders = void 0;
|
|
13
|
+
const core_1 = require("@shrub/core");
|
|
14
|
+
const tracing_1 = require("@shrub/tracing");
|
|
15
|
+
var TraceHeaders;
|
|
16
|
+
(function (TraceHeaders) {
|
|
17
|
+
TraceHeaders["traceId"] = "X-Trace-ID";
|
|
18
|
+
TraceHeaders["spanId"] = "X-Span-ID";
|
|
19
|
+
})(TraceHeaders = exports.TraceHeaders || (exports.TraceHeaders = {}));
|
|
20
|
+
exports.IExpressTracingService = core_1.createService("express-tracing-service");
|
|
21
|
+
function isRequestContextScope(scope) {
|
|
22
|
+
return scope.bag !== undefined && scope.services !== undefined;
|
|
23
|
+
}
|
|
24
|
+
function isRequestScope(scope) {
|
|
25
|
+
return scope.url !== undefined && scope.method !== undefined;
|
|
26
|
+
}
|
|
27
|
+
let ExpressTracingService = class ExpressTracingService {
|
|
28
|
+
constructor(tracingService) {
|
|
29
|
+
this.tracingService = tracingService;
|
|
30
|
+
}
|
|
31
|
+
startSpan(req, options) {
|
|
32
|
+
const builder = options && options.external
|
|
33
|
+
? this.getExternalBuilder()
|
|
34
|
+
: this.getInternalBuilder();
|
|
35
|
+
const tags = {
|
|
36
|
+
"http.url": req.originalUrl,
|
|
37
|
+
"http.method": req.method
|
|
38
|
+
};
|
|
39
|
+
this.addHeaderFieldTag(tags, req, "http.id", "X-Request-ID");
|
|
40
|
+
return builder.build(req).startSpan("http.request", tags);
|
|
41
|
+
}
|
|
42
|
+
endSpan(span, req, res) {
|
|
43
|
+
span.tag("http.status", res.statusCode);
|
|
44
|
+
span.done(req.context.bag.__error);
|
|
45
|
+
}
|
|
46
|
+
getExternalBuilder() {
|
|
47
|
+
this.externalBuilder = this.externalBuilder || this.tracingService.getBuilder();
|
|
48
|
+
return this.externalBuilder;
|
|
49
|
+
}
|
|
50
|
+
getInternalBuilder() {
|
|
51
|
+
this.internalBuilder = this.internalBuilder || this.tracingService.getBuilder().useContextProvider({
|
|
52
|
+
getSpanContext: scope => {
|
|
53
|
+
if (isRequestScope(scope)) {
|
|
54
|
+
const traceId = scope.get(TraceHeaders.traceId);
|
|
55
|
+
const scopeId = scope.get(TraceHeaders.spanId);
|
|
56
|
+
if (traceId && scopeId) {
|
|
57
|
+
return {
|
|
58
|
+
getParentSpanId: () => scopeId,
|
|
59
|
+
getTraceId: () => traceId
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (isRequestContextScope(scope) && scope.span) {
|
|
64
|
+
const scopeId = scope.span.id;
|
|
65
|
+
const traceId = scope.span.traceId;
|
|
66
|
+
return {
|
|
67
|
+
getParentSpanId: () => scopeId,
|
|
68
|
+
getTraceId: () => traceId
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return this.internalBuilder;
|
|
75
|
+
}
|
|
76
|
+
addHeaderFieldTag(tags, req, tagName, headerName) {
|
|
77
|
+
const value = req.get(headerName);
|
|
78
|
+
if (value !== undefined) {
|
|
79
|
+
tags[tagName] = value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
ExpressTracingService = __decorate([
|
|
84
|
+
core_1.Singleton,
|
|
85
|
+
__param(0, tracing_1.ITracingService)
|
|
86
|
+
], ExpressTracingService);
|
|
87
|
+
exports.ExpressTracingService = ExpressTracingService;
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUNBLHNDQUF1RDtBQUV2RCw0Q0FBK0U7QUFrQi9FLElBQVksWUFHWDtBQUhELFdBQVksWUFBWTtJQUNwQixzQ0FBc0IsQ0FBQTtJQUN0QixvQ0FBb0IsQ0FBQTtBQUN4QixDQUFDLEVBSFcsWUFBWSxHQUFaLG9CQUFZLEtBQVosb0JBQVksUUFHdkI7QUFFWSxRQUFBLHNCQUFzQixHQUFHLG9CQUFhLENBQXlCLHlCQUF5QixDQUFDLENBQUM7QUFFdkcsU0FBUyxxQkFBcUIsQ0FBQyxLQUFVO0lBQ3JDLE9BQXlCLEtBQU0sQ0FBQyxHQUFHLEtBQUssU0FBUyxJQUFzQixLQUFNLENBQUMsUUFBUSxLQUFLLFNBQVMsQ0FBQztBQUN6RyxDQUFDO0FBRUQsU0FBUyxjQUFjLENBQUMsS0FBVTtJQUM5QixPQUFpQixLQUFNLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBYyxLQUFNLENBQUMsTUFBTSxLQUFLLFNBQVMsQ0FBQztBQUN2RixDQUFDO0FBR0QsSUFBYSxxQkFBcUIsR0FBbEMsTUFBYSxxQkFBcUI7SUFJOUIsWUFBOEMsY0FBK0I7UUFBL0IsbUJBQWMsR0FBZCxjQUFjLENBQWlCO0lBQzdFLENBQUM7SUFFRCxTQUFTLENBQUMsR0FBWSxFQUFFLE9BQWdDO1FBQ3BELE1BQU0sT0FBTyxHQUFHLE9BQU8sSUFBSSxPQUFPLENBQUMsUUFBUTtZQUN2QyxDQUFDLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQzNCLENBQUMsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUVoQyxNQUFNLElBQUksR0FBVTtZQUNoQixVQUFVLEVBQUUsR0FBRyxDQUFDLFdBQVc7WUFDM0IsYUFBYSxFQUFFLEdBQUcsQ0FBQyxNQUFNO1NBQzVCLENBQUM7UUFFRixJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFDN0QsT0FBTyxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVELE9BQU8sQ0FBQyxJQUFXLEVBQUUsR0FBWSxFQUFFLEdBQWE7UUFDNUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVPLGtCQUFrQjtRQUN0QixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxlQUFlLElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoRixPQUFPLElBQUksQ0FBQyxlQUFlLENBQUM7SUFDaEMsQ0FBQztJQUVPLGtCQUFrQjtRQUN0QixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxlQUFlLElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQztZQUMvRixjQUFjLEVBQUUsS0FBSyxDQUFDLEVBQUU7Z0JBQ3BCLElBQUksY0FBYyxDQUFDLEtBQUssQ0FBQyxFQUFFO29CQUN2QixNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztvQkFDaEQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBRS9DLElBQUksT0FBTyxJQUFJLE9BQU8sRUFBRTt3QkFDcEIsT0FBTzs0QkFDSCxlQUFlLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTzs0QkFDOUIsVUFBVSxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU87eUJBQzVCLENBQUE7cUJBQ0o7aUJBQ0o7cUJBQ0ksSUFBSSxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFO29CQUNqRCxNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztvQkFDOUIsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7b0JBQ25DLE9BQU87d0JBQ0gsZUFBZSxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU87d0JBQzlCLFVBQVUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPO3FCQUM1QixDQUFBO2lCQUNKO2dCQUVELE9BQU8sU0FBUyxDQUFDO1lBQ3JCLENBQUM7U0FDSixDQUFDLENBQUM7UUFFSCxPQUFPLElBQUksQ0FBQyxlQUFlLENBQUM7SUFDaEMsQ0FBQztJQUVPLGlCQUFpQixDQUFDLElBQW9CLEVBQUUsR0FBWSxFQUFFLE9BQWUsRUFBRSxVQUFrQjtRQUM3RixNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2xDLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRTtZQUNyQixJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsS0FBSyxDQUFDO1NBQ3pCO0lBQ0wsQ0FBQztDQUNKLENBQUE7QUFuRVkscUJBQXFCO0lBRGpDLGdCQUFTO0lBS08sV0FBQSx5QkFBZSxDQUFBO0dBSm5CLHFCQUFxQixDQW1FakM7QUFuRVksc0RBQXFCIn0=
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shrub/express-tracing",
|
|
3
|
+
"description": "A module that provides middleware for tracing express requests.",
|
|
4
|
+
"version": "0.5.26",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/jjvainav/shrub.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"typings": "dist/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=10.18.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b .",
|
|
23
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
24
|
+
"clean": "rimraf ./dist && rimraf tsconfig.tsbuildinfo",
|
|
25
|
+
"test": "jest"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@sprig/event-emitter": "^0.1.69",
|
|
29
|
+
"@types/express": "4.17.9"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@shrub/core": "0.5.26",
|
|
33
|
+
"@shrub/express": "0.5.26",
|
|
34
|
+
"@shrub/logging": "0.5.26",
|
|
35
|
+
"@shrub/tracing": "0.5.26",
|
|
36
|
+
"express": "^4.17.1"
|
|
37
|
+
},
|
|
38
|
+
"gitHead": "bd8e68fee891bb035f6506401851cf993c1c614e"
|
|
39
|
+
}
|