Files
zlog/README.md
Yves Biener 29c6e48d86
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 54s
doc: update documentation to no longer mention the warning about the zig master version
2026-01-17 11:00:07 +01:00

156 lines
6.1 KiB
Markdown

# zlog
Standard Library log wrapper. `zlog` provides adjusted `std.log` output and a pretty print function for easy overwriting of user defined types.
## Install
Add or update this library as a dependency in your zig project run the following command:
```sh
zig fetch --save git+https://gitea.yves-biener.de/yves-biener/zlog
```
Afterwards add the library as a dependency to any module in your *build.zig*:
```zig
const zlog_dependency = b.dependency("zlog", .{
.target = target,
.optimize = optimize,
.timestamp = true, // default (only required if non-default value shall be used)
.stderr = true, // default (only required if non-default value shall be used)
.file = "", // default (only required if non-default value shall be used)
});
```
## Usage
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 Union = union {
int: i32,
boolean: bool,
// 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,
b,
c,
// 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 Struct = struct {
a: usize = 42,
b: bool = true,
c: [5]u16 = .{ 1, 2, 3, 4, 5 },
d: []const u8 = "string",
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 {
try zlog.pretty_format(value, fmt, options, writer);
}
};
pub fn main() void {
// without explicit scope (i.e. `.default` scope)
std.log.info("Without explicit scope or `.default` scope", .{});
// 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`)
// 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 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`.
log.debug("Debug message {any}", .{int_var});
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});
}
// apply *zlog* logging options to `std` logger
pub const std_options = zlog.std_options;
const std = @import("std");
const zlog = @import("zlog");
```
This will result in the following output:
```
2025/11/02 11:57:29 [info] Without explicit scope or `.default` scope
2025/11/02 11:57:29 [debug](main) Debug message 42
2025/11/02 11:57:29 [info](main) Info message { 1, 2, 3, 4 }
2025/11/02 11:57:29 [info](main) Info message "This is a test"
2025/11/02 11:57:29 [warning](main) Warning message .a
2025/11/02 11:57:29 [warning](main) Warning message .{ .nothing = void }
2025/11/02 11:57:29 [warning](main) Warning message .{ ... }
2025/11/02 11:57:29 [error](main) Error message .{ .a = 42, .b = true, .c = { 1, 2, 3, 4, 5 }, .d = { 115, 116, 114, 105, 110, 103 }, .e = .b, .f = .{ .one = -5 } }
2025/11/02 11:57:29 [error](main) Error message .{ .three = { 84, 104, 114, 101, 101 } }
```
## Customization
For more details about the output customization see the configuration options of the `zlog` module. Following options are available:
- *timestamp* (default: `true`): Prepend the current timestamp before each log message. If disabled the date and time (i.e. `2025/11/02 11:57:29 `) are not prepended to each log message.
- *stderr* (default: `true`): Print log messages to stderr.
- *file* (default: `""`): File path to log (appending; creates if does not exist) messages to. Without a path no log file will be created and logged to. Can be used in parallel with the *stderr* option.
---
> [!tip]
> The following list shows some tips on how to use logging more effectively.
> These tips do not apply just to `zlog` (and not even only to zig code).
- Use `errdefer` to directly print messages on failures in the same function they occur:
```zig
// assume log is already defined before (with the corresponding scope)
const port = port: {
errdefer |err| log.err("failed to read the port number: {}", .{err});
var buf: [fmt.count("{}\n", .{maxInt(u16)})]u8 = undefined;
const len = try process.stdout.?.readAll(&buf);
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` 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`. When using the *file* build option the *ansi* control characters are not omitted when logging to the file.