autotel-cloudflare 2.18.18 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actors.d.ts +54 -89
- package/dist/actors.d.ts.map +1 -0
- package/dist/actors.js +990 -986
- package/dist/actors.js.map +1 -1
- package/dist/agents.d.ts +102 -128
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +275 -259
- package/dist/agents.js.map +1 -1
- package/dist/bindings-C10RI6Il.js +975 -0
- package/dist/bindings-C10RI6Il.js.map +1 -0
- package/dist/bindings-xEZcXo5r.d.ts +149 -0
- package/dist/bindings-xEZcXo5r.d.ts.map +1 -0
- package/dist/bindings.d.ts +2 -139
- package/dist/bindings.js +3 -4
- package/dist/common-DiWH6nmG.js +63 -0
- package/dist/common-DiWH6nmG.js.map +1 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.js +3 -3
- package/dist/execution-logger-BxmgP8jF.js +65 -0
- package/dist/execution-logger-BxmgP8jF.js.map +1 -0
- package/dist/{logger-Cm_73k3-.d.ts → execution-logger-tgQmJeeU.d.ts} +9 -7
- package/dist/execution-logger-tgQmJeeU.d.ts.map +1 -0
- package/dist/handlers-CMg9l_T-.js +378 -0
- package/dist/handlers-CMg9l_T-.js.map +1 -0
- package/dist/handlers-C_VojUeA.d.ts +96 -0
- package/dist/handlers-C_VojUeA.d.ts.map +1 -0
- package/dist/handlers.d.ts +2 -112
- package/dist/handlers.js +3 -4
- package/dist/index.d.ts +20 -99
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +644 -600
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +3 -3
- package/dist/logger.js +5 -4
- package/dist/parse-error.d.ts +2 -1
- package/dist/parse-error.js +3 -3
- package/dist/sampling.d.ts +1 -4
- package/dist/sampling.js +3 -3
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +3 -3
- package/package.json +6 -6
- package/src/bindings/ai.test.ts +1 -1
- package/src/bindings/ai.ts +3 -1
- package/dist/bindings.js.map +0 -1
- package/dist/chunk-KAUHT25H.js +0 -1084
- package/dist/chunk-KAUHT25H.js.map +0 -1
- package/dist/chunk-MIDMNKDC.js +0 -333
- package/dist/chunk-MIDMNKDC.js.map +0 -1
- package/dist/chunk-O4IYKWPJ.js +0 -55
- package/dist/chunk-O4IYKWPJ.js.map +0 -1
- package/dist/chunk-RVVMMPWN.js +0 -63
- package/dist/chunk-RVVMMPWN.js.map +0 -1
- package/dist/events.js.map +0 -1
- package/dist/handlers.js.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/parse-error.js.map +0 -1
- package/dist/sampling.js.map +0 -1
- package/dist/testing.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,658 +1,702 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import { createInitialiser, getActiveConfig, shouldInstrumentPath, setConfig, WorkerTracerProvider, WorkerTracer, getServiceForPath } from 'autotel-edge';
|
|
9
|
-
export * from 'autotel-edge';
|
|
10
|
-
import { trace, SpanKind, propagation, context, SpanStatusCode } from '@opentelemetry/api';
|
|
11
|
-
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
1
|
+
import { a as getWorkflowLogger, i as getRequestLogger, n as getActorLogger, r as getQueueLogger, t as createWorkersLogger } from "./execution-logger-BxmgP8jF.js";
|
|
2
|
+
import { a as wrap, i as unwrap, n as proxyExecutionContext } from "./common-DiWH6nmG.js";
|
|
3
|
+
import { a as instrumentKV, c as instrumentImages, d as instrumentHyperdrive, f as instrumentVectorize, i as instrumentD1, l as instrumentAnalyticsEngine, n as instrumentRateLimiter, o as instrumentR2, p as instrumentAI, r as instrumentBindings, s as instrumentServiceBinding, t as instrumentBrowserRendering, u as instrumentQueueProducer } from "./bindings-C10RI6Il.js";
|
|
4
|
+
import { n as instrumentDO, t as instrumentWorkflow } from "./handlers-CMg9l_T-.js";
|
|
5
|
+
import { WorkerTracer, WorkerTracerProvider, createInitialiser, getActiveConfig, getServiceForPath, setConfig, shouldInstrumentPath } from "autotel-edge";
|
|
6
|
+
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
7
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
12
8
|
|
|
9
|
+
export * from "autotel-edge"
|
|
10
|
+
|
|
11
|
+
//#region src/global/fetch.ts
|
|
12
|
+
/**
|
|
13
|
+
* Global fetch() instrumentation for autotel-edge
|
|
14
|
+
*
|
|
15
|
+
* Automatically traces all outgoing fetch() calls with:
|
|
16
|
+
* - HTTP method, URL, status code
|
|
17
|
+
* - Request/response headers
|
|
18
|
+
* - Automatic context propagation
|
|
19
|
+
* - Error tracking
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Gather HTTP request attributes following OpenTelemetry semantic conventions
|
|
23
|
+
*/
|
|
13
24
|
function gatherRequestAttributes(request) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
25
|
+
const url = new URL(request.url);
|
|
26
|
+
const redactQuery = getActiveConfig()?.dataSafety?.redactQueryParams === true;
|
|
27
|
+
return {
|
|
28
|
+
"http.request.method": request.method.toUpperCase(),
|
|
29
|
+
"url.full": redactQuery ? `${url.origin}${url.pathname}` : request.url,
|
|
30
|
+
"url.scheme": url.protocol.replace(":", ""),
|
|
31
|
+
"server.address": url.host,
|
|
32
|
+
"url.path": url.pathname,
|
|
33
|
+
"url.query": redactQuery ? url.search ? "[REDACTED]" : "" : url.search,
|
|
34
|
+
"network.protocol.name": "http",
|
|
35
|
+
"user_agent.original": request.headers.get("user-agent") || void 0
|
|
36
|
+
};
|
|
27
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Gather HTTP response attributes
|
|
40
|
+
*/
|
|
28
41
|
function gatherResponseAttributes(response) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
return {
|
|
43
|
+
"http.response.status_code": response.status,
|
|
44
|
+
"http.response.body.size": response.headers.get("content-length") || void 0
|
|
45
|
+
};
|
|
33
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Instrument the global fetch function
|
|
49
|
+
*
|
|
50
|
+
* This wraps globalThis.fetch to automatically create spans for all outgoing HTTP requests.
|
|
51
|
+
*
|
|
52
|
+
* **Note:** This is called automatically when the library is initialized with
|
|
53
|
+
* `instrumentation.instrumentGlobalFetch: true` (default).
|
|
54
|
+
*/
|
|
34
55
|
function instrumentGlobalFetch() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
70
|
-
} else {
|
|
71
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
72
|
-
}
|
|
73
|
-
return response;
|
|
74
|
-
} catch (error) {
|
|
75
|
-
span.recordException(error);
|
|
76
|
-
span.setStatus({
|
|
77
|
-
code: SpanStatusCode.ERROR,
|
|
78
|
-
message: error instanceof Error ? error.message : String(error)
|
|
79
|
-
});
|
|
80
|
-
throw error;
|
|
81
|
-
} finally {
|
|
82
|
-
span.end();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
};
|
|
87
|
-
globalThis.fetch = instrumentedFetch;
|
|
56
|
+
const originalFetch = globalThis.fetch;
|
|
57
|
+
globalThis.fetch = function fetch(input, init) {
|
|
58
|
+
const request = new Request(input, init);
|
|
59
|
+
if (!request.url.startsWith("http")) return originalFetch(input, init);
|
|
60
|
+
const config = getActiveConfig();
|
|
61
|
+
if (!config) return originalFetch(input, init);
|
|
62
|
+
const tracer = trace.getTracer("autotel-edge");
|
|
63
|
+
const url = new URL(request.url);
|
|
64
|
+
const spanName = `${request.method} ${url.host}`;
|
|
65
|
+
return tracer.startActiveSpan(spanName, {
|
|
66
|
+
kind: SpanKind.CLIENT,
|
|
67
|
+
attributes: gatherRequestAttributes(request)
|
|
68
|
+
}, async (span) => {
|
|
69
|
+
try {
|
|
70
|
+
if (typeof config.fetch?.includeTraceContext === "function" ? config.fetch.includeTraceContext(request) : config.fetch?.includeTraceContext ?? true) propagation.inject(context.active(), request.headers, { set: (headers, key, value) => {
|
|
71
|
+
if (typeof value === "string") headers.set(key, value);
|
|
72
|
+
} });
|
|
73
|
+
const response = await originalFetch(request);
|
|
74
|
+
span.setAttributes(gatherResponseAttributes(response));
|
|
75
|
+
if (response.ok) span.setStatus({ code: SpanStatusCode.OK });
|
|
76
|
+
else span.setStatus({ code: SpanStatusCode.ERROR });
|
|
77
|
+
return response;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
span.recordException(error);
|
|
80
|
+
span.setStatus({
|
|
81
|
+
code: SpanStatusCode.ERROR,
|
|
82
|
+
message: error instanceof Error ? error.message : String(error)
|
|
83
|
+
});
|
|
84
|
+
throw error;
|
|
85
|
+
} finally {
|
|
86
|
+
span.end();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
};
|
|
88
90
|
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/global/cache.ts
|
|
94
|
+
/**
|
|
95
|
+
* Global Cache API instrumentation for Cloudflare Workers
|
|
96
|
+
*
|
|
97
|
+
* Automatically traces cache operations:
|
|
98
|
+
* - cache.match() - Read from cache
|
|
99
|
+
* - cache.put() - Write to cache
|
|
100
|
+
* - cache.delete() - Delete from cache
|
|
101
|
+
*/
|
|
102
|
+
/**
|
|
103
|
+
* Sanitize URL for span attributes (remove query params that might contain sensitive data)
|
|
104
|
+
*/
|
|
89
105
|
function sanitizeURL(url) {
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
const u = new URL(url);
|
|
107
|
+
return `${u.protocol}//${u.host}${u.pathname}`;
|
|
92
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Instrument a cache method (match, put, delete)
|
|
111
|
+
*/
|
|
93
112
|
function instrumentCacheMethod(fn, cacheName, operation) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
throw error;
|
|
125
|
-
} finally {
|
|
126
|
-
span.end();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
return wrap(fn, handler);
|
|
113
|
+
return wrap(fn, { async apply(target, thisArg, argArray) {
|
|
114
|
+
const tracer = trace.getTracer("autotel-edge");
|
|
115
|
+
const firstArg = argArray[0];
|
|
116
|
+
const url = firstArg instanceof Request ? firstArg.url : typeof firstArg === "string" ? firstArg : void 0;
|
|
117
|
+
const spanName = `Cache ${cacheName}.${operation}`;
|
|
118
|
+
return tracer.startActiveSpan(spanName, {
|
|
119
|
+
kind: SpanKind.CLIENT,
|
|
120
|
+
attributes: {
|
|
121
|
+
"cache.name": cacheName,
|
|
122
|
+
"cache.operation": operation,
|
|
123
|
+
"cache.key": url ? sanitizeURL(url) : void 0
|
|
124
|
+
}
|
|
125
|
+
}, async (span) => {
|
|
126
|
+
try {
|
|
127
|
+
const result = await Reflect.apply(target, thisArg, argArray);
|
|
128
|
+
if (operation === "match") span.setAttribute("cache.hit", !!result);
|
|
129
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
130
|
+
return result;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
span.recordException(error);
|
|
133
|
+
span.setStatus({
|
|
134
|
+
code: SpanStatusCode.ERROR,
|
|
135
|
+
message: error instanceof Error ? error.message : String(error)
|
|
136
|
+
});
|
|
137
|
+
throw error;
|
|
138
|
+
} finally {
|
|
139
|
+
span.end();
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
} });
|
|
133
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Instrument a Cache instance
|
|
146
|
+
*/
|
|
134
147
|
function instrumentCache(cache, cacheName) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
cacheName,
|
|
142
|
-
prop
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
if (typeof value === "function") {
|
|
146
|
-
return value.bind(target);
|
|
147
|
-
}
|
|
148
|
-
return value;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
return wrap(cache, handler);
|
|
148
|
+
return wrap(cache, { get(target, prop) {
|
|
149
|
+
const value = Reflect.get(target, prop);
|
|
150
|
+
if ((prop === "match" || prop === "put" || prop === "delete") && typeof value === "function") return instrumentCacheMethod(value.bind(target), cacheName, prop);
|
|
151
|
+
if (typeof value === "function") return value.bind(target);
|
|
152
|
+
return value;
|
|
153
|
+
} });
|
|
152
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Instrument caches.open()
|
|
157
|
+
*/
|
|
153
158
|
function instrumentCachesOpen(openFn) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return instrumentCache(cache, cacheName);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
return wrap(openFn, handler);
|
|
159
|
+
return wrap(openFn, { async apply(target, thisArg, argArray) {
|
|
160
|
+
const cacheName = argArray[0];
|
|
161
|
+
return instrumentCache(await Reflect.apply(target, thisArg, argArray), cacheName);
|
|
162
|
+
} });
|
|
162
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Instrument the global caches API
|
|
166
|
+
*
|
|
167
|
+
* This wraps globalThis.caches to automatically create spans for all cache operations.
|
|
168
|
+
*
|
|
169
|
+
* **Note:** This is called automatically when the library is initialized with
|
|
170
|
+
* `instrumentation.instrumentGlobalCache: true` (default).
|
|
171
|
+
*/
|
|
163
172
|
function instrumentGlobalCache() {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return Reflect.get(target, prop);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
globalThis.caches = wrap(caches, handler);
|
|
173
|
+
globalThis.caches = wrap(caches, { get(target, prop) {
|
|
174
|
+
if (prop === "default") return instrumentCache(target.default, "default");
|
|
175
|
+
else if (prop === "open") {
|
|
176
|
+
const openFn = Reflect.get(target, prop);
|
|
177
|
+
if (typeof openFn === "function") return instrumentCachesOpen(openFn.bind(target));
|
|
178
|
+
}
|
|
179
|
+
return Reflect.get(target, prop);
|
|
180
|
+
} });
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/wrappers/instrument.ts
|
|
185
|
+
/**
|
|
186
|
+
* Handler instrumentation for Cloudflare Workers
|
|
187
|
+
*
|
|
188
|
+
* Note: This file uses Cloudflare Workers types (ExportedHandler, Request, Response, etc.)
|
|
189
|
+
* which are globally available via @cloudflare/workers-types when listed in tsconfig.json.
|
|
190
|
+
* These types are devDependencies only - they're not runtime dependencies.
|
|
191
|
+
* At runtime, Cloudflare Workers runtime provides the actual implementations.
|
|
192
|
+
*
|
|
193
|
+
* Provides automatic OpenTelemetry tracing for:
|
|
194
|
+
* - HTTP handlers (fetch)
|
|
195
|
+
* - Scheduled/cron handlers
|
|
196
|
+
* - Queue handlers (with message tracking)
|
|
197
|
+
* - Email handlers
|
|
198
|
+
* - Auto-instrumentation of Cloudflare bindings (KV, R2, D1, Service Bindings)
|
|
199
|
+
* - Global fetch and cache instrumentation
|
|
200
|
+
* - Post-processor support for span customization
|
|
201
|
+
* - Tail sampling support
|
|
202
|
+
* - Cold start tracking
|
|
203
|
+
*/
|
|
204
|
+
const headersGetter = {
|
|
205
|
+
get: (carrier, key) => carrier.get(key) ?? void 0,
|
|
206
|
+
keys: (carrier) => [...carrier.keys()]
|
|
184
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* Create fetch handler instrumentation with config support for postProcess
|
|
210
|
+
*/
|
|
211
|
+
/**
|
|
212
|
+
* Extract Cloudflare-specific attributes from a request
|
|
213
|
+
*/
|
|
185
214
|
function extractCfAttributes(request) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
set("cloudflare.client_tcp_rtt", cf.clientTcpRtt);
|
|
209
|
-
return attrs;
|
|
215
|
+
const cf = request.cf;
|
|
216
|
+
if (!cf) return {};
|
|
217
|
+
const attrs = {};
|
|
218
|
+
const set = (key, value) => {
|
|
219
|
+
if (value !== void 0 && value !== null) attrs[key] = value;
|
|
220
|
+
};
|
|
221
|
+
set("cloudflare.colo", cf.colo);
|
|
222
|
+
const ray = request.headers.get("cf-ray");
|
|
223
|
+
if (ray) attrs["cloudflare.ray_id"] = ray;
|
|
224
|
+
set("cloudflare.country", cf.country);
|
|
225
|
+
set("cloudflare.city", cf.city);
|
|
226
|
+
set("cloudflare.region", cf.region);
|
|
227
|
+
set("cloudflare.continent", cf.continent);
|
|
228
|
+
set("cloudflare.timezone", cf.timezone);
|
|
229
|
+
set("cloudflare.latitude", cf.latitude);
|
|
230
|
+
set("cloudflare.longitude", cf.longitude);
|
|
231
|
+
set("cloudflare.asn", cf.asn);
|
|
232
|
+
set("cloudflare.as_organization", cf.asOrganization);
|
|
233
|
+
set("cloudflare.http_protocol", cf.httpProtocol);
|
|
234
|
+
set("cloudflare.tls_version", cf.tlsVersion);
|
|
235
|
+
set("cloudflare.client_tcp_rtt", cf.clientTcpRtt);
|
|
236
|
+
return attrs;
|
|
210
237
|
}
|
|
211
238
|
function createFetchInstrumentation(config) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
};
|
|
239
|
+
return {
|
|
240
|
+
getInitialSpanInfo: (request) => {
|
|
241
|
+
const url = new URL(request.url);
|
|
242
|
+
const routeService = getServiceForPath(url.pathname, config.handlers.fetch.routes);
|
|
243
|
+
const cfAttrs = config.extractCfAttributes === false ? {} : extractCfAttributes(request);
|
|
244
|
+
return {
|
|
245
|
+
name: `${request.method} ${url.pathname}`,
|
|
246
|
+
options: {
|
|
247
|
+
kind: SpanKind.SERVER,
|
|
248
|
+
attributes: {
|
|
249
|
+
"http.request.method": request.method,
|
|
250
|
+
"url.full": request.url,
|
|
251
|
+
...routeService ? {
|
|
252
|
+
"service.name": routeService,
|
|
253
|
+
"autotel.route.service": routeService
|
|
254
|
+
} : {},
|
|
255
|
+
...cfAttrs
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
context: propagation.extract(context.active(), request.headers, headersGetter)
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
getAttributesFromResult: (response) => ({ "http.response.status_code": response.status }),
|
|
262
|
+
executionSucces: (span, trigger, result) => {
|
|
263
|
+
if (result.status >= 500) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
264
|
+
if (config.handlers.fetch.postProcess) {
|
|
265
|
+
const readableSpan = span;
|
|
266
|
+
config.handlers.fetch.postProcess(span, {
|
|
267
|
+
request: trigger,
|
|
268
|
+
response: result,
|
|
269
|
+
readable: readableSpan
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
251
274
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
};
|
|
275
|
+
/**
|
|
276
|
+
* Scheduled handler instrumentation
|
|
277
|
+
*/
|
|
278
|
+
const scheduledInstrumentation = { getInitialSpanInfo: (event) => {
|
|
279
|
+
return {
|
|
280
|
+
name: `scheduledHandler ${event.cron || "unknown"}`,
|
|
281
|
+
options: {
|
|
282
|
+
kind: SpanKind.INTERNAL,
|
|
283
|
+
attributes: {
|
|
284
|
+
"faas.trigger": "timer",
|
|
285
|
+
"faas.cron": event.cron || "unknown",
|
|
286
|
+
"faas.scheduled_time": new Date(event.scheduledTime).toISOString()
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
} };
|
|
291
|
+
/**
|
|
292
|
+
* Tracks message status counts for queue processing
|
|
293
|
+
*/
|
|
267
294
|
var MessageStatusCount = class {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
295
|
+
succeeded = 0;
|
|
296
|
+
failed = 0;
|
|
297
|
+
implicitly_acked = 0;
|
|
298
|
+
implicitly_retried = 0;
|
|
299
|
+
total;
|
|
300
|
+
constructor(total) {
|
|
301
|
+
this.total = total;
|
|
302
|
+
}
|
|
303
|
+
ack() {
|
|
304
|
+
this.succeeded = this.succeeded + 1;
|
|
305
|
+
}
|
|
306
|
+
ackRemaining() {
|
|
307
|
+
this.implicitly_acked = this.total - this.succeeded - this.failed;
|
|
308
|
+
this.succeeded = this.total - this.failed;
|
|
309
|
+
}
|
|
310
|
+
retry() {
|
|
311
|
+
this.failed = this.failed + 1;
|
|
312
|
+
}
|
|
313
|
+
retryRemaining() {
|
|
314
|
+
this.implicitly_retried = this.total - this.succeeded - this.failed;
|
|
315
|
+
this.failed = this.total - this.succeeded;
|
|
316
|
+
}
|
|
317
|
+
toAttributes() {
|
|
318
|
+
return {
|
|
319
|
+
"queue.messages_count": this.total,
|
|
320
|
+
"queue.messages_success": this.succeeded,
|
|
321
|
+
"queue.messages_failed": this.failed,
|
|
322
|
+
"queue.batch_success": this.succeeded === this.total,
|
|
323
|
+
"queue.implicitly_acked": this.implicitly_acked,
|
|
324
|
+
"queue.implicitly_retried": this.implicitly_retried
|
|
325
|
+
};
|
|
326
|
+
}
|
|
300
327
|
};
|
|
328
|
+
/**
|
|
329
|
+
* Add event to active span
|
|
330
|
+
*/
|
|
301
331
|
function addQueueEvent(name, msg, delaySeconds) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (delaySeconds !== void 0) {
|
|
311
|
-
attrs["queue.retry_delay_seconds"] = delaySeconds;
|
|
312
|
-
}
|
|
313
|
-
trace.getActiveSpan()?.addEvent(name, attrs);
|
|
332
|
+
const attrs = {};
|
|
333
|
+
if (msg) {
|
|
334
|
+
attrs["queue.message_id"] = msg.id;
|
|
335
|
+
attrs["queue.message_timestamp"] = msg.timestamp.toISOString();
|
|
336
|
+
if ("attempts" in msg && typeof msg.attempts === "number") attrs["queue.message_attempts"] = msg.attempts;
|
|
337
|
+
}
|
|
338
|
+
if (delaySeconds !== void 0) attrs["queue.retry_delay_seconds"] = delaySeconds;
|
|
339
|
+
trace.getActiveSpan()?.addEvent(name, attrs);
|
|
314
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Proxy a queue message to track ack/retry operations
|
|
343
|
+
*/
|
|
315
344
|
function proxyQueueMessage(msg, count) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
count.retry();
|
|
341
|
-
const result = Reflect.apply(fnTarget, msg, args);
|
|
342
|
-
return result;
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
} else {
|
|
346
|
-
return Reflect.get(target, prop, msg);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
return wrap(msg, msgHandler);
|
|
345
|
+
return wrap(msg, { get: (target, prop) => {
|
|
346
|
+
if (prop === "ack") {
|
|
347
|
+
const ackFn = Reflect.get(target, prop);
|
|
348
|
+
return new Proxy(ackFn, { apply: (fnTarget) => {
|
|
349
|
+
addQueueEvent("messageAck", msg);
|
|
350
|
+
count.ack();
|
|
351
|
+
Reflect.apply(fnTarget, msg, []);
|
|
352
|
+
} });
|
|
353
|
+
} else if (prop === "retry") {
|
|
354
|
+
const retryFn = Reflect.get(target, prop);
|
|
355
|
+
return new Proxy(retryFn, { apply: (fnTarget, _thisArg, args) => {
|
|
356
|
+
const retryOptions = args[0];
|
|
357
|
+
const delaySeconds = retryOptions?.delaySeconds;
|
|
358
|
+
addQueueEvent("messageRetry", msg, delaySeconds);
|
|
359
|
+
if (retryOptions?.contentType) {
|
|
360
|
+
const span = trace.getActiveSpan();
|
|
361
|
+
if (span) span.setAttribute("queue.message.content_type", retryOptions.contentType);
|
|
362
|
+
}
|
|
363
|
+
count.retry();
|
|
364
|
+
return Reflect.apply(fnTarget, msg, args);
|
|
365
|
+
} });
|
|
366
|
+
} else return Reflect.get(target, prop, msg);
|
|
367
|
+
} });
|
|
351
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Proxy MessageBatch to track ackAll/retryAll operations
|
|
371
|
+
*/
|
|
352
372
|
function proxyMessageBatch(batch, count) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
});
|
|
377
|
-
} else if (prop === "retryAll") {
|
|
378
|
-
const retryFn = Reflect.get(target, prop);
|
|
379
|
-
return new Proxy(retryFn, {
|
|
380
|
-
apply: (fnTarget, _thisArg, args) => {
|
|
381
|
-
const retryOptions = args[0];
|
|
382
|
-
const delaySeconds = retryOptions?.delaySeconds;
|
|
383
|
-
addQueueEvent("retryAll", void 0, delaySeconds);
|
|
384
|
-
count.retryRemaining();
|
|
385
|
-
Reflect.apply(fnTarget, batch, args);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
return Reflect.get(target, prop);
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
return wrap(batch, batchHandler);
|
|
373
|
+
return wrap(batch, { get: (target, prop) => {
|
|
374
|
+
if (prop === "messages") return wrap(Reflect.get(target, prop), { get: (target, prop) => {
|
|
375
|
+
if (typeof prop === "string" && !isNaN(parseInt(prop))) return proxyQueueMessage(Reflect.get(target, prop), count);
|
|
376
|
+
else return Reflect.get(target, prop);
|
|
377
|
+
} });
|
|
378
|
+
else if (prop === "ackAll") {
|
|
379
|
+
const ackFn = Reflect.get(target, prop);
|
|
380
|
+
return new Proxy(ackFn, { apply: (fnTarget) => {
|
|
381
|
+
addQueueEvent("ackAll");
|
|
382
|
+
count.ackRemaining();
|
|
383
|
+
Reflect.apply(fnTarget, batch, []);
|
|
384
|
+
} });
|
|
385
|
+
} else if (prop === "retryAll") {
|
|
386
|
+
const retryFn = Reflect.get(target, prop);
|
|
387
|
+
return new Proxy(retryFn, { apply: (fnTarget, _thisArg, args) => {
|
|
388
|
+
const delaySeconds = args[0]?.delaySeconds;
|
|
389
|
+
addQueueEvent("retryAll", void 0, delaySeconds);
|
|
390
|
+
count.retryRemaining();
|
|
391
|
+
Reflect.apply(fnTarget, batch, args);
|
|
392
|
+
} });
|
|
393
|
+
}
|
|
394
|
+
return Reflect.get(target, prop);
|
|
395
|
+
} });
|
|
393
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Queue handler instrumentation with message tracking
|
|
399
|
+
*/
|
|
394
400
|
var QueueInstrumentation = class {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
401
|
+
count;
|
|
402
|
+
getInitialSpanInfo(batch) {
|
|
403
|
+
return {
|
|
404
|
+
name: `queueHandler ${batch.queue || "unknown"}`,
|
|
405
|
+
options: {
|
|
406
|
+
kind: SpanKind.CONSUMER,
|
|
407
|
+
attributes: {
|
|
408
|
+
"faas.trigger": "pubsub",
|
|
409
|
+
"queue.name": batch.queue || "unknown"
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
instrumentTrigger(batch) {
|
|
415
|
+
this.count = new MessageStatusCount(batch.messages.length);
|
|
416
|
+
return proxyMessageBatch(batch, this.count);
|
|
417
|
+
}
|
|
418
|
+
executionSucces(span, _trigger, _result) {
|
|
419
|
+
if (this.count) {
|
|
420
|
+
this.count.ackRemaining();
|
|
421
|
+
span.setAttributes(this.count.toAttributes());
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
executionFailed(span, _trigger, _error) {
|
|
425
|
+
if (this.count) {
|
|
426
|
+
this.count.retryRemaining();
|
|
427
|
+
span.setAttributes(this.count.toAttributes());
|
|
428
|
+
}
|
|
429
|
+
}
|
|
424
430
|
};
|
|
431
|
+
/**
|
|
432
|
+
* Converts email headers into OpenTelemetry attributes.
|
|
433
|
+
* When dataSafety.emailHeaderAllowlist is configured, only allowed headers are captured.
|
|
434
|
+
*/
|
|
425
435
|
function headerAttributes(message) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return attrs;
|
|
436
|
+
const attrs = {};
|
|
437
|
+
if (message.headers instanceof Headers) {
|
|
438
|
+
const allowlist = getActiveConfig()?.dataSafety?.emailHeaderAllowlist;
|
|
439
|
+
for (const [key, value] of message.headers.entries()) {
|
|
440
|
+
if (allowlist && !allowlist.includes(key.toLowerCase())) continue;
|
|
441
|
+
attrs[`email.header.${key}`] = value;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return attrs;
|
|
438
445
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
446
|
+
/**
|
|
447
|
+
* Email handler instrumentation
|
|
448
|
+
*/
|
|
449
|
+
const emailInstrumentation = { getInitialSpanInfo: (message) => {
|
|
450
|
+
const attributes = {
|
|
451
|
+
"faas.trigger": "other",
|
|
452
|
+
"messaging.destination.name": message.to || "unknown"
|
|
453
|
+
};
|
|
454
|
+
if ("headers" in message && message.headers instanceof Headers) {
|
|
455
|
+
const messageId = message.headers.get("Message-Id");
|
|
456
|
+
if (messageId) attributes["rpc.message.id"] = messageId;
|
|
457
|
+
Object.assign(attributes, headerAttributes(message));
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
name: `emailHandler ${message.to || "unknown"}`,
|
|
461
|
+
options: {
|
|
462
|
+
kind: SpanKind.CONSUMER,
|
|
463
|
+
attributes
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
} };
|
|
467
|
+
/**
|
|
468
|
+
* Export spans after request completes
|
|
469
|
+
*/
|
|
461
470
|
async function exportSpans(traceId, tracker, ctx) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
} catch (error) {
|
|
472
|
-
console.error("[autotel-edge] Failed to export spans:", error);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
471
|
+
const tracer = trace.getTracer("autotel-edge");
|
|
472
|
+
if (tracer instanceof WorkerTracer) try {
|
|
473
|
+
const ctxWithScheduler = ctx;
|
|
474
|
+
if (ctxWithScheduler.scheduler) await ctxWithScheduler.scheduler.wait(1);
|
|
475
|
+
await tracker?.wait();
|
|
476
|
+
await tracer.forceFlush(traceId);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error("[autotel-edge] Failed to export spans:", error);
|
|
479
|
+
}
|
|
475
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Create handler flow with instrumentation
|
|
483
|
+
*/
|
|
476
484
|
function createHandlerFlow(instrumentation) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
throw error;
|
|
511
|
-
} finally {
|
|
512
|
-
span.end();
|
|
513
|
-
context$1.waitUntil(exportSpans(span.spanContext().traceId, tracker, context$1));
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
};
|
|
485
|
+
return (handlerFn, [trigger, env, context$1]) => {
|
|
486
|
+
const { ctx: proxiedCtx, tracker } = proxyExecutionContext(context$1);
|
|
487
|
+
const tracer = trace.getTracer("autotel-edge");
|
|
488
|
+
const { name, options, context: spanContext } = instrumentation.getInitialSpanInfo(trigger);
|
|
489
|
+
if (options.attributes) options.attributes["faas.coldstart"] = coldStart;
|
|
490
|
+
else options.attributes = { "faas.coldstart": coldStart };
|
|
491
|
+
coldStart = false;
|
|
492
|
+
const parentContext = spanContext || context.active();
|
|
493
|
+
const instrumentedTrigger = instrumentation.instrumentTrigger ? instrumentation.instrumentTrigger(trigger) : trigger;
|
|
494
|
+
return tracer.startActiveSpan(name, options, parentContext, async (span) => {
|
|
495
|
+
try {
|
|
496
|
+
const result = await handlerFn(instrumentedTrigger, env, proxiedCtx);
|
|
497
|
+
if (instrumentation.getAttributesFromResult) {
|
|
498
|
+
const attributes = instrumentation.getAttributesFromResult(result);
|
|
499
|
+
span.setAttributes(attributes);
|
|
500
|
+
}
|
|
501
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
502
|
+
if (instrumentation.executionSucces) instrumentation.executionSucces(span, trigger, result);
|
|
503
|
+
return result;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
span.recordException(error);
|
|
506
|
+
span.setStatus({
|
|
507
|
+
code: SpanStatusCode.ERROR,
|
|
508
|
+
message: error instanceof Error ? error.message : String(error)
|
|
509
|
+
});
|
|
510
|
+
if (instrumentation.executionFailed) instrumentation.executionFailed(span, trigger, error);
|
|
511
|
+
throw error;
|
|
512
|
+
} finally {
|
|
513
|
+
span.end();
|
|
514
|
+
context$1.waitUntil(exportSpans(span.spanContext().traceId, tracker, context$1));
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
};
|
|
517
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Create handler proxy
|
|
521
|
+
*/
|
|
518
522
|
function createHandlerProxy(_handler, handlerFn, initialiser, instrumentation) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
523
|
+
return (trigger, env, ctx) => {
|
|
524
|
+
const config = initialiser(env, trigger);
|
|
525
|
+
if (config.instrumentation.disabled) return handlerFn(trigger, env, ctx);
|
|
526
|
+
const instrumentedEnv = instrumentBindings(env);
|
|
527
|
+
const configContext = setConfig(config);
|
|
528
|
+
initProvider(config);
|
|
529
|
+
const flowFn = createHandlerFlow(instrumentation);
|
|
530
|
+
return context.with(configContext, () => {
|
|
531
|
+
return flowFn(handlerFn, [
|
|
532
|
+
trigger,
|
|
533
|
+
instrumentedEnv,
|
|
534
|
+
ctx
|
|
535
|
+
]);
|
|
536
|
+
});
|
|
537
|
+
};
|
|
532
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Create handler proxy with dynamic instrumentation (for fetch with postProcess)
|
|
541
|
+
*/
|
|
533
542
|
function createHandlerProxyWithConfig(_handler, handlerFn, initialiser, createInstrumentation) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
};
|
|
543
|
+
return (trigger, env, ctx) => {
|
|
544
|
+
const config = initialiser(env, trigger);
|
|
545
|
+
if (config.instrumentation.disabled) return handlerFn(trigger, env, ctx);
|
|
546
|
+
if (trigger instanceof Request) {
|
|
547
|
+
const pathname = new URL(trigger.url).pathname;
|
|
548
|
+
const fetchCfg = config.handlers.fetch;
|
|
549
|
+
if (!shouldInstrumentPath(pathname, {
|
|
550
|
+
include: fetchCfg.include,
|
|
551
|
+
exclude: fetchCfg.exclude
|
|
552
|
+
})) return handlerFn(trigger, env, ctx);
|
|
553
|
+
}
|
|
554
|
+
const instrumentedEnv = instrumentBindings(env);
|
|
555
|
+
const configContext = setConfig(config);
|
|
556
|
+
initProvider(config);
|
|
557
|
+
const flowFn = createHandlerFlow(createInstrumentation(config));
|
|
558
|
+
return context.with(configContext, () => {
|
|
559
|
+
return flowFn(handlerFn, [
|
|
560
|
+
trigger,
|
|
561
|
+
instrumentedEnv,
|
|
562
|
+
ctx
|
|
563
|
+
]);
|
|
564
|
+
});
|
|
565
|
+
};
|
|
558
566
|
}
|
|
559
|
-
|
|
560
|
-
|
|
567
|
+
let providerInitialized = false;
|
|
568
|
+
let coldStart = true;
|
|
569
|
+
/**
|
|
570
|
+
* Initialize the tracer provider
|
|
571
|
+
*/
|
|
561
572
|
function initProvider(config) {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
});
|
|
579
|
-
const provider = new WorkerTracerProvider(config.spanProcessors, resource);
|
|
580
|
-
provider.register();
|
|
581
|
-
const tracer = trace.getTracer("autotel-edge");
|
|
582
|
-
tracer.setHeadSampler(config.sampling.headSampler);
|
|
583
|
-
providerInitialized = true;
|
|
573
|
+
if (providerInitialized) return;
|
|
574
|
+
if (config.instrumentation.instrumentGlobalFetch) instrumentGlobalFetch();
|
|
575
|
+
if (config.instrumentation.instrumentGlobalCache) instrumentGlobalCache();
|
|
576
|
+
propagation.setGlobalPropagator(config.propagator);
|
|
577
|
+
const resource = resourceFromAttributes({
|
|
578
|
+
"service.name": config.service.name,
|
|
579
|
+
"service.version": config.service.version,
|
|
580
|
+
"service.namespace": config.service.namespace,
|
|
581
|
+
"cloud.provider": "cloudflare",
|
|
582
|
+
"cloud.platform": "cloudflare.workers",
|
|
583
|
+
"telemetry.sdk.name": "autotel-edge",
|
|
584
|
+
"telemetry.sdk.language": "js"
|
|
585
|
+
});
|
|
586
|
+
new WorkerTracerProvider(config.spanProcessors, resource).register();
|
|
587
|
+
trace.getTracer("autotel-edge").setHeadSampler(config.sampling.headSampler);
|
|
588
|
+
providerInitialized = true;
|
|
584
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Instrument a Cloudflare Workers handler
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* ```typescript
|
|
595
|
+
* import { instrument } from 'autotel-edge'
|
|
596
|
+
*
|
|
597
|
+
* const handler = {
|
|
598
|
+
* async fetch(request, env, ctx) {
|
|
599
|
+
* return new Response('Hello World')
|
|
600
|
+
* }
|
|
601
|
+
* }
|
|
602
|
+
*
|
|
603
|
+
* export default instrument(handler, {
|
|
604
|
+
* exporter: {
|
|
605
|
+
* url: env.OTLP_ENDPOINT,
|
|
606
|
+
* headers: { 'x-api-key': env.API_KEY }
|
|
607
|
+
* },
|
|
608
|
+
* service: { name: 'my-worker' }
|
|
609
|
+
* })
|
|
610
|
+
* ```
|
|
611
|
+
*/
|
|
585
612
|
function instrument(handler, config) {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
initialiser,
|
|
593
|
-
createFetchInstrumentation
|
|
594
|
-
);
|
|
595
|
-
}
|
|
596
|
-
if (handler.scheduled) {
|
|
597
|
-
const scheduled = unwrap(handler.scheduled);
|
|
598
|
-
handler.scheduled = createHandlerProxy(
|
|
599
|
-
handler,
|
|
600
|
-
scheduled,
|
|
601
|
-
initialiser,
|
|
602
|
-
scheduledInstrumentation
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
if (handler.queue) {
|
|
606
|
-
const queue = unwrap(handler.queue);
|
|
607
|
-
handler.queue = createHandlerProxy(
|
|
608
|
-
handler,
|
|
609
|
-
queue,
|
|
610
|
-
initialiser,
|
|
611
|
-
new QueueInstrumentation()
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
if (handler.email) {
|
|
615
|
-
const email = unwrap(handler.email);
|
|
616
|
-
handler.email = createHandlerProxy(
|
|
617
|
-
handler,
|
|
618
|
-
email,
|
|
619
|
-
initialiser,
|
|
620
|
-
emailInstrumentation
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
return handler;
|
|
613
|
+
const initialiser = createInitialiser(config);
|
|
614
|
+
if (handler.fetch) handler.fetch = createHandlerProxyWithConfig(handler, unwrap(handler.fetch), initialiser, createFetchInstrumentation);
|
|
615
|
+
if (handler.scheduled) handler.scheduled = createHandlerProxy(handler, unwrap(handler.scheduled), initialiser, scheduledInstrumentation);
|
|
616
|
+
if (handler.queue) handler.queue = createHandlerProxy(handler, unwrap(handler.queue), initialiser, new QueueInstrumentation());
|
|
617
|
+
if (handler.email) handler.email = createHandlerProxy(handler, unwrap(handler.email), initialiser, emailInstrumentation);
|
|
618
|
+
return handler;
|
|
624
619
|
}
|
|
625
620
|
|
|
626
|
-
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region src/wrappers/wrap-module.ts
|
|
623
|
+
/**
|
|
624
|
+
* workers-honeycomb-logger style wrapper API
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```typescript
|
|
628
|
+
* import { wrapModule } from 'autotel-cloudflare'
|
|
629
|
+
*
|
|
630
|
+
* const handler = {
|
|
631
|
+
* async fetch(req, env, ctx) {
|
|
632
|
+
* return new Response('Hello')
|
|
633
|
+
* }
|
|
634
|
+
* }
|
|
635
|
+
*
|
|
636
|
+
* export default wrapModule(
|
|
637
|
+
* { service: { name: 'my-worker' } },
|
|
638
|
+
* handler
|
|
639
|
+
* )
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
/**
|
|
643
|
+
* Wrap a Cloudflare Workers module-style handler
|
|
644
|
+
* Alternative API style inspired by workers-honeycomb-logger
|
|
645
|
+
*
|
|
646
|
+
* @param config Configuration (can be static object or function)
|
|
647
|
+
* @param handler The worker handler to wrap
|
|
648
|
+
* @returns Instrumented handler
|
|
649
|
+
*/
|
|
627
650
|
function wrapModule(config, handler) {
|
|
628
|
-
|
|
651
|
+
return instrument(handler, config);
|
|
629
652
|
}
|
|
630
653
|
|
|
631
|
-
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/wrappers/wrap-do.ts
|
|
656
|
+
/**
|
|
657
|
+
* Durable Object wrapper
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```typescript
|
|
661
|
+
* import { wrapDurableObject } from 'autotel-cloudflare'
|
|
662
|
+
*
|
|
663
|
+
* class Counter implements DurableObject {
|
|
664
|
+
* async fetch(request: Request) {
|
|
665
|
+
* return new Response('count')
|
|
666
|
+
* }
|
|
667
|
+
* }
|
|
668
|
+
*
|
|
669
|
+
* export default wrapDurableObject({ service: { name: 'counter-do' } }, Counter)
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
/**
|
|
673
|
+
* Wrap a Durable Object class with instrumentation
|
|
674
|
+
* Alternative API style inspired by workers-honeycomb-logger
|
|
675
|
+
*
|
|
676
|
+
* @param config Configuration (can be static object or function)
|
|
677
|
+
* @param doClass The Durable Object class to wrap
|
|
678
|
+
* @returns Instrumented Durable Object class
|
|
679
|
+
*/
|
|
632
680
|
function wrapDurableObject(config, doClass) {
|
|
633
|
-
|
|
681
|
+
return instrumentDO(doClass, config);
|
|
634
682
|
}
|
|
635
683
|
|
|
636
|
-
|
|
684
|
+
//#endregion
|
|
685
|
+
//#region src/wrappers/define-worker-fetch.ts
|
|
686
|
+
/**
|
|
687
|
+
* Wrap a Workers fetch handler so:
|
|
688
|
+
* - the handler is instrumented (spans, propagation, waitUntil export flush)
|
|
689
|
+
* - the handler receives a request-scoped logger as its fourth argument
|
|
690
|
+
*/
|
|
637
691
|
function defineWorkerFetch(config, handler, loggerOptions = {}) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
},
|
|
645
|
-
config
|
|
646
|
-
);
|
|
647
|
-
return {
|
|
648
|
-
fetch(request, env, ctx) {
|
|
649
|
-
return Promise.resolve(
|
|
650
|
-
wrapped.fetch(request, env, ctx)
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
};
|
|
692
|
+
const wrapped = instrument({ fetch(request, env, ctx) {
|
|
693
|
+
return handler(request, env, ctx, createWorkersLogger(request, loggerOptions));
|
|
694
|
+
} }, config);
|
|
695
|
+
return { fetch(request, env, ctx) {
|
|
696
|
+
return Promise.resolve(wrapped.fetch(request, env, ctx));
|
|
697
|
+
} };
|
|
654
698
|
}
|
|
655
699
|
|
|
656
|
-
|
|
657
|
-
|
|
700
|
+
//#endregion
|
|
701
|
+
export { createWorkersLogger, defineWorkerFetch, getActorLogger, getQueueLogger, getRequestLogger, getWorkflowLogger, instrument, instrumentAI, instrumentAnalyticsEngine, instrumentBindings, instrumentBrowserRendering, instrumentD1, instrumentDO, instrumentGlobalCache, instrumentGlobalFetch, instrumentHyperdrive, instrumentImages, instrumentKV, instrumentQueueProducer, instrumentR2, instrumentRateLimiter, instrumentServiceBinding, instrumentVectorize, instrumentWorkflow, wrapDurableObject, wrapModule };
|
|
658
702
|
//# sourceMappingURL=index.js.map
|