Compare commits

..

48 Commits

Author SHA1 Message Date
b3c8a3ab1c fix: correct ordering of commands; append file logs into corresponding provided file build parameter
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 56s
2026-01-17 21:01:32 +01:00
29c6e48d86 doc: update documentation to no longer mention the warning about the zig master version
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 54s
2026-01-17 11:00:07 +01:00
40ced30a57 mod: adjust to latest release of zig for file writing option
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 55s
2026-01-17 10:55:09 +01:00
57631f1905 mod: pin implementation to latest release version of zig
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m30s
2026-01-17 10:51:29 +01:00
b868bb1300 fix: correct namespace
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m1s
2026-01-06 23:07:41 +01:00
4147c47f7e fix: add missing Io parameter for different configuration option
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m0s
2026-01-06 23:05:51 +01:00
e813a3e195 mod: bump zig master version
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m8s
2026-01-06 22:04:34 +01:00
6f62c61897 doc: correct code snippet for build.zig's inclusion of zlog
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 1m5s
2025-11-25 19:34:21 +01:00
f065c08e91 mod: bump action dependencies
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m53s
2025-11-05 22:44:25 +01:00
f43034cea9 fix(lint): correct typos
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m1s
2025-11-02 13:17:52 +01:00
69da9265b8 mod: rem zlog dependency; stream line logging structure; do not write ansi control characters when logging to file
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 1m21s
2025-11-02 13:09:34 +01:00
bd33f9c8f9 doc: update README; update comments regarding open tasks and further features
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 54s
2025-11-01 00:38:34 +01:00
a211aafed6 doc: remove unnecessary comment
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 54s
2025-11-01 00:29:41 +01:00
411a6dc358 mod: support appending through posix file handle
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 1m21s
2025-11-01 00:01:50 +01:00
cd99e5b4d3 doc: correct alert blocks 2025-10-26 21:52:15 +01:00
ab0898f9c4 chor: update zig version
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 55s
2025-09-29 23:28:53 +02:00
4e9fddc593 chor: update workflow version for zig compiler
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m1s
2025-08-04 21:03:06 +02:00
b43361da73 mod: rename file to be more agnostic to zig language styling 2025-08-04 21:02:46 +02:00
4714ec2bf9 chor: update zig version 2025-08-04 21:02:46 +02:00
a209fd6df5 chor: update ztime dependency 2025-08-04 21:02:45 +02:00
5b1a987a5b fix(workflow): correct version for zig installation
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 56s
2025-08-02 10:17:15 +02:00
eb2ea38e34 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
2025-07-17 21:55:22 +02:00
d13fcce836 mod: update dependency ztime
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 26s
2025-03-12 20:31:31 +01:00
c1108f3f3b fix: correct build.zig.zog contents with zig version update
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 48s
2025-03-12 20:27:00 +01:00
22bff91df9 doc: tip for looking through log files generated by zlog
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 31s
2025-03-03 16:33:24 +01:00
a21d84cfcf feat(zlog): colored level text and styled timestamps and scopes
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m27s
2025-02-24 14:01:49 +01:00
dd98e63bcb doc: update README
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 56s
2025-02-21 23:22:14 +01:00
04795091a8 mod: handle errors as @panic calls instead of silently ignoring them
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m25s
Import order updated as well as `inline` inner private functions
2025-02-19 14:39:03 +01:00
79f45c4cfd mod(dep): update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 57s
2025-01-23 23:57:58 +01:00
6f784817f2 mod(dep): update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 56s
2025-01-23 23:35:33 +01:00
e1473cde04 doc: remove unnecessary anyerror keyword
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 2m28s
Error union return type for format function documentation (and sample
implementation) has been adjusted. This results in the exact same code,
but without unnecessary error type definition, which is derived from the
compiler anyway.
2025-01-15 10:41:03 +01:00
2969c4354f mod: update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 57s
2025-01-02 18:12:38 +01:00
db10d51429 mod: update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 57s
2024-12-30 18:02:28 +01:00
b5641960e8 mod: update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 57s
2024-12-30 17:46:38 +01:00
cbc1da831a mod: update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 56s
2024-12-29 21:36:27 +01:00
5a7c41254b mod: update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 56s
2024-12-29 21:31:18 +01:00
f33ff72690 mod(dep): update ztime dependency
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 58s
2024-12-27 20:10:27 +01:00
ba84450417 mod: update dependency ztime
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 59s
2024-12-27 19:06:24 +01:00
df515b8cad mod: remove dependency to c std library and replace with ztime dependency for a pure zig implementation
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 57s
2024-11-30 14:45:57 +01:00
06752299be build: check step
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 53s
2024-11-19 22:01:08 +01:00
dda2199706 bump to zig 0.14.dev
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 53s
2024-11-16 21:06:42 +01:00
ace97e4bfc correct name in LICENSE
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 32s
2024-11-13 19:17:37 +01:00
87cd904c70 fix(timestamp): log without undefined values (0xAA)
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 42s
2024-11-04 22:32:59 +01:00
73991389f6 mod(action): update zig workflow from template
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 29s
2024-10-06 12:02:31 +02:00
f93e27fc8d doc: correct spell checking error
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 44s
2024-10-04 20:55:50 +02:00
145d54f09f mod(action): update action to zig template
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 28s
2024-10-04 20:52:17 +02:00
7889a6ec7a mod(options): build options to log to stderr and/or a file
All checks were successful
Run Tests / test (push) Successful in 43s
Run Tests / lint (push) Successful in 39s
NOTE: file logging will not work correctly yet due to opening files
appending using the std is currently not implemented (as of zig version
0.13.0)!
2024-08-29 16:36:58 +02:00
b03c770b59 doc: add tips section in README 2024-08-29 14:59:45 +02:00
11 changed files with 456 additions and 171 deletions

