Skip to content

Commit 0cdcccb

Browse files
committed
Unify global and scoped file attachment reading
1 parent a8aabff commit 0cdcccb

4 files changed

Lines changed: 45 additions & 103 deletions

File tree

src/sentry/javascript/bridge/src/sentry-bridge.ts

Lines changed: 26 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@ class IdStore<T> {
3131
}
3232
}
3333

34-
// Stores info about attachments loaded from C++ layer during event processing.
35-
interface AttachmentData {
36-
bytes: Uint8Array;
37-
filename: string;
38-
contentType?: string;
39-
attachmentType?: string;
40-
}
41-
4234
// The JS SDK has no notion of file attachments - it only takes bytes. A file is therefore added as an
4335
// attachment object marked with this property, which holds the path, and the C++ layer fills in the
4436
// bytes when an event is captured. Sentry copies only its own known fields into the envelope, so the
@@ -66,6 +58,17 @@ function safeParseJSON<T = any>(json: string, fallback: T): T {
6658
}
6759
}
6860

61+
// Builds an attachment whose bytes the C++ layer reads from the path when an event is captured.
62+
function makePendingAttachment(path: string, filename: string, contentType: string, attachmentType: string): Attachment {
63+
return {
64+
filename,
65+
data: new Uint8Array(0),
66+
...(contentType && { contentType }),
67+
...(attachmentType && { attachmentType }),
68+
[PENDING_PATH_KEY]: path,
69+
} as Attachment;
70+
}
71+
6972
function makeUser(id: string, username: string, email: string, ip: string): User {
7073
const user: User = {};
7174

@@ -119,23 +122,8 @@ class SentryBridge {
119122
this._objectStore.release(id);
120123
}
121124

122-
public pushAttachmentData(
123-
attachmentData: Array<AttachmentData>,
124-
bytes: Uint8Array,
125-
filename: string,
126-
contentType?: string,
127-
attachmentType?: string,
128-
): void {
129-
attachmentData.push({
130-
bytes,
131-
filename,
132-
contentType,
133-
attachmentType,
134-
});
135-
}
136-
137125
public init(
138-
beforeSendCallback: (event: Sentry.Event, outAttachments: Array<AttachmentData>) => void,
126+
beforeSendCallback: (event: Sentry.Event) => void,
139127
beforeSendLogCallback: ((log: Sentry.Log) => void) | null,
140128
beforeSendMetricCallback: ((metric: Metric) => void) | null,
141129
readAttachmentCallback: (request: AttachmentRequest) => void,
@@ -197,31 +185,13 @@ class SentryBridge {
197185
};
198186

199187
if (beforeSendCallback) {
200-
options.beforeSend = (event: Sentry.Event, hint: Sentry.EventHint) => {
188+
options.beforeSend = (event: Sentry.Event) => {
201189
if (!this.isEnabled()) {
202190
// SDK is disabled, skip processing.
203191
return null;
204192
}
205193

206-
// NOTE: Populated during processing in C++ layer
207-
const outAttachments: Array<AttachmentData> = [];
208-
209-
beforeSendCallback(event, outAttachments);
210-
211-
// Add attachments loaded from the C++ layer during event processing
212-
if (!hint.attachments) {
213-
hint.attachments = [];
214-
}
215-
for (const attachmentData of outAttachments) {
216-
if (attachmentData.bytes) {
217-
hint.attachments.push({
218-
data: attachmentData.bytes,
219-
filename: attachmentData.filename,
220-
...(attachmentData.contentType && { contentType: attachmentData.contentType }),
221-
...(attachmentData.attachmentType && { attachmentType: attachmentData.attachmentType }),
222-
} as any);
223-
}
224-
}
194+
beforeSendCallback(event);
225195

226196
const shouldDiscard: boolean = (event as any).shouldDiscard;
227197
delete (event as any).shouldDiscard;
@@ -363,13 +333,14 @@ class SentryBridge {
363333
});
364334
}
365335

366-
public scopeAddFileAttachment(scope: Sentry.Scope, path: string, filename: string, contentType: string): void {
367-
scope.addAttachment({
368-
filename,
369-
data: new Uint8Array(0),
370-
...(contentType && { contentType }),
371-
[PENDING_PATH_KEY]: path,
372-
} as Attachment);
336+
public scopeAddFileAttachment(
337+
scope: Sentry.Scope,
338+
path: string,
339+
filename: string,
340+
contentType: string,
341+
attachmentType: string,
342+
): void {
343+
scope.addAttachment(makePendingAttachment(path, filename, contentType, attachmentType));
373344
}
374345

375346
public scopeClear(scope: Sentry.Scope): void {
@@ -490,6 +461,10 @@ class SentryBridge {
490461
});
491462
}
492463

464+
public addFileAttachment(path: string, filename: string, contentType: string, attachmentType: string): void {
465+
Sentry.getIsolationScope().addAttachment(makePendingAttachment(path, filename, contentType, attachmentType));
466+
}
467+
493468
public clearAttachments(): void {
494469
Sentry.getIsolationScope().clearAttachments();
495470
}

src/sentry/javascript/javascript_scope.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ void JavaScriptScope::add_attachment(const Ref<SentryAttachment> &p_attachment)
7474
js_obj,
7575
p_attachment->get_path().utf8(),
7676
p_attachment->get_effective_filename().utf8(),
77-
p_attachment->get_content_type().utf8());
77+
p_attachment->get_content_type().utf8(),
78+
p_attachment->get_attachment_type().utf8());
7879
} else {
7980
js_bridge()->call("scopeAddBytesAttachment",
8081
js_obj,

src/sentry/javascript/javascript_sdk.cpp

Lines changed: 16 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -30,56 +30,17 @@ static JavaScriptSDK *js_sdk = nullptr;
3030
extern "C" {
3131

3232
static void before_send_wasm_callback(int32_t *p_ids, int32_t p_len) {
33-
ERR_FAIL_COND(p_len != 2);
34-
ERR_FAIL_NULL(js_sdk);
33+
ERR_FAIL_COND(p_len != 1);
3534

3635
JSObjectPtr event_obj = JSObject::from_id(p_ids[0]);
37-
JSObjectPtr out_attachments = JSObject::from_id(p_ids[1]);
3836
ERR_FAIL_COND(!event_obj);
39-
ERR_FAIL_COND(!out_attachments);
4037

4138
Ref<JavaScriptEvent> event = memnew(JavaScriptEvent(event_obj));
4239
Ref<JavaScriptEvent> processed = sentry::process_event(event);
4340

4441
// NOTE: We cannot return a value from a callback, so we use the same
4542
// event object to communicate the result back.
46-
if (unlikely(processed.is_null())) {
47-
// Discard event.
48-
event_obj->set("shouldDiscard", true);
49-
} else {
50-
event_obj->set("shouldDiscard", false);
51-
52-
// Read file-based attachments and include them with the event.
53-
for (const Ref<SentryAttachment> &att : js_sdk->get_file_attachments()) {
54-
if (att->get_path().is_empty()) {
55-
// Skip attachments with empty path.
56-
// NOTE: Byte attachments are not processed here - they are added immediately.
57-
continue;
58-
}
59-
60-
Ref<FileAccess> file = FileAccess::open(att->get_path(), FileAccess::READ);
61-
if (file.is_null()) {
62-
// NOTE: Some attachments may legitimately be missing (e.g. screenshots not created on non-main threads).
63-
sentry::logging::print_debug("Skipping attachment - file not found: " + att->get_path());
64-
continue;
65-
}
66-
67-
PackedByteArray bytes = file->get_buffer(file->get_length());
68-
if (bytes.is_empty()) {
69-
sentry::logging::print_debug("Skipping attachment - empty file: " + att->get_path());
70-
continue;
71-
}
72-
73-
sentry::logging::print_debug("Adding attachment: " + att->get_path());
74-
75-
js_bridge()->call("pushAttachmentData",
76-
out_attachments,
77-
bytes,
78-
att->get_effective_filename().utf8(),
79-
att->get_content_type().utf8(),
80-
att->get_attachment_type().utf8());
81-
}
82-
}
43+
event_obj->set("shouldDiscard", processed.is_null());
8344
}
8445

8546
// Reads the file a scope attachment refers to, leaving the bytes unset if it cannot be read.
@@ -269,10 +230,13 @@ void JavaScriptSDK::add_attachment(const Ref<SentryAttachment> &p_attachment) {
269230
ERR_FAIL_COND_MSG(p_attachment.is_null(), "Sentry: Can't add null attachment.");
270231

271232
if (!p_attachment->get_path().is_empty()) {
272-
// File attachment - add to list for on-demand loading during event processing.
273-
file_attachments.push_back(p_attachment);
233+
// The file is read when an event is captured, so it doesn't have to exist yet.
234+
js_bridge()->call("addFileAttachment",
235+
p_attachment->get_path().utf8(),
236+
p_attachment->get_effective_filename().utf8(),
237+
p_attachment->get_content_type().utf8(),
238+
p_attachment->get_attachment_type().utf8());
274239
} else {
275-
// Bytes attachment - added immediately
276240
ERR_FAIL_COND_MSG(p_attachment->get_filename().is_empty(), "Sentry: Can't add bytes attachment without filename.");
277241

278242
js_bridge()->call("addBytesAttachment",
@@ -285,9 +249,14 @@ void JavaScriptSDK::add_attachment(const Ref<SentryAttachment> &p_attachment) {
285249
void JavaScriptSDK::clear_attachments() {
286250
ERR_FAIL_COND(!js_bridge());
287251

288-
// Reset file attachments from options (Vector uses copy-on-write).
289-
file_attachments = SENTRY_OPTIONS()->get_default_attachments();
290252
js_bridge()->call("clearAttachments");
253+
_add_default_attachments();
254+
}
255+
256+
void JavaScriptSDK::_add_default_attachments() {
257+
for (const Ref<SentryAttachment> &att : SENTRY_OPTIONS()->get_default_attachments()) {
258+
add_attachment(att);
259+
}
291260
}
292261

293262
void JavaScriptSDK::metrics_add_count(const Ref<SentryScope> &p_scope, const String &p_name, int64_t p_value, const Dictionary &p_attributes) {
@@ -348,8 +317,6 @@ void JavaScriptSDK::set_trace(const String &p_trace_id, const String &p_parent_s
348317
void JavaScriptSDK::init() {
349318
ERR_FAIL_COND(!js_bridge());
350319

351-
file_attachments = SENTRY_OPTIONS()->get_default_attachments();
352-
353320
JSObjectPtr before_send_callback = JSObject::create_callback(before_send_wasm_callback);
354321
JSObjectPtr read_attachment_callback = JSObject::create_callback(read_attachment_wasm_callback);
355322

@@ -383,6 +350,7 @@ void JavaScriptSDK::init() {
383350

384351
if (is_enabled()) {
385352
set_user(SentryUser::create_default());
353+
_add_default_attachments();
386354
} else {
387355
ERR_PRINT("Sentry: Failed to initialize JavaScript SDK.");
388356
}
@@ -392,7 +360,6 @@ void JavaScriptSDK::close() {
392360
ERR_FAIL_COND(!js_bridge());
393361

394362
js_bridge()->call("close", SENTRY_OPTIONS()->get_shutdown_timeout_ms());
395-
file_attachments.clear();
396363
}
397364

398365
bool JavaScriptSDK::is_enabled() const {

src/sentry/javascript/javascript_sdk.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ namespace sentry::javascript {
99
// Internal SDK utilizing Sentry for JavaScript.
1010
class JavaScriptSDK : public InternalSDK {
1111
private:
12-
Vector<Ref<SentryAttachment>> file_attachments;
12+
void _add_default_attachments();
1313

1414
public:
15-
_FORCE_INLINE_ const Vector<Ref<SentryAttachment>> &get_file_attachments() const { return file_attachments; }
1615
virtual void set_context(const String &p_key, const Dictionary &p_value) override;
1716
virtual void remove_context(const String &p_key) override;
1817

0 commit comments

Comments
 (0)