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) {
|
pub const Item = union(enum) {
|
||||||
encoded: EncodedItem,
|
encoded: EncodedItem,
|
||||||
attachment: Attachment,
|
attachment: Attachment,
|
||||||
|
session: Session,
|
||||||
|
|
||||||
/// Convert the item to an encoded item. This modify the item
|
/// Convert the item to an encoded item. This modify the item
|
||||||
/// in place.
|
/// in place.
|
||||||
@ -288,6 +289,7 @@ pub const Item = union(enum) {
|
|||||||
const result: EncodedItem = switch (self.*) {
|
const result: EncodedItem = switch (self.*) {
|
||||||
.encoded => |v| return v,
|
.encoded => |v| return v,
|
||||||
.attachment => |*v| try v.encode(alloc),
|
.attachment => |*v| try v.encode(alloc),
|
||||||
|
.session => |*v| try v.encode(alloc),
|
||||||
};
|
};
|
||||||
self.* = .{ .encoded = result };
|
self.* = .{ .encoded = result };
|
||||||
return result;
|
return result;
|
||||||
@ -299,6 +301,7 @@ pub const Item = union(enum) {
|
|||||||
return switch (self) {
|
return switch (self) {
|
||||||
.encoded => |v| v.type,
|
.encoded => |v| v.type,
|
||||||
.attachment => .attachment,
|
.attachment => .attachment,
|
||||||
|
.session => .session,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +338,10 @@ pub const Item = union(enum) {
|
|||||||
alloc,
|
alloc,
|
||||||
encoded,
|
encoded,
|
||||||
) },
|
) },
|
||||||
|
.session => .{ .session = try .decode(
|
||||||
|
alloc,
|
||||||
|
encoded,
|
||||||
|
) },
|
||||||
else => return error.UnsupportedType,
|
else => return error.UnsupportedType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -349,9 +356,191 @@ pub const EncodedItem = struct {
|
|||||||
payload: []const u8,
|
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.
|
/// 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 {
|
pub const Attachment = struct {
|
||||||
/// "filename" field is the name of the uploaded file without
|
/// "filename" field is the name of the uploaded file without
|
||||||
/// a path component.
|
/// a path component.
|
||||||
|
Reference in New Issue
Block a user