View File

@@ -0,0 +1,33 @@
name: Release Zig Application
on:
release:
types: [published]
workflow_run:
workflows: [Zig Project Action]
types: [completed]
jobs:
release:
name: Release zig project
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup zig installation
uses: mlugg/setup-zig@v2
with:
version: latest
- name: Lint check
run: zig fmt --check .
- name: Spell checking
uses: crate-ci/typos@v1.39.0
with:
config: ./.typos-config
- name: Run tests
run: zig build --release=fast
- name: Release build artifacts
uses: akkuman/gitea-release-action@v1.3.2
with:
files: |-
./zig-out/bin/**

View File

@@ -1,4 +1,4 @@
name: Run Tests
name: Zig Project Action
on:
push:
branches: [main]
@@ -6,21 +6,22 @@ on:
types: [opened, synchronize, reopened]
jobs:
test:
run:
name: Lint, Spell-check and test zig project
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v1
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup zig installation
uses: mlugg/setup-zig@v2
with:
version: 0.13.0
- run: zig build test
lint:
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v1
version: latest
- name: Lint check
run: zig fmt --check .
- name: Spell checking
uses: crate-ci/typos@v1.39.0
with:
version: 0.13.0
- run: zig fmt --check .
config: ./.typos-config
- name: Run tests
run: zig build test

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.zig-cache/
zig-out/
log

5
.typos-config Normal file
View File

@@ -0,0 +1,5 @@
[files]
extend-exclude = []
[default.extend-words]
WRONLY = "WRONLY"

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 yves-biener
Copyright (c) 2024 Yves Biener
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

128
README.md
View File

@@ -1,17 +1,53 @@
# 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.
## 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 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,
@@ -19,7 +55,7 @@ const Options = enum {
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) anyerror!void {
pub fn format(value: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
try zlog.pretty_format(value, fmt, options, writer);
}
};
@@ -29,63 +65,91 @@ 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) anyerror!void {
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
// 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
// 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:
```
[2024-08-28 22:22] debug(main): Debug message 42
[2024-08-28 22:22] info(main): Info message { 1, 2, 3, 4 }
[2024-08-28 22:22] info(main): Info message "This is a test"
[2024-08-28 22:22] warning(main): Warning message main.Options = enum {
a,
b,
c,
} = a
[2024-08-28 22:22] 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/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.
- *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.

View File

@@ -4,11 +4,15 @@ const std = @import("std");
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// build options to customize the log message formating
// build options to customize the log message formatting
const include_timestamp = b.option(bool, "timestamp", "Enable inclusion of timestamps in log messages (default: true)") orelse true;
const stderr = b.option(bool, "stderr", "Print all log messages to stderr (default: true)") orelse true;
const file = b.option([]const u8, "file", "Print all log messages to the provided file.") orelse "";
const options = b.addOptions();
options.addOption(bool, "timestamp", include_timestamp);
options.addOption(bool, "stderr", stderr);
options.addOption([]const u8, "file", file);
const options_module = options.createModule();
@@ -26,20 +30,25 @@ pub fn build(b: *std.Build) void {
const zlog_module = b.addModule("zlog", .{
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/zlog.zig"),
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.link_libc = include_timestamp, // uses c std library <time.h>
.imports = &.{
.{ .name = "build_options", .module = options_module },
},
});
zlog_module.addImport("build_options", options_module);
const exe = b.addExecutable(.{
.name = "zlog",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "zlog", .module = zlog_module },
},
}),
});
exe.root_module.addImport("zlog", zlog_module);
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
@@ -72,9 +81,14 @@ pub fn build(b: *std.Build) void {
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/zlog.zig"),
.root_module = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "build_options", .module = options_module },
},
}),
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

View File

@@ -1,9 +1,10 @@
.{
.name = "zlog",
// version name should match the zig version except for the last number,
// which stands for the version inside a given zig version
.version = "0.13.0",
.minimum_zig_version = "0.13.0",
.name = .zlog,
// This is a [Semantic Version](https://semver.org/).
.version = "0.16.0",
.fingerprint = 0x55b82e3347a594e8,
.minimum_zig_version = "0.16.0-dev.463+f624191f9",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",

View File

@@ -1,7 +1,24 @@
const std = @import("std");
const zlog = @import("zlog");
const Union = union {
int: i32,
boolean: bool,
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,
@@ -9,7 +26,7 @@ const Options = enum {
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) anyerror!void {
pub fn format(value: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
try zlog.pretty_format(value, fmt, options, writer);
}
};
@@ -19,32 +36,49 @@ 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) anyerror!void {
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
// 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
// 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");

228
src/root.zig Normal file
View File

@@ -0,0 +1,228 @@
/// *zlog* defaultLog function replacement, which adjusts the surrounding contents of every `std.log` message.
fn logFn(
comptime message_level: log.Level,
comptime scope: @Type(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
var buf: [128]u8 = undefined;
if (comptime build_options.file.len > 0) {
const prefix = if (scope == .default) " " else "(" ++ @tagName(scope) ++ ") ";
const level_txt = switch (comptime message_level) {
.err => "[error]",
.warn => "[warning]",
.info => "[info]",
.debug => "[debug]",
};
// TODO let user configure the format he wants to use for logging and use a pretty good default one?
const complete_format = level_txt ++ prefix ++ format ++ "\n";
// TODO use common logging format, such that in integrates well with other logging frameworks
// (i.e. golang's logger, log4j, etc.)
const fd = std.posix.open(build_options.file, .{
.CREAT = true,
.APPEND = true,
.ACCMODE = .WRONLY,
}, 0o600) catch @panic("Could not append to log file");
defer std.posix.close(fd);
var file: fs.File = .{ .handle = fd };
var buffer = file.writer(&buf);
var writer = &buffer.interface;
defer writer.flush() catch unreachable;
log_writing(writer, complete_format, false, args);
}
if (comptime build_options.stderr) {
const prefix = if (scope == .default) " " else "(\x1b[2m" ++ @tagName(scope) ++ "\x1b[0m) ";
const level_txt = switch (comptime message_level) {
.err => "[\x1b[38;5;9merror\x1b[0m]",
.warn => "[\x1b[38;5;11mwarning\x1b[0m]",
.info => "[\x1b[38;5;10minfo\x1b[0m]",
.debug => "[\x1b[38;5;12mdebug\x1b[0m]",
};
const complete_format = level_txt ++ prefix ++ format ++ "\n";
std.debug.lockStdErr();
defer std.debug.unlockStdErr();
var buffer = fs.File.stderr().writer(&buf);
var writer = &buffer.interface;
defer writer.flush() catch unreachable;
log_writing(writer, complete_format, true, args);
}
}
inline fn log_writing(writer: *std.Io.Writer, comptime format: []const u8, comptime ansi: bool, args: anytype) void {
nosuspend {
if (build_options.timestamp) log_timestamp(writer, ansi);
writer.print(format, args) catch return;
}
}
/// Prepend the current timestamp in the following format:
/// `<year>/<month>/<day> <hour>:<minute>:<sec> `
///
/// NOTE all information are displayed using numbers
inline fn log_timestamp(writer: anytype, comptime ansi: bool) void {
const time = std.posix.clock_gettime(.REALTIME) catch @panic("Cannot request timestamp");
const epoch_secs: std.time.epoch.EpochSeconds = .{ .secs = @intCast(time.sec) };
const day_secs = epoch_secs.getDaySeconds();
const year_and_day = epoch_secs.getEpochDay().calculateYearDay();
const month_and_day = year_and_day.calculateMonthDay();
writer.print(
if (comptime ansi)
"\x1b[1m{d}/{d:0>2}/{d:0>2} {d:0>2}:{d:0>2}:{d:0>2}\x1b[0m "
else
"{d}/{d:0>2}/{d:0>2} {d:0>2}:{d:0>2}:{d:0>2} ",
.{
year_and_day.year,
month_and_day.month.numeric(),
month_and_day.day_index + 1,
day_secs.getHoursIntoDay(),
day_secs.getMinutesIntoHour(),
day_secs.getSecondsIntoMinute(),
},
) catch return;
}
pub fn pretty_format(object: anytype, comptime format: []const u8, options: fmt.FormatOptions, writer: anytype) !void {
try inner_format(object, format, options, writer, 0);
}
fn inner_format(object: anytype, comptime format: []const u8, options: 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), format, 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));
},
.@"union" => |u| {
try writer.writeAll(@typeName(Object));
try writer.writeAll(" = union");
if (u.tag_type) |Tag| {
_ = Tag;
try writer.writeAll("(enum)");
}
try writer.writeAll(" {\n");
inline for (u.fields) |field| {
try writer.writeAll("\t" ** (depth + 1));
try writer.writeAll(field.name);
if (@typeInfo(field.type) != .void) {
try writer.writeAll(": ");
try writer.writeAll(@typeName(field.type));
}
try writer.writeAll(",\n");
}
try writer.writeAll("\t" ** depth);
try writer.writeAll("} = ");
if (u.tag_type) |Tag| {
try writer.writeAll(".{ .");
try writer.writeAll(@tagName(object));
try writer.writeAll(" = ");
inline for (u.fields) |u_field| if (object == @field(Tag, u_field.name)) {
try inner_format(@field(object, u_field.name), format, options, writer, depth + 1);
};
try writer.writeAll(" }");
} else {
// NOTE the value of a union (untagged) is displayed like this also in the standard library,
// not sure if you can reflect the used variant (and its value)
try fmt.format(writer, "@{x}", .{@intFromPtr(&object)});
}
},
.pointer => |p| switch (p.size) {
.slice => switch (@typeInfo(p.child)) {
.int => |num| {
if (num.signedness != .unsigned) {
try fmt.format(writer, "[]{s}: {any}", .{ @typeName(p.child), object });
} else {
switch (num.bits) {
8 => try fmt.format(writer, "\"{s}\"", .{object}),
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(p.child), object }),
}
}
},
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(p.child), object }),
},
.c => switch (@typeInfo(p.child)) {
.int => |num| {
if (num.signedness != .unsigned) {
try fmt.format(writer, "[*c]{s}: {any}", .{ @typeName(p.child), object });
} else {
switch (num.bits) {
8 => try fmt.format(writer, "\"{s}\"", .{object}),
else => try fmt.format(writer, "[*c]{s}: {any}", .{ @typeName(p.child), object }),
}
}
},
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(p.child), object }),
},
.many => try fmt.format(writer, "[*]{s}: {any}", .{ @typeName(p.child), object }),
.one => try fmt.format(writer, "[1]{s}: {any}", .{ @typeName(p.child), object }),
},
.array => |a| switch (@typeInfo(a.child)) {
.int => |num| {
if (num.signedness != .unsigned) {
try fmt.format(writer, "[]{s}: {any}", .{ @typeName(a.child), object });
} else {
switch (num.bits) {
8 => try fmt.format(writer, "\"{s}\"", .{object}),
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(a.child), object }),
}
}
},
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(a.child), object }),
},
.vector => |v| switch (@typeInfo(v.child)) {
.int => |num| {
if (num.signedness != .unsigned) {
try fmt.format(writer, "[]{s}: {any}", .{ @typeName(v.child), object });
} else {
switch (num.bits) {
8 => try fmt.format(writer, "\"{s}\"", .{object}),
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(v.child), object }),
}
}
},
else => try fmt.format(writer, "[]{s}: {any}", .{ @typeName(v.child), object }),
},
else => try fmt.format(writer, format, .{object}),
}
}
pub const std_options: std.Options = .{
.logFn = logFn,
};
const std = @import("std");
const fs = std.fs;
const log = std.log;
const fmt = std.fmt;
const build_options = @import("build_options");

View File

@@ -1,96 +0,0 @@
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});
},
}
}