Files
zlog/src/zlog.zig
Yves Biener eb7e78697c
All checks were successful
Run Tests / test (push) Successful in 9m33s
Run Tests / lint (push) Successful in 9m43s
add(timestamp): timestamp build configuration option to add timestamps before log messages
2024-08-28 22:25:06 +02:00

97 lines
3.8 KiB
Zig

const build_options = @import("build_options");
const c_time = if (build_options.timestamp) @cImport(@cInclude("time.h")) else null;
const std = @import("std");
pub const std_options: std.Options = .{
.logFn = logFn,
};
// zlog defaultLog function replacement, which adjusts the surrounding contents of every std.log message.
fn logFn(
comptime message_level: std.log.Level,
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
// TODO: provide build time configuration to allow tweaking corresponding output
// - change output file for writing messages to (default `stderr`)
const level_txt = comptime message_level.asText();
const prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
const stderr = std.io.getStdErr().writer();
var bw = std.io.bufferedWriter(stderr);
const writer = bw.writer();
std.debug.lockStdErr();
defer std.debug.unlockStdErr();
nosuspend {
if (build_options.timestamp) {
const curtime = c_time.time(null);
const tm = c_time.localtime(&curtime);
var buf: [32]u8 = undefined;
_ = c_time.strftime(@ptrCast(&buf), 32, "%F %R", tm);
writer.print("[{s}] ", .{buf}) catch return;
}
writer.print(level_txt ++ prefix ++ format ++ "\n", args) catch return;
bw.flush() catch return;
}
}
pub fn pretty_format(object: anytype, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
try inner_format(object, fmt, options, writer, 0);
}
fn inner_format(object: anytype, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, comptime depth: u8) !void {
const Object = @TypeOf(object);
const object_info = @typeInfo(Object);
switch (object_info) {
.Struct => |s| {
try writer.writeAll(@typeName(Object));
try writer.writeAll(" = struct {\n");
inline for (s.fields) |field| {
try writer.writeAll("\t" ** (depth + 1));
try writer.writeAll(".");
try writer.writeAll(field.name);
try writer.writeAll(" = ");
// TODO check corresponding type and try to adapt fmt accordingly!
// TODO: pass along the already parsed formatting options too
try inner_format(@field(object, field.name), fmt, options, writer, depth + 1);
try writer.writeAll(",\n");
}
try writer.writeAll("\t" ** depth);
try writer.writeAll("}");
},
.Enum => |e| {
try writer.writeAll(@typeName(Object));
try writer.writeAll(" = enum {\n");
inline for (e.fields) |field| {
try writer.writeAll("\t" ** (depth + 1));
try writer.writeAll(field.name);
try writer.writeAll(",\n");
}
try writer.writeAll("\t" ** depth);
try writer.writeAll("} = ");
try writer.writeAll(@tagName(object));
},
// TODO: implement prett_printing for other user defined types (`union` and `vector`)
// TODO: recognize []const u8 types and print them as strings
.Array => |a| {
if (a.child == @TypeOf([:0]const u8)) {
try std.fmt.format(writer, "\"{s}\"", .{object});
} else {
try std.fmt.format(writer, "[]{s}: {any}", .{ @typeName(a.child), object });
}
},
.Vector => |v| {
if (v.child == @TypeOf([:0]const u8)) {
try std.fmt.format(writer, "\"{s}\"", .{object});
} else {
try std.fmt.format(writer, "[]{s}: {any}", .{ @typeName(v.child), object });
}
},
else => {
try std.fmt.format(writer, "{any}", .{object});
},
}
}