-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathclient.ts
More file actions
183 lines (163 loc) · 6.33 KB
/
client.ts
File metadata and controls
183 lines (163 loc) · 6.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import type { ClientOptions, Options, ServerRuntimeClientOptions } from '@sentry/core';
import { applySdkMetadata, debug, ServerRuntimeClient } from '@sentry/core';
import { DEBUG_BUILD } from './debug-build';
import type { makeFlushLock } from './flush';
import type { CloudflareTransportOptions } from './transport';
/**
* The Sentry Cloudflare SDK Client.
*
* @see CloudflareClientOptions for documentation on configuration options.
* @see ServerRuntimeClient for usage documentation.
*/
export class CloudflareClient extends ServerRuntimeClient {
private readonly _flushLock: ReturnType<typeof makeFlushLock> | void;
private _pendingSpans: Set<string> = new Set();
private _spanCompletionPromise: Promise<void> | null = null;
private _resolveSpanCompletion: (() => void) | null = null;
/**
* Creates a new Cloudflare SDK instance.
* @param options Configuration options for this SDK.
*/
public constructor(options: CloudflareClientOptions) {
applySdkMetadata(options, 'cloudflare');
options._metadata = options._metadata || {};
const { flushLock, ...serverOptions } = options;
const clientOptions: ServerRuntimeClientOptions = {
...serverOptions,
platform: 'javascript',
// TODO: Grab version information
runtime: { name: 'cloudflare' },
// TODO: Add server name
};
super(clientOptions);
this._flushLock = flushLock;
// Track span lifecycle to know when to flush
this.on('spanStart', span => {
const spanId = span.spanContext().spanId;
DEBUG_BUILD && debug.log('[CloudflareClient] Span started:', spanId);
this._pendingSpans.add(spanId);
if (!this._spanCompletionPromise) {
this._spanCompletionPromise = new Promise(resolve => {
this._resolveSpanCompletion = resolve;
});
}
});
this.on('spanEnd', span => {
const spanId = span.spanContext().spanId;
DEBUG_BUILD && debug.log('[CloudflareClient] Span ended:', spanId);
this._pendingSpans.delete(spanId);
// If no more pending spans, resolve the completion promise
if (this._pendingSpans.size === 0 && this._resolveSpanCompletion) {
DEBUG_BUILD && debug.log('[CloudflareClient] All spans completed, resolving promise');
this._resolveSpanCompletion();
this._resetSpanCompletionPromise();
}
});
}
/**
* Returns a promise that resolves when all waitUntil promises have completed.
* This allows the root span to stay open until all waitUntil work is done.
*
* @return {Promise<void>} A promise that resolves when all waitUntil promises are done.
*/
public async waitUntilDone(): Promise<void> {
if (this._flushLock) {
await this._flushLock.finalize();
}
}
/**
* Flushes pending operations and ensures all data is processed.
* If a timeout is provided, the operation will be completed within the specified time limit.
*
* It will wait for all pending spans to complete before flushing.
*
* @param {number} [timeout] - Optional timeout in milliseconds to force the completion of the flush operation.
* @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the flush operation was successful.
*/
public async flush(timeout?: number): Promise<boolean> {
await this.waitUntilDone();
if (this._pendingSpans.size > 0 && this._spanCompletionPromise) {
DEBUG_BUILD &&
debug.log('[CloudflareClient] Waiting for', this._pendingSpans.size, 'pending spans to complete...');
const timeoutMs = timeout ?? 5000;
const spanCompletionRace = Promise.race([
this._spanCompletionPromise,
new Promise(resolve =>
setTimeout(() => {
DEBUG_BUILD &&
debug.log('[CloudflareClient] Span completion timeout after', timeoutMs, 'ms, flushing anyway');
resolve(undefined);
}, timeoutMs),
),
]);
await spanCompletionRace;
}
return super.flush(timeout);
}
/**
* Resets the span completion promise and resolve function.
*/
private _resetSpanCompletionPromise(): void {
this._pendingSpans.clear();
this._spanCompletionPromise = null;
this._resolveSpanCompletion = null;
}
}
interface BaseCloudflareOptions {
/**
* @ignore Used internally to disable the deDupeIntegration for workflows.
* @hidden Used internally to disable the deDupeIntegration for workflows.
* @default true
*/
enableDedupe?: boolean;
/**
* The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility
* via a custom trace provider.
* This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.
* HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.
* This should be good enough for many, but not all integrations.
*
* If you want to opt-out of setting up the OpenTelemetry compatibility tracer, set this to `true`.
*
* @default false
*/
skipOpenTelemetrySetup?: boolean;
/**
* Enable instrumentation of prototype methods for DurableObjects.
*
* When `true`, the SDK will wrap all methods on the DurableObject prototype chain
* to automatically create spans and capture errors for RPC method calls.
*
* When an array of strings is provided, only the specified method names will be instrumented.
*
* This feature adds runtime overhead as it wraps methods at the prototype level.
* Only enable this if you need automatic instrumentation of prototype methods.
*
* @default false
* @example
* ```ts
* // Instrument all prototype methods
* instrumentPrototypeMethods: true
*
* // Instrument only specific methods
* instrumentPrototypeMethods: ['myMethod', 'anotherMethod']
* ```
*/
instrumentPrototypeMethods?: boolean | string[];
}
/**
* Configuration options for the Sentry Cloudflare SDK
*
* @see @sentry/core Options for more information.
*/
export interface CloudflareOptions extends Options<CloudflareTransportOptions>, BaseCloudflareOptions {
ctx?: ExecutionContext;
}
/**
* Configuration options for the Sentry Cloudflare SDK Client class
*
* @see CloudflareClient for more information.
*/
export interface CloudflareClientOptions extends ClientOptions<CloudflareTransportOptions>, BaseCloudflareOptions {
flushLock?: ReturnType<typeof makeFlushLock>;
}