Skip to content

Commit 50457cb

Browse files
authored
ref: Event processing (#202)
* Revise event processing to support Android. This fixes missing screenshots and view hierarchy on Android. * Introduce event processors (internally). This interface can also be later exposed in the public API if needed.
1 parent e59d02f commit 50457cb

24 files changed

+432
-193
lines changed

SConstruct

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ env.Append(CPPPATH=["src/"])
222222
sources = Glob("src/*.cpp")
223223
sources += Glob("src/editor/*.cpp")
224224
sources += Glob("src/sentry/*.cpp")
225+
sources += Glob("src/sentry/processing/*.cpp")
225226
sources += Glob("src/sentry/util/*.cpp")
226227
# Compile sentry-native code only on respective platforms.
227228
if env["platform"] in ["linux", "windows", "macos"]:

android_lib/src/main/java/io/sentry/godotplugin/SentryAndroidGodotPlugin.kt

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.godotengine.godot.Godot
1919
import org.godotengine.godot.plugin.GodotPlugin
2020
import org.godotengine.godot.plugin.UsedByGodot
2121
import org.godotengine.godot.variant.Callable
22+
import java.io.File
2223
import kotlin.random.Random
2324

2425

@@ -123,8 +124,14 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
123124
}
124125

125126
@UsedByGodot
126-
fun addGlobalAttachment(path: String) {
127-
val attachment = Attachment(path)
127+
fun addFileAttachment(path: String, filename: String, contentType: String, attachmentType: String) {
128+
val attachment = Attachment(
129+
path,
130+
filename.ifEmpty { File(path).name },
131+
contentType.ifEmpty { null },
132+
attachmentType.ifEmpty { null },
133+
false
134+
)
128135
Sentry.getGlobalScope().addAttachment(attachment)
129136
}
130137

@@ -335,6 +342,32 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
335342
getEvent(eventHandle)?.removeTag(key)
336343
}
337344

