diff --git a/CHANGELOG.md b/CHANGELOG.md
index 17af34e6..4959a840 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
- Initial Android support ([#169](https://github.com/getsentry/sentry-godot/pull/169))
- Refine demo for mobile screens ([#196](https://github.com/getsentry/sentry-godot/pull/196))
+- Add user attachments support ([#205](https://github.com/getsentry/sentry-godot/pull/205))
## Fixes
diff --git a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt
index 052206a9..c8b0a83b 100644
--- a/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt
+++ b/android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt
@@ -135,6 +135,18 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
Sentry.getGlobalScope().addAttachment(attachment)
}
+ @UsedByGodot
+ fun addBytesAttachment(bytes: ByteArray, filename: String, contentType: String, attachmentType: String) {
+ val attachment = Attachment(
+ bytes,
+ filename,
+ contentType.ifEmpty { null },
+ attachmentType.ifEmpty { null },
+ false
+ )
+ Sentry.getGlobalScope().addAttachment(attachment)
+ }
+
@UsedByGodot
fun setContext(key: String, value: Dictionary) {
Sentry.getGlobalScope().setContexts(key, value)
diff --git a/doc_classes/SentryAttachment.xml b/doc_classes/SentryAttachment.xml
new file mode 100644
index 00000000..c13f2b26
--- /dev/null
+++ b/doc_classes/SentryAttachment.xml
@@ -0,0 +1,60 @@
+
+
+
+ Represents a file attachment that can be sent with Sentry events.
+
+
+ SentryAttachment represents a file that can be attached to Sentry events to provide additional context. Attachments are files that are uploaded alongside error reports and can include log files, screenshots, configuration files, or any other relevant data.
+ Attachments can be created using [method SentryAttachment.create_with_path] for existing files on disk. Once created, they can be added to future events using [method SentrySDK.add_attachment]:
+ [codeblock]
+ var attachment := SentryAttachment.create_with_path("user://logs/godot.log")
+ attachment.content_type = "text/plain"
+ SentrySDK.add_attachment(attachment)
+ [/codeblock]
+ Attachments can also be created using [method SentryAttachment.create_with_bytes] for data already in memory:
+ [codeblock]
+ var bytes: PackedByteArray = "Hello, world!".to_ascii_buffer()
+ var attachment := SentryAttachment.create_with_bytes(bytes, "hello.txt")
+ attachment.content_type = "text/plain"
+ SentrySDK.add_attachment(attachment)
+ [/codeblock]
+ To learn more about attachments, visit [url=https://docs.sentry.io/platforms/godot/enriching-events/attachments/]Attachments documentation[/url].
+
+
+
+
+
+
+
+
+
+ Creates a new [SentryAttachment] with the specified [param bytes] data and [param filename]. The [param bytes] parameter contains the raw file data to be attached. The [param filename] parameter specifies the display name for the attachment in Sentry.
+ This method is useful when you have file data already loaded in memory or when creating attachments from generated content rather than existing files on disk.
+
+
+
+
+
+
+ Creates a new [SentryAttachment] with the specified file [param path] and optional [param filename] and [param content_type]. The [param path] should point to an existing file and supports Godot's virtual file system paths like "user://".
+ [b]Note:[/b] Modifying attachment properties after the attachment has been added with [method SentrySDK.add_attachment] will have no effect. To apply property changes, you need to re-add the attachment.
+ [b]Important:[/b] Attachments are read lazily at the time an event is sent to Sentry.
+
+
+
+
+
+ Contains the raw byte data of the attachment.
+
+
+ The MIME content type of the attachment file. This helps Sentry understand how to handle and display the attachment.
+ Sentry understands and renders the following MIME types: [code]text/plain[/code], [code]text/css[/code], [code]text/csv[/code], [code]text/html[/code], [code]text/javascript[/code], [code]text/json[/code] or [code]text/x-json[/code] or [code]application/json[/code] or [code]application/ld+json[/code], [code]image/jpeg[/code], [code]image/png[/code], [code]image/gif[/code].
+
+
+ The filename of the attachment. This is the name that will be displayed in Sentry. If not provided, the filename will be extracted from the [member path].
+
+
+ The file path of the attachment. This can be an absolute path or use Godot's virtual file system paths such as "user://".
+
+
+
diff --git a/doc_classes/SentrySDK.xml b/doc_classes/SentrySDK.xml
index de4196c7..01737a14 100644
--- a/doc_classes/SentrySDK.xml
+++ b/doc_classes/SentrySDK.xml
@@ -13,6 +13,14 @@
+
+
+
+
+ Attaches a file to future Sentry events. The [param attachment] should be a [SentryAttachment] object created with [method SentryAttachment.create_with_path] or [method SentryAttachment.create_with_bytes]. Supports Godot's virtual file system paths like "user://".
+ To learn more, visit [url=https://docs.sentry.io/platforms/godot/enriching-events/attachments/]Attachments documentation[/url].
+
+
diff --git a/project/test/suites/test_attachment.gd b/project/test/suites/test_attachment.gd
new file mode 100644
index 00000000..7b5b773b
--- /dev/null
+++ b/project/test/suites/test_attachment.gd
@@ -0,0 +1,32 @@
+extends GdUnitTestSuite
+## Basic tests for the SentryAttachment class.
+
+
+func test_create_with_path() -> void:
+ var attachment := SentryAttachment.create_with_path("user://logs/godot.log")
+ attachment.filename = "logfile.txt"
+ attachment.content_type = "text/plain"
+
+ assert_array(attachment.bytes).is_empty()
+ assert_str(attachment.path).is_equal("user://logs/godot.log")
+ assert_str(attachment.filename).is_equal("logfile.txt")
+ assert_str(attachment.content_type).is_equal("text/plain")
+
+
+func test_create_with_bytes() -> void:
+ var contents := """
+ Hello, world!
+ """
+ var bytes: PackedByteArray = contents.to_utf8_buffer()
+
+ var attachment := SentryAttachment.create_with_bytes(bytes, "hello.txt")
+ attachment.content_type = "text/plain"
+
+ assert_array(attachment.bytes).is_not_empty()
+ assert_str(attachment.path).is_empty()
+ assert_str(attachment.filename).is_equal("hello.txt")
+ assert_str(attachment.content_type).is_equal("text/plain")
+
+ assert_int(attachment.bytes.size()).is_equal(bytes.size())
+ for i in attachment.bytes.size():
+ assert_int(attachment.bytes[i]).is_equal(bytes[i])
diff --git a/project/test/suites/test_attachment.gd.uid b/project/test/suites/test_attachment.gd.uid
new file mode 100644
index 00000000..960d7b25
--- /dev/null
+++ b/project/test/suites/test_attachment.gd.uid
@@ -0,0 +1 @@
+uid://b10m6784fkgyb
diff --git a/project/views/enrich_events.gd b/project/views/enrich_events.gd
index b08bcc2c..ad7b4596 100644
--- a/project/views/enrich_events.gd
+++ b/project/views/enrich_events.gd
@@ -46,3 +46,12 @@ func _on_set_context_pressed() -> void:
DemoOutput.print_err("Failed set context: Dictionary is expected, but found: " + type_string(typeof(result)))
else:
DemoOutput.print_err("Failed to parse expression: " + expr.get_error_text())
+
+
+func _on_attach_button_pressed() -> void:
+ var content: String = %AttachmentContent.text
+ var bytes: PackedByteArray = content.to_utf8_buffer()
+ var attachment := SentryAttachment.create_with_bytes(bytes, "hello.txt")
+ attachment.content_type = "text/plain"
+ SentrySDK.add_attachment(attachment)
+ DemoOutput.print_info("Attachment added.")
diff --git a/project/views/enrich_events.tscn b/project/views/enrich_events.tscn
index 9e6a542e..4ca7c8e3 100644
--- a/project/views/enrich_events.tscn
+++ b/project/views/enrich_events.tscn
@@ -63,6 +63,24 @@ unique_name_in_owner = true
layout_mode = 2
text = "Add Tag"
+[node name="Attachment" type="HBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="Attachment"]
+layout_mode = 2
+text = "Attach:"
+
+[node name="AttachmentContent" type="LineEdit" parent="Attachment"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+text = "Hello, World!"
+placeholder_text = "Content"
+
+[node name="AttachButton" type="Button" parent="Attachment"]
+layout_mode = 2
+text = "Attach hello.txt"
+
[node name="Context" type="VBoxContainer" parent="."]
layout_mode = 2
@@ -97,4 +115,5 @@ text = "Add context"
[connection signal="pressed" from="Breadcrumb/AddBreadcrumbButton" to="." method="_on_add_breadcrumb_button_pressed"]
[connection signal="pressed" from="Tags/AddTagButton" to="." method="_on_add_tag_button_pressed"]
+[connection signal="pressed" from="Attachment/AttachButton" to="." method="_on_attach_button_pressed"]
[connection signal="pressed" from="Context/SetContext" to="." method="_on_set_context_pressed"]
diff --git a/src/register_types.cpp b/src/register_types.cpp
index 95d2bcc0..960881ea 100644
--- a/src/register_types.cpp
+++ b/src/register_types.cpp
@@ -5,6 +5,7 @@
#include "sentry/processing/screenshot_processor.h"
#include "sentry/processing/view_hierarchy_processor.h"
#include "sentry/util/print.h"
+#include "sentry_attachment.h"
#include "sentry_configuration.h"
#include "sentry_event.h"
#include "sentry_event_processor.h"
@@ -68,6 +69,7 @@ void initialize_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(SentryConfiguration);
GDREGISTER_CLASS(SentryUser);
GDREGISTER_CLASS(SentrySDK);
+ GDREGISTER_ABSTRACT_CLASS(SentryAttachment);
GDREGISTER_ABSTRACT_CLASS(SentryEvent);
GDREGISTER_INTERNAL_CLASS(DisabledEvent);
GDREGISTER_INTERNAL_CLASS(SentryEventProcessor);
diff --git a/src/sentry/android/android_sdk.cpp b/src/sentry/android/android_sdk.cpp
index 0c522753..947f0b65 100644
--- a/src/sentry/android/android_sdk.cpp
+++ b/src/sentry/android/android_sdk.cpp
@@ -2,10 +2,13 @@
#include "android_event.h"
#include "android_string_names.h"
+#include "sentry/common_defs.h"
#include "sentry/processing/process_event.h"
#include "sentry/util/print.h"
+#include "sentry_attachment.h"
#include
+#include
#include
using namespace godot;
@@ -101,19 +104,39 @@ String AndroidSDK::capture_event(const Ref &p_event) {
return android_event->get_id();
}
+void AndroidSDK::add_attachment(const Ref &p_attachment) {
+ ERR_FAIL_COND(p_attachment.is_null());
+
+ if (p_attachment->get_path().is_empty()) {
+ sentry::util::print_debug("attaching bytes with filename: ", p_attachment->get_filename());
+ android_plugin->call(ANDROID_SN(addBytesAttachment),
+ p_attachment->get_bytes(),
+ p_attachment->get_filename(),
+ p_attachment->get_content_type(),
+ String());
+ } else {
+ String absolute_path = ProjectSettings::get_singleton()->globalize_path(p_attachment->get_path());
+ sentry::util::print_debug("attaching file: ", absolute_path);
+ android_plugin->call(ANDROID_SN(addFileAttachment),
+ absolute_path,
+ p_attachment->get_filename(),
+ p_attachment->get_content_type(),
+ String());
+ }
+}
+
void AndroidSDK::initialize(const PackedStringArray &p_global_attachments) {
ERR_FAIL_NULL(android_plugin);
sentry::util::print_debug("Initializing Sentry Android SDK");
for (const String &path : p_global_attachments) {
- String file = path.get_file();
- bool is_view_hierarchy = file == "view-hierarchy.json";
+ bool is_view_hierarchy = path.ends_with(SENTRY_VIEW_HIERARCHY_FN);
android_plugin->call(ANDROID_SN(addFileAttachment),
path,
- file,
- is_view_hierarchy ? "application/json" : "",
- is_view_hierarchy ? "event.view_hierarchy" : "");
+ String(), // filename
+ is_view_hierarchy ? "application/json" : String(),
+ is_view_hierarchy ? "event.view_hierarchy" : String());
}
android_plugin->call("initialize",
diff --git a/src/sentry/android/android_sdk.h b/src/sentry/android/android_sdk.h
index 418f7737..ff71bc69 100644
--- a/src/sentry/android/android_sdk.h
+++ b/src/sentry/android/android_sdk.h
@@ -47,6 +47,8 @@ class AndroidSDK : public InternalSDK {
virtual Ref create_event() override;
virtual String capture_event(const Ref &p_event) override;
+ virtual void add_attachment(const Ref &p_attachment) override;
+
virtual void initialize(const PackedStringArray &p_global_attachments) override;
bool has_android_plugin() const { return android_plugin != nullptr; }
diff --git a/src/sentry/android/android_string_names.cpp b/src/sentry/android/android_string_names.cpp
index 5f9df030..c4f8b4f0 100644
--- a/src/sentry/android/android_string_names.cpp
+++ b/src/sentry/android/android_string_names.cpp
@@ -15,7 +15,6 @@ void AndroidStringNames::destroy_singleton() {
AndroidStringNames::AndroidStringNames() {
// API methods.
- addFileAttachment = StringName("addFileAttachment");
setContext = StringName("setContext");
removeContext = StringName("removeContext");
setTag = StringName("setTag");
@@ -29,6 +28,8 @@ AndroidStringNames::AndroidStringNames() {
createEvent = StringName("createEvent");
releaseEvent = StringName("releaseEvent");
captureEvent = StringName("captureEvent");
+ addFileAttachment = StringName("addFileAttachment");
+ addBytesAttachment = StringName("addBytesAttachment");
// Event methods.
eventGetId = StringName("eventGetId");
diff --git a/src/sentry/android/android_string_names.h b/src/sentry/android/android_string_names.h
index 1fa53b57..3467cba7 100644
--- a/src/sentry/android/android_string_names.h
+++ b/src/sentry/android/android_string_names.h
@@ -27,7 +27,6 @@ class AndroidStringNames {
_FORCE_INLINE_ static AndroidStringNames *get_singleton() { return singleton; }
// API methods.
- StringName addFileAttachment;
StringName setContext;
StringName removeContext;
StringName setTag;
@@ -41,6 +40,8 @@ class AndroidStringNames {
StringName createEvent;
StringName releaseEvent;
StringName captureEvent;
+ StringName addFileAttachment;
+ StringName addBytesAttachment;
// Event methods.
StringName eventGetId;
diff --git a/src/sentry/disabled_sdk.h b/src/sentry/disabled_sdk.h
index 773c8d87..1460ddd0 100644
--- a/src/sentry/disabled_sdk.h
+++ b/src/sentry/disabled_sdk.h
@@ -26,6 +26,8 @@ class DisabledSDK : public InternalSDK {
virtual Ref create_event() override { return memnew(DisabledEvent); }
virtual String capture_event(const Ref &p_event) override { return ""; }
+ virtual void add_attachment(const Ref &p_attachment) override {}
+
virtual void initialize(const PackedStringArray &p_global_attachments) override {}
};
diff --git a/src/sentry/internal_sdk.h b/src/sentry/internal_sdk.h
index 35fca6c9..1e6e66ee 100644
--- a/src/sentry/internal_sdk.h
+++ b/src/sentry/internal_sdk.h
@@ -9,6 +9,8 @@
#include
#include
+class SentryAttachment;
+
using namespace godot;
namespace sentry {
@@ -34,6 +36,8 @@ class InternalSDK {
virtual Ref create_event() = 0;
virtual String capture_event(const Ref &p_event) = 0;
+ virtual void add_attachment(const Ref &p_attachment) = 0;
+
virtual void initialize(const PackedStringArray &p_global_attachments) = 0;
virtual ~InternalSDK() = default;
diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp
index daf1a550..037abbf1 100644
--- a/src/sentry/native/native_sdk.cpp
+++ b/src/sentry/native/native_sdk.cpp
@@ -7,6 +7,8 @@
#include "sentry/native/native_util.h"
#include "sentry/processing/process_event.h"
#include "sentry/util/print.h"
+#include "sentry/util/screenshot.h"
+#include "sentry_attachment.h"
#include "sentry_options.h"
#include
@@ -186,6 +188,45 @@ String NativeSDK::capture_event(const Ref &p_event) {
return _uuid_as_string(uuid);
}
+void NativeSDK::add_attachment(const Ref &p_attachment) {
+ ERR_FAIL_COND_MSG(p_attachment.is_null(), "Sentry: Can't add null attachment.");
+ ERR_FAIL_NULL(ProjectSettings::get_singleton());
+
+ sentry_attachment_t *native_attachment = nullptr;
+
+ if (!p_attachment->get_path().is_empty()) {
+ String absolute_path = ProjectSettings::get_singleton()->globalize_path(p_attachment->get_path());
+ sentry::util::print_debug(vformat("attaching file: %s", absolute_path));
+
+ native_attachment = sentry_attach_file(absolute_path.utf8());
+
+ ERR_FAIL_NULL_MSG(native_attachment, vformat("Sentry: Failed to attach file: %s", absolute_path));
+
+ if (!p_attachment->get_filename().is_empty()) {
+ sentry_attachment_set_filename(native_attachment, p_attachment->get_filename().utf8());
+ }
+
+ } else {
+ PackedByteArray bytes = p_attachment->get_bytes();
+ ERR_FAIL_COND_MSG(bytes.is_empty(), "Sentry: Can't add attachment with empty bytes and no file path.");
+
+ sentry::util::print_debug(vformat("attaching bytes with filename: %s", p_attachment->get_filename()));
+
+ native_attachment = sentry_attach_bytes(
+ reinterpret_cast(bytes.ptr()),
+ bytes.size(),
+ p_attachment->get_filename().utf8());
+
+ ERR_FAIL_NULL_MSG(native_attachment, vformat("Sentry: Failed to attach bytes with filename: %s", p_attachment->get_filename()));
+ }
+
+ p_attachment->set_native_attachment(native_attachment);
+
+ if (!p_attachment->get_content_type().is_empty()) {
+ sentry_attachment_set_content_type(native_attachment, p_attachment->get_content_type().utf8());
+ }
+}
+
void NativeSDK::initialize(const PackedStringArray &p_global_attachments) {
ERR_FAIL_NULL(OS::get_singleton());
ERR_FAIL_NULL(ProjectSettings::get_singleton());
diff --git a/src/sentry/native/native_sdk.h b/src/sentry/native/native_sdk.h
index 6af831d0..fa4e988e 100644
--- a/src/sentry/native/native_sdk.h
+++ b/src/sentry/native/native_sdk.h
@@ -33,6 +33,8 @@ class NativeSDK : public InternalSDK {
virtual Ref create_event() override;
virtual String capture_event(const Ref &p_event) override;
+ virtual void add_attachment(const Ref &p_attachment) override;
+
virtual void initialize(const PackedStringArray &p_global_attachments) override;
virtual ~NativeSDK() override;
diff --git a/src/sentry_attachment.cpp b/src/sentry_attachment.cpp
new file mode 100644
index 00000000..41b7bf83
--- /dev/null
+++ b/src/sentry_attachment.cpp
@@ -0,0 +1,32 @@
+#include "sentry_attachment.h"
+
+#include "sentry/simple_bind.h"
+
+#include
+
+Ref SentryAttachment::create_with_path(const String &p_path) {
+ ERR_FAIL_COND_V_MSG(p_path.is_empty(), Ref(), "Sentry: Can't create attachment with an empty file path.");
+
+ Ref attachment = memnew(SentryAttachment);
+ attachment->path = p_path;
+ return attachment;
+}
+
+Ref SentryAttachment::create_with_bytes(const PackedByteArray &p_bytes, const String &p_filename) {
+ ERR_FAIL_COND_V_MSG(p_filename.is_empty(), Ref(), "Sentry: Can't create attachment with an empty filename.");
+
+ Ref attachment = memnew(SentryAttachment);
+ attachment->bytes = p_bytes;
+ attachment->filename = p_filename;
+ return attachment;
+}
+
+void SentryAttachment::_bind_methods() {
+ ClassDB::bind_static_method("SentryAttachment", D_METHOD("create_with_path", "path"), &SentryAttachment::create_with_path);
+ ClassDB::bind_static_method("SentryAttachment", D_METHOD("create_with_bytes", "bytes", "filename"), &SentryAttachment::create_with_bytes);
+
+ BIND_PROPERTY(SentryAttachment, PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes"), set_bytes, get_bytes);
+ BIND_PROPERTY(SentryAttachment, PropertyInfo(Variant::STRING, "path"), set_path, get_path);
+ BIND_PROPERTY(SentryAttachment, PropertyInfo(Variant::STRING, "filename"), set_filename, get_filename);
+ BIND_PROPERTY(SentryAttachment, PropertyInfo(Variant::STRING, "content_type"), set_content_type, get_content_type);
+}
diff --git a/src/sentry_attachment.h b/src/sentry_attachment.h
new file mode 100644
index 00000000..d02355e1
--- /dev/null
+++ b/src/sentry_attachment.h
@@ -0,0 +1,55 @@
+#ifndef SENTRY_ATTACHMENT_H
+#define SENTRY_ATTACHMENT_H
+
+#include
+#include
+#include
+
+#ifdef NATIVE_SDK
+#include
+#endif
+
+using namespace godot;
+
+// Represents attachments in the public API.
+class SentryAttachment : public RefCounted {
+ GDCLASS(SentryAttachment, RefCounted);
+
+private:
+ PackedByteArray bytes;
+ String path;
+ String filename;
+ String content_type;
+
+#ifdef NATIVE_SDK
+ sentry_attachment_t *native_attachment = nullptr;
+#endif
+
+protected:
+ static void _bind_methods();
+
+public:
+ static Ref create_with_path(const String &p_path);
+ static Ref create_with_bytes(const PackedByteArray &p_bytes, const String &p_filename);
+
+ PackedByteArray get_bytes() const { return bytes; }
+ void set_bytes(const PackedByteArray &p_bytes) { bytes = p_bytes; }
+
+ String get_path() const { return path; }
+ void set_path(const String &p_path) { path = p_path; }
+
+ String get_filename() const { return filename; }
+ void set_filename(const String &p_filename) { filename = p_filename; }
+
+ String get_content_type() const { return content_type; }
+ void set_content_type(const String &p_content_type) { content_type = p_content_type; }
+
+#ifdef NATIVE_SDK
+ sentry_attachment_t *get_native_attachment() const { return native_attachment; }
+ void set_native_attachment(sentry_attachment_t *p_native_attachment) { native_attachment = p_native_attachment; }
+#endif
+
+ ~SentryAttachment() = default;
+};
+
+#endif // SENTRY_ATTACHMENT_H
diff --git a/src/sentry_sdk.cpp b/src/sentry_sdk.cpp
index 89389fca..e786b3d8 100644
--- a/src/sentry_sdk.cpp
+++ b/src/sentry_sdk.cpp
@@ -7,6 +7,7 @@
#include "sentry/processing/screenshot_processor.h"
#include "sentry/processing/view_hierarchy_processor.h"
#include "sentry/util/print.h"
+#include "sentry_attachment.h"
#include
#include
@@ -79,6 +80,11 @@ String SentrySDK::capture_event(const Ref &p_event) {
return internal_sdk->capture_event(p_event);
}
+void SentrySDK::add_attachment(const Ref &p_attachment) {
+ ERR_FAIL_COND_MSG(p_attachment.is_null(), "Sentry: Can't add null attachment.");
+ internal_sdk->add_attachment(p_attachment);
+}
+
void SentrySDK::set_tag(const String &p_key, const String &p_value) {
ERR_FAIL_COND_MSG(p_key.is_empty(), "Sentry: Can't set tag with an empty key.");
internal_sdk->set_tag(p_key, p_value);
@@ -257,6 +263,7 @@ void SentrySDK::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_user"), &SentrySDK::remove_user);
ClassDB::bind_method(D_METHOD("create_event"), &SentrySDK::create_event);
ClassDB::bind_method(D_METHOD("capture_event", "event"), &SentrySDK::capture_event);
+ ClassDB::bind_method(D_METHOD("add_attachment", "attachment"), &SentrySDK::add_attachment);
// Hidden API methods -- used in testing.
ClassDB::bind_method(D_METHOD("_set_before_send", "callable"), &SentrySDK::set_before_send);
diff --git a/src/sentry_sdk.h b/src/sentry_sdk.h
index 5bc5fc0b..a8d9f59a 100644
--- a/src/sentry_sdk.h
+++ b/src/sentry_sdk.h
@@ -4,6 +4,7 @@
#include "runtime_config.h"
#include "sentry/internal_sdk.h"
#include "sentry/level.h"
+#include "sentry_attachment.h"
#include "sentry_event.h"
#include "sentry_options.h"
@@ -70,6 +71,8 @@ class SentrySDK : public Object {
Ref create_event() const;
String capture_event(const Ref &p_event);
+ void add_attachment(const Ref &p_attachment);
+
// * Hidden API methods -- used in testing
void set_before_send(const Callable &p_callable) { SentryOptions::get_singleton()->set_before_send(p_callable); }