Merge f8c9597a3a0d2c3fd79e716ae7ccc25bdc35a4d3 into 50a88dcfe7c35c8b21bcfb7011787a0270f3d7db

This commit is contained in:
marco-zan
2025-07-16 19:50:55 +02:00
committed by GitHub

View File

@ -278,6 +278,7 @@ pub const ItemType = enum {
pub const Item = union(enum) {
encoded: EncodedItem,
attachment: Attachment,
session: Session,
/// Convert the item to an encoded item. This modify the item
/// in place.
@ -288,6 +289,7 @@ pub const Item = union(enum) {
const result: EncodedItem = switch (self.*) {
.encoded => |v| return v,
.attachment => |*v| try v.encode(alloc),
.session => |*v| try v.encode(alloc),
};
self.* = .{ .encoded = result };
return result;
@ -299,6 +301,7 @@ pub const Item = union(enum) {
return switch (self) {
.encoded => |v| v.type,
.attachment => .attachment,
.session => .session,
};
}
@ -335,6 +338,10 @@ pub const Item = union(enum) {
alloc,
encoded,
) },
.session => .{ .session = try .decode(
alloc,
encoded,
) },
else => return error.UnsupportedType,
};
}
@ -349,9 +356,191 @@ pub const EncodedItem = struct {
payload: []const u8,
};
pub const SessionStatus = enum {
// Session is healthy, will never happend crash reporting
ok,
// Session terminated normally, will never happend crash reporting
exited,
// Session crashed
crashed,
// Session exited in an abnormal state that is not crash
abnormal,
};
pub const SessionAttrs = struct {
// Sentry release ID (release)
release: []const u8,
// Sentry environment
// Feel like it should be an enum, but possible values not provided
environment: []const u8,
// Sentry docs does say it's required, but also that is not persisted
ip_address: ?[]const u8,
// Sentry docs does say it's required, but also that is not persisted
user_agent: ?[]const u8,
};
/// A sentry session
///
/// https://develop.sentry.dev/sdk/data-model/envelope-items/#session
pub const Session = struct {
// Session start datetime string, ISO format
started: []const u8,
// Unique session id generated by the client, it can be
// empty for sessions in exited state
sid: ?[]const u8,
// Distinct id, should identify a machine or a user. It is an hash
did: ?[]const u8,
// Sequential number, logical clock, defaults to current unix
// timestamp in milliseconds
seq: ?u64,
// Timestamp of session change event, not sure of its meaning for crash handling
// Datetime string, ISO format
timestamp: ?[]const u8,
// Denotes that an event is the first recorded one for a specific session
init: ?bool = false,
// Represents the length of a session in seconds
duration: ?f64,
// The current status of the session, tracks reason of
// termination, should not be different from "crashed"
status: ?SessionStatus,
// Number of errors encountered during the session. The crash is
// always counted as an error
errors: u16,
// From the docs "The mechanism that caused the session to finish
// with an abnormal status...". If useless can be removed
abnormal_mechanism: ?[] const u8,
attrs: SessionAttrs,
pub fn decode(
alloc: Allocator,
encoded: EncodedItem,
) Item.DecodeError!Session {
_ = alloc;
std.debug.print("Decode requested correctly", .{});
var status: ?SessionStatus = null;
if (encoded.headers.get("status")) |v| {
const temp = switch (v) {
.string => |str| std.meta.stringToEnum( SessionStatus, str )
orelse return error.InvalidFieldType,
else => return error.InvalidFieldType,
};
if (temp != SessionStatus.crashed) {
log.debug("Found status different than crashed: {s}", .{ @tagName(temp) }) ;
}
status = temp;
}
const errors: u16 = if (encoded.headers.get("errors")) |v| switch (v) {
.integer => |n| @truncate( @as(u64, @intCast(n)) ),
else => return error.InvalidFieldType,
} else return error.MissingRequiredField;
if (errors < 1) {
log.debug("Found error count < 1, while crashing should count as one. Value: {}", .{ errors });
}
return .{
.started = if (encoded.headers.get("started")) |v| switch (v) {
.string => |str| str,
else => return error.InvalidFieldType,
} else return error.MissingRequiredField,
//
.sid = if (encoded.headers.get("sid")) |v| switch (v) {
.string => |str| str,
else => return error.InvalidFieldType
} else null,
//
// did: ?[]const u8,
.did = if (encoded.headers.get("did")) |v| switch (v) {
.string => |str| str,
else => return error.InvalidFieldType
} else null,
.seq = if (encoded.headers.get("seq")) |v| switch (v) {
.integer => |num| @intCast(num),
else => return error.InvalidFieldType
} else null,
.timestamp = if (encoded.headers.get("timestamp")) |v| switch (v) {
.string => |str| str,
else => return error.InvalidFieldType
} else null,
// Default to false if not present
.init = if (encoded.headers.get("init")) |v| switch (v) {
.bool => |b| b,
else => return error.InvalidFieldType
} else false,
// duration: ?f64,
.duration = if (encoded.headers.get("duration")) |v| switch (v) {
.float => |f| f,
else => return error.InvalidFieldType
} else null,
.status = status,
.errors = errors,
.attrs = if (encoded.headers.get("attrs")) |v| switch (v) {
.object => |obj| .{
.release = if (obj.get("release")) |u| switch (u) {
.string => |s| s,
else => return error.InvalidFieldType,
} else return error.MissingRequiredField,
.environment = if (obj.get("environment")) |u| switch(u) {
.string => |s| s,
else => return error.InvalidFieldType,
} else return error.MissingRequiredField,
.ip_address = if (obj.get("ip_address")) |u| switch(u) {
.string => |s| s,
else => return error.InvalidFieldType,
} else null,
.user_agent = if (obj.get("user_agent")) |u| switch(u) {
.string => |s| s,
else => return error.InvalidFieldType,
} else null
},
else => return error.InvalidFieldType,
} else return error.MissingRequiredField,
};
}
pub fn encode(
self: *Session,
alloc: Allocator,
) !EncodedItem {
_ = alloc;
_ = self;
std.debug.print("Encode requested correctly", .{});
return .{};
}
};
/// An arbitrary file attachment.
///
/// https://develop.sentry.dev/sdk/envelopes/#attachment
/// https://develop.sentry.dev/sdk/data-model/envelope-items/#attachment
pub const Attachment = struct {
/// "filename" field is the name of the uploaded file without
/// a path component.