You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The following APIs already exist but will behave differently:
@@ -119,6 +130,135 @@ You can still clone scopes manually the same way as before, e.g. via `Scope.clon
119
130
120
131
You can update the client of a scope via `scope.setClient(newClient)`. This will not affect any scope that has already been forked off this scope, but any scope forked off _after_ the client was updated will also receive the updated client.
121
132
133
+
Every scope is always tied to a client.
134
+
135
+
## Global Scope
136
+
137
+
In addition to the currently active scope, there will also be a new special scope, the **Global Scope**.
138
+
The global scope is _not_ the initial scope, but a special scope that belongs to a client and is applied to any event that belongs to this client.
139
+
140
+
You can get the current global scope via `getGlobalScope()`. There _may_ be a function `setGlobalScope(scope)` to update the global scope - or SDKs can decide that there is no need to update the global scope, you can only mutate it.
141
+
142
+
If you call `getGlobalScope()` before a client is initialized, we should still get a global scope back (tied to a Noop client). Once an actual client is initialized, the global scope of the noop client should be merged into the new global scope for the new client. This should ensure that even if you call `getGlobalScope().setTag(...)` before the SDK is initialized, no data is lost.
143
+
144
+
The reason that the global scope is not the same as the initial scope of a client, is that you cannot accidentally mutate it - nothing ever inherits off the global scope.
145
+
146
+
## Isolation Scopes
147
+
148
+
Furthermore, there can also be **Isolation Scopes**.
149
+
Similar to the global scope, these are also applied to events. However, isolation scopes can be created, either by us internally (the most common scenario), or also by users. The new APIs for this are:
150
+
151
+
```js
152
+
// Returns the currently active isolation scope.
153
+
exportfunctiongetIsolationScope(): Scope;
154
+
155
+
// Create a new isolation scope for this scope
156
+
// This will NOT make this scope the isolation scope, but will create a new isolation scope (based on the currently active isolation scope, if one exists)
157
+
scope.isolate();
158
+
159
+
// Similar to `withScope`, but it forks a new scope AND sets a new isolation scope for this context
160
+
export function withIsolationScope(callback: (scope) => void): void;
161
+
```
162
+
163
+
You can fetch the currently active isolation scope via `getIsolationScope()`. You can define a new isolation scope via `scope.isolate()`, which will define a new isolation scope for this scope, and for all scopes that will be forked off this scope. When a client is created & bound, an initial isolation scope will immediately be created, similar to the global scope for a client.
164
+
165
+
An isolation scope is attached to the current execution context, similar to the active scope. There is always exactly one active isolation scope. If you call `getIsolationScope()` before a client has been created, a noop isolation scope is returned, which should be merged in once a client is actually created (same as with the global scope).
166
+
167
+
Similar to the global scope, an isolation scope is always a separate scope, so nothing will inherit off it - except for a potential superseding isolation scope.
168
+
If an isolation scope is created, and there is already an isolation scope in the current execution context, then the new isolation scope should be forked off the previous one (with copy-on-write).
169
+
170
+
### When to create an isolation scope
171
+
172
+
For most server-side SDKs, an isolation scope will be created for each request being processed.
173
+
Roughly, it will equate to each time we currently fork a hub.
174
+
175
+
### Examples for isolation scopes
176
+
177
+
Example for instrumentation that we would write:
178
+
179
+
```ts
180
+
function wrapHttpServerRequest(original: Function): Function {
181
+
// Fork an execution context for this server request, that is isolated
182
+
returnSentry.withIsolatedScope((scope) => {
183
+
// anything in here will have the same isolated scope!
184
+
returnoriginal();
185
+
})
186
+
}
187
+
```
188
+
189
+
Example for hooking into external auto-instrumentation (e.g. OpenTelemetry):
190
+
191
+
```ts
192
+
let onRequestHook: (span:Span) =>void;
193
+
194
+
// This method is not defined by us, but is some external code
195
+
// Here just for demonstration purposes of how that may be implemented
196
+
function otelWrapHttpServerRequest(original:Function):Function {
197
+
// Fork an execution context for this server request,
198
+
// but without isolating this!
199
+
returnSentry.withScope((scope) => {
200
+
onRequestHook(trace.getActiveSpan());
201
+
returnoriginal();
202
+
});
203
+
}
204
+
205
+
// This would be our custom sentry configuration
206
+
onRequestHook= () => {
207
+
const scope =getScope();
208
+
scope.isolate(); // Add an isolation scope to the already forked scope
209
+
}
210
+
```
211
+
212
+
## Applying scopes
213
+
214
+
Scopes are applied in this order to events:
215
+
216
+
```ts
217
+
classScope {
218
+
public captureEvent(event:Event, additionalScope?:Scope) {
219
+
// Global scope is always applied first
220
+
const scopeData =getGlobalScope().getScopeData();
221
+
222
+
// Apply isolations cope next
223
+
const isolationScope =getIsolationScope();
224
+
merge(scopeData, isolationScope.getScopeData());
225
+
226
+
// Now the scope data itself is added
227
+
merge(scopeData, scope.getScopeData());
228
+
229
+
// If defined, add the captureContext/scope
230
+
// This is e.g. what you pass to Sentry.captureException(error, { tags: [] })
231
+
if (additionalScope) {
232
+
merge(scopeData, additionalScope.getScopeData());
233
+
}
234
+
235
+
// Finally, this is merged with event data, where event data takes precedence!
236
+
mergeIntoEvent(event, scopeData);
237
+
}
238
+
}
239
+
```
240
+
241
+
Note that there is _always_ exactly one global & one isolation scope active.
242
+
243
+
## What about environments that do not have isolation of execution contexts (e.g. mobile, browser)?
244
+
245
+
Where not useful, you simply don't have to use the isolation scope. But it's always there, if the need arises.
246
+
While it is empty it does nothing anyhow.
247
+
248
+
## What should be called from top level methods?
249
+
250
+
Top level APIs should generally interactive with the current active scope:
251
+
252
+
```js
253
+
Sentry.setTag();
254
+
Sentry.setUser();
255
+
Sentry.captureException();
256
+
// ...
257
+
```
258
+
259
+
The only exception is `addBreadcrumb()`. This should generally add breadcrumbs to the currently active isolation scope.
260
+
SDKs _may_ also add an option to the client to opt-in to put breadcrumbs on the global scope instead (e.g. for mobile or scenarios where you always want breadcrumbs to be global).
261
+
122
262
## Should users care about Clients?
123
263
124
264
Generally speaking, for most regular use cases the client should be mostly hidden away from users.
@@ -136,17 +276,21 @@ These occurences should be updated to instead take a scope or a client.
136
276
We should strive to provide a wrapper/proxy `getCurrentHub()` method that still exposes the key functionality to ease upgrading. E.g.:
0 commit comments