From b236071c570217a5b85a71a6e1585a6750d5ab59 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 16:44:10 +0100 Subject: [PATCH 01/51] Add SentryBreadcrumb abstract class --- src/register_types.cpp | 2 ++ src/sentry/sentry_breadcrumb.cpp | 12 +++++++++++ src/sentry/sentry_breadcrumb.h | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/sentry/sentry_breadcrumb.cpp create mode 100644 src/sentry/sentry_breadcrumb.h diff --git a/src/register_types.cpp b/src/register_types.cpp index 7c96be14..10197b4c 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" @@ -49,6 +50,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); diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp new file mode 100644 index 00000000..ae568085 --- /dev/null +++ b/src/sentry/sentry_breadcrumb.cpp @@ -0,0 +1,12 @@ +#include "sentry_breadcrumb.h" + +#include "sentry/simple_bind.h" +#include "sentry_sdk.h" // Needed for VariantCaster + +void SentryBreadcrumb::_bind_methods() { + 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); + BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::DICTIONARY, data); +} diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h new file mode 100644 index 00000000..f420aaff --- /dev/null +++ b/src/sentry/sentry_breadcrumb.h @@ -0,0 +1,36 @@ +#ifndef SENTRY_BREADCRUMB_H +#define SENTRY_BREADCRUMB_H + +#include "sentry/level.h" + +#include + +using namespace godot; + +// Represents breadcrumbs in the public API. +class SentryBreadcrumb : public RefCounted { + GDCLASS(SentryBreadcrumb, RefCounted); + +protected: + static void _bind_methods(); + +public: + 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 Dictionary get_data() const = 0; + + virtual ~SentryBreadcrumb() = default; +}; + +#endif // SENTRY_BREADCRUMB_H From fd8cb4a0827f674f937ce79316ec33ab7c7934b9 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 18:22:15 +0100 Subject: [PATCH 02/51] Add DisabledBreadcrumb --- src/sentry/disabled/disabled_breadcrumb.h | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/sentry/disabled/disabled_breadcrumb.h diff --git a/src/sentry/disabled/disabled_breadcrumb.h b/src/sentry/disabled/disabled_breadcrumb.h new file mode 100644 index 00000000..e482c37f --- /dev/null +++ b/src/sentry/disabled/disabled_breadcrumb.h @@ -0,0 +1,34 @@ +#ifndef DISABLED_BREADCRUMB_H +#define DISABLED_BREADCRUMB_H + +#include "sentry/level.h" +#include "sentry_breadcrumb.h" + +class DisabledBreadcrumb : public SentryBreadcrumb { + GDCLASS(DisabledBreadcrumb, SentryBreadcrumb); + +private: + String message; + String category; + sentry::Level level = sentry::Level::LEVEL_INFO; + String type; + Dictionary data; + +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 Dictionary get_data() const override { return data; } +}; + +#endif // DISABLED_BREADCRUMB_H From 554fc691840071e861101bed99fd920e67c9d19c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 18:22:47 +0100 Subject: [PATCH 03/51] Implement NativeBreadcrumb --- src/sentry/native/native_breadcrumb.cpp | 77 +++++++++++++++++++++++++ src/sentry/native/native_breadcrumb.h | 35 +++++++++++ src/sentry/native/native_util.h | 10 ++++ 3 files changed, 122 insertions(+) create mode 100644 src/sentry/native/native_breadcrumb.cpp create mode 100644 src/sentry/native/native_breadcrumb.h diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp new file mode 100644 index 00000000..597e50d6 --- /dev/null +++ b/src/sentry/native/native_breadcrumb.cpp @@ -0,0 +1,77 @@ +#include "native_breadcrumb.h" +#include "godot_cpp/core/error_macros.hpp" +#include "sentry/native/native_util.h" + +#include + +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 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 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_ERROR; + } + 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 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); +} + +Dictionary NativeBreadcrumb::get_data() const { + // TODO: implement sentry_value_to_variant() + // return sentry::native::sentry_value_to_variant(sentry_value_get_by_key(native_crumb, "data")); + WARN_PRINT("Not implemented."); + return Dictionary(); +} + +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_object(); + ERR_PRINT("Sentry: Internal error: Breadcrumb refcount is zero."); + } + native_crumb = p_native_crumb; +} + +NativeBreadcrumb::NativeBreadcrumb() { + native_crumb = sentry_value_new_object(); +} + +NativeBreadcrumb::~NativeBreadcrumb() { + sentry_value_decref(native_crumb); // release ownership +} diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h new file mode 100644 index 00000000..2d7f1d9f --- /dev/null +++ b/src/sentry/native/native_breadcrumb.h @@ -0,0 +1,35 @@ +#ifndef NATIVE_BREADCRUMB_H +#define NATIVE_BREADCRUMB_H + +#include "sentry_breadcrumb.h" + +#include + +class NativeBreadcrumb : public SentryBreadcrumb { + GDCLASS(NativeBreadcrumb, SentryBreadcrumb); + +private: + sentry_value_t native_crumb; + +public: + 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 Dictionary get_data() const override; + + NativeBreadcrumb(sentry_value_t p_native_crumb); + NativeBreadcrumb(); + virtual ~NativeBreadcrumb() override; +}; + +#endif // NATIVE_BREADCRUMB_H 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 From 487a2db98f5dfc1ebc732466aabc1957058836e9 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 18:23:02 +0100 Subject: [PATCH 04/51] Register those classes --- src/register_types.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/register_types.cpp b/src/register_types.cpp index 10197b4c..48444811 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -19,6 +19,7 @@ #include #ifdef SDK_NATIVE +#include "sentry/native/native_breadcrumb.h" #include "sentry/native/native_event.h" #endif // SDK_NATIVE @@ -59,6 +60,7 @@ void register_runtime_classes() { #ifdef SDK_NATIVE GDREGISTER_INTERNAL_CLASS(native::NativeEvent); + GDREGISTER_INTERNAL_CLASS(native::NativeBreadcrumb); #endif #ifdef SDK_ANDROID From 3766f13afe341e27caf79c55c7f79ac6f27603bc Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 18:29:42 +0100 Subject: [PATCH 05/51] Create breadcrumb internal method --- src/sentry/disabled/disabled_sdk.h | 2 ++ src/sentry/internal_sdk.h | 4 ++++ src/sentry/native/native_sdk.cpp | 5 +++++ src/sentry/native/native_sdk.h | 1 + 4 files changed, 12 insertions(+) diff --git a/src/sentry/disabled/disabled_sdk.h b/src/sentry/disabled/disabled_sdk.h index ace1d8a2..37b5eb74 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" @@ -19,6 +20,7 @@ class DisabledSDK : public InternalSDK { 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 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..b7c5f325 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" @@ -27,6 +28,9 @@ class InternalSDK { 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; + // 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_sdk.cpp b/src/sentry/native/native_sdk.cpp index 66335e81..2a024506 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" @@ -178,6 +179,10 @@ void NativeSDK::add_breadcrumb(const String &p_message, const String &p_category sentry_add_breadcrumb(crumb); } +Ref NativeSDK::create_breadcrumb() { + return memnew(NativeBreadcrumb); +} + String NativeSDK::capture_message(const String &p_message, Level p_level) { sentry_value_t event = sentry_value_new_message_event( native::level_to_native(p_level), diff --git a/src/sentry/native/native_sdk.h b/src/sentry/native/native_sdk.h index 0748ef6a..5a476dfc 100644 --- a/src/sentry/native/native_sdk.h +++ b/src/sentry/native/native_sdk.h @@ -27,6 +27,7 @@ class NativeSDK : public InternalSDK { 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 String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override; virtual String get_last_event_id() override; From 0625eccf114525987725a3a2d9a73ab7f72d3ccb Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 18:29:57 +0100 Subject: [PATCH 06/51] Fix gdextension compilation --- src/sentry/disabled/disabled_breadcrumb.h | 3 +++ src/sentry/native/native_breadcrumb.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/sentry/disabled/disabled_breadcrumb.h b/src/sentry/disabled/disabled_breadcrumb.h index e482c37f..031c43a4 100644 --- a/src/sentry/disabled/disabled_breadcrumb.h +++ b/src/sentry/disabled/disabled_breadcrumb.h @@ -14,6 +14,9 @@ class DisabledBreadcrumb : public SentryBreadcrumb { 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; } diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h index 2d7f1d9f..f1dcf23c 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -11,6 +11,9 @@ class NativeBreadcrumb : public SentryBreadcrumb { private: sentry_value_t native_crumb; +protected: + static void _bind_methods() {} + public: virtual void set_message(const String &p_message) override; virtual String get_message() const override; From 38744fffb0229150d745e20a4b752920c4fb64db Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 31 Jan 2025 18:32:20 +0100 Subject: [PATCH 07/51] New method SentrySDK.create_breadcrumb() --- src/sentry/sentry_sdk.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sentry/sentry_sdk.h b/src/sentry/sentry_sdk.h index a53a098b..11aa463b 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" @@ -65,6 +66,8 @@ class SentrySDK : public Object { 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()); + Ref create_breadcrumb() { return internal_sdk->create_breadcrumb(); } + void set_context(const String &p_key, const Dictionary &p_value); void set_tag(const String &p_key, const String &p_value); From 3863e4e8e2e86ba634f37ef10daa12a211be4e10 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 11:10:47 +0200 Subject: [PATCH 08/51] Fix simple_bind location --- src/sentry/sentry_breadcrumb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index ae568085..996c62c4 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -1,6 +1,6 @@ #include "sentry_breadcrumb.h" -#include "sentry/simple_bind.h" +#include "sentry/util/simple_bind.h" #include "sentry_sdk.h" // Needed for VariantCaster void SentryBreadcrumb::_bind_methods() { From 986008f4f91f52bdb3c23a456deabdbfc0809ffa Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 11:25:42 +0200 Subject: [PATCH 09/51] Fix DisabledBreadcrumb --- src/sentry/disabled/disabled_breadcrumb.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sentry/disabled/disabled_breadcrumb.h b/src/sentry/disabled/disabled_breadcrumb.h index 031c43a4..cf0d667e 100644 --- a/src/sentry/disabled/disabled_breadcrumb.h +++ b/src/sentry/disabled/disabled_breadcrumb.h @@ -2,7 +2,9 @@ #define DISABLED_BREADCRUMB_H #include "sentry/level.h" -#include "sentry_breadcrumb.h" +#include "sentry/sentry_breadcrumb.h" + +namespace sentry { class DisabledBreadcrumb : public SentryBreadcrumb { GDCLASS(DisabledBreadcrumb, SentryBreadcrumb); @@ -34,4 +36,6 @@ class DisabledBreadcrumb : public SentryBreadcrumb { virtual Dictionary get_data() const override { return data; } }; +} // namespace sentry + #endif // DISABLED_BREADCRUMB_H From c44674309144227a17da88b22609d261a84bb58c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 11:57:37 +0200 Subject: [PATCH 10/51] Add static factory methods for SentryBreadcrumb --- src/sentry/sentry_breadcrumb.cpp | 55 ++++++++++++++++++++++++++++++++ src/sentry/sentry_breadcrumb.h | 9 ++++++ 2 files changed, 64 insertions(+) diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index 996c62c4..7aa68f7e 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -3,10 +3,65 @@ #include "sentry/util/simple_bind.h" #include "sentry_sdk.h" // Needed for VariantCaster +namespace { + +using namespace sentry; + +inline Ref _create_breadcrumb() { + ERR_FAIL_NULL_V(SentrySDK::get_singleton(), nullptr); + auto internal_sdk = SentrySDK::get_singleton()->get_internal_sdk(); + Ref instance = internal_sdk->create_breadcrumb(); + ERR_FAIL_COND_V(instance.is_null(), nullptr); + return instance; +} + +} // unnamed namespace + +namespace sentry { + +Ref SentryBreadcrumb::debug(const String &p_message) { + Ref instance = _create_breadcrumb(); + instance->set_type("debug"); + instance->set_message(p_message); + instance->set_level(sentry::Level::LEVEL_DEBUG); + return instance; +} + +Ref SentryBreadcrumb::info(const String &p_message) { + Ref instance = _create_breadcrumb(); + instance->set_type("info"); + instance->set_message(p_message); + instance->set_level(sentry::Level::LEVEL_INFO); + return instance; +} + +Ref SentryBreadcrumb::error(const String &p_message) { + Ref instance = _create_breadcrumb(); + instance->set_type("error"); + instance->set_message(p_message); + instance->set_level(sentry::Level::LEVEL_ERROR); + return instance; +} + +Ref SentryBreadcrumb::query(const String &p_message) { + Ref instance = _create_breadcrumb(); + instance->set_type("query"); + instance->set_message(p_message); + instance->set_level(sentry::Level::LEVEL_INFO); + return instance; +} + void SentryBreadcrumb::_bind_methods() { + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("debug", "message"), &SentryBreadcrumb::debug); + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("info", "message"), &SentryBreadcrumb::info); + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("error", "message"), &SentryBreadcrumb::error); + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("query", "message"), &SentryBreadcrumb::query); + 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); BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::DICTIONARY, data); } + +} //namespace sentry diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h index f420aaff..71b24e3f 100644 --- a/src/sentry/sentry_breadcrumb.h +++ b/src/sentry/sentry_breadcrumb.h @@ -7,6 +7,8 @@ using namespace godot; +namespace sentry { + // Represents breadcrumbs in the public API. class SentryBreadcrumb : public RefCounted { GDCLASS(SentryBreadcrumb, RefCounted); @@ -15,6 +17,11 @@ class SentryBreadcrumb : public RefCounted { static void _bind_methods(); public: + static Ref debug(const String &p_message); + static Ref info(const String &p_message); + static Ref error(const String &p_message); + static Ref query(const String &p_message); + virtual void set_message(const String &p_message) = 0; virtual String get_message() const = 0; @@ -33,4 +40,6 @@ class SentryBreadcrumb : public RefCounted { virtual ~SentryBreadcrumb() = default; }; +} //namespace sentry + #endif // SENTRY_BREADCRUMB_H From 00b3e0bb5579b0c5ee5e6bf5971ff57a9e9ba935 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 11:58:38 +0200 Subject: [PATCH 11/51] Fix native include --- src/sentry/native/native_breadcrumb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h index f1dcf23c..891b0343 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -1,7 +1,7 @@ #ifndef NATIVE_BREADCRUMB_H #define NATIVE_BREADCRUMB_H -#include "sentry_breadcrumb.h" +#include "sentry/sentry_breadcrumb.h" #include From 9d4bf93694b32168250381e83130ab383a9e14d2 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 12:23:29 +0200 Subject: [PATCH 12/51] Use sentry::native namespace --- src/sentry/native/native_breadcrumb.cpp | 5 +++++ src/sentry/native/native_breadcrumb.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index 597e50d6..e0a42f79 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -1,9 +1,12 @@ #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); } @@ -75,3 +78,5 @@ NativeBreadcrumb::NativeBreadcrumb() { 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 index 891b0343..3a4f9eae 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -5,6 +5,8 @@ #include +namespace sentry::native { + class NativeBreadcrumb : public SentryBreadcrumb { GDCLASS(NativeBreadcrumb, SentryBreadcrumb); @@ -35,4 +37,6 @@ class NativeBreadcrumb : public SentryBreadcrumb { virtual ~NativeBreadcrumb() override; }; +} //namespace sentry::native + #endif // NATIVE_BREADCRUMB_H From c46f14e40d3bcd7aea662a867dd4566d2ed76973 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 14:10:13 +0200 Subject: [PATCH 13/51] Breadcrumb in Cocoa --- project/views/enrich_events.gd | 4 +- src/register_types.cpp | 2 + src/sentry/cocoa/cocoa_breadcrumb.h | 42 ++++++++++++++++++++ src/sentry/cocoa/cocoa_breadcrumb.mm | 58 ++++++++++++++++++++++++++++ src/sentry/cocoa/cocoa_includes.h | 1 + src/sentry/cocoa/cocoa_sdk.h | 4 +- src/sentry/cocoa/cocoa_sdk.mm | 21 +++++----- src/sentry/disabled/disabled_sdk.h | 3 +- src/sentry/internal_sdk.h | 3 +- src/sentry/sentry_sdk.cpp | 9 +++-- src/sentry/sentry_sdk.h | 3 +- 11 files changed, 125 insertions(+), 25 deletions(-) create mode 100644 src/sentry/cocoa/cocoa_breadcrumb.h create mode 100644 src/sentry/cocoa/cocoa_breadcrumb.mm diff --git a/project/views/enrich_events.gd b/project/views/enrich_events.gd index ad7b4596..06401be5 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.info(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 48444811..857a25a2 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -29,6 +29,7 @@ #endif // SDK_ANDROID #ifdef SDK_COCOA +#include "sentry/cocoa/cocoa_breadcrumb.h" #include "sentry/cocoa/cocoa_event.h" #endif // SDK_COCOA @@ -70,6 +71,7 @@ void register_runtime_classes() { #ifdef SDK_COCOA GDREGISTER_INTERNAL_CLASS(cocoa::CocoaEvent); + GDREGISTER_INTERNAL_CLASS(cocoa::CocoaBreadcrumb); #endif } diff --git a/src/sentry/cocoa/cocoa_breadcrumb.h b/src/sentry/cocoa/cocoa_breadcrumb.h new file mode 100644 index 00000000..fea53146 --- /dev/null +++ b/src/sentry/cocoa/cocoa_breadcrumb.h @@ -0,0 +1,42 @@ +#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 Dictionary get_data() const 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..f5bcb908 --- /dev/null +++ b/src/sentry/cocoa/cocoa_breadcrumb.mm @@ -0,0 +1,58 @@ +#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); +} + +Dictionary CocoaBreadcrumb::get_data() const { + // TODO: not implemented + // return dictionary_from_objc(cocoa_breadcrumb.data); + return Dictionary(); +} + +CocoaBreadcrumb::CocoaBreadcrumb() : + cocoa_breadcrumb([[objc::SentryBreadcrumb alloc] init]) { +} + +CocoaBreadcrumb::CocoaBreadcrumb(objc::SentryBreadcrumb *p_cocoa_breadcrumb) : + cocoa_breadcrumb(p_cocoa_breadcrumb) { +} + +} //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_sdk.h b/src/sentry/disabled/disabled_sdk.h index 37b5eb74..da755491 100644 --- a/src/sentry/disabled/disabled_sdk.h +++ b/src/sentry/disabled/disabled_sdk.h @@ -18,9 +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 b7c5f325..83a50fcd 100644 --- a/src/sentry/internal_sdk.h +++ b/src/sentry/internal_sdk.h @@ -26,9 +26,8 @@ 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; diff --git a/src/sentry/sentry_sdk.cpp b/src/sentry/sentry_sdk.cpp index 7345dc35..52824764 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,9 @@ void SentrySDK::_bind_methods() { BIND_ENUM_CONSTANT(LEVEL_FATAL); ClassDB::bind_method(D_METHOD("is_enabled"), &SentrySDK::is_enabled); + ClassDB::bind_method(D_METHOD("create_breadcrumb"), &SentrySDK::create_breadcrumb); + 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 11aa463b..c05cf1f3 100644 --- a/src/sentry/sentry_sdk.h +++ b/src/sentry/sentry_sdk.h @@ -64,9 +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()); Ref create_breadcrumb() { return internal_sdk->create_breadcrumb(); } + void add_breadcrumb(const Ref &p_breadcrumb); void set_context(const String &p_key, const Dictionary &p_value); From 3cf405d038ea36eecedbcd83fa3ca221ad66e156 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 14:10:39 +0200 Subject: [PATCH 14/51] Adjust logger to use new API calls --- src/sentry/sentry_logger.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/sentry/sentry_logger.cpp b/src/sentry/sentry_logger.cpp index 9ab037c4..7dc9c626 100644 --- a/src/sentry/sentry_logger.cpp +++ b/src/sentry/sentry_logger.cpp @@ -328,12 +328,10 @@ 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::error(error_message); + crumb->set_level(sentry::get_sentry_level_for_godot_error_type((GodotErrorType)p_error_type)); + crumb->set_data(data); + SentrySDK::get_singleton()->add_breadcrumb(crumb); } } @@ -357,11 +355,12 @@ 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 = SentrySDK::get_singleton()->create_breadcrumb(); + crumb->set_message(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() { From 4ab6179250324ee8892088032d6d725f06cec810 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 14:18:17 +0200 Subject: [PATCH 15/51] Add native implementation --- src/sentry/native/native_breadcrumb.h | 2 ++ src/sentry/native/native_sdk.cpp | 17 ++++++++--------- src/sentry/native/native_sdk.h | 3 +-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h index 3a4f9eae..bec469ce 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -17,6 +17,8 @@ class NativeBreadcrumb : public SentryBreadcrumb { static void _bind_methods() {} public: + _FORCE_INLINE_ sentry_value_t get_native_breadcrumb(); + virtual void set_message(const String &p_message) override; virtual String get_message() const override; diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp index 2a024506..1d952bd6 100644 --- a/src/sentry/native/native_sdk.cpp +++ b/src/sentry/native/native_sdk.cpp @@ -170,19 +170,18 @@ 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."); + Ref crumb = p_breadcrumb; + ERR_FAIL_COND(crumb.is_null()); + sentry_value_t native_crumb = crumb->get_native_breadcrumb(); + sentry_add_breadcrumb(native_crumb); +} + String NativeSDK::capture_message(const String &p_message, Level p_level) { sentry_value_t event = sentry_value_new_message_event( native::level_to_native(p_level), diff --git a/src/sentry/native/native_sdk.h b/src/sentry/native/native_sdk.h index 5a476dfc..3b3b195c 100644 --- a/src/sentry/native/native_sdk.h +++ b/src/sentry/native/native_sdk.h @@ -25,9 +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; From 2166dead237db87125ca8206776e3fb2a4b91cfd Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 14:53:32 +0200 Subject: [PATCH 16/51] Define get_native_breadcrumb() --- src/sentry/native/native_breadcrumb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h index bec469ce..57fc409e 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -17,7 +17,7 @@ class NativeBreadcrumb : public SentryBreadcrumb { static void _bind_methods() {} public: - _FORCE_INLINE_ sentry_value_t get_native_breadcrumb(); + _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; From 055c6d468b020273d386e92e43ce53eff194f4e1 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 15:00:38 +0200 Subject: [PATCH 17/51] Remove breadcrumb data getter --- src/sentry/cocoa/cocoa_breadcrumb.h | 1 - src/sentry/cocoa/cocoa_breadcrumb.mm | 6 ------ src/sentry/disabled/disabled_breadcrumb.h | 1 - src/sentry/native/native_breadcrumb.cpp | 7 ------- src/sentry/native/native_breadcrumb.h | 1 - src/sentry/sentry_breadcrumb.cpp | 3 ++- src/sentry/sentry_breadcrumb.h | 1 - 7 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/sentry/cocoa/cocoa_breadcrumb.h b/src/sentry/cocoa/cocoa_breadcrumb.h index fea53146..7b0183e9 100644 --- a/src/sentry/cocoa/cocoa_breadcrumb.h +++ b/src/sentry/cocoa/cocoa_breadcrumb.h @@ -31,7 +31,6 @@ class CocoaBreadcrumb : public SentryBreadcrumb { virtual String get_type() const override; virtual void set_data(const Dictionary &p_data) override; - virtual Dictionary get_data() const override; CocoaBreadcrumb(); CocoaBreadcrumb(objc::SentryBreadcrumb *p_cocoa_breadcrumb); diff --git a/src/sentry/cocoa/cocoa_breadcrumb.mm b/src/sentry/cocoa/cocoa_breadcrumb.mm index f5bcb908..8c5b6797 100644 --- a/src/sentry/cocoa/cocoa_breadcrumb.mm +++ b/src/sentry/cocoa/cocoa_breadcrumb.mm @@ -41,12 +41,6 @@ cocoa_breadcrumb.data = dictionary_to_objc(p_data); } -Dictionary CocoaBreadcrumb::get_data() const { - // TODO: not implemented - // return dictionary_from_objc(cocoa_breadcrumb.data); - return Dictionary(); -} - CocoaBreadcrumb::CocoaBreadcrumb() : cocoa_breadcrumb([[objc::SentryBreadcrumb alloc] init]) { } diff --git a/src/sentry/disabled/disabled_breadcrumb.h b/src/sentry/disabled/disabled_breadcrumb.h index cf0d667e..86a79f74 100644 --- a/src/sentry/disabled/disabled_breadcrumb.h +++ b/src/sentry/disabled/disabled_breadcrumb.h @@ -33,7 +33,6 @@ class DisabledBreadcrumb : public SentryBreadcrumb { virtual String get_type() const override { return type; } virtual void set_data(const Dictionary &p_data) override { data = p_data; } - virtual Dictionary get_data() const override { return data; } }; } // namespace sentry diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index e0a42f79..197a93e0 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -52,13 +52,6 @@ void NativeBreadcrumb::set_data(const Dictionary &p_data) { sentry_value_set_by_key(native_crumb, "data", native_data); } -Dictionary NativeBreadcrumb::get_data() const { - // TODO: implement sentry_value_to_variant() - // return sentry::native::sentry_value_to_variant(sentry_value_get_by_key(native_crumb, "data")); - WARN_PRINT("Not implemented."); - return Dictionary(); -} - NativeBreadcrumb::NativeBreadcrumb(sentry_value_t p_native_crumb) { if (sentry_value_refcount(p_native_crumb) > 0) { sentry_value_incref(p_native_crumb); // acquire ownership diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h index 57fc409e..45c8561f 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -32,7 +32,6 @@ class NativeBreadcrumb : public SentryBreadcrumb { virtual String get_type() const override; virtual void set_data(const Dictionary &p_data) override; - virtual Dictionary get_data() const override; NativeBreadcrumb(sentry_value_t p_native_crumb); NativeBreadcrumb(); diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index 7aa68f7e..626600ef 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -61,7 +61,8 @@ void SentryBreadcrumb::_bind_methods() { 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); - BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::DICTIONARY, data); + + ClassDB::bind_method(D_METHOD("set_data", "data"), &SentryBreadcrumb::set_data); } } //namespace sentry diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h index 71b24e3f..5ca0ff93 100644 --- a/src/sentry/sentry_breadcrumb.h +++ b/src/sentry/sentry_breadcrumb.h @@ -35,7 +35,6 @@ class SentryBreadcrumb : public RefCounted { virtual String get_type() const = 0; virtual void set_data(const Dictionary &p_data) = 0; - virtual Dictionary get_data() const = 0; virtual ~SentryBreadcrumb() = default; }; From f2234a321e32cbecc2c8a5057ecbdb1358eb9601 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 15:34:26 +0200 Subject: [PATCH 18/51] Add SentryBreadcrumb.user() static factory method --- src/sentry/sentry_breadcrumb.cpp | 10 ++++++++++ src/sentry/sentry_breadcrumb.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index 626600ef..4e230551 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -51,11 +51,21 @@ Ref SentryBreadcrumb::query(const String &p_message) { return instance; } +Ref SentryBreadcrumb::user(const String &p_category, const String &p_message) { + Ref instance = _create_breadcrumb(); + instance->set_type("user"); + instance->set_category(p_category); + instance->set_message(p_message); + instance->set_level(sentry::Level::LEVEL_INFO); + return instance; +} + void SentryBreadcrumb::_bind_methods() { ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("debug", "message"), &SentryBreadcrumb::debug); ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("info", "message"), &SentryBreadcrumb::info); ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("error", "message"), &SentryBreadcrumb::error); ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("query", "message"), &SentryBreadcrumb::query); + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("user", "category", "message"), &SentryBreadcrumb::user); BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, message); BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, category); diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h index 5ca0ff93..dfd0046a 100644 --- a/src/sentry/sentry_breadcrumb.h +++ b/src/sentry/sentry_breadcrumb.h @@ -21,6 +21,7 @@ class SentryBreadcrumb : public RefCounted { static Ref info(const String &p_message); static Ref error(const String &p_message); static Ref query(const String &p_message); + static Ref user(const String &p_category, const String &p_message); virtual void set_message(const String &p_message) = 0; virtual String get_message() const = 0; From ed8c3912ffdb3f463c24858532dbf304632acca8 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 15:34:44 +0200 Subject: [PATCH 19/51] Add basic tests --- project/test/suites/test_breadcrumb.gd | 74 ++++++++++++++++++++++ project/test/suites/test_breadcrumb.gd.uid | 1 + 2 files changed, 75 insertions(+) create mode 100644 project/test/suites/test_breadcrumb.gd create mode 100644 project/test/suites/test_breadcrumb.gd.uid diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd new file mode 100644 index 00000000..915970f9 --- /dev/null +++ b/project/test/suites/test_breadcrumb.gd @@ -0,0 +1,74 @@ +extends GdUnitTestSuite +## Test SentryBreadcrumb class properties and methods. + + +func test_breadcrumb_message() -> void: + var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + crumb.message = "test-message" + assert_str(crumb.message).is_equal("test-message") + + +func test_breadcrumb_category() -> void: + var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + crumb.category = "test-category" + assert_str(crumb.category).is_equal("test-category") + + +func test_breadcrumb_level() -> void: + var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + 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_type() -> void: + var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + crumb.type = "test-type" + assert_str(crumb.type).is_equal("test-type") + + +func test_breadcrumb_info_shortcut() -> void: + var crumb := SentryBreadcrumb.info("Info message") + assert_str(crumb.message).is_equal("Info message") + assert_str(crumb.type).is_equal("info") + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + assert_str(crumb.category).is_equal("default") + + +func test_breadcrumb_debug_shortcut() -> void: + var crumb := SentryBreadcrumb.debug("Debug message") + assert_str(crumb.message).is_equal("Debug message") + assert_str(crumb.type).is_equal("debug") + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_DEBUG) + assert_str(crumb.category).is_equal("default") + + +func test_breadcrumb_error_shortcut() -> void: + var crumb := SentryBreadcrumb.error("Error happened") + assert_str(crumb.message).is_equal("Error happened") + assert_str(crumb.type).is_equal("error") + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_ERROR) + assert_str(crumb.category).is_equal("default") + + +func test_breadcrumb_query_shortcut() -> void: + var crumb := SentryBreadcrumb.query("Query performed") + assert_str(crumb.message).is_equal("Query performed") + assert_str(crumb.type).is_equal("query") + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + assert_str(crumb.category).is_equal("default") + + +func test_breadcrumb_user_shortcut() -> void: + var crumb := SentryBreadcrumb.user("ui.click", "User clicked button") + assert_str(crumb.message).is_equal("User clicked button") + assert_str(crumb.type).is_equal("user") + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + assert_str(crumb.category).is_equal("ui.click") 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 From 82e2ce1278ed6933c83f85eb3ebb66ca73e78a20 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 16:56:34 +0200 Subject: [PATCH 20/51] Add Android implementation --- .../godotplugin/SentryAndroidGodotPlugin.kt | 117 +++++++++++++++--- src/register_types.cpp | 2 + src/sentry/android/android_breadcrumb.cpp | 65 ++++++++++ src/sentry/android/android_breadcrumb.h | 42 +++++++ src/sentry/android/android_sdk.cpp | 15 ++- src/sentry/android/android_sdk.h | 4 +- src/sentry/android/android_string_names.cpp | 13 ++ src/sentry/android/android_string_names.h | 13 ++ 8 files changed, 249 insertions(+), 22 deletions(-) create mode 100644 src/sentry/android/android_breadcrumb.cpp create mode 100644 src/sentry/android/android_breadcrumb.h 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..4af1b54f 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,73 @@ 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 + } + } + } diff --git a/src/register_types.cpp b/src/register_types.cpp index 857a25a2..af509ff0 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -24,6 +24,7 @@ #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 @@ -66,6 +67,7 @@ void register_runtime_classes() { #ifdef SDK_ANDROID GDREGISTER_INTERNAL_CLASS(android::AndroidEvent); + GDREGISTER_INTERNAL_CLASS(android::AndroidBreadcrumb); GDREGISTER_INTERNAL_CLASS(android::SentryAndroidBeforeSendHandler); #endif diff --git a/src/sentry/android/android_breadcrumb.cpp b/src/sentry/android/android_breadcrumb.cpp new file mode 100644 index 00000000..20106fd2 --- /dev/null +++ b/src/sentry/android/android_breadcrumb.cpp @@ -0,0 +1,65 @@ +#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); +} + +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); +} + +AndroidBreadcrumb::AndroidBreadcrumb(Object *android_plugin, int32_t p_breadcrumb_handle) : + android_plugin(android_plugin), handle(p_breadcrumb_handle) { + ERR_FAIL_NULL(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..ad5e7bb8 --- /dev/null +++ b/src/sentry/android/android_breadcrumb.h @@ -0,0 +1,42 @@ +#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; + + AndroidBreadcrumb() = default; + AndroidBreadcrumb(Object *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..9a019333 100644 --- a/src/sentry/android/android_string_names.cpp +++ b/src/sentry/android/android_string_names.cpp @@ -60,6 +60,19 @@ 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"); } } //namespace sentry::android diff --git a/src/sentry/android/android_string_names.h b/src/sentry/android/android_string_names.h index 03ec76b8..549c3508 100644 --- a/src/sentry/android/android_string_names.h +++ b/src/sentry/android/android_string_names.h @@ -72,6 +72,19 @@ 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; }; } //namespace sentry::android From b188fa7b4963d72feac968af1e9ce1476fff2885 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 29 Aug 2025 17:07:02 +0200 Subject: [PATCH 21/51] Update test_breadcrumb.gd --- project/test/suites/test_breadcrumb.gd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index 915970f9..c419525c 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -34,6 +34,19 @@ func test_breadcrumb_type() -> void: assert_str(crumb.type).is_equal("test-type") +func test_breadcrumb_can_set_data() -> void: + var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + crumb.set_data({"biome": "forest", "time_of_day": 0.42}) + + +func test_breadcrumb_default_values() -> void: + var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + assert_str(crumb.message).is_empty() + assert_str(crumb.type).is_empty() + assert_str(crumb.category).is_equal("default") + assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + + func test_breadcrumb_info_shortcut() -> void: var crumb := SentryBreadcrumb.info("Info message") assert_str(crumb.message).is_equal("Info message") From 4402963eccf9666122a903853c0f9928347318a2 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 08:56:31 +0200 Subject: [PATCH 22/51] Fix default breadcrumb level --- src/sentry/native/native_breadcrumb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index 197a93e0..fbe5d45b 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -33,7 +33,7 @@ void NativeBreadcrumb::set_level(sentry::Level 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_ERROR; + return sentry::Level::LEVEL_INFO; } return sentry::native::cstring_to_level(sentry_value_as_string(value)); } From b327f693ca427ecf10c619374dcbb13ea34be40b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 08:57:22 +0200 Subject: [PATCH 23/51] Comment out "category" test --- project/test/suites/test_breadcrumb.gd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index c419525c..63185385 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -43,8 +43,8 @@ func test_breadcrumb_default_values() -> void: var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() assert_str(crumb.message).is_empty() assert_str(crumb.type).is_empty() - assert_str(crumb.category).is_equal("default") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) + #assert_str(crumb.category).is_equal("default") func test_breadcrumb_info_shortcut() -> void: @@ -52,7 +52,7 @@ func test_breadcrumb_info_shortcut() -> void: assert_str(crumb.message).is_equal("Info message") assert_str(crumb.type).is_equal("info") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - assert_str(crumb.category).is_equal("default") + #assert_str(crumb.category).is_equal("default") func test_breadcrumb_debug_shortcut() -> void: @@ -60,7 +60,7 @@ func test_breadcrumb_debug_shortcut() -> void: assert_str(crumb.message).is_equal("Debug message") assert_str(crumb.type).is_equal("debug") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_DEBUG) - assert_str(crumb.category).is_equal("default") + #assert_str(crumb.category).is_equal("default") func test_breadcrumb_error_shortcut() -> void: @@ -68,7 +68,7 @@ func test_breadcrumb_error_shortcut() -> void: assert_str(crumb.message).is_equal("Error happened") assert_str(crumb.type).is_equal("error") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_ERROR) - assert_str(crumb.category).is_equal("default") + #assert_str(crumb.category).is_equal("default") func test_breadcrumb_query_shortcut() -> void: @@ -76,7 +76,7 @@ func test_breadcrumb_query_shortcut() -> void: assert_str(crumb.message).is_equal("Query performed") assert_str(crumb.type).is_equal("query") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - assert_str(crumb.category).is_equal("default") + #assert_str(crumb.category).is_equal("default") func test_breadcrumb_user_shortcut() -> void: From 6c63ee365b050e057a7154082e24fdc7723d956e Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 10:12:47 +0200 Subject: [PATCH 24/51] Fix potential memory leak in NativeBreadcrumb constructor --- src/sentry/native/native_breadcrumb.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index fbe5d45b..1a3d2b82 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -61,7 +61,6 @@ NativeBreadcrumb::NativeBreadcrumb(sentry_value_t p_native_crumb) { native_crumb = sentry_value_new_object(); ERR_PRINT("Sentry: Internal error: Breadcrumb refcount is zero."); } - native_crumb = p_native_crumb; } NativeBreadcrumb::NativeBreadcrumb() { From 27b007b1bdbe23e5285033eccf34c2b7f6edde3b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 10:49:56 +0200 Subject: [PATCH 25/51] Fix native issues --- src/sentry/native/native_breadcrumb.cpp | 2 +- src/sentry/native/native_sdk.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index 1a3d2b82..0756e6ea 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -59,7 +59,7 @@ NativeBreadcrumb::NativeBreadcrumb(sentry_value_t p_native_crumb) { } else { // Shouldn't happen in healthy code. native_crumb = sentry_value_new_object(); - ERR_PRINT("Sentry: Internal error: Breadcrumb refcount is zero."); + ERR_PRINT_ONCE("Sentry: Internal error: Breadcrumb refcount is zero."); } } diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp index 1d952bd6..e720fda0 100644 --- a/src/sentry/native/native_sdk.cpp +++ b/src/sentry/native/native_sdk.cpp @@ -176,9 +176,10 @@ Ref NativeSDK::create_breadcrumb() { 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."); - Ref crumb = p_breadcrumb; - ERR_FAIL_COND(crumb.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); // pass ownership to native sentry_add_breadcrumb(native_crumb); } From c3b7fe0ba54093fe843c5a3f20624a04cdd807b1 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 10:56:48 +0200 Subject: [PATCH 26/51] Clarify comment --- src/sentry/native/native_sdk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp index e720fda0..1087a5d8 100644 --- a/src/sentry/native/native_sdk.cpp +++ b/src/sentry/native/native_sdk.cpp @@ -179,7 +179,7 @@ void NativeSDK::add_breadcrumb(const Ref &p_breadcrumb) { 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); // pass ownership to native + sentry_value_incref(native_crumb); // give ownership to native sentry_add_breadcrumb(native_crumb); } From eb8247a9cc7dae4679013bd038f01df8e691b09c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 11:30:58 +0200 Subject: [PATCH 27/51] Update tests --- project/test/suites/test_breadcrumb.gd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index 63185385..5eed0f1d 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -44,7 +44,7 @@ func test_breadcrumb_default_values() -> void: assert_str(crumb.message).is_empty() assert_str(crumb.type).is_empty() assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - #assert_str(crumb.category).is_equal("default") + assert_bool(crumb.category in ["", "default"]).is_true() func test_breadcrumb_info_shortcut() -> void: @@ -52,7 +52,7 @@ func test_breadcrumb_info_shortcut() -> void: assert_str(crumb.message).is_equal("Info message") assert_str(crumb.type).is_equal("info") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - #assert_str(crumb.category).is_equal("default") + assert_bool(crumb.category in ["", "default"]).is_true() func test_breadcrumb_debug_shortcut() -> void: @@ -60,7 +60,7 @@ func test_breadcrumb_debug_shortcut() -> void: assert_str(crumb.message).is_equal("Debug message") assert_str(crumb.type).is_equal("debug") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_DEBUG) - #assert_str(crumb.category).is_equal("default") + assert_bool(crumb.category in ["", "default"]).is_true() func test_breadcrumb_error_shortcut() -> void: @@ -68,7 +68,7 @@ func test_breadcrumb_error_shortcut() -> void: assert_str(crumb.message).is_equal("Error happened") assert_str(crumb.type).is_equal("error") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_ERROR) - #assert_str(crumb.category).is_equal("default") + assert_bool(crumb.category in ["", "default"]).is_true() func test_breadcrumb_query_shortcut() -> void: @@ -76,7 +76,7 @@ func test_breadcrumb_query_shortcut() -> void: assert_str(crumb.message).is_equal("Query performed") assert_str(crumb.type).is_equal("query") assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - #assert_str(crumb.category).is_equal("default") + assert_bool(crumb.category in ["", "default"]).is_true() func test_breadcrumb_user_shortcut() -> void: From ec0fe7da0f4a44e319a93ca5d529031e565f2800 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:06:44 +0200 Subject: [PATCH 28/51] Add class docs --- doc_classes/SentryBreadcrumb.xml | 65 ++++++++++++++++++++++++++++++++ doc_classes/SentrySDK.xml | 15 +++++--- 2 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 doc_classes/SentryBreadcrumb.xml diff --git a/doc_classes/SentryBreadcrumb.xml b/doc_classes/SentryBreadcrumb.xml new file mode 100644 index 00000000..f9000f5a --- /dev/null +++ b/doc_classes/SentryBreadcrumb.xml @@ -0,0 +1,65 @@ + + + + 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. + For more information, check out the [url=https://docs.sentry.io/platforms/unreal/enriching-events/breadcrumbs/]Breadcrumbs documentation[/url]. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..fa9fcefc 100644 --- a/doc_classes/SentrySDK.xml +++ b/doc_classes/SentrySDK.xml @@ -23,13 +23,10 @@ - - - - - + - Adds a breadcrumb to the next event. Sentry uses breadcrumbs to create a trail of events that happened prior to an issue. + Adds a breadcrumb to the next event. You can create a [SentryBreadcrumb] object with [method SentryBreadcrumb.create]. + 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]. @@ -48,6 +45,12 @@ Captures an event with [param message] and sends it to Sentry, returning the event ID. + + + + Creates a new [SentryBreadcrumb] object. You can add the breadcrumb with [method SentrySDK.add_breadcrumb]. + + From d49ee6455370beb9b5e05112fa9e01a4279a7658 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:10:46 +0200 Subject: [PATCH 29/51] Remove helpers, and leave a single factory method that takes a message --- src/sentry/sentry_breadcrumb.cpp | 58 ++------------------------------ src/sentry/sentry_breadcrumb.h | 6 +--- 2 files changed, 4 insertions(+), 60 deletions(-) diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index 4e230551..c6c607ae 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -3,69 +3,17 @@ #include "sentry/util/simple_bind.h" #include "sentry_sdk.h" // Needed for VariantCaster -namespace { - -using namespace sentry; +namespace sentry { -inline Ref _create_breadcrumb() { +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(); - ERR_FAIL_COND_V(instance.is_null(), nullptr); - return instance; -} - -} // unnamed namespace - -namespace sentry { - -Ref SentryBreadcrumb::debug(const String &p_message) { - Ref instance = _create_breadcrumb(); - instance->set_type("debug"); - instance->set_message(p_message); - instance->set_level(sentry::Level::LEVEL_DEBUG); - return instance; -} - -Ref SentryBreadcrumb::info(const String &p_message) { - Ref instance = _create_breadcrumb(); - instance->set_type("info"); - instance->set_message(p_message); - instance->set_level(sentry::Level::LEVEL_INFO); - return instance; -} - -Ref SentryBreadcrumb::error(const String &p_message) { - Ref instance = _create_breadcrumb(); - instance->set_type("error"); - instance->set_message(p_message); - instance->set_level(sentry::Level::LEVEL_ERROR); - return instance; -} - -Ref SentryBreadcrumb::query(const String &p_message) { - Ref instance = _create_breadcrumb(); - instance->set_type("query"); - instance->set_message(p_message); - instance->set_level(sentry::Level::LEVEL_INFO); - return instance; -} - -Ref SentryBreadcrumb::user(const String &p_category, const String &p_message) { - Ref instance = _create_breadcrumb(); - instance->set_type("user"); - instance->set_category(p_category); - instance->set_message(p_message); - instance->set_level(sentry::Level::LEVEL_INFO); return instance; } void SentryBreadcrumb::_bind_methods() { - ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("debug", "message"), &SentryBreadcrumb::debug); - ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("info", "message"), &SentryBreadcrumb::info); - ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("error", "message"), &SentryBreadcrumb::error); - ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("query", "message"), &SentryBreadcrumb::query); - ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("user", "category", "message"), &SentryBreadcrumb::user); + ClassDB::bind_static_method("SentryBreadcrumb", D_METHOD("create", "message"), &SentryBreadcrumb::create); BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, message); BIND_PROPERTY_SIMPLE(SentryBreadcrumb, Variant::STRING, category); diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h index dfd0046a..f52f0d1a 100644 --- a/src/sentry/sentry_breadcrumb.h +++ b/src/sentry/sentry_breadcrumb.h @@ -17,11 +17,7 @@ class SentryBreadcrumb : public RefCounted { static void _bind_methods(); public: - static Ref debug(const String &p_message); - static Ref info(const String &p_message); - static Ref error(const String &p_message); - static Ref query(const String &p_message); - static Ref user(const String &p_category, const String &p_message); + static Ref create(const String &p_message); virtual void set_message(const String &p_message) = 0; virtual String get_message() const = 0; From 3355ade8e23704fdb58e873665e8b887ad20c529 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:12:24 +0200 Subject: [PATCH 30/51] Remove SentrySDK.create_breadcrumb --- src/sentry/sentry_sdk.cpp | 1 - src/sentry/sentry_sdk.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/sentry/sentry_sdk.cpp b/src/sentry/sentry_sdk.cpp index 52824764..1759c130 100644 --- a/src/sentry/sentry_sdk.cpp +++ b/src/sentry/sentry_sdk.cpp @@ -380,7 +380,6 @@ void SentrySDK::_bind_methods() { BIND_ENUM_CONSTANT(LEVEL_FATAL); ClassDB::bind_method(D_METHOD("is_enabled"), &SentrySDK::is_enabled); - ClassDB::bind_method(D_METHOD("create_breadcrumb"), &SentrySDK::create_breadcrumb); 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("get_last_event_id"), &SentrySDK::get_last_event_id); diff --git a/src/sentry/sentry_sdk.h b/src/sentry/sentry_sdk.h index c05cf1f3..90917172 100644 --- a/src/sentry/sentry_sdk.h +++ b/src/sentry/sentry_sdk.h @@ -64,7 +64,6 @@ class SentrySDK : public Object { bool is_enabled() const { return enabled; } - Ref create_breadcrumb() { return internal_sdk->create_breadcrumb(); } void add_breadcrumb(const Ref &p_breadcrumb); void set_context(const String &p_key, const Dictionary &p_value); From a53b1432f42446b134d6d0e7e408c7cc5307d90d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:16:09 +0200 Subject: [PATCH 31/51] Update test_breadcrumb.gd --- project/test/suites/test_breadcrumb.gd | 57 +++++--------------------- 1 file changed, 11 insertions(+), 46 deletions(-) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index 5eed0f1d..51e7eeec 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -2,20 +2,25 @@ extends GdUnitTestSuite ## Test SentryBreadcrumb class properties and methods. +func test_breadcrumb_create_with_message() -> void: + var crumb := SentryBreadcrumb.create("test-message") + assert_str(crumb.message).is_equal("test-message") + + func test_breadcrumb_message() -> void: - var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + var crumb := SentryBreadcrumb.create() crumb.message = "test-message" assert_str(crumb.message).is_equal("test-message") func test_breadcrumb_category() -> void: - var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + var crumb := SentryBreadcrumb.create() crumb.category = "test-category" assert_str(crumb.category).is_equal("test-category") func test_breadcrumb_level() -> void: - var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + var crumb := SentryBreadcrumb.create() crumb.level = SentrySDK.LEVEL_DEBUG assert_int(crumb.level).is_equal(SentrySDK.LEVEL_DEBUG) crumb.level = SentrySDK.LEVEL_INFO @@ -29,59 +34,19 @@ func test_breadcrumb_level() -> void: func test_breadcrumb_type() -> void: - var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + var crumb := SentryBreadcrumb.create() crumb.type = "test-type" assert_str(crumb.type).is_equal("test-type") func test_breadcrumb_can_set_data() -> void: - var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + var crumb := SentryBreadcrumb.create() crumb.set_data({"biome": "forest", "time_of_day": 0.42}) func test_breadcrumb_default_values() -> void: - var crumb: SentryBreadcrumb = SentrySDK.create_breadcrumb() + 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_info_shortcut() -> void: - var crumb := SentryBreadcrumb.info("Info message") - assert_str(crumb.message).is_equal("Info message") - assert_str(crumb.type).is_equal("info") - assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - assert_bool(crumb.category in ["", "default"]).is_true() - - -func test_breadcrumb_debug_shortcut() -> void: - var crumb := SentryBreadcrumb.debug("Debug message") - assert_str(crumb.message).is_equal("Debug message") - assert_str(crumb.type).is_equal("debug") - assert_int(crumb.level).is_equal(SentrySDK.LEVEL_DEBUG) - assert_bool(crumb.category in ["", "default"]).is_true() - - -func test_breadcrumb_error_shortcut() -> void: - var crumb := SentryBreadcrumb.error("Error happened") - assert_str(crumb.message).is_equal("Error happened") - assert_str(crumb.type).is_equal("error") - assert_int(crumb.level).is_equal(SentrySDK.LEVEL_ERROR) - assert_bool(crumb.category in ["", "default"]).is_true() - - -func test_breadcrumb_query_shortcut() -> void: - var crumb := SentryBreadcrumb.query("Query performed") - assert_str(crumb.message).is_equal("Query performed") - assert_str(crumb.type).is_equal("query") - assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - assert_bool(crumb.category in ["", "default"]).is_true() - - -func test_breadcrumb_user_shortcut() -> void: - var crumb := SentryBreadcrumb.user("ui.click", "User clicked button") - assert_str(crumb.message).is_equal("User clicked button") - assert_str(crumb.type).is_equal("user") - assert_int(crumb.level).is_equal(SentrySDK.LEVEL_INFO) - assert_str(crumb.category).is_equal("ui.click") From 1b33aa3deeb3f96270721d42e8cc1fdc080a9146 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:20:20 +0200 Subject: [PATCH 32/51] Default to empty message in factory method, and actually set it --- src/sentry/sentry_breadcrumb.cpp | 5 ++++- src/sentry/sentry_breadcrumb.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index c6c607ae..fdd19729 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -9,11 +9,14 @@ 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); + 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); diff --git a/src/sentry/sentry_breadcrumb.h b/src/sentry/sentry_breadcrumb.h index f52f0d1a..7177487e 100644 --- a/src/sentry/sentry_breadcrumb.h +++ b/src/sentry/sentry_breadcrumb.h @@ -17,7 +17,7 @@ class SentryBreadcrumb : public RefCounted { static void _bind_methods(); public: - static Ref create(const String &p_message); + static Ref create(const String &p_message = ""); virtual void set_message(const String &p_message) = 0; virtual String get_message() const = 0; From 7d933d07103f224a2bc1d6fe8fac16a0b6e59c28 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:22:09 +0200 Subject: [PATCH 33/51] Use new API in logger --- src/sentry/sentry_logger.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sentry/sentry_logger.cpp b/src/sentry/sentry_logger.cpp index 7dc9c626..c3589877 100644 --- a/src/sentry/sentry_logger.cpp +++ b/src/sentry/sentry_logger.cpp @@ -328,7 +328,7 @@ 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)]); - Ref crumb = SentryBreadcrumb::error(error_message); + Ref crumb = SentryBreadcrumb::create(error_message); crumb->set_level(sentry::get_sentry_level_for_godot_error_type((GodotErrorType)p_error_type)); crumb->set_data(data); SentrySDK::get_singleton()->add_breadcrumb(crumb); @@ -355,8 +355,7 @@ void SentryLogger::_log_message(const String &p_message, bool p_error) { } } - Ref crumb = SentrySDK::get_singleton()->create_breadcrumb(); - crumb->set_message(p_message); + 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"); From 82a5f9c8730639b21e0e212b6d86a371f161c44f Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:39:09 +0200 Subject: [PATCH 34/51] Update docs --- doc_classes/SentryBreadcrumb.xml | 38 +++++++++----------------------- doc_classes/SentrySDK.xml | 11 ++------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/doc_classes/SentryBreadcrumb.xml b/doc_classes/SentryBreadcrumb.xml index f9000f5a..8125c6d2 100644 --- a/doc_classes/SentryBreadcrumb.xml +++ b/doc_classes/SentryBreadcrumb.xml @@ -5,46 +5,30 @@ 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/unreal/enriching-events/breadcrumbs/]Breadcrumbs documentation[/url]. - + - - - - - - - - - - - - - - - - - - - + + Creates a new [SentryBreadcrumb] object. You can add the breadcrumb with [method SentrySDK.add_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. diff --git a/doc_classes/SentrySDK.xml b/doc_classes/SentrySDK.xml index fa9fcefc..b3dec615 100644 --- a/doc_classes/SentrySDK.xml +++ b/doc_classes/SentrySDK.xml @@ -25,9 +25,8 @@ - Adds a breadcrumb to the next event. You can create a [SentryBreadcrumb] object with [method SentryBreadcrumb.create]. - 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. @@ -45,12 +44,6 @@ Captures an event with [param message] and sends it to Sentry, returning the event ID. - - - - Creates a new [SentryBreadcrumb] object. You can add the breadcrumb with [method SentrySDK.add_breadcrumb]. - - From 579afe06d8503aa09cde2b6c6c1317b70ca840b4 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:39:21 +0200 Subject: [PATCH 35/51] Update demo --- project/views/enrich_events.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/views/enrich_events.gd b/project/views/enrich_events.gd index 06401be5..3545a6ad 100644 --- a/project/views/enrich_events.gd +++ b/project/views/enrich_events.gd @@ -13,7 +13,7 @@ func _ready() -> void: func _on_add_breadcrumb_button_pressed() -> void: - var crumb := SentryBreadcrumb.info(breadcrumb_message.text) + var crumb := SentryBreadcrumb.create(breadcrumb_message.text) crumb.category = breadcrumb_category.text SentrySDK.add_breadcrumb(crumb) DemoOutput.print_info("Breadcrumb added.") From b706b0738cacf819873d4bb5062c80dd9536d5c2 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 12:42:32 +0200 Subject: [PATCH 36/51] Fix wrong link --- doc_classes/SentryBreadcrumb.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_classes/SentryBreadcrumb.xml b/doc_classes/SentryBreadcrumb.xml index 8125c6d2..60aa5c46 100644 --- a/doc_classes/SentryBreadcrumb.xml +++ b/doc_classes/SentryBreadcrumb.xml @@ -12,7 +12,7 @@ crumb.category = "level.finished" SentrySDK.add_breadcrumb(crumb) [/codeblock] - For more information, check out the [url=https://docs.sentry.io/platforms/unreal/enriching-events/breadcrumbs/]Breadcrumbs documentation[/url]. + For more information, check out the [url=https://docs.sentry.io/platforms/godot/enriching-events/breadcrumbs/]Breadcrumbs documentation[/url]. From 8284b3d83cf13eb0ab74ea382cda7a200cc24254 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 13:02:21 +0200 Subject: [PATCH 37/51] Add get_timestamp() with native implementation --- src/sentry/android/android_breadcrumb.cpp | 4 ++++ src/sentry/android/android_breadcrumb.h | 2 ++ src/sentry/cocoa/cocoa_breadcrumb.h | 2 ++ src/sentry/cocoa/cocoa_breadcrumb.mm | 4 ++++ src/sentry/disabled/disabled_breadcrumb.h | 2 ++ src/sentry/native/native_breadcrumb.cpp | 5 +++++ src/sentry/native/native_breadcrumb.h | 2 ++ src/sentry/sentry_breadcrumb.cpp | 1 + src/sentry/sentry_breadcrumb.h | 3 +++ 9 files changed, 25 insertions(+) diff --git a/src/sentry/android/android_breadcrumb.cpp b/src/sentry/android/android_breadcrumb.cpp index 20106fd2..2b2df9da 100644 --- a/src/sentry/android/android_breadcrumb.cpp +++ b/src/sentry/android/android_breadcrumb.cpp @@ -51,6 +51,10 @@ void AndroidBreadcrumb::set_data(const Dictionary &p_data) { android_plugin->call(ANDROID_SN(breadcrumbSetData), handle, p_data); } +Ref AndroidBreadcrumb::get_timestamp() { + // not implemented +} + AndroidBreadcrumb::AndroidBreadcrumb(Object *android_plugin, int32_t p_breadcrumb_handle) : android_plugin(android_plugin), handle(p_breadcrumb_handle) { ERR_FAIL_NULL(android_plugin); diff --git a/src/sentry/android/android_breadcrumb.h b/src/sentry/android/android_breadcrumb.h index ad5e7bb8..30db0e1b 100644 --- a/src/sentry/android/android_breadcrumb.h +++ b/src/sentry/android/android_breadcrumb.h @@ -32,6 +32,8 @@ class AndroidBreadcrumb : public SentryBreadcrumb { virtual void set_data(const Dictionary &p_data) override; + virtual Ref get_timestamp() override; + AndroidBreadcrumb() = default; AndroidBreadcrumb(Object *android_plugin, int32_t p_breadcrumb_handle); virtual ~AndroidBreadcrumb() override; diff --git a/src/sentry/cocoa/cocoa_breadcrumb.h b/src/sentry/cocoa/cocoa_breadcrumb.h index 7b0183e9..cdd74346 100644 --- a/src/sentry/cocoa/cocoa_breadcrumb.h +++ b/src/sentry/cocoa/cocoa_breadcrumb.h @@ -32,6 +32,8 @@ class CocoaBreadcrumb : public SentryBreadcrumb { virtual void set_data(const Dictionary &p_data) override; + virtual Ref get_timestamp() override; + CocoaBreadcrumb(); CocoaBreadcrumb(objc::SentryBreadcrumb *p_cocoa_breadcrumb); }; diff --git a/src/sentry/cocoa/cocoa_breadcrumb.mm b/src/sentry/cocoa/cocoa_breadcrumb.mm index 8c5b6797..958d7c37 100644 --- a/src/sentry/cocoa/cocoa_breadcrumb.mm +++ b/src/sentry/cocoa/cocoa_breadcrumb.mm @@ -41,6 +41,10 @@ cocoa_breadcrumb.data = dictionary_to_objc(p_data); } +Ref CocoaBreadcrumb::get_timestamp() { + // not implemented +} + CocoaBreadcrumb::CocoaBreadcrumb() : cocoa_breadcrumb([[objc::SentryBreadcrumb alloc] init]) { } diff --git a/src/sentry/disabled/disabled_breadcrumb.h b/src/sentry/disabled/disabled_breadcrumb.h index 86a79f74..d84e8cf1 100644 --- a/src/sentry/disabled/disabled_breadcrumb.h +++ b/src/sentry/disabled/disabled_breadcrumb.h @@ -33,6 +33,8 @@ class DisabledBreadcrumb : public SentryBreadcrumb { 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 diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index 0756e6ea..f6c72556 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -52,6 +52,11 @@ void NativeBreadcrumb::set_data(const Dictionary &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 diff --git a/src/sentry/native/native_breadcrumb.h b/src/sentry/native/native_breadcrumb.h index 45c8561f..db094d66 100644 --- a/src/sentry/native/native_breadcrumb.h +++ b/src/sentry/native/native_breadcrumb.h @@ -33,6 +33,8 @@ class NativeBreadcrumb : public SentryBreadcrumb { 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; diff --git a/src/sentry/sentry_breadcrumb.cpp b/src/sentry/sentry_breadcrumb.cpp index fdd19729..10afffc9 100644 --- a/src/sentry/sentry_breadcrumb.cpp +++ b/src/sentry/sentry_breadcrumb.cpp @@ -24,6 +24,7 @@ void SentryBreadcrumb::_bind_methods() { 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 index 7177487e..beda28a5 100644 --- a/src/sentry/sentry_breadcrumb.h +++ b/src/sentry/sentry_breadcrumb.h @@ -2,6 +2,7 @@ #define SENTRY_BREADCRUMB_H #include "sentry/level.h" +#include "sentry/sentry_timestamp.h" #include @@ -33,6 +34,8 @@ class SentryBreadcrumb : public RefCounted { virtual void set_data(const Dictionary &p_data) = 0; + virtual Ref get_timestamp() = 0; + virtual ~SentryBreadcrumb() = default; }; From 2a8b56b29936c3d7260147ef7b4d9103e624ed14 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 13:08:36 +0200 Subject: [PATCH 38/51] Add cocoa get_timestamp() --- src/sentry/cocoa/cocoa_breadcrumb.mm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sentry/cocoa/cocoa_breadcrumb.mm b/src/sentry/cocoa/cocoa_breadcrumb.mm index 958d7c37..882cc684 100644 --- a/src/sentry/cocoa/cocoa_breadcrumb.mm +++ b/src/sentry/cocoa/cocoa_breadcrumb.mm @@ -42,7 +42,12 @@ } Ref CocoaBreadcrumb::get_timestamp() { - // not implemented + if (cocoa_breadcrumb.timestamp == nil) { + return Ref(); + } + + NSTimeInterval seconds = [cocoa_breadcrumb.timestamp timeIntervalSince1970]; + return SentryTimestamp::from_unix_time(seconds); } CocoaBreadcrumb::CocoaBreadcrumb() : @@ -51,6 +56,10 @@ 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 From f2c7695f5aec5ca7d19c2fbc54dddfc92360e36c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 13:13:14 +0200 Subject: [PATCH 39/51] Add android breadcrumb implementation --- .../java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt | 6 ++++++ src/sentry/android/android_breadcrumb.cpp | 4 +++- src/sentry/android/android_string_names.cpp | 1 + src/sentry/android/android_string_names.h | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) 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 4af1b54f..f7fc5ec6 100644 --- a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt +++ b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt @@ -525,4 +525,10 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { } } + @UsedByGodot + fun breadcrumbGetTimestamp(handle: Int): Long { + val crumb = getBreadcrumb(handle) ?: return 0 + return crumb.timestamp.toMicros() + } + } diff --git a/src/sentry/android/android_breadcrumb.cpp b/src/sentry/android/android_breadcrumb.cpp index 2b2df9da..b4a95d2b 100644 --- a/src/sentry/android/android_breadcrumb.cpp +++ b/src/sentry/android/android_breadcrumb.cpp @@ -52,7 +52,9 @@ void AndroidBreadcrumb::set_data(const Dictionary &p_data) { } Ref AndroidBreadcrumb::get_timestamp() { - // not implemented + 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 *android_plugin, int32_t p_breadcrumb_handle) : diff --git a/src/sentry/android/android_string_names.cpp b/src/sentry/android/android_string_names.cpp index 9a019333..15655f86 100644 --- a/src/sentry/android/android_string_names.cpp +++ b/src/sentry/android/android_string_names.cpp @@ -73,6 +73,7 @@ AndroidStringNames::AndroidStringNames() { 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 549c3508..a9c1344b 100644 --- a/src/sentry/android/android_string_names.h +++ b/src/sentry/android/android_string_names.h @@ -85,6 +85,7 @@ class AndroidStringNames { StringName breadcrumbSetLevel; StringName breadcrumbGetLevel; StringName breadcrumbSetData; + StringName breadcrumbGetTimestamp; }; } //namespace sentry::android From edafb3a085f9bea49e96baaacf63fa4988fc9eb6 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 13:48:00 +0200 Subject: [PATCH 40/51] Update SentryBreadcrumb.xml --- doc_classes/SentryBreadcrumb.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc_classes/SentryBreadcrumb.xml b/doc_classes/SentryBreadcrumb.xml index 60aa5c46..ad048c98 100644 --- a/doc_classes/SentryBreadcrumb.xml +++ b/doc_classes/SentryBreadcrumb.xml @@ -21,7 +21,12 @@ - Creates a new [SentryBreadcrumb] object. You can add the breadcrumb with [method SentrySDK.add_breadcrumb]. + + + + + + Returns the timestamp of the breadcrumb. From 0f7fa1730735bd34cf81b830e585ca7615103093 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 13:55:05 +0200 Subject: [PATCH 41/51] Set error type and category on error breadcrumbs --- src/sentry/sentry_logger.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sentry/sentry_logger.cpp b/src/sentry/sentry_logger.cpp index c3589877..22a991b9 100644 --- a/src/sentry/sentry_logger.cpp +++ b/src/sentry/sentry_logger.cpp @@ -330,6 +330,8 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in 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); } From 888c713bfab58f8f233b605f886cc1c678782c25 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 14:10:36 +0200 Subject: [PATCH 42/51] Test for get_timestamp() --- project/test/suites/test_breadcrumb.gd | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index 51e7eeec..47a0d442 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -50,3 +50,17 @@ func test_breadcrumb_default_values() -> void: 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 micros: int = crumb.get_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) From a632acccb9cffb5c7e481c20428d6bbf3bee30d6 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 14:19:57 +0200 Subject: [PATCH 43/51] Fix missing message on Android --- src/sentry/android/android_breadcrumb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/android/android_breadcrumb.cpp b/src/sentry/android/android_breadcrumb.cpp index b4a95d2b..537686a9 100644 --- a/src/sentry/android/android_breadcrumb.cpp +++ b/src/sentry/android/android_breadcrumb.cpp @@ -6,7 +6,7 @@ namespace sentry::android { void AndroidBreadcrumb::set_message(const String &p_message) { ERR_FAIL_NULL(android_plugin); - android_plugin->call(ANDROID_SN(breadcrumbSetMessage), handle); + android_plugin->call(ANDROID_SN(breadcrumbSetMessage), handle, p_message); } String AndroidBreadcrumb::get_message() const { From f0492eca35fbe5f79e5ca8f39418059f6fda8d8e Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 14:52:12 +0200 Subject: [PATCH 44/51] Fix native breadcrumb creation --- src/sentry/native/native_breadcrumb.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index f6c72556..1cebe3cb 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -2,6 +2,7 @@ #include "godot_cpp/core/error_macros.hpp" #include "sentry/native/native_util.h" +#include "sentry/util/print.h" #include @@ -54,6 +55,7 @@ void NativeBreadcrumb::set_data(const Dictionary &p_data) { Ref NativeBreadcrumb::get_timestamp() { sentry_value_t value = sentry_value_get_by_key(native_crumb, "timestamp"); + sentry::util::print_debug("breadcrumb timestamp: ", String(sentry_value_as_string(value))); return SentryTimestamp::parse_rfc3339_cstr(sentry_value_as_string(value)); } @@ -63,13 +65,13 @@ NativeBreadcrumb::NativeBreadcrumb(sentry_value_t p_native_crumb) { native_crumb = p_native_crumb; } else { // Shouldn't happen in healthy code. - native_crumb = sentry_value_new_object(); + 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_object(); + native_crumb = sentry_value_new_breadcrumb(NULL, NULL); } NativeBreadcrumb::~NativeBreadcrumb() { From 205fc57f831ede3daacbf1ccdb61fccaab3cd08e Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 15:06:33 +0200 Subject: [PATCH 45/51] Update CHANGELOG.md --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 From 2b4113c1d409a5c03fce66fd02f963801d3bd595 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 15:11:12 +0200 Subject: [PATCH 46/51] Fix arg name --- src/sentry/android/android_breadcrumb.cpp | 6 +++--- src/sentry/android/android_breadcrumb.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sentry/android/android_breadcrumb.cpp b/src/sentry/android/android_breadcrumb.cpp index 537686a9..8558ebd9 100644 --- a/src/sentry/android/android_breadcrumb.cpp +++ b/src/sentry/android/android_breadcrumb.cpp @@ -57,9 +57,9 @@ Ref AndroidBreadcrumb::get_timestamp() { return SentryTimestamp::from_microseconds_since_unix_epoch(micros); } -AndroidBreadcrumb::AndroidBreadcrumb(Object *android_plugin, int32_t p_breadcrumb_handle) : - android_plugin(android_plugin), handle(p_breadcrumb_handle) { - ERR_FAIL_NULL(android_plugin); +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() { diff --git a/src/sentry/android/android_breadcrumb.h b/src/sentry/android/android_breadcrumb.h index 30db0e1b..46b38216 100644 --- a/src/sentry/android/android_breadcrumb.h +++ b/src/sentry/android/android_breadcrumb.h @@ -35,7 +35,7 @@ class AndroidBreadcrumb : public SentryBreadcrumb { virtual Ref get_timestamp() override; AndroidBreadcrumb() = default; - AndroidBreadcrumb(Object *android_plugin, int32_t p_breadcrumb_handle); + AndroidBreadcrumb(Object *p_android_plugin, int32_t p_breadcrumb_handle); virtual ~AndroidBreadcrumb() override; }; From c7171b3954785d7fa9ee177acb6a7884b4b9e520 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Sep 2025 15:56:37 +0200 Subject: [PATCH 47/51] Update test --- project/test/suites/test_breadcrumb.gd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index 47a0d442..edf001b9 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -55,12 +55,16 @@ func test_breadcrumb_default_values() -> void: 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 micros: int = crumb.get_timestamp().microseconds_since_unix_epoch + 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) From a7936a4d4a9485a3b8a4b2c32fc824b1343d43d0 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 2 Sep 2025 11:40:00 +0200 Subject: [PATCH 48/51] Fix debug print slipped in --- src/sentry/native/native_breadcrumb.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index 1cebe3cb..aa6c9a79 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -2,7 +2,6 @@ #include "godot_cpp/core/error_macros.hpp" #include "sentry/native/native_util.h" -#include "sentry/util/print.h" #include @@ -55,7 +54,6 @@ void NativeBreadcrumb::set_data(const Dictionary &p_data) { Ref NativeBreadcrumb::get_timestamp() { sentry_value_t value = sentry_value_get_by_key(native_crumb, "timestamp"); - sentry::util::print_debug("breadcrumb timestamp: ", String(sentry_value_as_string(value))); return SentryTimestamp::parse_rfc3339_cstr(sentry_value_as_string(value)); } From 54a30fa3ff3007bb1d0078d6e8d23fd13d9c5479 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 2 Sep 2025 11:48:01 +0200 Subject: [PATCH 49/51] Update android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt Co-authored-by: J-P Nurmi --- .../main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f7fc5ec6..eafb3970 100644 --- a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt +++ b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt @@ -110,7 +110,7 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) { } var handle = Random.nextInt() - while(breadcrumbsMap.containsKey(handle)) { + while (breadcrumbsMap.containsKey(handle)) { handle = Random.nextInt() } From ad1c10ad3c83ea995d29673da944baa726ad984b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 2 Sep 2025 14:53:21 +0200 Subject: [PATCH 50/51] breadcrumb-class: Update tests with UTF-8 --- project/test/suites/test_breadcrumb.gd | 32 +++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/project/test/suites/test_breadcrumb.gd b/project/test/suites/test_breadcrumb.gd index edf001b9..c20ad414 100644 --- a/project/test/suites/test_breadcrumb.gd +++ b/project/test/suites/test_breadcrumb.gd @@ -2,21 +2,23 @@ extends GdUnitTestSuite ## Test SentryBreadcrumb class properties and methods. -func test_breadcrumb_create_with_message() -> void: - var crumb := SentryBreadcrumb.create("test-message") - assert_str(crumb.message).is_equal("test-message") - - -func test_breadcrumb_message() -> void: +## 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.message = "test-message" - assert_str(crumb.message).is_equal("test-message") + 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_category() -> void: - var crumb := SentryBreadcrumb.create() - crumb.category = "test-category" - assert_str(crumb.category).is_equal("test-category") +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: @@ -33,12 +35,6 @@ func test_breadcrumb_level() -> void: assert_int(crumb.level).is_equal(SentrySDK.LEVEL_FATAL) -func test_breadcrumb_type() -> void: - var crumb := SentryBreadcrumb.create() - crumb.type = "test-type" - assert_str(crumb.type).is_equal("test-type") - - func test_breadcrumb_can_set_data() -> void: var crumb := SentryBreadcrumb.create() crumb.set_data({"biome": "forest", "time_of_day": 0.42}) From 3b7e04bcf44e23ee19d4cd66f79b58d258ae3dd0 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 2 Sep 2025 15:15:42 +0200 Subject: [PATCH 51/51] Fix UTF-8 issues --- src/sentry/native/native_breadcrumb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sentry/native/native_breadcrumb.cpp b/src/sentry/native/native_breadcrumb.cpp index aa6c9a79..05fe0b0f 100644 --- a/src/sentry/native/native_breadcrumb.cpp +++ b/src/sentry/native/native_breadcrumb.cpp @@ -12,8 +12,8 @@ void NativeBreadcrumb::set_message(const String &p_message) { } String NativeBreadcrumb::get_message() const { - return sentry_value_as_string( - sentry_value_get_by_key(native_crumb, "message")); + return String::utf8(sentry_value_as_string( + sentry_value_get_by_key(native_crumb, "message"))); } void NativeBreadcrumb::set_category(const String &p_category) { @@ -21,8 +21,8 @@ void NativeBreadcrumb::set_category(const String &p_category) { } String NativeBreadcrumb::get_category() const { - return sentry_value_as_string( - sentry_value_get_by_key(native_crumb, "category")); + return String::utf8(sentry_value_as_string( + sentry_value_get_by_key(native_crumb, "category"))); } void NativeBreadcrumb::set_level(sentry::Level p_level) { @@ -43,8 +43,8 @@ void NativeBreadcrumb::set_type(const String &p_type) { } String NativeBreadcrumb::get_type() const { - return sentry_value_as_string( - sentry_value_get_by_key(native_crumb, "type")); + return String::utf8(sentry_value_as_string( + sentry_value_get_by_key(native_crumb, "type"))); } void NativeBreadcrumb::set_data(const Dictionary &p_data) {