feat(pretty_print): handle union and pointer (including slices)
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 37s

This commit is contained in:
2025-07-17 21:55:22 +02:00
parent d13fcce836
commit eb2ea38e34
3 changed files with 234 additions and 78 deletions

110
README.md
View File

@@ -1,20 +1,36 @@
# zlog
Standard Library log wrapper. `zlog` provides adjusted `std.log` output and a pretty print function for easy overwriting of user defined types (`struct`, `enum`, `union` and `vector`).
Standard Library log wrapper. `zlog` provides adjusted `std.log` output and a pretty print function for easy overwriting of user defined types.
> [!CAUTION]
> Only builds using the zig master version are tested to work.
## Usage
The following snippet shows an example usage of `zlog` to change the default log format and add pretty printing to both user defined types (`Options` and `Struct`):
The following snippet shows an example usage of `zlog` to change the default log format and add pretty printing to the user defined types:
```zig
const std = @import("std");
const zlog = @import("zlog");
const Union = union {
int: i32,
boolean: bool,
// use this to overwrite the default log format of `std.log`
pub const std_options = zlog.std_options;
// copy and paste this function into your user defined types to enable pretty printing for these types
pub fn format(value: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
try zlog.pretty_format(value, fmt, options, writer);
}
};
const TaggedUnion = union(enum) {
one: i16,
two: u32,
three: []const u8,
nothing,
// copy and paste this function into your user defined types to enable pretty printing for these types
pub fn format(value: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
try zlog.pretty_format(value, fmt, options, writer);
}
};
const Options = enum {
a,
@@ -32,7 +48,8 @@ const Struct = struct {
b: bool = true,
c: [5]u16 = .{ 1, 2, 3, 4, 5 },
d: []const u8 = "string",
e: Options = Options.b,
e: Options = .b,
f: TaggedUnion = .{ .one = -5 },
// same function as above...
pub fn format(value: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@@ -43,15 +60,20 @@ const Struct = struct {
pub fn main() void {
// initialize zlog with the scope of `main`
const log = std.log.scoped(.main);
// NOTE: the scope of `default` will result in no scoping being printed in
// the resulting output (default behavior of `std.log.defaultLog`)
// NOTE the scope of `default` will result in no scoping being printed in
// the resulting output (default behavior of `std.log.defaultLog`)
// some variables to log
const int_var = 42;
const array_var = [_]i32{ 1, 2, 3, 4 };
const string_var = "This is a test";
const option_var = Options.a;
const option_var: Options = .a;
const struct_var: Struct = .{};
const tagged_union: TaggedUnion = .{
.three = "Three",
};
const void_tagged_union: TaggedUnion = .nothing;
const uniun: Union = .{ .boolean = true };
// NOTE: Depending on the optimization target some of these log messages
// will not show, which is inline with the behavior of `std.log`.
@@ -59,32 +81,62 @@ pub fn main() void {
log.info("Info message {any}", .{array_var});
log.info("Info message \"{s}\"", .{string_var});
log.warn("Warning message {any}", .{option_var});
log.warn("Warning message {any}", .{void_tagged_union});
log.warn("Warning message {any}", .{uniun});
log.err("Error message {any}", .{struct_var});
log.err("Error message {any}", .{tagged_union});
}
pub const std_options = zlog.std_options;
const std = @import("std");
const zlog = @import("zlog");
```
This will result in the following output:
```
[2025-02-24 13:00:08] [debug](main): Debug message 42
[2025-02-24 13:00:08] [info](main): Info message { 1, 2, 3, 4 }
[2025-02-24 13:00:08] [info](main): Info message "This is a test"
[2025-02-24 13:00:08] [warning](main): Warning message main.Options = enum {
a,
b,
c,
} = a
[2025-02-24 13:00:08] [error](main): Error message main.Struct = struct {
.a = 42,
.b = true,
.c = []u16: { 1, 2, 3, 4, 5 },
.d = { 115, 116, 114, 105, 110, 103 },
.e = main.Options = enum {
a,
b,
c,
} = b,
[2025-07-17 19:41:43] [debug](main): Debug message 42
[2025-07-17 19:41:43] [info](main): Info message { 1, 2, 3, 4 }
[2025-07-17 19:41:43] [info](main): Info message "This is a test"
[2025-07-17 19:41:43] [warning](main): Warning message main.Options = enum {
a,
b,
c,
} = .a
[2025-07-17 19:41:43] [warning](main): Warning message main.TaggedUnion = union(enum) {
one: i16,
two: u32,
three: []const u8,
nothing,
} = .{ .nothing = void }
[2025-07-17 19:41:43] [warning](main): Warning message main.Union = union {
int: i32,
boolean: bool,
} = @7fffd1c71048
[2025-07-17 19:41:43] [error](main): Error message main.Struct = struct {
.a = 42,
.b = true,
.c = []u16: { 1, 2, 3, 4, 5 },
.d = "string",
.e = main.Options = enum {
a,
b,
c,
} = .b,
.f = main.TaggedUnion = union(enum) {
one: i16,
two: u32,
three: []const u8,
nothing,
} = .{ .one = -5 },
}
[2025-07-17 19:41:43] [error](main): Error message main.TaggedUnion = union(enum) {
one: i16,
two: u32,
three: []const u8,
nothing,
} = .{ .three = "Three" }
```
## Customization
@@ -112,4 +164,4 @@ The following list shows some tips on how to use logging more effectively. These
break :port try fmt.parseInt(u16, buf[0 .. len -| 1], 10);
};
```
- When looking through the output of the log (i.e. written to disk by `program 2> log`) the `log` file contains control code characters (*ansi*) for the colored outputs. You can still see them correctly with the following command `less -rf log`
- When looking through the output of the log (i.e. written to disk by `program 2> log` when using the _stderr_ build option) the `log` file contains control code characters (*ansi*) for the colored outputs. You can still see them correctly with the following command `less -R log`.