mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-18 17:56:09 +03:00
Merge f8c9597a3a0d2c3fd79e716ae7ccc25bdc35a4d3 into 50a88dcfe7c35c8b21bcfb7011787a0270f3d7db
This commit is contained in:
@ -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.
|
||||
|
Reference in New Issue
Block a user