Scopes and Hubs
When an event is captured and sent to Sentry, SDKs will merge that event data with extra information from the current scope. SDKs will typically automatically manage the scopes for you in the framework integrations and you don't need to think about them. However, you should know what a scope is and how you can use it for your advantage.
What's a Scope, What's a Hub
You can think of the hub as the central point that our SDKs use to route an
event to Sentry. When you call init()
a hub is created and a client and a
blank scope are created on it. That hub is then associated with the current
thread and will internally hold a stack of scopes.
The scope will hold useful information that should be sent along with the event. For instance contexts or breadcrumbs are stored on the scope. When a scope is pushed, it inherits all data from the parent scope and when it pops all modifications are reverted.
The default SDK integrations will push and pop scopes intelligently. For instance web framework integrations will create and destroy scopes around your routes or controllers.
How the Scope and Hub Work
As you start using an SDK, a scope and hub are automatically created for you out
of the box. You are unlikely to be interacting with the hub directly unless you
are writing an integration or you want to create or destroy scopes. Scopes, on the
other hand are more user facing. You can at any point in time call
configure-scope
to modify data stored on the scope. This is for instance
used to modify the context.
Effectively this means that when you spawn a task in .NET and the execution flow is not suppressed all the context you have bound to the scope in Sentry will flow along. If however you suppress the flow, you get new scope data.
When you call a global function such as capture_event
internally Sentry
discovers the current hub and asks it to capture an event. Internally the hub will
then merge the event with the topmost scope's data.
Configuring the Scope
The most useful operation when working with scopes is the configure-scope
function. It can be used to reconfigure the current scope.
You can, for instance, add custom tags or inform Sentry about the currently authenticated user.
Using SentrySDK:configureScope
lets you set context data globally, which will be attached to all future events.
import Sentry
SentrySDK.configureScope { scope in
scope.setTag(value: "my-tag", key: "my value")
let user = User()
user.email = "john.doe@example.com"
scope.setUser(user)
}
Passing a Scope Instance
Setting an instance of Scope is helpful when you want to completely control what should be attached to the event.
import Sentry
let exception = NSException(name: NSExceptionName("My Custom exception"), reason: "User clicked the button", userInfo: nil)
let scope = Scope()
scope.setLevel(.fatal)
// By explicity just passing the scope, only the data in this scope object will be added to the event
// The global scope (calls to configureScope) will be ignored
// Only do this if you have mastered this SDK, otherwise, you risk losing useful info
// If you just want to mutate what's in the scope use the callback, see: captureError
SentrySDK.capture(exception: exception, scope: scope)
Using Scope Callback
To maintain global state, but mutate context data for one capture call, use the Scope callback:
import Sentry
let userInfo = [NSLocalizedDescriptionKey : "Object does not exist"]
let error = NSError(domain: "YourErrorDomain", code: 0, userInfo: userInfo)
SentrySDK.capture(error: error) { (scope) in
// Changes in here will only be captured for this event
// The scope in this callback is a clone of the current scope
// It contains all data but mutations only influence the event being sent
scope.setTag(value: "value", key: "myTag")
}
You can also apply this configuration when unsetting a user at logout:
import Sentry
SentrySDK.setUser(nil)
To learn what useful information can be associated with scopes see the context documentation.
Local Scopes
We also have support for pushing and configuring a scope in one go. This is typically called with-scope
or push-scope
which is also very helpful if you only want to send data with one specific event. In the following example we are using that function to attach a level
and a tag
to only one specific error:
import Sentry
SentrySDK.capture(error: error) { scope in
scope.setLevel(.warning)
// will be tagged with my-tag="my value"
scope.setTag(value: "my value", key: "my-tag")
}
// will not be tagged with my-tag
SentrySDK.capture(error: error)
While this example looks similar to configure-scope
, it's very different, in the sense that configure-scope
actually changes the current active scope, all successive calls to configure-scope
will keep the changes.
While on the other hand using with-scope
creates a clone of the current scope
and will stay isolated until the function call is completed. So you can either
set context information in there that you don't want to be somewhere else or not
attach any context information at all by calling clear
on the scope, while the
"global" scope remains unchanged.
Keep in mind that with-scope
will not capture any exceptions that happen inside its callback function,
and every error that happens there will be silently ignored and not reported.
- Package:
- cocoapods:sentry-cocoa
- Version:
- 8.32.0
- Repository:
- https://github.com/getsentry/sentry-cocoa