diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ff4471..95e5cf85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ ## Unreleased +### Breaking changes + +We've redesigned the breadcrumb API for a cleaner, more intuitive interface. Previously, `add_breadcrumb()` method accepted 5 parameters (3 of which were strings), making it confusing to use. The new approach uses a dedicated `SentryBreadcrumb` class: + +```gdscript +var crumb := SentryBreadcrumb.create("Something happened") +crumb.type = "info" +crumb.set_data({"some": "data"}) +SentrySDK.add_breadcrumb(crumb) +``` + +For simple breadcrumbs, you can use a one-liner: +```gdscript +SentrySDK.add_breadcrumb(SentryBreadcrumb.create("Something happened")) +``` + +This change provides better type safety, improved readability, and enables future support for the `before_breadcrumb` callback. + ### Features - Add support for script context and variables on Apple platforms ([#306](https://github.com/getsentry/sentry-godot/pull/306)) @@ -10,6 +28,7 @@ ### Improvements - Improve initialization flow ([#322](https://github.com/getsentry/sentry-godot/pull/322)) +- Introduce `SentryBreadcrumb` class ([#332](https://github.com/getsentry/sentry-godot/pull/332)) ### Fixes 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 5c08fc1e..eafb3970 100644 --- a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt +++ b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt @@ -43,6 +43,12 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { } } + private val breadcrumbsByHandle = object : ThreadLocal>() { + override fun initialValue(): MutableMap { + return mutableMapOf() + } + } + private fun getEvent(eventHandle: Int): SentryEvent? { val event: SentryEvent? = eventsByHandle.get()?.get(eventHandle) if (event == null) { @@ -59,6 +65,14 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { return exception } + private fun getBreadcrumb(breadcrumbHandle: Int): Breadcrumb? { + var crumb: Breadcrumb? = breadcrumbsByHandle.get()?.get(breadcrumbHandle) + if (crumb == null) { + Log.e(TAG, "Internal Error -- Breadcrumb not found: $breadcrumbHandle") + } + return crumb + } + private fun registerEvent(event: SentryEvent): Int { val eventsMap = eventsByHandle.get() ?: run { Log.e(TAG, "Internal Error -- eventsByHandle is null") @@ -89,6 +103,21 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { return handle } + private fun registerBreadcrumb(crumb: Breadcrumb): Int { + val breadcrumbsMap = breadcrumbsByHandle.get() ?: run { + Log.e(TAG, "Internal Error -- breadcrumbsByHandle is null") + return 0 + } + + var handle = Random.nextInt() + while (breadcrumbsMap.containsKey(handle)) { + handle = Random.nextInt() + } + + breadcrumbsMap[handle] = crumb + return handle + } + override fun getPluginName(): String { return "SentryAndroidGodotPlugin" } @@ -193,23 +222,8 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { } @UsedByGodot - fun addBreadcrumb( - message: String, - category: String, - level: Int, - type: String, - data: Dictionary - ) { - val crumb = Breadcrumb() - crumb.message = message - crumb.category = category - crumb.level = level.toSentryLevel() - crumb.type = type - - for ((k, v) in data) { - crumb.data[k] = v - } - + fun addBreadcrumb(handle: Int) { + val crumb = getBreadcrumb(handle) ?: return Sentry.addBreadcrumb(crumb) } @@ -442,4 +456,79 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { } event.exceptions?.add(exception) } + + @UsedByGodot + fun createBreadcrumb(): Int { + val crumb = Breadcrumb() + val handle = registerBreadcrumb(crumb) + return handle + } + + @UsedByGodot + fun releaseBreadcrumb(handle: Int) { + val breadcrumbsMap = breadcrumbsByHandle.get() ?: run { + Log.e(TAG, "Internal Error -- breadcrumbsByHandle is null") + return + } + + breadcrumbsMap.remove(handle) + } + + @UsedByGodot + fun breadcrumbSetMessage(handle: Int, message: String) { + getBreadcrumb(handle)?.message = message + } + + @UsedByGodot + fun breadcrumbGetMessage(handle: Int): String { + return getBreadcrumb(handle)?.message ?: "" + } + + @UsedByGodot + fun breadcrumbSetCategory(handle: Int, category: String) { + getBreadcrumb(handle)?.category = category + } + + @UsedByGodot + fun breadcrumbGetCategory(handle: Int): String { + return getBreadcrumb(handle)?.category ?: "" + } + + @UsedByGodot + fun breadcrumbSetLevel(handle: Int, level: Int) { + getBreadcrumb(handle)?.level = level.toSentryLevel() + } + + @UsedByGodot + fun breadcrumbGetLevel(handle: Int): Int { + return getBreadcrumb(handle)?.level?.toInt() ?: SentryLevel.INFO.toInt() + } + + @UsedByGodot + fun breadcrumbSetType(handle: Int, type: String) { + getBreadcrumb(handle)?.type = type + } + + @UsedByGodot + fun breadcrumbGetType(handle: Int): String { + return getBreadcrumb(handle)?.type ?: "" + } + + @UsedByGodot + fun breadcrumbSetData(handle: Int, data: Dictionary) { + val crumb = getBreadcrumb(handle) ?: return + + crumb.data.clear() + + for ((k, v) in data) { + crumb.data[k] = v + } + } + + @UsedByGodot + fun breadcrumbGetTimestamp(handle: Int): Long { + val crumb = getBreadcrumb(handle) ?: return 0 + return crumb.timestamp.toMicros() + } + } diff --git a/doc_classes/SentryBreadcrumb.xml b/doc_classes/SentryBreadcrumb.xml new file mode 100644 index 00000000..ad048c98 --- /dev/null +++ b/doc_classes/SentryBreadcrumb.xml @@ -0,0 +1,54 @@ + + + + Sentry breadcrumb for error reporting context. + + + A breadcrumb is a small piece of diagnostic data that records an event or action leading up to an error or crash. These breadcrumbs create a trail that shows what the user and your game was doing right before something went wrong. + To create a breadcrumb, use the static [method SentryBreadcrumb.create] method with an optional message parameter. Once created, you can configure additional properties like category, level, type, and data before adding it to Sentry using [method SentrySDK.add_breadcrumb]. + [codeblock] + var crumb := SentryBreadcrumb.create("Level finished!") + crumb.type = "info" + crumb.category = "level.finished" + SentrySDK.add_breadcrumb(crumb) + [/codeblock] + For more information, check out the [url=https://docs.sentry.io/platforms/godot/enriching-events/breadcrumbs/]Breadcrumbs documentation[/url]. + + + + + + + + + + + + + + Returns the timestamp of the breadcrumb. + + + + + + + Sets additional data associated with the breadcrumb. This can be useful for providing more context about the event or action that triggered the breadcrumb. + + + + + + A category label that shows what type of action this breadcrumb represents. Use dot notation like "ui.click" for a button click or "network.request" for an API call. + + + The severity level of the breadcrumb. Defaults to [code]LEVEL_INFO[/code]. See [enum SentrySDK.Level]. + + + A short description of what happened. + + + The type of breadcrumb that determines how it appears in the Sentry interface. Defaults to [code]default[/code] which renders as a Debug entry. Common types include [code]http[/code] for network requests, [code]navigation[/code] for page/screen changes, [code]user[/code] for user interactions, and [code]error[/code] for error events. See [url=https://develop.sentry.dev/sdk/data-model/event-payloads/breadcrumbs/#breadcrumb-types]Breadcrumb Types[/url] for the complete list. + + + diff --git a/doc_classes/SentrySDK.xml b/doc_classes/SentrySDK.xml index 01737a14..b3dec615 100644 --- a/doc_classes/SentrySDK.xml +++ b/doc_classes/SentrySDK.xml @@ -23,14 +23,10 @@ - - - - - + - Adds a breadcrumb to the next event. Sentry uses breadcrumbs to create a trail of events that happened prior to an issue. - To learn more, visit [url=https://docs.sentry.io/platforms/godot/enriching-events/breadcrumbs/]Breadcrumbs documentation[/url]. + Adds a breadcrumb to the future events. You can create a [SentryBreadcrumb] object using the static [method SentryBreadcrumb.create] method with an optional message parameter. + Sentry uses breadcrumbs to create a trail of events that happened prior to an issue. For more information, see [SentryBreadcrumb] class. diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd new file mode 100644 index 00000000..c20ad414 --- /dev/null +++ b/project/test/suites/test_breadcrumb.gd @@ -0,0 +1,66 @@ +extends GdUnitTestSuite +## Test SentryBreadcrumb class properties and methods. + + +## Test string properties accessors and UTF-8 encoding preservation. +@warning_ignore("unused_parameter") +func test_string_properties_and_utf8(property: String, test_parameters := [ + ["message"], + ["category"], + ["type"], +]) -> void: + var crumb := SentryBreadcrumb.create() + crumb.set(property, "Hello, World!") + assert_str(crumb.get(property)).is_equal("Hello, World!") + crumb.set(property, "Hello δΈ–η•Œ! πŸ‘‹") + assert_str(crumb.get(property)).is_equal("Hello δΈ–η•Œ! πŸ‘‹") + + +func test_breadcrumb_create_with_message() -> void: + var crumb := SentryBreadcrumb.create("test-message") + assert_str(crumb.message).is_equal("test-message") + + +func test_breadcrumb_level() -> void: + var crumb := SentryBreadcrumb.create() + crumb.level = SentrySDK.LEVEL_DEBUG + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_DEBUG) + crumb.level = SentrySDK.LEVEL_INFO + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + crumb.level = SentrySDK.LEVEL_WARNING + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_WARNING) + crumb.level = SentrySDK.LEVEL_ERROR + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_ERROR) + crumb.level = SentrySDK.LEVEL_FATAL + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_FATAL) + + +func test_breadcrumb_can_set_data() -> void: + var crumb := SentryBreadcrumb.create() + crumb.set_data({"biome": "forest", "time_of_day": 0.42}) + + +func test_breadcrumb_default_values() -> void: + var crumb := SentryBreadcrumb.create() + assert_str(crumb.message).is_empty() + assert_str(crumb.type).is_empty() + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + assert_bool(crumb.category in ["", "default"]).is_true() + + +func test_breadcrumb_timestamp_is_set_automatically() -> void: + var time_before: float = Time.get_unix_time_from_system() + await get_tree().process_frame # small delay to ensure timestamp differs + + var crumb := SentryBreadcrumb.create() + + await get_tree().process_frame + var time_after: float = Time.get_unix_time_from_system() + + # Timestamp should be between time_before and time_after + var timestamp: SentryTimestamp = crumb.get_timestamp() + assert_object(timestamp).is_not_null() + var micros: int = timestamp.microseconds_since_unix_epoch + var timestamp_unix: float = float(micros) / 1_000_000.0 + assert_float(timestamp_unix).is_greater_equal(time_before) + assert_float(timestamp_unix).is_less_equal(time_after) diff --git a/project/test/suites/test_breadcrumb.gd.uid b/project/test/suites/test_breadcrumb.gd.uid new file mode 100644 index 00000000..9b0f0548 --- /dev/null +++ b/project/test/suites/test_breadcrumb.gd.uid @@ -0,0 +1 @@ +uid://dexm1kq44m4cv diff --git a/project/views/enrich_events.gd b/project/views/enrich_events.gd index ad7b4596..3545a6ad 100644 --- a/project/views/enrich_events.gd +++ b/project/views/enrich_events.gd @@ -13,7 +13,9 @@ func _ready() -> void: func _on_add_breadcrumb_button_pressed() -> void: - SentrySDK.add_breadcrumb(breadcrumb_message.text, breadcrumb_category.text, SentrySDK.LEVEL_ERROR, "default") + var crumb := SentryBreadcrumb.create(breadcrumb_message.text) + crumb.category = breadcrumb_category.text + SentrySDK.add_breadcrumb(crumb) DemoOutput.print_info("Breadcrumb added.") diff --git a/src/register_types.cpp b/src/register_types.cpp index 7c96be14..af509ff0 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -6,6 +6,7 @@ #include "sentry/processing/view_hierarchy_processor.h" #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" @@ -18,15 +19,18 @@ #include #ifdef SDK_NATIVE +#include "sentry/native/native_breadcrumb.h" #include "sentry/native/native_event.h" #endif // SDK_NATIVE #ifdef SDK_ANDROID +#include "sentry/android/android_breadcrumb.h" #include "sentry/android/android_event.h" #include "sentry/android/android_sdk.h" #endif // SDK_ANDROID #ifdef SDK_COCOA +#include "sentry/cocoa/cocoa_breadcrumb.h" #include "sentry/cocoa/cocoa_event.h" #endif // SDK_COCOA @@ -49,6 +53,7 @@ void register_runtime_classes() { GDREGISTER_CLASS(SentrySDK); GDREGISTER_ABSTRACT_CLASS(SentryAttachment); GDREGISTER_ABSTRACT_CLASS(SentryEvent); + GDREGISTER_ABSTRACT_CLASS(SentryBreadcrumb); GDREGISTER_INTERNAL_CLASS(DisabledEvent); GDREGISTER_INTERNAL_CLASS(SentryEventProcessor); GDREGISTER_INTERNAL_CLASS(ScreenshotProcessor); @@ -57,15 +62,18 @@ void register_runtime_classes() { #ifdef SDK_NATIVE GDREGISTER_INTERNAL_CLASS(native::NativeEvent); + GDREGISTER_INTERNAL_CLASS(native::NativeBreadcrumb); #endif #ifdef SDK_ANDROID GDREGISTER_INTERNAL_CLASS(android::AndroidEvent); + GDREGISTER_INTERNAL_CLASS(android::AndroidBreadcrumb); GDREGISTER_INTERNAL_CLASS(android::SentryAndroidBeforeSendHandler); #endif #ifdef SDK_COCOA GDREGISTER_INTERNAL_CLASS(cocoa::CocoaEvent); + GDREGISTER_INTERNAL_CLASS(cocoa::CocoaBreadcrumb); #endif } diff --git a/src/sentry/android/android_breadcrumb.cpp b/src/sentry/android/android_breadcrumb.cpp new file mode 100644 index 00000000..8558ebd9 --- /dev/null +++ b/src/sentry/android/android_breadcrumb.cpp @@ -0,0 +1,71 @@ +#include "android_breadcrumb.h" + +#include "android_string_names.h" + +namespace sentry::android { + +void AndroidBreadcrumb::set_message(const String &p_message) { + ERR_FAIL_NULL(android_plugin); + android_plugin->call(ANDROID_SN(breadcrumbSetMessage), handle, p_message); +} + +String AndroidBreadcrumb::get_message() const { + ERR_FAIL_NULL_V(android_plugin, String()); + return android_plugin->call(ANDROID_SN(breadcrumbGetMessage), handle); +} + +void AndroidBreadcrumb::set_category(const String &p_category) { + ERR_FAIL_NULL(android_plugin); + android_plugin->call(ANDROID_SN(breadcrumbSetCategory), handle, p_category); +} + +String AndroidBreadcrumb::get_category() const { + ERR_FAIL_NULL_V(android_plugin, String()); + return android_plugin->call(ANDROID_SN(breadcrumbGetCategory), handle); +} + +void AndroidBreadcrumb::set_level(sentry::Level p_level) { + ERR_FAIL_NULL(android_plugin); + android_plugin->call(ANDROID_SN(breadcrumbSetLevel), handle, p_level); +} + +sentry::Level AndroidBreadcrumb::get_level() const { + ERR_FAIL_NULL_V(android_plugin, sentry::Level::LEVEL_INFO); + Variant result = android_plugin->call(ANDROID_SN(breadcrumbGetLevel), handle); + ERR_FAIL_COND_V(result.get_type() != Variant::INT, sentry::Level::LEVEL_INFO); + return sentry::int_to_level(result); +} + +void AndroidBreadcrumb::set_type(const String &p_type) { + ERR_FAIL_NULL(android_plugin); + android_plugin->call(ANDROID_SN(breadcrumbSetType), handle, p_type); +} + +String AndroidBreadcrumb::get_type() const { + ERR_FAIL_NULL_V(android_plugin, String()); + return android_plugin->call(ANDROID_SN(breadcrumbGetType), handle); +} + +void AndroidBreadcrumb::set_data(const Dictionary &p_data) { + ERR_FAIL_NULL(android_plugin); + android_plugin->call(ANDROID_SN(breadcrumbSetData), handle, p_data); +} + +Ref AndroidBreadcrumb::get_timestamp() { + ERR_FAIL_NULL_V(android_plugin, nullptr); + int64_t micros = android_plugin->call(ANDROID_SN(breadcrumbGetTimestamp), handle); + return SentryTimestamp::from_microseconds_since_unix_epoch(micros); +} + +AndroidBreadcrumb::AndroidBreadcrumb(Object *p_android_plugin, int32_t p_breadcrumb_handle) : + android_plugin(p_android_plugin), handle(p_breadcrumb_handle) { + ERR_FAIL_NULL(p_android_plugin); +} + +AndroidBreadcrumb::~AndroidBreadcrumb() { + if (android_plugin) { + android_plugin->call(ANDROID_SN(releaseBreadcrumb), handle); + } +} + +} //namespace sentry::android diff --git a/src/sentry/android/android_breadcrumb.h b/src/sentry/android/android_breadcrumb.h new file mode 100644 index 00000000..46b38216 --- /dev/null +++ b/src/sentry/android/android_breadcrumb.h @@ -0,0 +1,44 @@ +#ifndef ANDROID_BREADCRUMB_H +#define ANDROID_BREADCRUMB_H + +#include "sentry/sentry_breadcrumb.h" + +namespace sentry::android { + +class AndroidBreadcrumb : public SentryBreadcrumb { + GDCLASS(AndroidBreadcrumb, SentryBreadcrumb); + +private: + Object *android_plugin = nullptr; + int32_t handle = 0; + +protected: + static void _bind_methods() {} + +public: + int32_t get_handle() { return handle; } + + virtual void set_message(const String &p_message) override; + virtual String get_message() const override; + + virtual void set_category(const String &p_category) override; + virtual String get_category() const override; + + virtual void set_level(sentry::Level p_level) override; + virtual sentry::Level get_level() const override; + + virtual void set_type(const String &p_type) override; + virtual String get_type() const override; + + virtual void set_data(const Dictionary &p_data) override; + + virtual Ref get_timestamp() override; + + AndroidBreadcrumb() = default; + AndroidBreadcrumb(Object *p_android_plugin, int32_t p_breadcrumb_handle); + virtual ~AndroidBreadcrumb() override; +}; + +} //namespace sentry::android + +#endif // ANDROID_BREADCRUMB_H diff --git a/src/sentry/android/android_sdk.cpp b/src/sentry/android/android_sdk.cpp index 16037fa0..b39de1a9 100644 --- a/src/sentry/android/android_sdk.cpp +++ b/src/sentry/android/android_sdk.cpp @@ -1,5 +1,6 @@ #include "android_sdk.h" +#include "android_breadcrumb.h" #include "android_event.h" #include "android_string_names.h" #include "sentry/common_defs.h" @@ -71,10 +72,18 @@ void AndroidSDK::remove_user() { android_plugin->call(ANDROID_SN(removeUser)); } -void AndroidSDK::add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type, const Dictionary &p_data) { +Ref AndroidSDK::create_breadcrumb() { + ERR_FAIL_NULL_V(android_plugin, nullptr); + int32_t handle = android_plugin->call(ANDROID_SN(createBreadcrumb)); + Ref crumb = memnew(AndroidBreadcrumb(android_plugin, handle)); + return crumb; +} + +void AndroidSDK::add_breadcrumb(const Ref &p_breadcrumb) { ERR_FAIL_NULL(android_plugin); - android_plugin->call(ANDROID_SN(addBreadcrumb), p_message, p_category, p_level, p_type, p_data); + Ref crumb = p_breadcrumb; + ERR_FAIL_COND(crumb.is_null()); + android_plugin->call(ANDROID_SN(addBreadcrumb), crumb->get_handle()); } String AndroidSDK::capture_message(const String &p_message, Level p_level) { diff --git a/src/sentry/android/android_sdk.h b/src/sentry/android/android_sdk.h index 91e669bf..27a7d225 100644 --- a/src/sentry/android/android_sdk.h +++ b/src/sentry/android/android_sdk.h @@ -38,8 +38,8 @@ class AndroidSDK : public InternalSDK { virtual void set_user(const Ref &p_user) override; virtual void remove_user() override; - virtual void add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type = "default", const Dictionary &p_data = Dictionary()) override; + virtual Ref create_breadcrumb() override; + virtual void add_breadcrumb(const Ref &p_breadcrumb) override; virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override; virtual String get_last_event_id() override; diff --git a/src/sentry/android/android_string_names.cpp b/src/sentry/android/android_string_names.cpp index 68d882b0..15655f86 100644 --- a/src/sentry/android/android_string_names.cpp +++ b/src/sentry/android/android_string_names.cpp @@ -60,6 +60,20 @@ AndroidStringNames::AndroidStringNames() { releaseException = StringName("releaseException"); exceptionAppendStackFrame = StringName("exceptionAppendStackFrame"); eventAddException = StringName("eventAddException"); + + // Breadcrumbs. + createBreadcrumb = StringName("createBreadcrumb"); + releaseBreadcrumb = StringName("releaseBreadcrumb"); + breadcrumbSetMessage = StringName("breadcrumbSetMessage"); + breadcrumbGetMessage = StringName("breadcrumbGetMessage"); + breadcrumbSetType = StringName("breadcrumbSetType"); + breadcrumbGetType = StringName("breadcrumbGetType"); + breadcrumbSetCategory = StringName("breadcrumbSetCategory"); + breadcrumbGetCategory = StringName("breadcrumbGetCategory"); + breadcrumbSetLevel = StringName("breadcrumbSetLevel"); + breadcrumbGetLevel = StringName("breadcrumbGetLevel"); + breadcrumbSetData = StringName("breadcrumbSetData"); + breadcrumbGetTimestamp = StringName("breadcrumbGetTimestamp"); } } //namespace sentry::android diff --git a/src/sentry/android/android_string_names.h b/src/sentry/android/android_string_names.h index 03ec76b8..a9c1344b 100644 --- a/src/sentry/android/android_string_names.h +++ b/src/sentry/android/android_string_names.h @@ -72,6 +72,20 @@ class AndroidStringNames { StringName releaseException; StringName exceptionAppendStackFrame; StringName eventAddException; + + // Breadcrumbs. + StringName createBreadcrumb; + StringName releaseBreadcrumb; + StringName breadcrumbSetMessage; + StringName breadcrumbGetMessage; + StringName breadcrumbSetType; + StringName breadcrumbGetType; + StringName breadcrumbSetCategory; + StringName breadcrumbGetCategory; + StringName breadcrumbSetLevel; + StringName breadcrumbGetLevel; + StringName breadcrumbSetData; + StringName breadcrumbGetTimestamp; }; } //namespace sentry::android diff --git a/src/sentry/cocoa/cocoa_breadcrumb.h b/src/sentry/cocoa/cocoa_breadcrumb.h new file mode 100644 index 00000000..cdd74346 --- /dev/null +++ b/src/sentry/cocoa/cocoa_breadcrumb.h @@ -0,0 +1,43 @@ +#ifndef COCOA_BREADCRUMB_H +#define COCOA_BREADCRUMB_H + +#include "sentry/cocoa/cocoa_includes.h" +#include "sentry/sentry_breadcrumb.h" + +namespace sentry::cocoa { + +class CocoaBreadcrumb : public SentryBreadcrumb { + GDCLASS(CocoaBreadcrumb, SentryBreadcrumb); + +private: + objc::SentryBreadcrumb *cocoa_breadcrumb = nullptr; + +protected: + static void _bind_methods() {} + +public: + _FORCE_INLINE_ objc::SentryBreadcrumb *get_cocoa_breadcrumb() const { return cocoa_breadcrumb; } + + virtual void set_message(const String &p_message) override; + virtual String get_message() const override; + + virtual void set_category(const String &p_category) override; + virtual String get_category() const override; + + virtual void set_level(sentry::Level p_level) override; + virtual sentry::Level get_level() const override; + + virtual void set_type(const String &p_type) override; + virtual String get_type() const override; + + virtual void set_data(const Dictionary &p_data) override; + + virtual Ref get_timestamp() override; + + CocoaBreadcrumb(); + CocoaBreadcrumb(objc::SentryBreadcrumb *p_cocoa_breadcrumb); +}; + +} //namespace sentry::cocoa + +#endif // COCOA_BREADCRUMB_H diff --git a/src/sentry/cocoa/cocoa_breadcrumb.mm b/src/sentry/cocoa/cocoa_breadcrumb.mm new file mode 100644 index 00000000..882cc684 --- /dev/null +++ b/src/sentry/cocoa/cocoa_breadcrumb.mm @@ -0,0 +1,65 @@ +#include "cocoa_breadcrumb.h" + +#include "sentry/cocoa/cocoa_includes.h" +#include "sentry/cocoa/cocoa_util.h" + +namespace sentry::cocoa { + +void CocoaBreadcrumb::set_message(const String &p_message) { + cocoa_breadcrumb.message = string_to_objc_or_nil_if_empty(p_message); +} + +String CocoaBreadcrumb::get_message() const { + return string_from_objc(cocoa_breadcrumb.message); +} + +void CocoaBreadcrumb::set_category(const String &p_category) { + cocoa_breadcrumb.category = string_to_objc_or_nil_if_empty(p_category); +} + +String CocoaBreadcrumb::get_category() const { + return string_from_objc(cocoa_breadcrumb.category); +} + +void CocoaBreadcrumb::set_level(sentry::Level p_level) { + cocoa_breadcrumb.level = sentry_level_to_objc(p_level); +} + +sentry::Level CocoaBreadcrumb::get_level() const { + return sentry_level_from_objc(cocoa_breadcrumb.level); +} + +void CocoaBreadcrumb::set_type(const String &p_type) { + cocoa_breadcrumb.type = string_to_objc_or_nil_if_empty(p_type); +} + +String CocoaBreadcrumb::get_type() const { + return string_from_objc(cocoa_breadcrumb.type); +} + +void CocoaBreadcrumb::set_data(const Dictionary &p_data) { + cocoa_breadcrumb.data = dictionary_to_objc(p_data); +} + +Ref CocoaBreadcrumb::get_timestamp() { + if (cocoa_breadcrumb.timestamp == nil) { + return Ref(); + } + + NSTimeInterval seconds = [cocoa_breadcrumb.timestamp timeIntervalSince1970]; + return SentryTimestamp::from_unix_time(seconds); +} + +CocoaBreadcrumb::CocoaBreadcrumb() : + cocoa_breadcrumb([[objc::SentryBreadcrumb alloc] init]) { +} + +CocoaBreadcrumb::CocoaBreadcrumb(objc::SentryBreadcrumb *p_cocoa_breadcrumb) : + cocoa_breadcrumb(p_cocoa_breadcrumb) { + if (!p_cocoa_breadcrumb) { + cocoa_breadcrumb = [[objc::SentryBreadcrumb alloc] init]; + ERR_PRINT_ONCE("Sentry: Internal error - cocoa breadcrumb instance is null"); + } +} + +} //namespace sentry::cocoa diff --git a/src/sentry/cocoa/cocoa_includes.h b/src/sentry/cocoa/cocoa_includes.h index d60c97f8..a4bf6bc5 100644 --- a/src/sentry/cocoa/cocoa_includes.h +++ b/src/sentry/cocoa/cocoa_includes.h @@ -37,6 +37,7 @@ using SentryThread = ::SentryThread; // In C++ context, make objc::SentryEvent an alias to void namespace objc { using SentryEvent = void; +using SentryBreadcrumb = void; } // namespace objc #endif // __OBJC__ diff --git a/src/sentry/cocoa/cocoa_sdk.h b/src/sentry/cocoa/cocoa_sdk.h index 217eb14c..a2fa00bd 100644 --- a/src/sentry/cocoa/cocoa_sdk.h +++ b/src/sentry/cocoa/cocoa_sdk.h @@ -25,8 +25,8 @@ class CocoaSDK : public InternalSDK { virtual void set_user(const Ref &p_user) override; virtual void remove_user() override; - virtual void add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type = "default", const Dictionary &p_data = Dictionary()) override; + virtual Ref create_breadcrumb() override; + virtual void add_breadcrumb(const Ref &p_breadcrumb) override; virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override; virtual String get_last_event_id() override; diff --git a/src/sentry/cocoa/cocoa_sdk.mm b/src/sentry/cocoa/cocoa_sdk.mm index 4484b083..e7e128b9 100644 --- a/src/sentry/cocoa/cocoa_sdk.mm +++ b/src/sentry/cocoa/cocoa_sdk.mm @@ -1,5 +1,6 @@ #include "cocoa_sdk.h" +#include "cocoa_breadcrumb.h" #include "cocoa_event.h" #include "cocoa_includes.h" #include "cocoa_util.h" @@ -66,20 +67,16 @@ [objc::SentrySDK setUser:nil]; } -void CocoaSDK::add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type, const Dictionary &p_data) { - objc::SentryBreadcrumb *breadcrumb = [[objc::SentryBreadcrumb alloc] init]; - - breadcrumb.level = sentry_level_to_objc(p_level); - breadcrumb.message = string_to_objc_or_nil_if_empty(p_message); - breadcrumb.category = string_to_objc_or_nil_if_empty(p_category); - breadcrumb.type = string_to_objc_or_nil_if_empty(p_type); +Ref CocoaSDK::create_breadcrumb() { + return memnew(CocoaBreadcrumb); +} - if (!p_data.is_empty()) { - breadcrumb.data = dictionary_to_objc(p_data); - } +void CocoaSDK::add_breadcrumb(const Ref &p_breadcrumb) { + ERR_FAIL_COND(p_breadcrumb.is_null()); - [objc::SentrySDK addBreadcrumb:breadcrumb]; + Ref crumb = p_breadcrumb; + ERR_FAIL_COND(crumb.is_null()); + [objc::SentrySDK addBreadcrumb:crumb->get_cocoa_breadcrumb()]; } String CocoaSDK::capture_message(const String &p_message, Level p_level) { diff --git a/src/sentry/disabled/disabled_breadcrumb.h b/src/sentry/disabled/disabled_breadcrumb.h new file mode 100644 index 00000000..d84e8cf1 --- /dev/null +++ b/src/sentry/disabled/disabled_breadcrumb.h @@ -0,0 +1,42 @@ +#ifndef DISABLED_BREADCRUMB_H +#define DISABLED_BREADCRUMB_H + +#include "sentry/level.h" +#include "sentry/sentry_breadcrumb.h" + +namespace sentry { + +class DisabledBreadcrumb : public SentryBreadcrumb { + GDCLASS(DisabledBreadcrumb, SentryBreadcrumb); + +private: + String message; + String category; + sentry::Level level = sentry::Level::LEVEL_INFO; + String type; + Dictionary data; + +protected: + static void _bind_methods() {} + +public: + virtual void set_message(const String &p_message) override { message = p_message; } + virtual String get_message() const override { return message; } + + virtual void set_category(const String &p_category) override { category = p_category; } + virtual String get_category() const override { return category; } + + virtual void set_level(sentry::Level p_level) override { level = p_level; } + virtual sentry::Level get_level() const override { return level; } + + virtual void set_type(const String &p_type) override { type = p_type; } + virtual String get_type() const override { return type; } + + virtual void set_data(const Dictionary &p_data) override { data = p_data; } + + virtual Ref get_timestamp() override { return memnew(SentryTimestamp); } +}; + +} // namespace sentry + +#endif // DISABLED_BREADCRUMB_H diff --git a/src/sentry/disabled/disabled_sdk.h b/src/sentry/disabled/disabled_sdk.h index ace1d8a2..da755491 100644 --- a/src/sentry/disabled/disabled_sdk.h +++ b/src/sentry/disabled/disabled_sdk.h @@ -1,6 +1,7 @@ #ifndef DISABLED_SDK_H #define DISABLED_SDK_H +#include "disabled_breadcrumb.h" #include "disabled_event.h" #include "sentry/internal_sdk.h" @@ -17,8 +18,8 @@ class DisabledSDK : public InternalSDK { virtual void set_user(const Ref &p_user) override {} virtual void remove_user() override {} - virtual void add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type = "default", const Dictionary &p_data = Dictionary()) override {} + virtual Ref create_breadcrumb() override { return memnew(DisabledBreadcrumb); } + virtual void add_breadcrumb(const Ref &p_breadcrumb) override {} virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override { return ""; } virtual String get_last_event_id() override { return ""; } diff --git a/src/sentry/internal_sdk.h b/src/sentry/internal_sdk.h index c643bed8..83a50fcd 100644 --- a/src/sentry/internal_sdk.h +++ b/src/sentry/internal_sdk.h @@ -3,6 +3,7 @@ #include "sentry/level.h" #include "sentry/sentry_attachment.h" +#include "sentry/sentry_breadcrumb.h" #include "sentry/sentry_event.h" #include "sentry/sentry_user.h" @@ -25,8 +26,10 @@ class InternalSDK { virtual void set_user(const Ref &p_user) = 0; virtual void remove_user() = 0; - virtual void add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type = "default", const Dictionary &p_data = Dictionary()) = 0; + virtual Ref create_breadcrumb() = 0; + virtual void add_breadcrumb(const Ref &p_breadcrumb) = 0; + // TODO: Consider adding the following function. + // virtual void clear_breadcrumbs() = 0; virtual String capture_message(const String &p_message, Level p_level) = 0; virtual String get_last_event_id() = 0; diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp new file mode 100644 index 00000000..05fe0b0f --- /dev/null +++ b/src/sentry/native/native_breadcrumb.cpp @@ -0,0 +1,79 @@ +#include "native_breadcrumb.h" + +#include "godot_cpp/core/error_macros.hpp" +#include "sentry/native/native_util.h" + +#include + +namespace sentry::native { + +void NativeBreadcrumb::set_message(const String &p_message) { + sentry::native::sentry_value_set_or_remove_string_by_key(native_crumb, "message", p_message); +} + +String NativeBreadcrumb::get_message() const { + return String::utf8(sentry_value_as_string( + sentry_value_get_by_key(native_crumb, "message"))); +} + +void NativeBreadcrumb::set_category(const String &p_category) { + sentry::native::sentry_value_set_or_remove_string_by_key(native_crumb, "category", p_category); +} + +String NativeBreadcrumb::get_category() const { + return String::utf8(sentry_value_as_string( + sentry_value_get_by_key(native_crumb, "category"))); +} + +void NativeBreadcrumb::set_level(sentry::Level p_level) { + sentry_value_set_by_key(native_crumb, "level", + sentry_value_new_string(sentry::native::level_to_cstring(p_level))); +} + +sentry::Level NativeBreadcrumb::get_level() const { + sentry_value_t value = sentry_value_get_by_key(native_crumb, "level"); + if (sentry_value_is_null(value)) { + return sentry::Level::LEVEL_INFO; + } + return sentry::native::cstring_to_level(sentry_value_as_string(value)); +} + +void NativeBreadcrumb::set_type(const String &p_type) { + sentry::native::sentry_value_set_or_remove_string_by_key(native_crumb, "type", p_type); +} + +String NativeBreadcrumb::get_type() const { + return String::utf8(sentry_value_as_string( + sentry_value_get_by_key(native_crumb, "type"))); +} + +void NativeBreadcrumb::set_data(const Dictionary &p_data) { + sentry_value_t native_data = sentry::native::variant_to_sentry_value(p_data); + sentry_value_set_by_key(native_crumb, "data", native_data); +} + +Ref NativeBreadcrumb::get_timestamp() { + sentry_value_t value = sentry_value_get_by_key(native_crumb, "timestamp"); + return SentryTimestamp::parse_rfc3339_cstr(sentry_value_as_string(value)); +} + +NativeBreadcrumb::NativeBreadcrumb(sentry_value_t p_native_crumb) { + if (sentry_value_refcount(p_native_crumb) > 0) { + sentry_value_incref(p_native_crumb); // acquire ownership + native_crumb = p_native_crumb; + } else { + // Shouldn't happen in healthy code. + native_crumb = sentry_value_new_breadcrumb(NULL, NULL); + ERR_PRINT_ONCE("Sentry: Internal error: Breadcrumb refcount is zero."); + } +} + +NativeBreadcrumb::NativeBreadcrumb() { + native_crumb = sentry_value_new_breadcrumb(NULL, NULL); +} + +NativeBreadcrumb::~NativeBreadcrumb() { + sentry_value_decref(native_crumb); // release ownership +} + +} //namespace sentry::native diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h new file mode 100644 index 00000000..db094d66 --- /dev/null +++ b/src/sentry/native/native_breadcrumb.h @@ -0,0 +1,45 @@ +#ifndef NATIVE_BREADCRUMB_H +#define NATIVE_BREADCRUMB_H + +#include "sentry/sentry_breadcrumb.h" + +#include + +namespace sentry::native { + +class NativeBreadcrumb : public SentryBreadcrumb { + GDCLASS(NativeBreadcrumb, SentryBreadcrumb); + +private: + sentry_value_t native_crumb; + +protected: + static void _bind_methods() {} + +public: + _FORCE_INLINE_ sentry_value_t get_native_breadcrumb() { return native_crumb; } + + virtual void set_message(const String &p_message) override; + virtual String get_message() const override; + + virtual void set_category(const String &p_category) override; + virtual String get_category() const override; + + virtual void set_level(sentry::Level p_level) override; + virtual sentry::Level get_level() const override; + + virtual void set_type(const String &p_type) override; + virtual String get_type() const override; + + virtual void set_data(const Dictionary &p_data) override; + + virtual Ref get_timestamp() override; + + NativeBreadcrumb(sentry_value_t p_native_crumb); + NativeBreadcrumb(); + virtual ~NativeBreadcrumb() override; +}; + +} //namespace sentry::native + +#endif // NATIVE_BREADCRUMB_H diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp index 66335e81..1087a5d8 100644 --- a/src/sentry/native/native_sdk.cpp +++ b/src/sentry/native/native_sdk.cpp @@ -3,6 +3,7 @@ #include "sentry.h" #include "sentry/common_defs.h" #include "sentry/level.h" +#include "sentry/native/native_breadcrumb.h" #include "sentry/native/native_event.h" #include "sentry/native/native_util.h" #include "sentry/processing/process_event.h" @@ -169,13 +170,17 @@ void NativeSDK::remove_user() { sentry_remove_user(); } -void NativeSDK::add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type, const Dictionary &p_data) { - sentry_value_t crumb = sentry_value_new_breadcrumb(p_type.utf8().ptr(), p_message.utf8().ptr()); - sentry_value_set_by_key(crumb, "category", sentry_value_new_string(p_category.utf8().ptr())); - sentry_value_set_by_key(crumb, "level", sentry_value_new_string(sentry::level_as_cstring(p_level))); - sentry_value_set_by_key(crumb, "data", sentry::native::variant_to_sentry_value(p_data)); - sentry_add_breadcrumb(crumb); +Ref NativeSDK::create_breadcrumb() { + return memnew(NativeBreadcrumb); +} + +void NativeSDK::add_breadcrumb(const Ref &p_breadcrumb) { + ERR_FAIL_COND_MSG(p_breadcrumb.is_null(), "Sentry: Can't add breadcrumb - breadcrumb object is null."); + NativeBreadcrumb *crumb = Object::cast_to(p_breadcrumb.ptr()); + ERR_FAIL_NULL(crumb); + sentry_value_t native_crumb = crumb->get_native_breadcrumb(); + sentry_value_incref(native_crumb); // give ownership to native + sentry_add_breadcrumb(native_crumb); } String NativeSDK::capture_message(const String &p_message, Level p_level) { diff --git a/src/sentry/native/native_sdk.h b/src/sentry/native/native_sdk.h index 0748ef6a..3b3b195c 100644 --- a/src/sentry/native/native_sdk.h +++ b/src/sentry/native/native_sdk.h @@ -25,8 +25,8 @@ class NativeSDK : public InternalSDK { virtual void set_user(const Ref &p_user) override; virtual void remove_user() override; - virtual void add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type = "default", const Dictionary &p_data = Dictionary()) override; + virtual Ref create_breadcrumb() override; + virtual void add_breadcrumb(const Ref &p_breadcrumb) override; virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override; virtual String get_last_event_id() override; diff --git a/src/sentry/native/native_util.h b/src/sentry/native/native_util.h index 2e4edcaf..45eab089 100644 --- a/src/sentry/native/native_util.h +++ b/src/sentry/native/native_util.h @@ -1,6 +1,7 @@ #ifndef NATIVE_UTIL_H #define NATIVE_UTIL_H +#include "godot_cpp/core/defs.hpp" #include "sentry/level.h" #include @@ -21,9 +22,18 @@ sentry_value_t strings_to_sentry_list(const PackedStringArray &p_strings); sentry_level_t level_to_native(Level p_level); Level native_to_level(sentry_level_t p_native_level); +// TODO: move this to level.h CharString level_to_cstring(Level p_level); Level cstring_to_level(const CharString &p_cstring); +_FORCE_INLINE_ void sentry_value_set_or_remove_string_by_key(sentry_value_t value, const char *k, const String &v) { + if (v.is_empty()) { + sentry_value_remove_by_key(value, k); + } else { + sentry_value_set_by_key(value, k, sentry_value_new_string(v.utf8())); + } +} + } //namespace sentry::native #endif // NATIVE_UTIL_H diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp new file mode 100644 index 00000000..10afffc9 --- /dev/null +++ b/src/sentry/sentry_breadcrumb.cpp @@ -0,0 +1,30 @@ +#include "sentry_breadcrumb.h" + +#include "sentry/util/simple_bind.h" +#include "sentry_sdk.h" // Needed for VariantCaster + +namespace sentry { + +Ref SentryBreadcrumb::create(const String &p_message) { + ERR_FAIL_NULL_V(SentrySDK::get_singleton(), nullptr); + auto internal_sdk = SentrySDK::get_singleton()->get_internal_sdk(); + Ref instance = internal_sdk->create_breadcrumb(); + if (!p_message.is_empty()) { + instance->set_message(p_message); + } + return instance; +} + +void SentryBreadcrumb::_bind_methods() { + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("create", "message"), &SentryBreadcrumb::create, DEFVAL(String())); + + BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, message); + BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, category); + BIND_PROPERTY(SentryBreadcrumb, sentry::make_level_enum_property("level"), set_level, get_level); + BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, type); + + ClassDB::bind_method(D_METHOD("set_data", "data"), &SentryBreadcrumb::set_data); + ClassDB::bind_method(D_METHOD("get_timestamp"), &SentryBreadcrumb::get_timestamp); +} + +} //namespace sentry diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h new file mode 100644 index 00000000..beda28a5 --- /dev/null +++ b/src/sentry/sentry_breadcrumb.h @@ -0,0 +1,44 @@ +#ifndef SENTRY_BREADCRUMB_H +#define SENTRY_BREADCRUMB_H + +#include "sentry/level.h" +#include "sentry/sentry_timestamp.h" + +#include + +using namespace godot; + +namespace sentry { + +// Represents breadcrumbs in the public API. +class SentryBreadcrumb : public RefCounted { + GDCLASS(SentryBreadcrumb, RefCounted); + +protected: + static void _bind_methods(); + +public: + static Ref create(const String &p_message = ""); + + virtual void set_message(const String &p_message) = 0; + virtual String get_message() const = 0; + + virtual void set_category(const String &p_category) = 0; + virtual String get_category() const = 0; + + virtual void set_level(sentry::Level p_level) = 0; + virtual sentry::Level get_level() const = 0; + + virtual void set_type(const String &p_type) = 0; + virtual String get_type() const = 0; + + virtual void set_data(const Dictionary &p_data) = 0; + + virtual Ref get_timestamp() = 0; + + virtual ~SentryBreadcrumb() = default; +}; + +} //namespace sentry + +#endif // SENTRY_BREADCRUMB_H diff --git a/src/sentry/sentry_logger.cpp b/src/sentry/sentry_logger.cpp index 9ab037c4..22a991b9 100644 --- a/src/sentry/sentry_logger.cpp +++ b/src/sentry/sentry_logger.cpp @@ -328,12 +328,12 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in data["rationale"] = p_rationale; data["error_type"] = String(error_type_as_string[int(p_error_type)]); - SentrySDK::get_singleton()->add_breadcrumb( - error_message, - "error", - sentry::get_sentry_level_for_godot_error_type((GodotErrorType)p_error_type), - "error", - data); + Ref crumb = SentryBreadcrumb::create(error_message); + crumb->set_level(sentry::get_sentry_level_for_godot_error_type((GodotErrorType)p_error_type)); + crumb->set_type("error"); + crumb->set_category("error"); + crumb->set_data(data); + SentrySDK::get_singleton()->add_breadcrumb(crumb); } } @@ -357,11 +357,11 @@ void SentryLogger::_log_message(const String &p_message, bool p_error) { } } - SentrySDK::get_singleton()->add_breadcrumb( - p_message, - "log", - p_error ? sentry::Level::LEVEL_ERROR : sentry::Level::LEVEL_INFO, - "debug"); + Ref crumb = SentryBreadcrumb::create(p_message); + crumb->set_category("log"); + crumb->set_level(p_error ? sentry::Level::LEVEL_ERROR : sentry::Level::LEVEL_INFO); + crumb->set_type("debug"); + SentrySDK::get_singleton()->add_breadcrumb(crumb); } void SentryLogger::_bind_methods() { diff --git a/src/sentry/sentry_sdk.cpp b/src/sentry/sentry_sdk.cpp index 7345dc35..1759c130 100644 --- a/src/sentry/sentry_sdk.cpp +++ b/src/sentry/sentry_sdk.cpp @@ -122,9 +122,9 @@ String SentrySDK::capture_message(const String &p_message, Level p_level) { return internal_sdk->capture_message(p_message, p_level); } -void SentrySDK::add_breadcrumb(const String &p_message, const String &p_category, Level p_level, - const String &p_type, const Dictionary &p_data) { - internal_sdk->add_breadcrumb(p_message, p_category, p_level, p_type, p_data); +void SentrySDK::add_breadcrumb(const Ref &p_breadcrumb) { + ERR_FAIL_COND_MSG(p_breadcrumb.is_null(), "Sentry: Can't add null breadcrumb."); + internal_sdk->add_breadcrumb(p_breadcrumb); } String SentrySDK::get_last_event_id() const { @@ -380,8 +380,8 @@ void SentrySDK::_bind_methods() { BIND_ENUM_CONSTANT(LEVEL_FATAL); 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)); - ClassDB::bind_method(D_METHOD("add_breadcrumb", "message", "category", "level", "type", "data"), &SentrySDK::add_breadcrumb, DEFVAL(LEVEL_INFO), DEFVAL("default"), DEFVAL(Dictionary())); ClassDB::bind_method(D_METHOD("get_last_event_id"), &SentrySDK::get_last_event_id); ClassDB::bind_method(D_METHOD("set_context", "key", "value"), &SentrySDK::set_context); ClassDB::bind_method(D_METHOD("set_tag", "key", "value"), &SentrySDK::set_tag); diff --git a/src/sentry/sentry_sdk.h b/src/sentry/sentry_sdk.h index a53a098b..90917172 100644 --- a/src/sentry/sentry_sdk.h +++ b/src/sentry/sentry_sdk.h @@ -5,6 +5,7 @@ #include "sentry/internal_sdk.h" #include "sentry/level.h" #include "sentry/sentry_attachment.h" +#include "sentry/sentry_breadcrumb.h" #include "sentry/sentry_event.h" #include "sentry/sentry_logger.h" #include "sentry/sentry_options.h" @@ -63,8 +64,8 @@ class SentrySDK : public Object { bool is_enabled() const { return enabled; } - void add_breadcrumb(const String &p_message, const String &p_category, sentry::Level p_level, - const String &p_type = "default", const Dictionary &p_data = Dictionary()); + void add_breadcrumb(const Ref &p_breadcrumb); + void set_context(const String &p_key, const Dictionary &p_value); void set_tag(const String &p_key, const String &p_value);