345+
@UsedByGodot
346+
fun eventMergeContext(eventHandle: Int, key: String, value: Dictionary) {
347+
val event = getEvent(eventHandle) ?: return
348+
349+
val existingContext: Any? = event.contexts[key]
350+
351+
if (existingContext is Dictionary) {
352+
// Fast path: merge Dictionary directly.
353+
existingContext.putAll(value)
354+
} else {
355+
val existingMap = existingContext as? Map<*, *>
356+
existingMap?.let {
357+
// Merge elements from existing context into new context, but only for keys
358+
// that don't already exist in the new context.
359+
for ((k, v) in it) {
360+
val kStr: String = k as? String ?: continue
361+
if (!value.containsKey(kStr)) {
362+
value[kStr] = v
363+
}
364+
}
365+
}
366+
event.contexts[key] = value
367+
}
368+
369+
}
370+
338371
@UsedByGodot
339372
fun eventIsCrash(eventHandle: Int): Boolean {
340373
return getEvent(eventHandle)?.isCrashed == true

src/register_types.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
#include "editor/sentry_editor_plugin.h"
33
#include "runtime_config.h"
44
#include "sentry/disabled_event.h"
5+
#include "sentry/processing/screenshot_processor.h"
6+
#include "sentry/processing/view_hierarchy_processor.h"
57
#include "sentry/util/print.h"
68
#include "sentry_configuration.h"
79
#include "sentry_event.h"
10+
#include "sentry_event_processor.h"
811
#include "sentry_logger.h"
912
#include "sentry_options.h"
1013
#include "sentry_sdk.h"
@@ -67,6 +70,9 @@ void initialize_module(ModuleInitializationLevel p_level) {
6770
GDREGISTER_CLASS(SentrySDK);
6871
GDREGISTER_ABSTRACT_CLASS(SentryEvent);
6972
GDREGISTER_INTERNAL_CLASS(DisabledEvent);
73+
GDREGISTER_INTERNAL_CLASS(SentryEventProcessor);
74+
GDREGISTER_INTERNAL_CLASS(ScreenshotProcessor);
75+
GDREGISTER_INTERNAL_CLASS(ViewHierarchyProcessor);
7076
GDREGISTER_INTERNAL_CLASS(SentryLogger);
7177

7278
#ifdef NATIVE_SDK

src/sentry/android/android_event.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ String AndroidEvent::get_tag(const String &p_key) {
101101
return android_plugin->call(ANDROID_SN(eventGetTag), event_handle, p_key);
102102
}
103103

104+
void AndroidEvent::merge_context(const String &p_key, const Dictionary &p_value) {
105+
ERR_FAIL_COND_MSG(p_key.is_empty(), "Sentry: Can't merge context with an empty key.");
106+
android_plugin->call(ANDROID_SN(eventMergeContext), event_handle, p_key, p_value);
107+
}
108+
104109
void AndroidEvent::add_exception(const Exception &p_exception) {
105110
ERR_FAIL_NULL(android_plugin);
106111

src/sentry/android/android_event.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class AndroidEvent : public SentryEvent {
4949
virtual void remove_tag(const String &p_key) override;
5050
virtual String get_tag(const String &p_key) override;
5151

52+
virtual void merge_context(const String &p_key, const Dictionary &p_value) override;
53+
5254
virtual void add_exception(const Exception &p_exception) override;
5355

5456
virtual bool is_crash() const override;

src/sentry/android/android_sdk.cpp

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "android_event.h"
44
#include "android_string_names.h"
5+
#include "sentry/processing/process_event.h"
56
#include "sentry/util/print.h"
67

78
#include <godot_cpp/classes/engine.hpp>
@@ -17,27 +18,15 @@ void SentryAndroidBeforeSendHandler::_initialize(Object *p_android_plugin) {
1718

1819
void SentryAndroidBeforeSendHandler::_before_send(int32_t p_event_handle) {
1920
sentry::util::print_debug("handling before_send: ", p_event_handle);
21+
2022
Ref<AndroidEvent> event_obj = memnew(AndroidEvent(android_plugin, p_event_handle));
2123
event_obj->set_as_borrowed();
22-
if (const Callable &before_send = SentryOptions::get_singleton()->get_before_send(); before_send.is_valid()) {
23-
Ref<AndroidEvent> processed = before_send.call(event_obj);
24-
if (processed.is_valid() && processed != event_obj) {
25-
static bool first_print = true;
26-
if (unlikely(first_print)) {
27-
// Note: Only push error once to avoid infinite feedback loop.
28-
ERR_PRINT("Sentry: before_send callback must return the same event object or null.");
29-
first_print = false;
30-
} else {
31-
sentry::util::print_error("before_send callback must return the same event object or null.");
32-
}
33-
}
34-
if (processed.is_null()) {
35-
// Discard event.
36-
sentry::util::print_debug("event discarded by before_send callback: ", event_obj->get_id());
37-
android_plugin->call(ANDROID_SN(releaseEvent), p_event_handle);
38-
} else {
39-
sentry::util::print_debug("event processed by before_send callback: ", event_obj->get_id());
40-
}
24+
25+
Ref<AndroidEvent> processed = sentry::process_event(event_obj);
26+
27+
if (processed.is_null()) {
28+
// Discard event.
29+
android_plugin->call(ANDROID_SN(releaseEvent), p_event_handle);
4130
}
4231
}
4332

@@ -118,7 +107,13 @@ void AndroidSDK::initialize(const PackedStringArray &p_global_attachments) {
118107
sentry::util::print_debug("Initializing Sentry Android SDK");
119108

120109
for (const String &path : p_global_attachments) {
121-
android_plugin->call(ANDROID_SN(addGlobalAttachment), path);
110+
String file = path.get_file();
111+
bool is_view_hierarchy = file == "view-hierarchy.json";
112+
android_plugin->call(ANDROID_SN(addFileAttachment),
113+
path,
114+
file,
115+
is_view_hierarchy ? "application/json" : "",
116+
is_view_hierarchy ? "event.view_hierarchy" : "");
122117
}
123118

124119
android_plugin->call("initialize",

src/sentry/android/android_string_names.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void AndroidStringNames::destroy_singleton() {
1515

1616
AndroidStringNames::AndroidStringNames() {
1717
// API methods.
18-
addGlobalAttachment = StringName("addGlobalAttachment");
18+
addFileAttachment = StringName("addFileAttachment");
1919
setContext = StringName("setContext");
2020
removeContext = StringName("removeContext");
2121
setTag = StringName("setTag");
@@ -50,6 +50,7 @@ AndroidStringNames::AndroidStringNames() {
5050
eventSetTag = StringName("eventSetTag");
5151
eventRemoveTag = StringName("eventRemoveTag");
5252
eventGetTag = StringName("eventGetTag");
53+
eventMergeContext = StringName("eventMergeContext");
5354
eventIsCrash = StringName("eventIsCrash");
5455

5556
// Exceptions.

src/sentry/android/android_string_names.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class AndroidStringNames {
2727
_FORCE_INLINE_ static AndroidStringNames *get_singleton() { return singleton; }
2828

2929
// API methods.
30-
StringName addGlobalAttachment;
30+
StringName addFileAttachment;
3131
StringName setContext;
3232
StringName removeContext;
3333
StringName setTag;
@@ -62,6 +62,7 @@ class AndroidStringNames {
6262
StringName eventSetTag;
6363
StringName eventRemoveTag;
6464
StringName eventGetTag;
65+
StringName eventMergeContext;
6566
StringName eventIsCrash;
6667

6768
// Exceptions.

src/sentry/disabled_event.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class DisabledEvent : public SentryEvent {
4949
virtual void remove_tag(const String &p_key) override {}
5050
virtual String get_tag(const String &p_key) override { return ""; }
5151

52+
virtual void merge_context(const String &p_key, const Dictionary &p_value) override {}
53+
5254
virtual void add_exception(const Exception &p_exception) override {}
5355

5456
virtual bool is_crash() const override { return false; }

src/sentry/native/native_event.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,35 @@ inline void _sentry_value_set_or_remove_string_by_key(sentry_value_t value, cons
1717
}
1818
}
1919

20+
void sentry_event_merge_context(sentry_value_t p_event, const char *p_context_name, const Dictionary &p_context) {
21+
ERR_FAIL_COND(sentry_value_get_type(p_event) != SENTRY_VALUE_TYPE_OBJECT);
22+
ERR_FAIL_COND(p_context_name == nullptr || strlen(p_context_name) == 0);
23+
24+
if (p_context.is_empty()) {
25+
return;
26+
}
27+
28+
sentry_value_t contexts = sentry_value_get_by_key(p_event, "contexts");
29+
if (sentry_value_is_null(contexts)) {
30+
contexts = sentry_value_new_object();
31+
sentry_value_set_by_key(p_event, "contexts", contexts);
32+
}
33+
34+
// Check if context exists and update or add it.
35+
sentry_value_t ctx = sentry_value_get_by_key(contexts, p_context_name);
36+
if (!sentry_value_is_null(ctx)) {
37+
// If context exists, update it with new values.
38+
const Array &updated_keys = p_context.keys();
39+
for (int i = 0; i < updated_keys.size(); i++) {
40+
const String &key = updated_keys[i];
41+
sentry_value_set_by_key(ctx, key.utf8(), sentry::native::variant_to_sentry_value(p_context[key]));
42+
}
43+
} else {
44+
// If context doesn't exist, add it.
45+
sentry_value_set_by_key(contexts, p_context_name, sentry::native::variant_to_sentry_value(p_context));
46+
}
47+
}
48+
2049
} // unnamed namespace
2150

2251
String NativeEvent::get_id() const {
@@ -141,6 +170,11 @@ String NativeEvent::get_tag(const String &p_key) {
141170
return String();
142171
}
143172

173+
void NativeEvent::merge_context(const String &p_key, const Dictionary &p_value) {
174+
ERR_FAIL_COND_MSG(p_key.is_empty(), "Sentry: Can't merge context with an empty key.");
175+
sentry_event_merge_context(native_event, p_key.utf8(), p_value);
176+
}
177+
144178
void NativeEvent::add_exception(const Exception &p_exception) {
145179
sentry_value_t native_exception = sentry_value_new_exception(p_exception.type.utf8(), p_exception.value.utf8());
146180
sentry_value_t stack_trace = sentry_value_new_object();

0 commit comments

Comments
 (0)