@astroscope/opentelemetry 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @astroscope/opentelemetry
2
2
 
3
- OpenTelemetry support for Astro SSR.
3
+ > **Note:** This package is in active development. APIs may change between versions.
4
+
5
+ OpenTelemetry for Astro — tracing, metrics, and component instrumentation that works in dev mode. No monkey-patching required.
4
6
 
5
7
  ## Examples
6
8
 
@@ -5,20 +5,31 @@ import {
5
5
  // src/fetch.ts
6
6
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
7
7
  var LIB_NAME = "@astroscope/opentelemetry";
8
+ var INSTRUMENTED = /* @__PURE__ */ Symbol.for("@astroscope/opentelemetry/fetch");
8
9
  function instrumentFetch() {
10
+ if (globalThis.fetch[INSTRUMENTED]) {
11
+ return;
12
+ }
9
13
  const originalFetch = globalThis.fetch;
10
14
  async function instrumentedFetch(input, init) {
11
15
  const tracer = trace.getTracer(LIB_NAME);
12
16
  const activeContext = context.active();
13
- const request = new Request(input, init);
14
- const url = new URL(request.url);
17
+ let url;
18
+ let method;
19
+ if (input instanceof Request) {
20
+ url = new URL(input.url);
21
+ method = input.method;
22
+ } else {
23
+ url = new URL(input.toString(), globalThis.location?.href);
24
+ method = init?.method ?? "GET";
25
+ }
15
26
  const span = tracer.startSpan(
16
- `FETCH ${request.method}`,
27
+ `FETCH ${method}`,
17
28
  {
18
29
  kind: SpanKind.CLIENT,
19
30
  attributes: {
20
- "http.request.method": request.method,
21
- "url.full": request.url,
31
+ "http.request.method": method,
32
+ "url.full": url.href,
22
33
  "url.path": url.pathname,
23
34
  "url.scheme": url.protocol.replace(":", ""),
24
35
  "server.address": url.hostname,
@@ -27,20 +38,23 @@ function instrumentFetch() {
27
38
  },
28
39
  activeContext
29
40
  );
30
- const headers = new Headers(request.headers);
31
41
  const carrier = {};
32
42
  propagation.inject(trace.setSpan(activeContext, span), carrier);
43
+ const existingHeaders = init?.headers ?? (input instanceof Request ? input.headers : void 0);
44
+ const headers = new Headers(existingHeaders);
33
45
  for (const [key, value] of Object.entries(carrier)) {
34
46
  headers.set(key, value);
35
47
  }
48
+ const hasBody = init?.body !== void 0 || input instanceof Request && input.body !== null;
49
+ const newInit = {
50
+ ...init,
51
+ headers,
52
+ // required in node.js 18.13+
53
+ ...hasBody && { duplex: "half" }
54
+ };
36
55
  const startTime = performance.now();
37
56
  try {
38
- const response = await originalFetch(request.url, {
39
- ...init,
40
- method: request.method,
41
- headers,
42
- body: request.body
43
- });
57
+ const response = await originalFetch(input, newInit);
44
58
  span.setAttribute("http.response.status_code", response.status);
45
59
  if (response.status >= 400) {
46
60
  span.setStatus({
@@ -52,7 +66,7 @@ function instrumentFetch() {
52
66
  }
53
67
  span.end();
54
68
  recordFetchRequestDuration(
55
- { method: request.method, host: url.hostname, status: response.status },
69
+ { method, host: url.hostname, status: response.status },
56
70
  performance.now() - startTime
57
71
  );
58
72
  return response;
@@ -62,13 +76,11 @@ function instrumentFetch() {
62
76
  message: error instanceof Error ? error.message : "Unknown error"
63
77
  });
64
78
  span.end();
65
- recordFetchRequestDuration(
66
- { method: request.method, host: url.hostname, status: 0 },
67
- performance.now() - startTime
68
- );
79
+ recordFetchRequestDuration({ method, host: url.hostname, status: 0 }, performance.now() - startTime);
69
80
  throw error;
70
81
  }
71
82
  }
83
+ instrumentedFetch[INSTRUMENTED] = true;
72
84
  globalThis.fetch = Object.assign(instrumentedFetch, originalFetch);
73
85
  }
74
86
 
@@ -5,9 +5,9 @@ import {
5
5
  } from "./chunk-JU6FJKLT.js";
6
6
 
7
7
  // src/middleware.ts
8
+ import { shouldExclude } from "@astroscope/excludes";
8
9
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
9
10
  import { RPCType, setRPCMetadata } from "@opentelemetry/core";
10
- import { shouldExclude } from "@astroscope/excludes";
11
11
  var LIB_NAME = "@astroscope/opentelemetry";
12
12
  var ACTIONS_PREFIX = "/_actions/";
13
13
  function getClientIp(request) {
@@ -21,7 +21,7 @@ function createOpenTelemetryMiddleware(options = {}) {
21
21
  return next();
22
22
  }
23
23
  const startTime = performance.now();
24
- const { request, url } = ctx;
24
+ const { request, url, routePattern } = ctx;
25
25
  const input = { traceparent: request.headers.get("traceparent"), tracestate: request.headers.get("tracestate") };
26
26
  const parentContext = propagation.extract(context.active(), input);
27
27
  const contentLength = request.headers.get("content-length");
@@ -29,6 +29,7 @@ function createOpenTelemetryMiddleware(options = {}) {
29
29
  const spanOptions = {
30
30
  attributes: {
31
31
  "http.request.method": request.method,
32
+ "http.route": routePattern,
32
33
  "url.full": request.url,
33
34
  "url.path": url.pathname,
34
35
  "url.query": url.search.slice(1),
@@ -43,11 +44,11 @@ function createOpenTelemetryMiddleware(options = {}) {
43
44
  };
44
45
  const isAction = url.pathname.startsWith(ACTIONS_PREFIX);
45
46
  const actionName = url.pathname.slice(ACTIONS_PREFIX.length).replace(/\/$/, "");
46
- const spanName = isAction ? `ACTION ${actionName}` : `${request.method} ${url.pathname}`;
47
+ const spanName = isAction ? `ACTION ${actionName}` : `${request.method} ${routePattern}`;
47
48
  const span = tracer.startSpan(spanName, spanOptions, parentContext);
48
49
  const spanContext = trace.setSpan(parentContext, span);
49
50
  const rpcMetadata = { type: RPCType.HTTP, span };
50
- const endActiveRequest = recordHttpRequestStart({ method: request.method, route: url.pathname });
51
+ const endActiveRequest = recordHttpRequestStart({ method: request.method, route: routePattern });
51
52
  return context.with(setRPCMetadata(spanContext, rpcMetadata), async () => {
52
53
  const finalize = (status, responseSize) => {
53
54
  span.setAttribute("http.response.status_code", status);
@@ -63,7 +64,7 @@ function createOpenTelemetryMiddleware(options = {}) {
63
64
  span.end();
64
65
  endActiveRequest();
65
66
  const duration = performance.now() - startTime;
66
- recordHttpRequestDuration({ method: request.method, route: url.pathname, status }, duration);
67
+ recordHttpRequestDuration({ method: request.method, route: routePattern, status }, duration);
67
68
  if (isAction) {
68
69
  recordActionDuration({ name: actionName, status }, duration);
69
70
  }
@@ -100,7 +101,7 @@ function createOpenTelemetryMiddleware(options = {}) {
100
101
  span.end();
101
102
  endActiveRequest();
102
103
  const duration = performance.now() - startTime;
103
- recordHttpRequestDuration({ method: request.method, route: url.pathname, status: 500 }, duration);
104
+ recordHttpRequestDuration({ method: request.method, route: routePattern, status: 500 }, duration);
104
105
  if (isAction) {
105
106
  recordActionDuration({ name: actionName, status: 500 }, duration);
106
107
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  instrumentFetch
3
- } from "./chunk-45TY3U7U.js";
3
+ } from "./chunk-A7VHVQTG.js";
4
4
  import "./chunk-JU6FJKLT.js";
5
5
  export {
6
6
  instrumentFetch
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ interface OpenTelemetryMiddlewareOptions {
6
6
  * Paths to exclude from tracing.
7
7
  * Can be an array of patterns or a function that returns true to exclude.
8
8
  */
9
- exclude?: ExcludePattern[] | ((context: APIContext) => boolean);
9
+ exclude?: ExcludePattern[] | ((context: APIContext) => boolean) | undefined;
10
10
  }
11
11
 
12
12
  /**
@@ -58,16 +58,16 @@ interface OpenTelemetryIntegrationOptions {
58
58
  */
59
59
  http?: {
60
60
  enabled: boolean;
61
- exclude?: ExcludePattern[];
62
- };
61
+ exclude?: ExcludePattern[] | undefined;
62
+ } | undefined;
63
63
  /**
64
64
  * Fetch outgoing request instrumentation.
65
65
  * @default { enabled: true }
66
66
  */
67
67
  fetch?: {
68
68
  enabled: boolean;
69
- };
70
- };
69
+ } | undefined;
70
+ } | undefined;
71
71
  }
72
72
  /**
73
73
  * Astro integration for OpenTelemetry instrumentation.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createOpenTelemetryMiddleware
3
- } from "./chunk-SNVIK2H6.js";
3
+ } from "./chunk-OIBEYPAM.js";
4
4
  import {
5
5
  instrumentFetch
6
- } from "./chunk-45TY3U7U.js";
6
+ } from "./chunk-A7VHVQTG.js";
7
7
  import "./chunk-JU6FJKLT.js";
8
8
 
9
9
  // src/integration.ts
@@ -55,7 +55,7 @@ function opentelemetry(options = {}) {
55
55
  if (isBuild) return;
56
56
  server.httpServer?.once("listening", async () => {
57
57
  try {
58
- const { instrumentFetch: instrumentFetch2 } = await import("./fetch-Z2DBQPE2.js");
58
+ const { instrumentFetch: instrumentFetch2 } = await import("./fetch-PVKAGUFE.js");
59
59
  instrumentFetch2();
60
60
  logger.info("fetch instrumentation enabled");
61
61
  } catch (error) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createOpenTelemetryMiddleware
3
- } from "./chunk-SNVIK2H6.js";
3
+ } from "./chunk-OIBEYPAM.js";
4
4
  import "./chunk-JU6FJKLT.js";
5
5
 
6
6
  // src/middleware-entrypoint.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astroscope/opentelemetry",
3
- "version": "0.2.1",
4
- "description": "OpenTelemetry tracing middleware for Astro SSR",
3
+ "version": "0.2.3",
4
+ "description": "OpenTelemetry for Astro — tracing, metrics, and component instrumentation that works in dev mode",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -55,13 +55,13 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@opentelemetry/api": "^1.9.0",
58
- "@opentelemetry/core": "^2.2.0",
59
- "astro": "^5.1.0",
58
+ "@opentelemetry/core": "^2.3.0",
59
+ "astro": "^5.16.9",
60
60
  "tsup": "^8.5.1",
61
61
  "typescript": "^5.9.3"
62
62
  },
63
63
  "peerDependencies": {
64
- "@astroscope/excludes": "^0.1.0",
64
+ "@astroscope/excludes": "^0.1.3",
65
65
  "@opentelemetry/api": "^1.0.0",
66
66
  "@opentelemetry/core": "^2.0.0",
67
67
  "astro": "^5.0.0"
@@ -1,146 +0,0 @@
1
- import {
2
- recordHttpRequestDuration,
3
- recordHttpRequestStart
4
- } from "./chunk-UPNRPRAW.js";
5
-
6
- // src/middleware.ts
7
- import {
8
- SpanKind,
9
- SpanStatusCode,
10
- context,
11
- propagation,
12
- trace
13
- } from "@opentelemetry/api";
14
- import { RPCType, setRPCMetadata } from "@opentelemetry/core";
15
- var LIB_NAME = "@astroscope/opentelemetry";
16
- var ACTIONS_PREFIX = "/_actions/";
17
- function matchesPattern(path, pattern) {
18
- if ("pattern" in pattern) {
19
- return pattern.pattern.test(path);
20
- }
21
- if ("prefix" in pattern) {
22
- return path.startsWith(pattern.prefix);
23
- }
24
- return path === pattern.exact;
25
- }
26
- function shouldExclude(ctx, exclude) {
27
- if (!exclude) return false;
28
- if (typeof exclude === "function") {
29
- return exclude(ctx);
30
- }
31
- const path = ctx.url.pathname;
32
- return exclude.some((pattern) => matchesPattern(path, pattern));
33
- }
34
- function getClientIp(request) {
35
- return request.headers.get("x-forwarded-for")?.split(",")[0].trim() ?? request.headers.get("x-real-ip") ?? request.headers.get("cf-connecting-ip") ?? // Cloudflare
36
- void 0;
37
- }
38
- function createOpenTelemetryMiddleware(options = {}) {
39
- const tracer = trace.getTracer(LIB_NAME);
40
- return async (ctx, next) => {
41
- if (shouldExclude(ctx, options.exclude)) {
42
- return next();
43
- }
44
- const { request, url } = ctx;
45
- const input = {
46
- traceparent: request.headers.get("traceparent"),
47
- tracestate: request.headers.get("tracestate")
48
- };
49
- const parentContext = propagation.extract(context.active(), input);
50
- const clientIp = getClientIp(request);
51
- const contentLength = request.headers.get("content-length");
52
- const spanOptions = {
53
- attributes: {
54
- "http.request.method": request.method,
55
- "url.full": request.url,
56
- "url.path": url.pathname,
57
- "url.query": url.search.slice(1),
58
- // Remove leading "?"
59
- "url.scheme": url.protocol.replace(":", ""),
60
- "server.address": url.hostname,
61
- "server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80,
62
- "user_agent.original": request.headers.get("user-agent") ?? "",
63
- ...contentLength && { "http.request.body.size": parseInt(contentLength) },
64
- ...clientIp && { "client.address": clientIp }
65
- },
66
- kind: SpanKind.SERVER
67
- };
68
- const isAction = url.pathname.startsWith(ACTIONS_PREFIX);
69
- const actionName = url.pathname.slice(ACTIONS_PREFIX.length).replace(/\/$/, "");
70
- const spanName = isAction ? `ACTION ${actionName}` : `${request.method} ${url.pathname}`;
71
- const span = tracer.startSpan(spanName, spanOptions, parentContext);
72
- const spanContext = trace.setSpan(parentContext, span);
73
- const rpcMetadata = { type: RPCType.HTTP, span };
74
- const metricsEnabled = options.metrics ?? false;
75
- const startTime = metricsEnabled ? performance.now() : 0;
76
- const endActiveRequest = metricsEnabled ? recordHttpRequestStart({ method: request.method, route: url.pathname }) : void 0;
77
- return context.with(
78
- setRPCMetadata(spanContext, rpcMetadata),
79
- async () => {
80
- const finalize = (status, responseSize) => {
81
- span.setAttribute("http.response.status_code", status);
82
- span.setAttribute("http.response.body.size", responseSize);
83
- if (status >= 400) {
84
- span.setStatus({
85
- code: SpanStatusCode.ERROR,
86
- message: `HTTP ${status}`
87
- });
88
- } else {
89
- span.setStatus({ code: SpanStatusCode.OK });
90
- }
91
- span.end();
92
- if (metricsEnabled) {
93
- endActiveRequest?.();
94
- recordHttpRequestDuration(
95
- { method: request.method, route: url.pathname, status },
96
- performance.now() - startTime
97
- );
98
- }
99
- };
100
- try {
101
- const response = await next();
102
- if (!response.body) {
103
- finalize(response.status, 0);
104
- return response;
105
- }
106
- const [measureStream, clientStream] = response.body.tee();
107
- let responseSize = 0;
108
- (async () => {
109
- const reader = measureStream.getReader();
110
- try {
111
- while (true) {
112
- const { done, value } = await reader.read();
113
- if (done) break;
114
- responseSize += value.length;
115
- }
116
- } finally {
117
- finalize(response.status, responseSize);
118
- }
119
- })();
120
- return new Response(clientStream, {
121
- status: response.status,
122
- headers: response.headers
123
- });
124
- } catch (e) {
125
- span.setStatus({
126
- code: SpanStatusCode.ERROR,
127
- message: e instanceof Error ? e.message : "Unknown error"
128
- });
129
- span.end();
130
- if (metricsEnabled) {
131
- endActiveRequest?.();
132
- recordHttpRequestDuration(
133
- { method: request.method, route: url.pathname, status: 500 },
134
- performance.now() - startTime
135
- );
136
- }
137
- throw e;
138
- }
139
- }
140
- );
141
- };
142
- }
143
-
144
- export {
145
- createOpenTelemetryMiddleware
146
- };
@@ -1,150 +0,0 @@
1
- import {
2
- recordActionDuration,
3
- recordHttpRequestDuration,
4
- recordHttpRequestStart
5
- } from "./chunk-DPYEL3WF.js";
6
-
7
- // src/middleware.ts
8
- import {
9
- SpanKind,
10
- SpanStatusCode,
11
- context,
12
- propagation,
13
- trace
14
- } from "@opentelemetry/api";
15
- import { RPCType, setRPCMetadata } from "@opentelemetry/core";
16
- var LIB_NAME = "@astroscope/opentelemetry";
17
- var ACTIONS_PREFIX = "/_actions/";
18
- function matchesPattern(path, pattern) {
19
- if ("pattern" in pattern) {
20
- return pattern.pattern.test(path);
21
- }
22
- if ("prefix" in pattern) {
23
- return path.startsWith(pattern.prefix);
24
- }
25
- return path === pattern.exact;
26
- }
27
- function shouldExclude(ctx, exclude) {
28
- if (!exclude) return false;
29
- if (typeof exclude === "function") {
30
- return exclude(ctx);
31
- }
32
- const path = ctx.url.pathname;
33
- return exclude.some((pattern) => matchesPattern(path, pattern));
34
- }
35
- function getClientIp(request) {
36
- return request.headers.get("x-forwarded-for")?.split(",")[0].trim() ?? request.headers.get("x-real-ip") ?? request.headers.get("cf-connecting-ip") ?? // Cloudflare
37
- void 0;
38
- }
39
- function createOpenTelemetryMiddleware(options = {}) {
40
- const tracer = trace.getTracer(LIB_NAME);
41
- return async (ctx, next) => {
42
- if (shouldExclude(ctx, options.exclude)) {
43
- return next();
44
- }
45
- const { request, url } = ctx;
46
- const input = {
47
- traceparent: request.headers.get("traceparent"),
48
- tracestate: request.headers.get("tracestate")
49
- };
50
- const parentContext = propagation.extract(context.active(), input);
51
- const clientIp = getClientIp(request);
52
- const contentLength = request.headers.get("content-length");
53
- const spanOptions = {
54
- attributes: {
55
- "http.request.method": request.method,
56
- "url.full": request.url,
57
- "url.path": url.pathname,
58
- "url.query": url.search.slice(1),
59
- // Remove leading "?"
60
- "url.scheme": url.protocol.replace(":", ""),
61
- "server.address": url.hostname,
62
- "server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80,
63
- "user_agent.original": request.headers.get("user-agent") ?? "",
64
- ...contentLength && { "http.request.body.size": parseInt(contentLength) },
65
- ...clientIp && { "client.address": clientIp }
66
- },
67
- kind: SpanKind.SERVER
68
- };
69
- const isAction = url.pathname.startsWith(ACTIONS_PREFIX);
70
- const actionName = url.pathname.slice(ACTIONS_PREFIX.length).replace(/\/$/, "");
71
- const spanName = isAction ? `ACTION ${actionName}` : `${request.method} ${url.pathname}`;
72
- const span = tracer.startSpan(spanName, spanOptions, parentContext);
73
- const spanContext = trace.setSpan(parentContext, span);
74
- const rpcMetadata = { type: RPCType.HTTP, span };
75
- const startTime = performance.now();
76
- const endActiveRequest = recordHttpRequestStart({ method: request.method, route: url.pathname });
77
- return context.with(
78
- setRPCMetadata(spanContext, rpcMetadata),
79
- async () => {
80
- const finalize = (status, responseSize) => {
81
- span.setAttribute("http.response.status_code", status);
82
- span.setAttribute("http.response.body.size", responseSize);
83
- if (status >= 400) {
84
- span.setStatus({
85
- code: SpanStatusCode.ERROR,
86
- message: `HTTP ${status}`
87
- });
88
- } else {
89
- span.setStatus({ code: SpanStatusCode.OK });
90
- }
91
- span.end();
92
- endActiveRequest();
93
- const duration = performance.now() - startTime;
94
- recordHttpRequestDuration(
95
- { method: request.method, route: url.pathname, status },
96
- duration
97
- );
98
- if (isAction) {
99
- recordActionDuration({ name: actionName, status }, duration);
100
- }
101
- };
102
- try {
103
- const response = await next();
104
- if (!response.body) {
105
- finalize(response.status, 0);
106
- return response;
107
- }
108
- const [measureStream, clientStream] = response.body.tee();
109
- let responseSize = 0;
110
- (async () => {
111
- const reader = measureStream.getReader();
112
- try {
113
- while (true) {
114
- const { done, value } = await reader.read();
115
- if (done) break;
116
- responseSize += value.length;
117
- }
118
- } finally {
119
- finalize(response.status, responseSize);
120
- }
121
- })();
122
- return new Response(clientStream, {
123
- status: response.status,
124
- headers: response.headers
125
- });
126
- } catch (e) {
127
- span.setStatus({
128
- code: SpanStatusCode.ERROR,
129
- message: e instanceof Error ? e.message : "Unknown error"
130
- });
131
- span.end();
132
- endActiveRequest();
133
- const duration = performance.now() - startTime;
134
- recordHttpRequestDuration(
135
- { method: request.method, route: url.pathname, status: 500 },
136
- duration
137
- );
138
- if (isAction) {
139
- recordActionDuration({ name: actionName, status: 500 }, duration);
140
- }
141
- throw e;
142
- }
143
- }
144
- );
145
- };
146
- }
147
-
148
- export {
149
- createOpenTelemetryMiddleware
150
- };
@@ -1,83 +0,0 @@
1
- import {
2
- recordFetchRequestDuration
3
- } from "./chunk-DPYEL3WF.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
- function instrumentFetch() {
15
- const originalFetch = globalThis.fetch;
16
- async function instrumentedFetch(input, init) {
17
- const tracer = trace.getTracer(LIB_NAME);
18
- const activeContext = context.active();
19
- const request = new Request(input, init);
20
- const url = new URL(request.url);
21
- const span = tracer.startSpan(
22
- `FETCH ${request.method}`,
23
- {
24
- kind: SpanKind.CLIENT,
25
- attributes: {
26
- "http.request.method": request.method,
27
- "url.full": request.url,
28
- "url.path": url.pathname,
29
- "url.scheme": url.protocol.replace(":", ""),
30
- "server.address": url.hostname,
31
- "server.port": url.port ? parseInt(url.port) : url.protocol === "https:" ? 443 : 80
32
- }
33
- },
34
- activeContext
35
- );
36
- const headers = new Headers(request.headers);
37
- const carrier = {};
38
- propagation.inject(trace.setSpan(activeContext, span), carrier);
39
- for (const [key, value] of Object.entries(carrier)) {
40
- headers.set(key, value);
41
- }
42
- const startTime = performance.now();
43
- try {
44
- const response = await originalFetch(request.url, {
45
- ...init,
46
- method: request.method,
47
- headers,
48
- body: request.body
49
- });
50
- span.setAttribute("http.response.status_code", response.status);
51
- if (response.status >= 400) {
52
- span.setStatus({
53
- code: SpanStatusCode.ERROR,
54
- message: `HTTP ${response.status}`
55
- });
56
- } else {
57
- span.setStatus({ code: SpanStatusCode.OK });
58
- }
59
- span.end();
60
- recordFetchRequestDuration(
61
- { method: request.method, host: url.hostname, status: response.status },
62
- performance.now() - startTime
63
- );
64
- return response;
65
- } catch (error) {
66
- span.setStatus({
67
- code: SpanStatusCode.ERROR,
68
- message: error instanceof Error ? error.message : "Unknown error"
69
- });
70
- span.end();
71
- recordFetchRequestDuration(
72
- { method: request.method, host: url.hostname, status: 0 },
73
- performance.now() - startTime
74
- );
75
- throw error;
76
- }
77
- }
78
- globalThis.fetch = Object.assign(instrumentedFetch, originalFetch);
79
- }
80
-
81
- export {
82
- instrumentFetch
83
- };