@astroscope/opentelemetry 0.1.0 → 0.1.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/{chunk-BQFWPPEO.js → chunk-45TY3U7U.js} +2 -8
- package/dist/{chunk-DPYEL3WF.js → chunk-JU6FJKLT.js} +1 -2
- package/dist/{chunk-7RBJ7XB3.js → chunk-PCCKGEEG.js} +60 -80
- package/dist/fetch-Z2DBQPE2.js +7 -0
- package/dist/index.js +15 -38
- package/dist/middleware-entrypoint.js +3 -5
- package/package.json +3 -3
- package/dist/chunk-63MOS4WA.js +0 -146
- package/dist/chunk-CEPTXEJV.js +0 -89
- package/dist/chunk-FEC4ETRL.js +0 -70
- package/dist/chunk-KLGWLEAU.js +0 -123
- package/dist/chunk-OFMO3ZKX.js +0 -124
- package/dist/chunk-QIWOBUML.js +0 -89
- package/dist/chunk-QTSNLOSC.js +0 -70
- package/dist/chunk-UPNRPRAW.js +0 -72
- package/dist/chunk-VHXZF2FP.js +0 -124
- package/dist/chunk-WTUM3JRA.js +0 -155
- package/dist/fetch-55MNQVLN.js +0 -7
- package/dist/fetch-FUKHXS77.js +0 -6
- package/dist/fetch-JGQFPQSL.js +0 -6
- package/dist/fetch-RERXGIJA.js +0 -7
- package/dist/fetch-SVGRNFLY.js +0 -7
package/dist/chunk-KLGWLEAU.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// src/middleware.ts
|
|
2
|
-
import {
|
|
3
|
-
SpanKind,
|
|
4
|
-
SpanStatusCode,
|
|
5
|
-
context,
|
|
6
|
-
propagation,
|
|
7
|
-
trace
|
|
8
|
-
} from "@opentelemetry/api";
|
|
9
|
-
import { RPCType, setRPCMetadata } from "@opentelemetry/core";
|
|
10
|
-
var LIB_NAME = "@astroscope/opentelemetry";
|
|
11
|
-
var ACTIONS_PREFIX = "/_actions/";
|
|
12
|
-
function matchesPattern(path, pattern) {
|
|
13
|
-
if ("pattern" in pattern) {
|
|
14
|
-
return pattern.pattern.test(path);
|
|
15
|
-
}
|
|
16
|
-
if ("prefix" in pattern) {
|
|
17
|
-
return path.startsWith(pattern.prefix);
|
|
18
|
-
}
|
|
19
|
-
return path === pattern.exact;
|
|
20
|
-
}
|
|
21
|
-
function shouldExclude(ctx, exclude) {
|
|
22
|
-
if (!exclude) return false;
|
|
23
|
-
if (typeof exclude === "function") {
|
|
24
|
-
return exclude(ctx);
|
|
25
|
-
}
|
|
26
|
-
const path = ctx.url.pathname;
|
|
27
|
-
return exclude.some((pattern) => matchesPattern(path, pattern));
|
|
28
|
-
}
|
|
29
|
-
function getClientIp(request) {
|
|
30
|
-
return request.headers.get("x-forwarded-for")?.split(",")[0].trim() ?? request.headers.get("x-real-ip") ?? request.headers.get("cf-connecting-ip") ?? // Cloudflare
|
|
31
|
-
void 0;
|
|
32
|
-
}
|
|
33
|
-
function createOpenTelemetryMiddleware(options = {}) {
|
|
34
|
-
const tracer = trace.getTracer(LIB_NAME);
|
|
35
|
-
return async (ctx, next) => {
|
|
36
|
-
if (shouldExclude(ctx, options.exclude)) {
|
|
37
|
-
return next();
|
|
38
|
-
}
|
|
39
|
-
const { request, url } = ctx;
|
|
40
|
-
const input = {
|
|
41
|
-
traceparent: request.headers.get("traceparent"),
|
|
42
|
-
tracestate: request.headers.get("tracestate")
|
|
43
|
-
};
|
|
44
|
-
const parentContext = propagation.extract(context.active(), input);
|
|
45
|
-
const clientIp = getClientIp(request);
|
|
46
|
-
const contentLength = request.headers.get("content-length");
|
|
47
|
-
const spanOptions = {
|
|
48
|
-
attributes: {
|
|
49
|
-
"http.request.method": request.method,
|
|
50
|
-
"url.full": request.url,
|
|
51
|
-
"url.path": url.pathname,
|
|
52
|
-
"url.query": url.search.slice(1),
|
|
53
|
-
// Remove leading "?"
|
|
54
|
-
"url.scheme": url.protocol.replace(":", ""),
|
|
55
|
-
"server.address": url.hostname,
|
|
56
|
-
"server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80,
|
|
57
|
-
"user_agent.original": request.headers.get("user-agent") ?? "",
|
|
58
|
-
...contentLength && { "http.request.body.size": parseInt(contentLength) },
|
|
59
|
-
...clientIp && { "client.address": clientIp }
|
|
60
|
-
},
|
|
61
|
-
kind: SpanKind.SERVER
|
|
62
|
-
};
|
|
63
|
-
const isAction = url.pathname.startsWith(ACTIONS_PREFIX);
|
|
64
|
-
const spanName = isAction ? `ACTION ${url.pathname.slice(ACTIONS_PREFIX.length)}` : `${request.method} ${url.pathname}`;
|
|
65
|
-
const span = tracer.startSpan(spanName, spanOptions, parentContext);
|
|
66
|
-
const spanContext = trace.setSpan(parentContext, span);
|
|
67
|
-
const rpcMetadata = { type: RPCType.HTTP, span };
|
|
68
|
-
return context.with(
|
|
69
|
-
setRPCMetadata(spanContext, rpcMetadata),
|
|
70
|
-
async () => {
|
|
71
|
-
const finalize = (status, responseSize) => {
|
|
72
|
-
span.setAttribute("http.response.status_code", status);
|
|
73
|
-
span.setAttribute("http.response.body.size", responseSize);
|
|
74
|
-
if (status >= 400) {
|
|
75
|
-
span.setStatus({
|
|
76
|
-
code: SpanStatusCode.ERROR,
|
|
77
|
-
message: `HTTP ${status}`
|
|
78
|
-
});
|
|
79
|
-
} else {
|
|
80
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
81
|
-
}
|
|
82
|
-
span.end();
|
|
83
|
-
};
|
|
84
|
-
try {
|
|
85
|
-
const response = await next();
|
|
86
|
-
if (!response.body) {
|
|
87
|
-
finalize(response.status, 0);
|
|
88
|
-
return response;
|
|
89
|
-
}
|
|
90
|
-
const [measureStream, clientStream] = response.body.tee();
|
|
91
|
-
let responseSize = 0;
|
|
92
|
-
(async () => {
|
|
93
|
-
const reader = measureStream.getReader();
|
|
94
|
-
try {
|
|
95
|
-
while (true) {
|
|
96
|
-
const { done, value } = await reader.read();
|
|
97
|
-
if (done) break;
|
|
98
|
-
responseSize += value.length;
|
|
99
|
-
}
|
|
100
|
-
} finally {
|
|
101
|
-
finalize(response.status, responseSize);
|
|
102
|
-
}
|
|
103
|
-
})();
|
|
104
|
-
return new Response(clientStream, {
|
|
105
|
-
status: response.status,
|
|
106
|
-
headers: response.headers
|
|
107
|
-
});
|
|
108
|
-
} catch (e) {
|
|
109
|
-
span.setStatus({
|
|
110
|
-
code: SpanStatusCode.ERROR,
|
|
111
|
-
message: e instanceof Error ? e.message : "Unknown error"
|
|
112
|
-
});
|
|
113
|
-
span.end();
|
|
114
|
-
throw e;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
);
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export {
|
|
122
|
-
createOpenTelemetryMiddleware
|
|
123
|
-
};
|
package/dist/chunk-OFMO3ZKX.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
// src/middleware.ts
|
|
2
|
-
import {
|
|
3
|
-
SpanKind,
|
|
4
|
-
SpanStatusCode,
|
|
5
|
-
context,
|
|
6
|
-
propagation,
|
|
7
|
-
trace
|
|
8
|
-
} from "@opentelemetry/api";
|
|
9
|
-
import { RPCType, setRPCMetadata } from "@opentelemetry/core";
|
|
10
|
-
var LIB_NAME = "@astroscope/opentelemetry";
|
|
11
|
-
function matchesPattern(path, pattern) {
|
|
12
|
-
if ("pattern" in pattern) {
|
|
13
|
-
return pattern.pattern.test(path);
|
|
14
|
-
}
|
|
15
|
-
if ("prefix" in pattern) {
|
|
16
|
-
return path.startsWith(pattern.prefix);
|
|
17
|
-
}
|
|
18
|
-
return path === pattern.exact;
|
|
19
|
-
}
|
|
20
|
-
function shouldExclude(ctx, exclude) {
|
|
21
|
-
if (!exclude) return false;
|
|
22
|
-
if (typeof exclude === "function") {
|
|
23
|
-
return exclude(ctx);
|
|
24
|
-
}
|
|
25
|
-
const path = ctx.url.pathname;
|
|
26
|
-
return exclude.some((pattern) => matchesPattern(path, pattern));
|
|
27
|
-
}
|
|
28
|
-
function getClientIp(request) {
|
|
29
|
-
return request.headers.get("x-forwarded-for")?.split(",")[0].trim() ?? request.headers.get("x-real-ip") ?? request.headers.get("cf-connecting-ip") ?? // Cloudflare
|
|
30
|
-
void 0;
|
|
31
|
-
}
|
|
32
|
-
function createOpenTelemetryMiddleware(options = {}) {
|
|
33
|
-
const tracer = trace.getTracer(LIB_NAME);
|
|
34
|
-
return async (ctx, next) => {
|
|
35
|
-
if (shouldExclude(ctx, options.exclude)) {
|
|
36
|
-
return next();
|
|
37
|
-
}
|
|
38
|
-
const { request, url } = ctx;
|
|
39
|
-
const input = {
|
|
40
|
-
traceparent: request.headers.get("traceparent"),
|
|
41
|
-
tracestate: request.headers.get("tracestate")
|
|
42
|
-
};
|
|
43
|
-
const parentContext = propagation.extract(context.active(), input);
|
|
44
|
-
const clientIp = getClientIp(request);
|
|
45
|
-
const contentLength = request.headers.get("content-length");
|
|
46
|
-
const spanOptions = {
|
|
47
|
-
attributes: {
|
|
48
|
-
"http.request.method": request.method,
|
|
49
|
-
"url.full": request.url,
|
|
50
|
-
"url.path": url.pathname,
|
|
51
|
-
"url.query": url.search.slice(1),
|
|
52
|
-
// Remove leading "?"
|
|
53
|
-
"url.scheme": url.protocol.replace(":", ""),
|
|
54
|
-
"server.address": url.hostname,
|
|
55
|
-
"server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80,
|
|
56
|
-
"user_agent.original": request.headers.get("user-agent") ?? "",
|
|
57
|
-
...contentLength && { "http.request.body.size": parseInt(contentLength) },
|
|
58
|
-
...clientIp && { "client.address": clientIp }
|
|
59
|
-
},
|
|
60
|
-
kind: SpanKind.SERVER
|
|
61
|
-
};
|
|
62
|
-
const span = tracer.startSpan(
|
|
63
|
-
`${request.method} ${url.pathname}`,
|
|
64
|
-
spanOptions,
|
|
65
|
-
parentContext
|
|
66
|
-
);
|
|
67
|
-
const spanContext = trace.setSpan(parentContext, span);
|
|
68
|
-
const rpcMetadata = { type: RPCType.HTTP, span };
|
|
69
|
-
return context.with(
|
|
70
|
-
setRPCMetadata(spanContext, rpcMetadata),
|
|
71
|
-
async () => {
|
|
72
|
-
const finalize = (status, responseSize) => {
|
|
73
|
-
span.setAttribute("http.response.status_code", status);
|
|
74
|
-
span.setAttribute("http.response.body.size", responseSize);
|
|
75
|
-
if (status >= 400) {
|
|
76
|
-
span.setStatus({
|
|
77
|
-
code: SpanStatusCode.ERROR,
|
|
78
|
-
message: `HTTP ${status}`
|
|
79
|
-
});
|
|
80
|
-
} else {
|
|
81
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
82
|
-
}
|
|
83
|
-
span.end();
|
|
84
|
-
};
|
|
85
|
-
try {
|
|
86
|
-
const response = await next();
|
|
87
|
-
if (!response.body) {
|
|
88
|
-
finalize(response.status, 0);
|
|
89
|
-
return response;
|
|
90
|
-
}
|
|
91
|
-
const [measureStream, clientStream] = response.body.tee();
|
|
92
|
-
let responseSize = 0;
|
|
93
|
-
(async () => {
|
|
94
|
-
const reader = measureStream.getReader();
|
|
95
|
-
try {
|
|
96
|
-
while (true) {
|
|
97
|
-
const { done, value } = await reader.read();
|
|
98
|
-
if (done) break;
|
|
99
|
-
responseSize += value.length;
|
|
100
|
-
}
|
|
101
|
-
} finally {
|
|
102
|
-
finalize(response.status, responseSize);
|
|
103
|
-
}
|
|
104
|
-
})();
|
|
105
|
-
return new Response(clientStream, {
|
|
106
|
-
status: response.status,
|
|
107
|
-
headers: response.headers
|
|
108
|
-
});
|
|
109
|
-
} catch (e) {
|
|
110
|
-
span.setStatus({
|
|
111
|
-
code: SpanStatusCode.ERROR,
|
|
112
|
-
message: e instanceof Error ? e.message : "Unknown error"
|
|
113
|
-
});
|
|
114
|
-
span.end();
|
|
115
|
-
throw e;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export {
|
|
123
|
-
createOpenTelemetryMiddleware
|
|
124
|
-
};
|
package/dist/chunk-QIWOBUML.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
recordFetchRequestDuration
|
|
3
|
-
} from "./chunk-UPNRPRAW.js";
|
|
4
|
-
|
|
5
|
-
// src/fetch.ts
|
|
6
|
-
import {
|
|
7
|
-
SpanKind,
|
|
8
|
-
SpanStatusCode,
|
|
9
|
-
context,
|
|
10
|
-
propagation,
|
|
11
|
-
trace
|
|
12
|
-
} from "@opentelemetry/api";
|
|
13
|
-
var LIB_NAME = "@astroscope/opentelemetry";
|
|
14
|
-
var metricsEnabled = false;
|
|
15
|
-
function instrumentFetch(options) {
|
|
16
|
-
metricsEnabled = options?.metrics ?? false;
|
|
17
|
-
const originalFetch = globalThis.fetch;
|
|
18
|
-
async function instrumentedFetch(input, init) {
|
|
19
|
-
const tracer = trace.getTracer(LIB_NAME);
|
|
20
|
-
const activeContext = context.active();
|
|
21
|
-
const request = new Request(input, init);
|
|
22
|
-
const url = new URL(request.url);
|
|
23
|
-
const span = tracer.startSpan(
|
|
24
|
-
`FETCH ${request.method}`,
|
|
25
|
-
{
|
|
26
|
-
kind: SpanKind.CLIENT,
|
|
27
|
-
attributes: {
|
|
28
|
-
"http.request.method": request.method,
|
|
29
|
-
"url.full": request.url,
|
|
30
|
-
"url.path": url.pathname,
|
|
31
|
-
"url.scheme": url.protocol.replace(":", ""),
|
|
32
|
-
"server.address": url.hostname,
|
|
33
|
-
"server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
activeContext
|
|
37
|
-
);
|
|
38
|
-
const headers = new Headers(request.headers);
|
|
39
|
-
const carrier = {};
|
|
40
|
-
propagation.inject(trace.setSpan(activeContext, span), carrier);
|
|
41
|
-
for (const [key, value] of Object.entries(carrier)) {
|
|
42
|
-
headers.set(key, value);
|
|
43
|
-
}
|
|
44
|
-
const startTime = metricsEnabled ? performance.now() : 0;
|
|
45
|
-
try {
|
|
46
|
-
const response = await originalFetch(request.url, {
|
|
47
|
-
...init,
|
|
48
|
-
method: request.method,
|
|
49
|
-
headers,
|
|
50
|
-
body: request.body
|
|
51
|
-
});
|
|
52
|
-
span.setAttribute("http.response.status_code", response.status);
|
|
53
|
-
if (response.status >= 400) {
|
|
54
|
-
span.setStatus({
|
|
55
|
-
code: SpanStatusCode.ERROR,
|
|
56
|
-
message: `HTTP ${response.status}`
|
|
57
|
-
});
|
|
58
|
-
} else {
|
|
59
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
60
|
-
}
|
|
61
|
-
span.end();
|
|
62
|
-
if (metricsEnabled) {
|
|
63
|
-
recordFetchRequestDuration(
|
|
64
|
-
{ method: request.method, host: url.hostname, status: response.status },
|
|
65
|
-
performance.now() - startTime
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
return response;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
span.setStatus({
|
|
71
|
-
code: SpanStatusCode.ERROR,
|
|
72
|
-
message: error instanceof Error ? error.message : "Unknown error"
|
|
73
|
-
});
|
|
74
|
-
span.end();
|
|
75
|
-
if (metricsEnabled) {
|
|
76
|
-
recordFetchRequestDuration(
|
|
77
|
-
{ method: request.method, host: url.hostname, status: 0 },
|
|
78
|
-
performance.now() - startTime
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
globalThis.fetch = Object.assign(instrumentedFetch, originalFetch);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export {
|
|
88
|
-
instrumentFetch
|
|
89
|
-
};
|
package/dist/chunk-QTSNLOSC.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// src/fetch.ts
|
|
2
|
-
import {
|
|
3
|
-
SpanKind,
|
|
4
|
-
SpanStatusCode,
|
|
5
|
-
context,
|
|
6
|
-
propagation,
|
|
7
|
-
trace
|
|
8
|
-
} from "@opentelemetry/api";
|
|
9
|
-
var LIB_NAME = "@astroscope/opentelemetry";
|
|
10
|
-
function instrumentFetch() {
|
|
11
|
-
const originalFetch = globalThis.fetch;
|
|
12
|
-
async function instrumentedFetch(input, init) {
|
|
13
|
-
const tracer = trace.getTracer(LIB_NAME);
|
|
14
|
-
const activeContext = context.active();
|
|
15
|
-
const request = new Request(input, init);
|
|
16
|
-
const url = new URL(request.url);
|
|
17
|
-
const span = tracer.startSpan(
|
|
18
|
-
`HTTP ${request.method}`,
|
|
19
|
-
{
|
|
20
|
-
kind: SpanKind.CLIENT,
|
|
21
|
-
attributes: {
|
|
22
|
-
"http.request.method": request.method,
|
|
23
|
-
"url.full": request.url,
|
|
24
|
-
"url.path": url.pathname,
|
|
25
|
-
"url.scheme": url.protocol.replace(":", ""),
|
|
26
|
-
"server.address": url.hostname,
|
|
27
|
-
"server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
activeContext
|
|
31
|
-
);
|
|
32
|
-
const headers = new Headers(request.headers);
|
|
33
|
-
const carrier = {};
|
|
34
|
-
propagation.inject(trace.setSpan(activeContext, span), carrier);
|
|
35
|
-
for (const [key, value] of Object.entries(carrier)) {
|
|
36
|
-
headers.set(key, value);
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
const response = await originalFetch(request.url, {
|
|
40
|
-
...init,
|
|
41
|
-
method: request.method,
|
|
42
|
-
headers,
|
|
43
|
-
body: request.body
|
|
44
|
-
});
|
|
45
|
-
span.setAttribute("http.response.status_code", response.status);
|
|
46
|
-
if (response.status >= 400) {
|
|
47
|
-
span.setStatus({
|
|
48
|
-
code: SpanStatusCode.ERROR,
|
|
49
|
-
message: `HTTP ${response.status}`
|
|
50
|
-
});
|
|
51
|
-
} else {
|
|
52
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
53
|
-
}
|
|
54
|
-
span.end();
|
|
55
|
-
return response;
|
|
56
|
-
} catch (error) {
|
|
57
|
-
span.setStatus({
|
|
58
|
-
code: SpanStatusCode.ERROR,
|
|
59
|
-
message: error instanceof Error ? error.message : "Unknown error"
|
|
60
|
-
});
|
|
61
|
-
span.end();
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
globalThis.fetch = Object.assign(instrumentedFetch, originalFetch);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export {
|
|
69
|
-
instrumentFetch
|
|
70
|
-
};
|
package/dist/chunk-UPNRPRAW.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// src/metrics.ts
|
|
2
|
-
import { metrics, ValueType } from "@opentelemetry/api";
|
|
3
|
-
var LIB_NAME = "@astroscope/opentelemetry";
|
|
4
|
-
var httpRequestDuration = null;
|
|
5
|
-
var httpActiveRequests = null;
|
|
6
|
-
var fetchRequestDuration = null;
|
|
7
|
-
function getHttpRequestDuration() {
|
|
8
|
-
if (!httpRequestDuration) {
|
|
9
|
-
const meter = metrics.getMeter(LIB_NAME);
|
|
10
|
-
httpRequestDuration = meter.createHistogram("http.server.request.duration", {
|
|
11
|
-
description: "Duration of HTTP server requests",
|
|
12
|
-
unit: "s",
|
|
13
|
-
valueType: ValueType.DOUBLE
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return httpRequestDuration;
|
|
17
|
-
}
|
|
18
|
-
function getHttpActiveRequests() {
|
|
19
|
-
if (!httpActiveRequests) {
|
|
20
|
-
const meter = metrics.getMeter(LIB_NAME);
|
|
21
|
-
httpActiveRequests = meter.createUpDownCounter("http.server.active_requests", {
|
|
22
|
-
description: "Number of active HTTP server requests",
|
|
23
|
-
unit: "{request}",
|
|
24
|
-
valueType: ValueType.INT
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
return httpActiveRequests;
|
|
28
|
-
}
|
|
29
|
-
function getFetchRequestDuration() {
|
|
30
|
-
if (!fetchRequestDuration) {
|
|
31
|
-
const meter = metrics.getMeter(LIB_NAME);
|
|
32
|
-
fetchRequestDuration = meter.createHistogram("http.client.request.duration", {
|
|
33
|
-
description: "Duration of HTTP client requests (fetch)",
|
|
34
|
-
unit: "s",
|
|
35
|
-
valueType: ValueType.DOUBLE
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return fetchRequestDuration;
|
|
39
|
-
}
|
|
40
|
-
function recordHttpRequestStart(attributes) {
|
|
41
|
-
const startTime = performance.now();
|
|
42
|
-
getHttpActiveRequests().add(1, {
|
|
43
|
-
"http.request.method": attributes.method,
|
|
44
|
-
"http.route": attributes.route
|
|
45
|
-
});
|
|
46
|
-
return () => {
|
|
47
|
-
getHttpActiveRequests().add(-1, {
|
|
48
|
-
"http.request.method": attributes.method,
|
|
49
|
-
"http.route": attributes.route
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function recordHttpRequestDuration(attributes, durationMs) {
|
|
54
|
-
getHttpRequestDuration().record(durationMs / 1e3, {
|
|
55
|
-
"http.request.method": attributes.method,
|
|
56
|
-
"http.route": attributes.route,
|
|
57
|
-
"http.response.status_code": attributes.status
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function recordFetchRequestDuration(attributes, durationMs) {
|
|
61
|
-
getFetchRequestDuration().record(durationMs / 1e3, {
|
|
62
|
-
"http.request.method": attributes.method,
|
|
63
|
-
"server.address": attributes.host,
|
|
64
|
-
"http.response.status_code": attributes.status
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export {
|
|
69
|
-
recordHttpRequestStart,
|
|
70
|
-
recordHttpRequestDuration,
|
|
71
|
-
recordFetchRequestDuration
|
|
72
|
-
};
|
package/dist/chunk-VHXZF2FP.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
// src/middleware.ts
|
|
2
|
-
import {
|
|
3
|
-
SpanKind,
|
|
4
|
-
SpanStatusCode,
|
|
5
|
-
context,
|
|
6
|
-
propagation,
|
|
7
|
-
trace
|
|
8
|
-
} from "@opentelemetry/api";
|
|
9
|
-
import { RPCType, setRPCMetadata } from "@opentelemetry/core";
|
|
10
|
-
var LIB_NAME = "@astroscope/opentelemetry";
|
|
11
|
-
var ACTIONS_PREFIX = "/_actions/";
|
|
12
|
-
function matchesPattern(path, pattern) {
|
|
13
|
-
if ("pattern" in pattern) {
|
|
14
|
-
return pattern.pattern.test(path);
|
|
15
|
-
}
|
|
16
|
-
if ("prefix" in pattern) {
|
|
17
|
-
return path.startsWith(pattern.prefix);
|
|
18
|
-
}
|
|
19
|
-
return path === pattern.exact;
|
|
20
|
-
}
|
|
21
|
-
function shouldExclude(ctx, exclude) {
|
|
22
|
-
if (!exclude) return false;
|
|
23
|
-
if (typeof exclude === "function") {
|
|
24
|
-
return exclude(ctx);
|
|
25
|
-
}
|
|
26
|
-
const path = ctx.url.pathname;
|
|
27
|
-
return exclude.some((pattern) => matchesPattern(path, pattern));
|
|
28
|
-
}
|
|
29
|
-
function getClientIp(request) {
|
|
30
|
-
return request.headers.get("x-forwarded-for")?.split(",")[0].trim() ?? request.headers.get("x-real-ip") ?? request.headers.get("cf-connecting-ip") ?? // Cloudflare
|
|
31
|
-
void 0;
|
|
32
|
-
}
|
|
33
|
-
function createOpenTelemetryMiddleware(options = {}) {
|
|
34
|
-
const tracer = trace.getTracer(LIB_NAME);
|
|
35
|
-
return async (ctx, next) => {
|
|
36
|
-
if (shouldExclude(ctx, options.exclude)) {
|
|
37
|
-
return next();
|
|
38
|
-
}
|
|
39
|
-
const { request, url } = ctx;
|
|
40
|
-
const input = {
|
|
41
|
-
traceparent: request.headers.get("traceparent"),
|
|
42
|
-
tracestate: request.headers.get("tracestate")
|
|
43
|
-
};
|
|
44
|
-
const parentContext = propagation.extract(context.active(), input);
|
|
45
|
-
const clientIp = getClientIp(request);
|
|
46
|
-
const contentLength = request.headers.get("content-length");
|
|
47
|
-
const spanOptions = {
|
|
48
|
-
attributes: {
|
|
49
|
-
"http.request.method": request.method,
|
|
50
|
-
"url.full": request.url,
|
|
51
|
-
"url.path": url.pathname,
|
|
52
|
-
"url.query": url.search.slice(1),
|
|
53
|
-
// Remove leading "?"
|
|
54
|
-
"url.scheme": url.protocol.replace(":", ""),
|
|
55
|
-
"server.address": url.hostname,
|
|
56
|
-
"server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80,
|
|
57
|
-
"user_agent.original": request.headers.get("user-agent") ?? "",
|
|
58
|
-
...contentLength && { "http.request.body.size": parseInt(contentLength) },
|
|
59
|
-
...clientIp && { "client.address": clientIp }
|
|
60
|
-
},
|
|
61
|
-
kind: SpanKind.SERVER
|
|
62
|
-
};
|
|
63
|
-
const isAction = url.pathname.startsWith(ACTIONS_PREFIX);
|
|
64
|
-
const actionName = url.pathname.slice(ACTIONS_PREFIX.length).replace(/\/$/, "");
|
|
65
|
-
const spanName = isAction ? `ACTION ${actionName}` : `${request.method} ${url.pathname}`;
|
|
66
|
-
const span = tracer.startSpan(spanName, spanOptions, parentContext);
|
|
67
|
-
const spanContext = trace.setSpan(parentContext, span);
|
|
68
|
-
const rpcMetadata = { type: RPCType.HTTP, span };
|
|
69
|
-
return context.with(
|
|
70
|
-
setRPCMetadata(spanContext, rpcMetadata),
|
|
71
|
-
async () => {
|
|
72
|
-
const finalize = (status, responseSize) => {
|
|
73
|
-
span.setAttribute("http.response.status_code", status);
|
|
74
|
-
span.setAttribute("http.response.body.size", responseSize);
|
|
75
|
-
if (status >= 400) {
|
|
76
|
-
span.setStatus({
|
|
77
|
-
code: SpanStatusCode.ERROR,
|
|
78
|
-
message: `HTTP ${status}`
|
|
79
|
-
});
|
|
80
|
-
} else {
|
|
81
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
82
|
-
}
|
|
83
|
-
span.end();
|
|
84
|
-
};
|
|
85
|
-
try {
|
|
86
|
-
const response = await next();
|
|
87
|
-
if (!response.body) {
|
|
88
|
-
finalize(response.status, 0);
|
|
89
|
-
return response;
|
|
90
|
-
}
|
|
91
|
-
const [measureStream, clientStream] = response.body.tee();
|
|
92
|
-
let responseSize = 0;
|
|
93
|
-
(async () => {
|
|
94
|
-
const reader = measureStream.getReader();
|
|
95
|
-
try {
|
|
96
|
-
while (true) {
|
|
97
|
-
const { done, value } = await reader.read();
|
|
98
|
-
if (done) break;
|
|
99
|
-
responseSize += value.length;
|
|
100
|
-
}
|
|
101
|
-
} finally {
|
|
102
|
-
finalize(response.status, responseSize);
|
|
103
|
-
}
|
|
104
|
-
})();
|
|
105
|
-
return new Response(clientStream, {
|
|
106
|
-
status: response.status,
|
|
107
|
-
headers: response.headers
|
|
108
|
-
});
|
|
109
|
-
} catch (e) {
|
|
110
|
-
span.setStatus({
|
|
111
|
-
code: SpanStatusCode.ERROR,
|
|
112
|
-
message: e instanceof Error ? e.message : "Unknown error"
|
|
113
|
-
});
|
|
114
|
-
span.end();
|
|
115
|
-
throw e;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export {
|
|
123
|
-
createOpenTelemetryMiddleware
|
|
124
|
-
};
|