diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7b69a11..fa0c1af5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,16 @@
## Unreleased
+### Breaking changes
+
+- Configuration script support and `SentryConfiguration` class are removed. Instead, please use manual initialization with a configuration callback, if you need to set up SDK from code. See [#321](https://github.com/getsentry/sentry-godot/pull/321) for details.
+- `enabled` option is renamed to `auto_init` for clarity, and removed from SentryOptions properties (setting it from code has no sense - we auto-initialize very early).
+- `disabled_in_editor_play` option is renamed to `skip_auto_init_on_editor_play` for clarity, and removed from SentryOptions properties.
+
### Features
- Support local variables on Android ([#334](https://github.com/getsentry/sentry-godot/pull/334))
+- Allow initializing manually and shutting down SentrySDK ([#321](https://github.com/getsentry/sentry-godot/pull/321))
### Other changes
diff --git a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt
index 841e3020..3b6a2c4f 100644
--- a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt
+++ b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt
@@ -123,7 +123,7 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
}
@UsedByGodot
- fun initialize(
+ fun init(
beforeSendHandlerId: Long,
dsn: String,
debug: Boolean,
@@ -154,6 +154,16 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
}
}
+ @UsedByGodot
+ fun close() {
+ Sentry.close()
+ }
+
+ @UsedByGodot
+ fun isEnabled(): Boolean {
+ return Sentry.isEnabled()
+ }
+
@UsedByGodot
fun addFileAttachment(path: String, filename: String, contentType: String, attachmentType: String) {
val attachment = Attachment(
diff --git a/doc_classes/SentryConfiguration.xml b/doc_classes/SentryConfiguration.xml
deleted file mode 100644
index 72dc1f26..00000000
--- a/doc_classes/SentryConfiguration.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
- Base class for user configuration script in Sentry SDK.
-
-
- Allows configuring Sentry SDK from GDScript. The [method _configure] method is called just before Sentry SDK initializes.
- To define a configuration script, create a new script that extends the [SentryConfiguration] class. Then, assign your configuration script in the Project Settings under the [b]Sentry[/b] category.
- [b]Important[/b]: If a user configuration script is assigned, SDK initialization will be delayed until [b]ScriptServer[/b] becomes available during game startup.
- [codeblock]
- extends SentryConfiguration
-
- func _configure(options: SentryOptions):
- if OS.is_debug_build():
- options.environment = "debug"
- options.debug = true
- options.release = "mygame@1.0.0"
- options.before_send = _process_event
-
- func _process_event(event: SentryEvent) -> SentryEvent:
- if event.environment == "debug":
- # Discard event if running in a debug build.
- return null
- if event.message.contains("Bruno"):
- # Remove sensitive information from the event.
- event.message = event.message.replace("Bruno", "REDACTED")
- return event
- [/codeblock]
-
-
-
-
-
-
-
-
- Called just before the Sentry SDK initializes. You can override SDK [param options] here and register [member SentryOptions.before_send] callback if needed.
-
-
-
-
diff --git a/doc_classes/SentryOptions.xml b/doc_classes/SentryOptions.xml
index 64332d56..1640596c 100644
--- a/doc_classes/SentryOptions.xml
+++ b/doc_classes/SentryOptions.xml
@@ -4,7 +4,10 @@
Contains Sentry SDK options.
- Defines options for the Sentry SDK. These options can be modified in the Project Settings under the [b]Sentry[/b] category or through a user configuration script. See [SentryConfiguration] for details on creating such a script.
+ Defines options for the Sentry SDK. These options can be modified in the Project Settings under the [b]Sentry[/b] category or from code through a configuration callback using manual initialization. See [method SentrySDK.init].
+ There are two options, that only appear in the project settings:
+ [b]Auto Init[/b] - Enables automatic initialization of the Sentry SDK when the game starts.
+ [b]Skip Auto Init on Editor Play[/b] - Prevents automatic initialization when running the game from the editor (by pressing the play button or F5).
To learn more, visit [url=https://docs.sentry.io/platforms/godot/configuration/options/]Options documentation[/url].
@@ -29,7 +32,7 @@
[/codeblock]
- If assigned, this callback runs before an event is sent to Sentry. It takes [SentryEvent] as a parameter and return either the same event object, with or without modifications, or [code]null[/code] to skip reporting the event. You can assign it in a [SentryConfiguration] script. To check if the event is a crash, use [method SentryEvent.is_crash].
+ If assigned, this callback runs before an event is sent to Sentry. It takes [SentryEvent] as a parameter and return either the same event object, with or without modifications, or [code]null[/code] to skip reporting the event. You can assign it in a configuration callback using manual initialization (see [method SentrySDK.init]). To check if the event is a crash, use [method SentryEvent.is_crash].
[codeblock]
func _before_send(event: SentryEvent) -> SentryEvent:
if event.environment == "editor_dev_run":
@@ -49,20 +52,14 @@
Specifies the minimum level of messages to be printed if [member debug] is enabled.
-
- If [code]true[/code], the SDK will not initialize when you play/run your project from within the Godot editor (using the play button or F5).
-
The application's distribution. Distributions are used to disambiguate build or deployment variants of the same release of an application.
Data Source Name (DSN): Specifies where the SDK should send the events. If this value is not provided, the SDK will try to read it from the [code]SENTRY_DSN[/code] environment variable. If that variable also does not exist, the SDK will just not send any events.
-
- If [code]false[/code], the SDK will not initialize. This is useful for temporarily disabling the SDK in the Project Settings, or in a [SentryConfiguration] script.
-
- Environments indicate where an error occurred, such as in a release export, headless server, QA build, or another deployment. The SDK automatically detects Godot-specific environments, such as [code]headless_server[/code] and [code]export_release[/code], but you can also assign it in a [SentryConfiguration] script.
+ Environments indicate where an error occurred, such as in a release export, headless server, QA build, or another deployment. The SDK automatically detects Godot-specific environments, such as [code]headless_server[/code] and [code]export_release[/code], but you can also assign it in a configuration callback using manual initialization (see [method SentrySDK.init]).
To learn more, visit [url=https://docs.sentry.io/platforms/godot/configuration/environments/]Environments documentation[/url].
diff --git a/doc_classes/SentrySDK.xml b/doc_classes/SentrySDK.xml
index b3dec615..38510b60 100644
--- a/doc_classes/SentrySDK.xml
+++ b/doc_classes/SentrySDK.xml
@@ -44,6 +44,12 @@
Captures an event with [param message] and sends it to Sentry, returning the event ID.
+
+
+
+ Closes the SDK and flushes any pending events to ensure all queued data is sent to Sentry before shutdown. Called automatically when the application exits.
+
+
@@ -62,6 +68,35 @@
Returns the currently set user. See [SentryUser].
+
+
+
+
+ Initializes the SDK. Called automatically during startup if "Auto Init" is enabled in the project settings.
+ Manual initialization is only needed when you want to control the exact timing of SDK startup, or when you need to initialize options from code, or use callbacks such as [member SentryOptions.before_send]. Make sure to disable "Auto Init" in the project settings if you intend to manually initialize the SDK.
+ [b]Note[/b]: The earliest place to initialize Sentry from script is in the [method MainLoop._initialize].
+ You can customize the SDK's options by passing a [param configuration_callback] function. Here's a complete example:
+ [codeblock]
+ class_name ProjectMainLoop
+ extends SceneTree
+ # Tip: Assign ProjectMainLoop as your main loop type in the project settings
+ # under `application/run/main_loop_type`.
+
+ func _initialize() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ options.debug = true
+ options.release = "my-game@1.2.3"
+ options.environment = "production"
+ options.before_send = _on_before_send_to_sentry
+ )
+
+ func _on_before_send_to_sentry(event: SentryEvent) -> SentryEvent:
+ # Process event and return either the same event object,
+ # with or withour modifications, or null to skip reporting the event.
+ return event
+ [/codeblock]
+
+
diff --git a/project/example_configuration.gd b/project/example_configuration.gd
deleted file mode 100644
index 5cf1f5ba..00000000
--- a/project/example_configuration.gd
+++ /dev/null
@@ -1,31 +0,0 @@
-extends SentryConfiguration
-## Example Sentry configuration script.
-##
-## Tip: You can assign configuration script in the project settings.
-
-
-## Configure Sentry SDK options
-func _configure(options: SentryOptions) -> void:
- print("INFO: [example_configuration.gd] Configuring SDK options via GDScript")
-
- options.debug = true
- options.release = "sentry-godot-demo@" + ProjectSettings.get_setting("application/config/version")
- options.environment = "demo"
-
- # Set up event callbacks
- options.before_send = _before_send
-
- # Unit testing hooks (if you're exploring the demo project, just ignore the following line).
- load("res://testing_configuration.gd").configure_options(options)
-
-
-## before_send callback example
-func _before_send(ev: SentryEvent) -> SentryEvent:
- print("INFO: [example_configuration.gd] Processing event: ", ev.id)
- if ev.message.contains("Bruno"):
- print("INFO: [example_configuration.gd] Removing sensitive information from the event")
- ev.message = ev.message.replace("Bruno", "REDACTED")
- elif ev.message == "junk":
- print("INFO: [example_configuration.gd] Discarding event with message 'junk'")
- return null
- return ev
diff --git a/project/example_configuration.gd.uid b/project/example_configuration.gd.uid
deleted file mode 100644
index 23891733..00000000
--- a/project/example_configuration.gd.uid
+++ /dev/null
@@ -1 +0,0 @@
-uid://dmavxxyqbqght
diff --git a/project/project.godot b/project/project.godot
index 88c1a35e..e2cc425e 100644
--- a/project/project.godot
+++ b/project/project.godot
@@ -13,6 +13,7 @@ config_version=5
config/name="Sentry demo project"
config/version="1.0.0-beta.1"
run/main_scene="uid://cqiowj0jydds1"
+run/main_loop_type="ProjectMainLoop"
config/features=PackedStringArray("4.5")
run/flush_stdout_on_print=true
config/icon="uid://djdyhgbcdue6n"
@@ -45,8 +46,8 @@ textures/vram_compression/import_etc2_astc=true
[sentry]
+options/auto_init=false
options/dsn="https://3f1e095cf2e14598a0bd5b4ff324f712@o447951.ingest.us.sentry.io/6680910"
-options/configuration_script="uid://dmavxxyqbqght"
options/attach_screenshot=true
options/attach_scene_tree=true
logger/include_variables=true
diff --git a/project/project_main_loop.gd b/project/project_main_loop.gd
new file mode 100644
index 00000000..b6678e52
--- /dev/null
+++ b/project/project_main_loop.gd
@@ -0,0 +1,36 @@
+class_name ProjectMainLoop
+extends SceneTree
+## Example of initializing and configuring Sentry from code.
+##
+## The earliest place to initialize Sentry in script is in the MainLoop._initialize().
+## Tip: You can assign "ProjectMainLoop" as your main loop class in the project settings
+## under `application/run/main_loop_type`.
+
+
+func _initialize() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ print("INFO: [ProjectMainLoop] Initializing SDK from GDScript")
+
+ options.debug = true
+ options.release = "sentry-godot-demo@" + ProjectSettings.get_setting("application/config/version")
+ options.environment = "demo"
+
+ # Set up event callbacks
+ options.before_send = _on_before_send_to_sentry
+ )
+
+ # Post-initialize
+ # SentrySDK.add_attachment(...)
+ # ...
+
+
+## before_send example
+func _on_before_send_to_sentry(ev: SentryEvent) -> SentryEvent:
+ print("INFO: [ProjectMainLoop] Processing event: ", ev.id)
+ if ev.message.contains("Bruno"):
+ print("INFO: [ProjectMainLoop] Removing sensitive information from the event")
+ ev.message = ev.message.replace("Bruno", "REDACTED")
+ elif ev.message == "junk":
+ print("INFO: [ProjectMainLoop] Discarding event with message 'junk'")
+ return null
+ return ev
diff --git a/project/project_main_loop.gd.uid b/project/project_main_loop.gd.uid
new file mode 100644
index 00000000..14b29ada
--- /dev/null
+++ b/project/project_main_loop.gd.uid
@@ -0,0 +1 @@
+uid://ckyjdnckvfm8b
diff --git a/project/test/isolated/test_limit_events_per_frame.gd b/project/test/isolated/test_limit_events_per_frame.gd
index 493f38f4..663d5ffc 100644
--- a/project/test/isolated/test_limit_events_per_frame.gd
+++ b/project/test/isolated/test_limit_events_per_frame.gd
@@ -7,16 +7,15 @@ signal callback_processed
var _num_events: int = 0
-static func configure_options(options: SentryOptions) -> void:
- # Only one error is allowed to be logged as event per processed frame.
- options.logger_limits.events_per_frame = 1
- # Make sure other limits are not interfering.
- options.logger_limits.repeated_error_window_ms = 0
- options.logger_limits.throttle_events = 88
-
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ # Only one error is allowed to be logged as event per processed frame.
+ options.logger_limits.events_per_frame = 1
+ # Make sure other limits are not interfering.
+ options.logger_limits.repeated_error_window_ms = 0
+ options.logger_limits.throttle_events = 88
+ options.before_send = _before_send
+ )
func _before_send(_ev: SentryEvent) -> SentryEvent:
@@ -27,10 +26,12 @@ func _before_send(_ev: SentryEvent) -> SentryEvent:
## Only one error should be logged within 1 processed frame.
func test_events_per_frame_limit() -> void:
- var monitor := monitor_signals(self, false)
+ monitor_signals(self, false)
+
push_error("dummy-error")
push_error("dummy-error")
push_error("dummy-error")
- await assert_signal(monitor).is_emitted("callback_processed")
+
+ await assert_signal(self).is_emitted("callback_processed")
await get_tree().create_timer(0.1).timeout
assert_int(_num_events).is_equal(1)
diff --git a/project/test/isolated/test_limit_repeating_error_window.gd b/project/test/isolated/test_limit_repeating_error_window.gd
index 2ae788c4..c5d43115 100644
--- a/project/test/isolated/test_limit_repeating_error_window.gd
+++ b/project/test/isolated/test_limit_repeating_error_window.gd
@@ -7,16 +7,15 @@ signal callback_processed
var _num_events: int = 0
-static func configure_options(options: SentryOptions) -> void:
- # Ignore duplicate errors within 1 second window.
- options.logger_limits.repeated_error_window_ms = 1000
- # Make sure other limits are not interfering.
- options.logger_limits.events_per_frame = 88
- options.logger_limits.throttle_events = 88
-
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ # Ignore duplicate errors within 1 second window.
+ options.logger_limits.repeated_error_window_ms = 1000
+ # Make sure other limits are not interfering.
+ options.logger_limits.events_per_frame = 88
+ options.logger_limits.throttle_events = 88
+ options.before_send = _before_send
+ )
func _before_send(_ev: SentryEvent) -> SentryEvent:
diff --git a/project/test/isolated/test_limit_throttling.gd b/project/test/isolated/test_limit_throttling.gd
index 182c0806..3739a86c 100644
--- a/project/test/isolated/test_limit_throttling.gd
+++ b/project/test/isolated/test_limit_throttling.gd
@@ -7,17 +7,16 @@ signal callback_processed
var _num_events: int = 0
-static func configure_options(options: SentryOptions) -> void:
- # Allow only two errors to be logged as events within 1 second time window.
- options.logger_limits.throttle_events = 2
- options.logger_limits.throttle_window_ms = 1000
- # Make sure other limits are not interfering.
- options.logger_limits.events_per_frame = 88
- options.logger_limits.repeated_error_window_ms = 0
-
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ # Allow only two errors to be logged as events within 1 second time window.
+ options.logger_limits.throttle_events = 2
+ options.logger_limits.throttle_window_ms = 1000
+ # Make sure other limits are not interfering.
+ options.logger_limits.events_per_frame = 88
+ options.logger_limits.repeated_error_window_ms = 0
+ options.before_send = _before_send
+ )
func _before_send(_ev: SentryEvent) -> SentryEvent:
diff --git a/project/test/isolated/test_logger_disabled.gd b/project/test/isolated/test_logger_disabled.gd
index 34416f46..2e987230 100644
--- a/project/test/isolated/test_logger_disabled.gd
+++ b/project/test/isolated/test_logger_disabled.gd
@@ -7,30 +7,30 @@ signal callback_processed
var _num_events: int = 0
-static func configure_options(options: SentryOptions) -> void:
- options.logger_enabled = false
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ options.logger_enabled = false
- # Make sure other limits are not interfering.
- options.logger_limits.events_per_frame = 88
- options.logger_limits.throttle_events = 88
- options.logger_limits.repeated_error_window_ms = 0
- options.logger_limits.throttle_window_ms = 0
+ # Make sure other limits are not interfering.
+ options.logger_limits.events_per_frame = 88
+ options.logger_limits.throttle_events = 88
+ options.logger_limits.repeated_error_window_ms = 0
+ options.logger_limits.throttle_window_ms = 0
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+ options.before_send = _before_send
+ )
func _before_send(_ev: SentryEvent) -> SentryEvent:
- _num_events += 1
- callback_processed.emit()
- return null
+ _num_events += 1
+ callback_processed.emit()
+ return null
func test_event_and_breadcrumb_masks() -> void:
- var monitor := monitor_signals(self, false)
- push_error("dummy-error")
- push_warning("dummy-warning")
- await assert_signal(monitor).is_not_emitted("callback_processed")
+ var monitor := monitor_signals(self, false)
+ push_error("dummy-error")
+ push_warning("dummy-warning")
+ await assert_signal(monitor).is_not_emitted("callback_processed")
- assert_int(_num_events).is_equal(0)
+ assert_int(_num_events).is_equal(0)
diff --git a/project/test/isolated/test_logger_with_masks.gd b/project/test/isolated/test_logger_with_masks.gd
index 83af8719..0e90dcfa 100644
--- a/project/test/isolated/test_logger_with_masks.gd
+++ b/project/test/isolated/test_logger_with_masks.gd
@@ -8,35 +8,35 @@ signal callback_processed
var _num_events: int = 0
-static func configure_options(options: SentryOptions) -> void:
- var mask = SentryOptions.MASK_ERROR | SentryOptions.MASK_SCRIPT | SentryOptions.MASK_SHADER | SentryOptions.MASK_WARNING
- options.logger_event_mask = mask
- options.logger_breadcrumb_mask = mask
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ var mask = SentryOptions.MASK_ERROR | SentryOptions.MASK_SCRIPT | SentryOptions.MASK_SHADER | SentryOptions.MASK_WARNING
+ options.logger_event_mask = mask
+ options.logger_breadcrumb_mask = mask
- # Make sure other limits are not interfering.
- options.logger_limits.events_per_frame = 88
- options.logger_limits.throttle_events = 88
- options.logger_limits.repeated_error_window_ms = 0
- options.logger_limits.throttle_window_ms = 0
+ # Make sure other limits are not interfering.
+ options.logger_limits.events_per_frame = 88
+ options.logger_limits.throttle_events = 88
+ options.logger_limits.repeated_error_window_ms = 0
+ options.logger_limits.throttle_window_ms = 0
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+ options.before_send = _before_send
+ )
func _before_send(_ev: SentryEvent) -> SentryEvent:
- _num_events += 1
- callback_processed.emit()
- return null
+ _num_events += 1
+ callback_processed.emit()
+ return null
## Both events or breadcrumbs should be logged for error and warning.
## TODO: can't verify breadcrumbs yet, maybe later.
func test_event_and_breadcrumb_masks() -> void:
- var monitor := monitor_signals(self, false)
- push_error("dummy-error")
- push_warning("dummy-warning")
- await assert_signal(monitor).is_emitted("callback_processed")
+ var monitor := monitor_signals(self, false)
+ push_error("dummy-error")
+ push_warning("dummy-warning")
+ await assert_signal(monitor).is_emitted("callback_processed")
- await get_tree().create_timer(0.1).timeout
- assert_int(_num_events).is_equal(2)
+ await get_tree().create_timer(0.1).timeout
+ assert_int(_num_events).is_equal(2)
diff --git a/project/test/isolated/test_logger_with_masks_empty.gd b/project/test/isolated/test_logger_with_masks_empty.gd
index 989cfa61..888d9fc0 100644
--- a/project/test/isolated/test_logger_with_masks_empty.gd
+++ b/project/test/isolated/test_logger_with_masks_empty.gd
@@ -8,28 +8,28 @@ signal callback_processed
var _num_events: int = 0
-static func configure_options(options: SentryOptions) -> void:
- options.logger_event_mask = 0
- options.logger_breadcrumb_mask = 0
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ options.logger_event_mask = 0
+ options.logger_breadcrumb_mask = 0
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+ options.before_send = _before_send
+ )
func _before_send(_ev: SentryEvent) -> SentryEvent:
- _num_events += 1
- callback_processed.emit()
- return null
+ _num_events += 1
+ callback_processed.emit()
+ return null
## No events or breadcrumbs should be logged for errors.
## TODO: can't verify breadcrumbs yet, maybe later.
func test_event_and_breadcrumb_masks() -> void:
- var monitor := monitor_signals(self, false)
- push_error("dummy-error")
- push_warning("dummy-warning")
- await assert_signal(monitor).is_not_emitted("callback_processed")
+ var monitor := monitor_signals(self, false)
+ push_error("dummy-error")
+ push_warning("dummy-warning")
+ await assert_signal(monitor).is_not_emitted("callback_processed")
- await get_tree().create_timer(0.1).timeout
- assert_int(_num_events).is_equal(0)
+ await get_tree().create_timer(0.1).timeout
+ assert_int(_num_events).is_equal(0)
diff --git a/project/test/isolated/test_options_integrity.gd b/project/test/isolated/test_options_integrity.gd
index a64029f4..f099280c 100644
--- a/project/test/isolated/test_options_integrity.gd
+++ b/project/test/isolated/test_options_integrity.gd
@@ -5,13 +5,13 @@ extends GdUnitTestSuite
signal callback_processed
-static func configure_options(options: SentryOptions) -> void:
- options.release = "1.2.3"
- options.environment = "testing"
+func before() -> void:
+ SentrySDK.init(func(options: SentryOptions) -> void:
+ options.release = "1.2.3"
+ options.environment = "testing"
-
-func before_test() -> void:
- SentrySDK._set_before_send(_before_send)
+ options.before_send = _before_send
+ )
func _before_send(ev: SentryEvent) -> SentryEvent:
diff --git a/project/test/isolated/test_sdk_lifecycle.gd b/project/test/isolated/test_sdk_lifecycle.gd
new file mode 100644
index 00000000..89df4487
--- /dev/null
+++ b/project/test/isolated/test_sdk_lifecycle.gd
@@ -0,0 +1,35 @@
+extends GdUnitTestSuite
+## Test lifecycle methods.
+
+
+signal callback_processed
+
+
+func _before_send(_ev: SentryEvent) -> SentryEvent:
+ callback_processed.emit()
+ return null
+
+
+## Test manual initialization and shutdown of SDK.
+func test_sdk_lifecycle() -> void:
+ monitor_signals(self, false)
+
+ # SDK should be disabled at start.
+ assert_bool(SentrySDK.is_enabled()).is_false()
+
+ SentrySDK.capture_message("message not captured before SDK is initialized")
+ await assert_signal(self).is_not_emitted("callback_processed")
+
+ SentrySDK.init(func (options: SentryOptions) -> void:
+ options.before_send = _before_send
+ )
+ assert_bool(SentrySDK.is_enabled()).is_true()
+
+ SentrySDK.capture_message("message captured when SDK is initialiazed")
+ await assert_signal(self).is_emitted("callback_processed")
+
+ SentrySDK.close()
+ assert_bool(SentrySDK.is_enabled()).is_false()
+
+ SentrySDK.capture_message("message not captured when SDK is closed")
+ await assert_signal(self).is_not_emitted("callback_processed")
diff --git a/project/test/isolated/test_sdk_lifecycle.gd.uid b/project/test/isolated/test_sdk_lifecycle.gd.uid
new file mode 100644
index 00000000..c66c2e19
--- /dev/null
+++ b/project/test/isolated/test_sdk_lifecycle.gd.uid
@@ -0,0 +1 @@
+uid://bnpw7sgibliel
diff --git a/project/test/suites/test_event.gd b/project/test/suites/test_event.gd
index ae6b1d49..dca68a51 100644
--- a/project/test/suites/test_event.gd
+++ b/project/test/suites/test_event.gd
@@ -2,6 +2,11 @@ extends GdUnitTestSuite
## Basic tests for the SentryEvent class.
+func before() -> void:
+ if not SentrySDK.is_enabled():
+ SentrySDK.init()
+
+
## Test string properties accessors and UTF-8 encoding preservation.
@warning_ignore("unused_parameter")
func test_string_properties_and_utf8(property: String, test_parameters := [
diff --git a/project/test/suites/test_event_integrity.gd b/project/test/suites/test_event_integrity.gd
index eeeadecf..7d26e9f8 100644
--- a/project/test/suites/test_event_integrity.gd
+++ b/project/test/suites/test_event_integrity.gd
@@ -7,6 +7,11 @@ signal callback_processed
var created_id: String
+func before() -> void:
+ if not SentrySDK.is_enabled():
+ SentrySDK.init()
+
+
func before_test() -> void:
SentrySDK._set_before_send(_before_send)
diff --git a/project/test/suites/test_options.gd b/project/test/suites/test_options.gd
index 484df4d3..fdf56b5e 100644
--- a/project/test/suites/test_options.gd
+++ b/project/test/suites/test_options.gd
@@ -12,8 +12,6 @@ func before_test() -> void:
## Test simple bool properties.
@warning_ignore("unused_parameter")
func test_bool_properties(property: String, test_parameters := [
- ["enabled"],
- ["disabled_in_editor_play"],
["debug"],
["attach_log"],
["attach_screenshot"],
diff --git a/project/test/suites/test_sdk.gd b/project/test/suites/test_sdk.gd
index 5eec51ac..ddbd0f6d 100644
--- a/project/test/suites/test_sdk.gd
+++ b/project/test/suites/test_sdk.gd
@@ -5,6 +5,11 @@ extends GdUnitTestSuite
signal callback_processed
+func before() -> void:
+ if not SentrySDK.is_enabled():
+ SentrySDK.init()
+
+
## SentrySDK.capture_message() should return a non-empty event ID, which must match the ID returned by the get_last_event_id() call.
func test_capture_message_id() -> void:
var event_id := SentrySDK.capture_message("capture_message_test", SentrySDK.LEVEL_DEBUG)
diff --git a/project/testing_configuration.gd b/project/testing_configuration.gd
deleted file mode 100644
index 674f4063..00000000
--- a/project/testing_configuration.gd
+++ /dev/null
@@ -1,19 +0,0 @@
-extends RefCounted
-
-
-## Detects whether an isolated test suite is running and calls its static `configure_options()` method.
-## - Since SentryOptions can be configured only once, such test suites must be executed separately.
-## - This method is called from "example_configuration.gd".
-static func configure_options(options: SentryOptions):
- var args: PackedStringArray = OS.get_cmdline_args()
- var idx := args.find("-a")
- if idx == -1 or args.size() == idx + 1:
- return
- var path := args[idx + 1]
- if not path.ends_with(".gd"):
- return
- if not FileAccess.file_exists(path):
- return
- var scr: GDScript = load(path)
- if scr.has_method(&"configure_options"):
- scr.call(&"configure_options", options)
diff --git a/project/testing_configuration.gd.uid b/project/testing_configuration.gd.uid
deleted file mode 100644
index 65da1c9f..00000000
--- a/project/testing_configuration.gd.uid
+++ /dev/null
@@ -1 +0,0 @@
-uid://dr6g5e14adpqf
diff --git a/src/register_types.cpp b/src/register_types.cpp
index af509ff0..7297f5a7 100644
--- a/src/register_types.cpp
+++ b/src/register_types.cpp
@@ -7,7 +7,6 @@
#include "sentry/runtime_config.h"
#include "sentry/sentry_attachment.h"
#include "sentry/sentry_breadcrumb.h"
-#include "sentry/sentry_configuration.h"
#include "sentry/sentry_event.h"
#include "sentry/sentry_logger.h"
#include "sentry/sentry_options.h"
@@ -47,7 +46,6 @@ void register_runtime_classes() {
GDREGISTER_CLASS(SentryLoggerLimits);
GDREGISTER_CLASS(SentryOptions);
GDREGISTER_INTERNAL_CLASS(RuntimeConfig);
- GDREGISTER_CLASS(SentryConfiguration);
GDREGISTER_CLASS(SentryUser);
GDREGISTER_CLASS(SentryTimestamp);
GDREGISTER_CLASS(SentrySDK);
diff --git a/src/sentry/android/android_sdk.cpp b/src/sentry/android/android_sdk.cpp
index b39de1a9..159bd265 100644
--- a/src/sentry/android/android_sdk.cpp
+++ b/src/sentry/android/android_sdk.cpp
@@ -134,10 +134,12 @@ void AndroidSDK::add_attachment(const Ref &p_attachment) {
}
}
-void AndroidSDK::init(const PackedStringArray &p_global_attachments) {
+void AndroidSDK::init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) {
ERR_FAIL_NULL(android_plugin);
- sentry::util::print_debug("Initializing Sentry Android SDK");
+ if (p_configuration_callback.is_valid()) {
+ p_configuration_callback.call(SentryOptions::get_singleton());
+ }
for (const String &path : p_global_attachments) {
bool is_view_hierarchy = path.ends_with(SENTRY_VIEW_HIERARCHY_FN);
@@ -148,7 +150,7 @@ void AndroidSDK::init(const PackedStringArray &p_global_attachments) {
is_view_hierarchy ? "event.view_hierarchy" : String());
}
- android_plugin->call("initialize",
+ android_plugin->call(ANDROID_SN(init),
before_send_handler->get_instance_id(),
SentryOptions::get_singleton()->get_dsn(),
SentryOptions::get_singleton()->is_debug_enabled(),
@@ -159,6 +161,16 @@ void AndroidSDK::init(const PackedStringArray &p_global_attachments) {
SentryOptions::get_singleton()->get_max_breadcrumbs());
}
+void AndroidSDK::close() {
+ if (android_plugin != nullptr) {
+ android_plugin->call(ANDROID_SN(close));
+ }
+}
+
+bool AndroidSDK::is_enabled() const {
+ return android_plugin && android_plugin->call(ANDROID_SN(isEnabled));
+}
+
AndroidSDK::AndroidSDK() {
AndroidStringNames::create_singleton();
@@ -170,6 +182,10 @@ AndroidSDK::AndroidSDK() {
}
AndroidSDK::~AndroidSDK() {
+ if (is_enabled()) {
+ close();
+ }
+
AndroidStringNames::destroy_singleton();
if (before_send_handler != nullptr) {
memdelete(before_send_handler);
diff --git a/src/sentry/android/android_sdk.h b/src/sentry/android/android_sdk.h
index 27a7d225..5dbad09c 100644
--- a/src/sentry/android/android_sdk.h
+++ b/src/sentry/android/android_sdk.h
@@ -49,7 +49,9 @@ class AndroidSDK : public InternalSDK {
virtual void add_attachment(const Ref &p_attachment) override;
- virtual void init(const PackedStringArray &p_global_attachments) override;
+ virtual void init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) override;
+ virtual void close() override;
+ virtual bool is_enabled() const override;
bool has_android_plugin() const { return android_plugin != nullptr; }
diff --git a/src/sentry/android/android_string_names.cpp b/src/sentry/android/android_string_names.cpp
index 15655f86..4426aa56 100644
--- a/src/sentry/android/android_string_names.cpp
+++ b/src/sentry/android/android_string_names.cpp
@@ -15,6 +15,9 @@ void AndroidStringNames::destroy_singleton() {
AndroidStringNames::AndroidStringNames() {
// API methods.
+ init = StringName("init");
+ close = StringName("close");
+ isEnabled = StringName("isEnabled");
setContext = StringName("setContext");
removeContext = StringName("removeContext");
setTag = StringName("setTag");
diff --git a/src/sentry/android/android_string_names.h b/src/sentry/android/android_string_names.h
index a9c1344b..7ee3eef1 100644
--- a/src/sentry/android/android_string_names.h
+++ b/src/sentry/android/android_string_names.h
@@ -27,6 +27,9 @@ class AndroidStringNames {
_FORCE_INLINE_ static AndroidStringNames *get_singleton() { return singleton; }
// API methods.
+ StringName init;
+ StringName close;
+ StringName isEnabled;
StringName setContext;
StringName removeContext;
StringName setTag;
diff --git a/src/sentry/cocoa/cocoa_sdk.h b/src/sentry/cocoa/cocoa_sdk.h
index a2fa00bd..6bf2c3b9 100644
--- a/src/sentry/cocoa/cocoa_sdk.h
+++ b/src/sentry/cocoa/cocoa_sdk.h
@@ -36,8 +36,9 @@ class CocoaSDK : public InternalSDK {
virtual void add_attachment(const Ref &p_attachment) override;
- virtual void init(const PackedStringArray &p_global_attachments) override;
- bool is_enabled() const;
+ virtual void init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) override;
+ virtual void close() override;
+ virtual bool is_enabled() const override;
CocoaSDK();
virtual ~CocoaSDK() override;
diff --git a/src/sentry/cocoa/cocoa_sdk.mm b/src/sentry/cocoa/cocoa_sdk.mm
index e7e128b9..b77e4ed0 100644
--- a/src/sentry/cocoa/cocoa_sdk.mm
+++ b/src/sentry/cocoa/cocoa_sdk.mm
@@ -141,10 +141,14 @@
}];
}
-void CocoaSDK::init(const PackedStringArray &p_global_attachments) {
+void CocoaSDK::init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) {
[PrivateSentrySDKOnly setSdkName:@"sentry.cocoa.godot"];
[objc::SentrySDK startWithConfigureOptions:^(objc::SentryOptions *options) {
+ if (p_configuration_callback.is_valid()) {
+ p_configuration_callback.call(SentryOptions::get_singleton());
+ }
+
options.dsn = string_to_objc(SentryOptions::get_singleton()->get_dsn());
options.debug = SentryOptions::get_singleton()->is_debug_enabled();
options.releaseName = string_to_objc(SentryOptions::get_singleton()->get_release());
@@ -197,6 +201,10 @@
}];
}
+void CocoaSDK::close() {
+ [objc::SentrySDK close];
+}
+
bool CocoaSDK::is_enabled() const {
return [objc::SentrySDK isEnabled];
}
@@ -207,7 +215,7 @@
CocoaSDK::~CocoaSDK() {
if (is_enabled()) {
- [objc::SentrySDK close];
+ close();
}
}
diff --git a/src/sentry/contexts.cpp b/src/sentry/contexts.cpp
index 6e1db803..49d58d95 100644
--- a/src/sentry/contexts.cpp
+++ b/src/sentry/contexts.cpp
@@ -3,7 +3,6 @@
#include "gen/sdk_version.gen.h"
#include "sentry/environment.h"
#include "sentry/sentry_options.h"
-#include "sentry/uuid.h"
#include
#include
diff --git a/src/sentry/disabled/disabled_sdk.h b/src/sentry/disabled/disabled_sdk.h
index da755491..84a48ed0 100644
--- a/src/sentry/disabled/disabled_sdk.h
+++ b/src/sentry/disabled/disabled_sdk.h
@@ -29,7 +29,9 @@ class DisabledSDK : public InternalSDK {
virtual void add_attachment(const Ref &p_attachment) override {}
- virtual void init(const PackedStringArray &p_global_attachments) override {}
+ virtual void init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) override {}
+ virtual void close() override {}
+ virtual bool is_enabled() const override { return false; }
};
} // namespace sentry
diff --git a/src/sentry/internal_sdk.h b/src/sentry/internal_sdk.h
index 83a50fcd..28b0928a 100644
--- a/src/sentry/internal_sdk.h
+++ b/src/sentry/internal_sdk.h
@@ -39,7 +39,9 @@ class InternalSDK {
virtual void add_attachment(const Ref &p_attachment) = 0;
- virtual void init(const PackedStringArray &p_global_attachments) = 0;
+ virtual void init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) = 0;
+ virtual void close() = 0;
+ virtual bool is_enabled() const = 0;
virtual ~InternalSDK() = default;
};
diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp
index 1087a5d8..e385140d 100644
--- a/src/sentry/native/native_sdk.cpp
+++ b/src/sentry/native/native_sdk.cpp
@@ -266,10 +266,14 @@ void NativeSDK::add_attachment(const Ref &p_attachment) {
}
}
-void NativeSDK::init(const PackedStringArray &p_global_attachments) {
+void NativeSDK::init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) {
ERR_FAIL_NULL(OS::get_singleton());
ERR_FAIL_NULL(ProjectSettings::get_singleton());
+ if (p_configuration_callback.is_valid()) {
+ p_configuration_callback.call(SentryOptions::get_singleton());
+ }
+
sentry_options_t *options = sentry_options_new();
sentry_options_set_dsn(options, SentryOptions::get_singleton()->get_dsn().utf8());
@@ -335,13 +339,28 @@ void NativeSDK::init(const PackedStringArray &p_global_attachments) {
}
}
+void NativeSDK::close() {
+ int err = sentry_close();
+ initialized = false;
+
+ if (err != 0) {
+ ERR_PRINT("Sentry: Failed to close native SDK cleanly. Error code: " + itos(err));
+ }
+}
+
+bool NativeSDK::is_enabled() const {
+ return initialized;
+}
+
NativeSDK::NativeSDK() {
last_uuid_mutex.instantiate();
last_uuid = sentry_uuid_nil();
}
NativeSDK::~NativeSDK() {
- sentry_close();
+ if (is_enabled()) {
+ close();
+ }
}
} //namespace sentry::native
diff --git a/src/sentry/native/native_sdk.h b/src/sentry/native/native_sdk.h
index 3b3b195c..1634b095 100644
--- a/src/sentry/native/native_sdk.h
+++ b/src/sentry/native/native_sdk.h
@@ -36,7 +36,9 @@ class NativeSDK : public InternalSDK {
virtual void add_attachment(const Ref &p_attachment) override;
- virtual void init(const PackedStringArray &p_global_attachments) override;
+ virtual void init(const PackedStringArray &p_global_attachments, const Callable &p_configuration_callback) override;
+ virtual void close() override;
+ virtual bool is_enabled() const override;
NativeSDK();
virtual ~NativeSDK() override;
diff --git a/src/sentry/sentry_configuration.cpp b/src/sentry/sentry_configuration.cpp
deleted file mode 100644
index 86987da3..00000000
--- a/src/sentry/sentry_configuration.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "sentry_configuration.h"
-
-#include "sentry_sdk.h"
-
-namespace sentry {
-
-void SentryConfiguration::_call_configure(const Ref &p_options) {
- ERR_FAIL_COND(p_options.is_null());
- GDVIRTUAL_CALL(_configure, p_options);
- ERR_FAIL_NULL(SentrySDK::get_singleton());
- SentrySDK::get_singleton()->notify_options_configured();
-}
-
-void SentryConfiguration::_notification(int p_what) {
- if (p_what == NOTIFICATION_READY) {
- _call_configure(SentryOptions::get_singleton());
- }
-}
-
-void SentryConfiguration::_bind_methods() {
- GDVIRTUAL_BIND(_configure, "options");
-}
-
-} // namespace sentry
diff --git a/src/sentry/sentry_configuration.h b/src/sentry/sentry_configuration.h
deleted file mode 100644
index 670b77ea..00000000
--- a/src/sentry/sentry_configuration.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef SENTRY_CONFIGURATION_H
-#define SENTRY_CONFIGURATION_H
-
-#include "sentry/sentry_options.h"
-
-#include
-#include
-
-using namespace godot;
-
-namespace sentry {
-
-class SentryConfiguration : public Node {
- GDCLASS(SentryConfiguration, Node);
-
-protected:
- static void _bind_methods();
- void _notification(int p_what);
-
- GDVIRTUAL1(_configure, Ref);
-
- void _call_configure(const Ref &p_options);
-
-public:
- SentryConfiguration() {}
-};
-
-} // namespace sentry
-
-#endif // SENTRY_CONFIGURATION_H
diff --git a/src/sentry/sentry_options.cpp b/src/sentry/sentry_options.cpp
index 216c0501..28ed824c 100644
--- a/src/sentry/sentry_options.cpp
+++ b/src/sentry/sentry_options.cpp
@@ -57,14 +57,14 @@ void SentryOptions::_define_project_settings(const Ref &p_options
ERR_FAIL_COND(p_options.is_null());
ERR_FAIL_NULL(ProjectSettings::get_singleton());
- _define_setting("sentry/options/enabled", p_options->enabled);
- _define_setting("sentry/options/disabled_in_editor_play", p_options->disabled_in_editor_play);
+ _define_setting("sentry/options/auto_init", p_options->auto_init);
+ _define_setting("sentry/options/skip_auto_init_on_editor_play", p_options->skip_auto_init_on_editor_play);
+
_define_setting("sentry/options/dsn", p_options->dsn);
_define_setting("sentry/options/release", p_options->release, false);
_define_setting("sentry/options/dist", p_options->dist, false);
_define_setting(PropertyInfo(Variant::INT, "sentry/options/debug_printing", PROPERTY_HINT_ENUM, "Off,On,Auto"), (int)SentryOptions::DEBUG_DEFAULT);
_define_setting(sentry::make_level_enum_property("sentry/options/diagnostic_level"), p_options->diagnostic_level);
- _define_setting(PropertyInfo(Variant::STRING, "sentry/options/configuration_script", PROPERTY_HINT_FILE, "*.gd"), p_options->configuration_script, false);
_define_setting(PropertyInfo(Variant::FLOAT, "sentry/options/sample_rate", PROPERTY_HINT_RANGE, "0.0,1.0"), p_options->sample_rate, false);
_define_setting(PropertyInfo(Variant::INT, "sentry/options/max_breadcrumbs", PROPERTY_HINT_RANGE, "0, 500"), p_options->max_breadcrumbs, false);
_define_setting("sentry/options/send_default_pii", p_options->send_default_pii);
@@ -92,19 +92,19 @@ void SentryOptions::_load_project_settings(const Ref &p_options)
ERR_FAIL_COND(p_options.is_null());
ERR_FAIL_NULL(ProjectSettings::get_singleton());
- p_options->enabled = ProjectSettings::get_singleton()->get_setting("sentry/options/enabled", p_options->enabled);
- p_options->disabled_in_editor_play = ProjectSettings::get_singleton()->get_setting("sentry/options/disabled_in_editor_play", p_options->disabled_in_editor_play);
+ p_options->auto_init = ProjectSettings::get_singleton()->get_setting("sentry/options/auto_init", p_options->auto_init);
+ p_options->skip_auto_init_on_editor_play = ProjectSettings::get_singleton()->get_setting("sentry/options/skip_auto_init_on_editor_play", p_options->skip_auto_init_on_editor_play);
+
p_options->dsn = ProjectSettings::get_singleton()->get_setting("sentry/options/dsn", p_options->dsn);
p_options->set_release(ProjectSettings::get_singleton()->get_setting("sentry/options/release", p_options->release));
p_options->dist = ProjectSettings::get_singleton()->get_setting("sentry/options/dist", p_options->dist);
// DebugMode is only used to represent the debug option in the project settings.
- // The user may also set the `debug` option explicitly in a configuration script.
+ // The user may also set the `debug` option explicitly in a configuration callback.
DebugMode mode = (DebugMode)(int)ProjectSettings::get_singleton()->get_setting("sentry/options/debug_printing", (int)SentryOptions::DEBUG_DEFAULT);
p_options->_init_debug_option(mode);
p_options->diagnostic_level = (sentry::Level)(int)ProjectSettings::get_singleton()->get_setting("sentry/options/diagnostic_level", p_options->diagnostic_level);
- p_options->configuration_script = ProjectSettings::get_singleton()->get_setting("sentry/options/configuration_script", p_options->configuration_script);
p_options->sample_rate = ProjectSettings::get_singleton()->get_setting("sentry/options/sample_rate", p_options->sample_rate);
p_options->max_breadcrumbs = ProjectSettings::get_singleton()->get_setting("sentry/options/max_breadcrumbs", p_options->max_breadcrumbs);
p_options->send_default_pii = ProjectSettings::get_singleton()->get_setting("sentry/options/send_default_pii", p_options->send_default_pii);
@@ -177,8 +177,6 @@ void SentryOptions::remove_event_processor(const Ref &p_pr
}
void SentryOptions::_bind_methods() {
- BIND_PROPERTY(SentryOptions, PropertyInfo(Variant::BOOL, "enabled"), set_enabled, is_enabled);
- BIND_PROPERTY(SentryOptions, PropertyInfo(Variant::BOOL, "disabled_in_editor_play"), set_disabled_in_editor_play, is_disabled_in_editor_play);
BIND_PROPERTY(SentryOptions, PropertyInfo(Variant::STRING, "dsn"), set_dsn, get_dsn);
BIND_PROPERTY(SentryOptions, PropertyInfo(Variant::STRING, "release"), set_release, get_release);
BIND_PROPERTY(SentryOptions, PropertyInfo(Variant::STRING, "dist"), set_dist, get_dist);
diff --git a/src/sentry/sentry_options.h b/src/sentry/sentry_options.h
index a4648344..10cfae6f 100644
--- a/src/sentry/sentry_options.h
+++ b/src/sentry/sentry_options.h
@@ -48,8 +48,8 @@ class SentryOptions : public RefCounted {
};
static constexpr DebugMode DEBUG_DEFAULT = DebugMode::DEBUG_AUTO;
- bool enabled = true;
- bool disabled_in_editor_play = false;
+ bool auto_init = true;
+ bool skip_auto_init_on_editor_play = false;
String dsn = "";
String release = "{app_name}@{app_version}";
String dist = "";
@@ -73,7 +73,6 @@ class SentryOptions : public RefCounted {
BitField logger_breadcrumb_mask = int(GodotErrorMask::MASK_ALL);
Ref logger_limits;
- String configuration_script;
Callable before_send;
Callable before_capture_screenshot;
@@ -92,11 +91,11 @@ class SentryOptions : public RefCounted {
static void destroy_singleton();
_FORCE_INLINE_ static Ref get_singleton() { return singleton; }
- _FORCE_INLINE_ bool is_enabled() const { return enabled; }
- _FORCE_INLINE_ void set_enabled(bool p_enabled) { enabled = p_enabled; }
+ _FORCE_INLINE_ bool is_auto_init_enabled() const { return auto_init; }
+ _FORCE_INLINE_ void set_auto_init(bool p_enabled) { auto_init = p_enabled; }
- _FORCE_INLINE_ bool is_disabled_in_editor_play() const { return disabled_in_editor_play; }
- _FORCE_INLINE_ void set_disabled_in_editor_play(bool p_disabled_in_editor_play) { disabled_in_editor_play = p_disabled_in_editor_play; }
+ _FORCE_INLINE_ bool should_skip_auto_init_on_editor_play() const { return skip_auto_init_on_editor_play; }
+ _FORCE_INLINE_ void set_skip_auto_init_on_editor_play(bool p_skip) { skip_auto_init_on_editor_play = p_skip; }
_FORCE_INLINE_ String get_dsn() const { return dsn; }
_FORCE_INLINE_ void set_dsn(const String &p_dsn) { dsn = p_dsn; }
@@ -161,8 +160,6 @@ class SentryOptions : public RefCounted {
_FORCE_INLINE_ Ref get_logger_limits() const { return logger_limits; }
void set_logger_limits(const Ref &p_limits);
- _FORCE_INLINE_ String get_configuration_script() const { return configuration_script; }
-
_FORCE_INLINE_ Callable get_before_send() const { return before_send; }
_FORCE_INLINE_ void set_before_send(const Callable &p_before_send) { before_send = p_before_send; }
diff --git a/src/sentry/sentry_sdk.cpp b/src/sentry/sentry_sdk.cpp
index 1759c130..f7f668e3 100644
--- a/src/sentry/sentry_sdk.cpp
+++ b/src/sentry/sentry_sdk.cpp
@@ -118,6 +118,48 @@ void SentrySDK::destroy_singleton() {
singleton = nullptr;
}
+void SentrySDK::init(const Callable &p_configuration_callback) {
+ ERR_FAIL_COND_MSG(internal_sdk->is_enabled(), "Attempted to initialize SentrySDK that is already initialized");
+
+#if SDK_ANDROID
+ if (OS::get_singleton()->has_feature("editor")) {
+ ERR_FAIL_MSG("Initializing in Android editor is not supported!");
+ return;
+ }
+#endif
+
+ sentry::util::print_debug("Initializing Sentry SDK");
+ internal_sdk->init(_get_global_attachments(), p_configuration_callback);
+
+ if (internal_sdk->is_enabled()) {
+ if (is_auto_initializing) {
+ // Delay contexts initialization until engine singletons are ready during early initialization.
+ callable_mp(this, &SentrySDK::_init_contexts).call_deferred();
+ } else {
+ // TODO: move this into sentry::contexts
+ _init_contexts();
+ }
+
+ if (SentryOptions::get_singleton()->is_logger_enabled()) {
+ if (logger.is_null()) {
+ logger.instantiate();
+ }
+ OS::get_singleton()->add_logger(logger);
+ }
+ }
+}
+
+void SentrySDK::close() {
+ if (internal_sdk->is_enabled()) {
+ sentry::util::print_debug("Shutting down Sentry SDK");
+ if (logger.is_valid()) {
+ OS::get_singleton()->remove_logger(logger);
+ logger.unref();
+ }
+ internal_sdk->close();
+ }
+}
+
String SentrySDK::capture_message(const String &p_message, Level p_level) {
return internal_sdk->capture_message(p_message, p_level);
}
@@ -238,63 +280,38 @@ PackedStringArray SentrySDK::_get_global_attachments() {
void SentrySDK::_auto_initialize() {
sentry::util::print_debug("starting Sentry SDK version " + String(SENTRY_GODOT_SDK_VERSION));
- // Initialize user if it wasn't set explicitly in the configuration script.
- if (user.is_null()) {
- user.instantiate();
- user->set_id(runtime_config->get_installation_id());
- if (SentryOptions::get_singleton()->is_send_default_pii_enabled()) {
- user->infer_ip_address();
- }
- }
- set_user(user);
-
bool should_enable = true;
- if (!SentryOptions::get_singleton()->is_enabled()) {
+ if (!SentryOptions::get_singleton()->is_auto_init_enabled()) {
should_enable = false;
- sentry::util::print_debug("Sentry SDK is disabled in options.");
+ sentry::util::print_debug("Automatic initialization is disabled in the project settings.");
}
if (Engine::get_singleton()->is_editor_hint()) {
should_enable = false;
- sentry::util::print_debug("Sentry SDK is disabled in the editor.");
+ sentry::util::print_debug("Automatic initialization is disabled in the editor.");
}
- if (!Engine::get_singleton()->is_editor_hint() && OS::get_singleton()->has_feature("editor") && SentryOptions::get_singleton()->is_disabled_in_editor_play()) {
+ if (!Engine::get_singleton()->is_editor_hint() && OS::get_singleton()->has_feature("editor") && SentryOptions::get_singleton()->should_skip_auto_init_on_editor_play()) {
should_enable = false;
- sentry::util::print_debug("Sentry SDK is disabled when project is played from the editor. Tip: This can be changed in the project settings.");
+ sentry::util::print_debug("Automatic initialization is disabled when project is played from the editor. Tip: This can be changed in the project settings.");
}
-#if SDK_ANDROID
- if (should_enable) {
- if (OS::get_singleton()->has_feature("editor")) {
- should_enable = false;
- }
+ if (SentryOptions::get_singleton()->get_dsn().is_empty()) {
+ should_enable = false;
+ sentry::util::print_debug("Automatic initialization is disabled because no DSN was provided. Tip: You can obtain a DSN from Sentry's dashboard and add it in the project settings.");
}
-#endif
-
- enabled = should_enable;
- if (!enabled) {
- sentry::util::print_info("Sentry SDK is DISABLED! Operations with Sentry SDK will result in no-ops.");
+ if (!should_enable) {
+ sentry::util::print_info("Automatic initialization is disabled! Operations with Sentry SDK will result in no-ops.");
return;
}
- internal_sdk->init(_get_global_attachments());
+ sentry::util::print_debug("Proceeding with automatic initialization.");
- if (SentryOptions::get_singleton()->is_logger_enabled()) {
- logger.instantiate();
- OS::get_singleton()->add_logger(logger);
- }
-}
-
-void SentrySDK::_check_if_configuration_succeeded() {
- if (!configuration_succeeded) {
- // Push error and initialize anyway.
- ERR_PRINT("Sentry: Configuration via user script failed. Will try to initialize SDK anyway.");
- sentry::util::print_error("initializing late because configuration via user script failed");
- _auto_initialize();
- }
+ is_auto_initializing = true;
+ init();
+ is_auto_initializing = false;
}
void SentrySDK::_demo_helper_crash_app() {
@@ -302,18 +319,21 @@ void SentrySDK::_demo_helper_crash_app() {
sentry::util::print_fatal("Crash by access violation ", ptr); // this is going to crash the app
}
-void SentrySDK::notify_options_configured() {
- sentry::util::print_debug("finished configuring options via user script");
- configuration_succeeded = true;
- _auto_initialize();
- _init_contexts();
-}
-
void SentrySDK::prepare_and_auto_initialize() {
// Load the runtime configuration from the user's data directory.
runtime_config.instantiate();
runtime_config->load_file(OS::get_singleton()->get_user_data_dir() + "/sentry.dat");
+ // Initialize user.
+ if (user.is_null()) {
+ user.instantiate();
+ user->set_id(runtime_config->get_installation_id());
+ if (SentryOptions::get_singleton()->is_send_default_pii_enabled()) {
+ user->infer_ip_address();
+ }
+ }
+ set_user(user);
+
// Verify project settings and notify user via errors if there are any issues (deferred).
callable_mp_static(_verify_project_settings).call_deferred();
@@ -337,28 +357,7 @@ void SentrySDK::prepare_and_auto_initialize() {
SentryOptions::get_singleton()->add_event_processor(memnew(ViewHierarchyProcessor));
}
- // Auto-initialize SDK.
- if (SentryOptions::get_singleton()->get_configuration_script().is_empty() || Engine::get_singleton()->is_editor_hint()) {
- // Early initialization path.
- _auto_initialize();
- // Delay contexts initialization until the engine singletons are ready.
- callable_mp(this, &SentrySDK::_init_contexts).call_deferred();
- } else {
- // Register an autoload singleton, which is a user script extending the
- // `SentryConfiguration` class. It will be instantiated and added to the
- // scene tree by the engine shortly after ScriptServer is initialized.
- // When this happens, the `SentryConfiguration` instance receives
- // `NOTIFICATION_READY`, triggering our notification processing code in
- // C++, which calls `_configure()` on the user script and then invokes
- // `notify_options_configured()` in `SentrySDK`. This, in turn,
- // auto-initializes the SDK.
- sentry::util::print_debug("waiting for user configuration autoload");
- ERR_FAIL_NULL(ProjectSettings::get_singleton());
- ProjectSettings::get_singleton()->set_setting("autoload/SentryConfigurationScript",
- SentryOptions::get_singleton()->get_configuration_script());
- // Ensure issues with the configuration script are detected.
- callable_mp(this, &SentrySDK::_check_if_configuration_succeeded).call_deferred();
- }
+ _auto_initialize();
}
void SentrySDK::_notification(int p_what) {
@@ -379,6 +378,8 @@ void SentrySDK::_bind_methods() {
BIND_ENUM_CONSTANT(LEVEL_ERROR);
BIND_ENUM_CONSTANT(LEVEL_FATAL);
+ ClassDB::bind_method(D_METHOD("init", "configuration_callback"), &SentrySDK::init, DEFVAL(Callable()));
+ ClassDB::bind_method(D_METHOD("close"), &SentrySDK::close);
ClassDB::bind_method(D_METHOD("is_enabled"), &SentrySDK::is_enabled);
ClassDB::bind_method(D_METHOD("add_breadcrumb", "breadcrumb"), &SentrySDK::add_breadcrumb);
ClassDB::bind_method(D_METHOD("capture_message", "message", "level"), &SentrySDK::capture_message, DEFVAL(LEVEL_INFO));
diff --git a/src/sentry/sentry_sdk.h b/src/sentry/sentry_sdk.h
index 90917172..65a44117 100644
--- a/src/sentry/sentry_sdk.h
+++ b/src/sentry/sentry_sdk.h
@@ -37,13 +37,11 @@ class SentrySDK : public Object {
Ref user;
Ref user_mutex;
Ref logger;
- bool enabled = false;
- bool configuration_succeeded = false;
+ bool is_auto_initializing = false;
void _init_contexts();
PackedStringArray _get_global_attachments();
void _auto_initialize();
- void _check_if_configuration_succeeded();
void _demo_helper_crash_app();
protected:
@@ -58,11 +56,11 @@ class SentrySDK : public Object {
_FORCE_INLINE_ std::shared_ptr get_internal_sdk() const { return internal_sdk; }
- void notify_options_configured();
-
// * Exported API
- bool is_enabled() const { return enabled; }
+ void init(const Callable &p_configuration_callback = Callable());
+ void close();
+ bool is_enabled() const { return internal_sdk->is_enabled(); }
void add_breadcrumb(const Ref &p_breadcrumb);