diff --git a/pkg/sentry/envelope.zig b/pkg/sentry/envelope.zig index 17aae57f5..0d0e38e12 100644 --- a/pkg/sentry/envelope.zig +++ b/pkg/sentry/envelope.zig @@ -17,6 +17,12 @@ pub const Envelope = opaque { ) != 0) return error.WriteFailed; } + pub fn serialize(self: *Envelope) []u8 { + var len: usize = 0; + const ptr = c.sentry_envelope_serialize(@ptrCast(self), &len).?; + return ptr[0..len]; + } + pub fn event(self: *Envelope) ?Value { const val: Value = .{ .value = c.sentry_envelope_get_event(@ptrCast(self)) }; if (val.isNull()) return null; diff --git a/pkg/sentry/main.zig b/pkg/sentry/main.zig index dfcc323c4..94821470b 100644 --- a/pkg/sentry/main.zig +++ b/pkg/sentry/main.zig @@ -18,6 +18,10 @@ pub fn setTag(key: []const u8, value: []const u8) void { c.sentry_set_tag_n(key.ptr, key.len, value.ptr, value.len); } +pub fn free(ptr: *anyopaque) void { + c.sentry_free(ptr); +} + test { @import("std").testing.refAllDecls(@This()); } diff --git a/src/crash/sentry.zig b/src/crash/sentry.zig index e0b8d4aa9..1b126a7ae 100644 --- a/src/crash/sentry.zig +++ b/src/crash/sentry.zig @@ -5,6 +5,7 @@ const builtin = @import("builtin"); const build_config = @import("../build_config.zig"); const sentry = @import("sentry"); const internal_os = @import("../os/main.zig"); +const crash = @import("main.zig"); const state = &@import("../global.zig").state; const log = std.log.scoped(.sentry); @@ -89,13 +90,16 @@ pub const Transport = struct { /// Implementation of send but we can use Zig errors. fn sendInternal(envelope: *sentry.Envelope) !void { - // If our envelope doesn't have an event then we don't do anything. - // TODO: figure out how to not encode empty envelopes. - var arena = std.heap.ArenaAllocator.init(state.alloc); defer arena.deinit(); const alloc = arena.allocator(); + // Some envelopes don't contain a crash report. Discord them. + if (try shouldDiscard(alloc, envelope)) { + log.info("sentry envelope does not contain crash, discarding", .{}); + return; + } + // Generate a UUID for this envelope. The envelope DOES have an event_id // header but I don't think there is any public API way to get it // afaict so we generate a new UUID for the filename just so we don't @@ -117,4 +121,26 @@ pub const Transport = struct { log.warn("crash report written to disk path={s}", .{path}); } + + fn shouldDiscard(alloc: Allocator, envelope: *sentry.Envelope) !bool { + // If our envelope doesn't have an event then we don't do anything. + // To figure this out we first encode it into a string, parse it, + // and check if it has an event. Kind of wasteful but the best + // option we have at the time of writing this since the C API doesn't + // expose this information. + const json = envelope.serialize(); + defer sentry.free(@ptrCast(json.ptr)); + + // Parse into an envelope structure + var fbs = std.io.fixedBufferStream(json); + var parsed = try crash.Envelope.parse(alloc, fbs.reader()); + defer parsed.deinit(); + + // If we have an event item then we're good. + for (parsed.items) |item| { + if (item.type == .event) return false; + } + + return true; + } }; diff --git a/src/crash/sentry_envelope.zig b/src/crash/sentry_envelope.zig index 84ac49819..00c7ac931 100644 --- a/src/crash/sentry_envelope.zig +++ b/src/crash/sentry_envelope.zig @@ -138,10 +138,13 @@ pub const Envelope = struct { // Parse the header JSON const headers: std.json.ObjectMap = headers: { + const line = std.mem.trim(u8, buf.items, " \t"); + if (line.len == 0) return null; + const value = try std.json.parseFromSliceLeaky( std.json.Value, alloc, - buf.items, + line, .{ .allocate = .alloc_if_needed }, ); @@ -262,3 +265,20 @@ test "Envelope parse session" { try testing.expectEqual(@as(usize, 1), v.items.len); try testing.expectEqual(ItemType.session, v.items[0].type); } + +test "Envelope parse end in new line" { + const testing = std.testing; + const alloc = testing.allocator; + + var fbs = std.io.fixedBufferStream( + \\{} + \\{"type":"session","length":218} + \\{"init":true,"sid":"c148cc2f-5f9f-4231-575c-2e85504d6434","status":"abnormal","errors":0,"started":"2024-08-29T02:38:57.607016Z","duration":0.000343,"attrs":{"release":"0.1.0-HEAD+d37b7d09","environment":"production"}} + \\ + ); + var v = try Envelope.parse(alloc, fbs.reader()); + defer v.deinit(); + + try testing.expectEqual(@as(usize, 1), v.items.len); + try testing.expectEqual(ItemType.session, v.items[0].type); +}