Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v5
- uses: mlugg/setup-zig@v2
with:
version: 0.15.1
version: master
- run: zig build test --summary all
- run: zig build run
working-directory: example
Expand All @@ -32,5 +32,5 @@ jobs:
- uses: actions/checkout@v5
- uses: mlugg/setup-zig@v2
with:
version: 0.15.1
version: master
- run: zig fmt --check .
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/configure-pages@v5
- uses: mlugg/setup-zig@v2
with:
version: 0.15.1
version: master
- run: zig build docs
- uses: actions/upload-pages-artifact@v4
id: deployment
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.zig-cache/
zig-out/
zig-pkg/
.direnv/

example/log.txt
Expand Down
8 changes: 4 additions & 4 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.{
.name = .axe,
.version = "0.7.0",
.version = "0.8.0",
.fingerprint = 0x6c6a1e2ce5546233,
.minimum_zig_version = "0.15.1",
.minimum_zig_version = "0.16.0",
.dependencies = .{
.zeit = .{
.url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88",
.hash = "zeit-0.6.0-5I6bk0J9AgCVa0nnyL0lNY9Xa9F68hHq-ZarhuXNV-Jb",
.url = "git+https://github.com/rockorager/zeit?ref=0.16#fdaad9c5884f41a3640c768f4f9fc116e763c321",
.hash = "zeit-0.6.0-5I6bk3K6AgCFnc_9CN7KJjaeNJz1VuwP4DA7Gs9vgxuz",
},
},
.paths = .{
Expand Down
39 changes: 18 additions & 21 deletions example/example.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ const std = @import("std");
const axe = @import("axe");

const std_log = axe.Axe(.{
// .progress_stderr uses std.Progress.[un]lockStdErr.
// This specific mutex is recommended for a global stderr logger.
.mutex = .{ .function = .progress_stderr },
.mutex = .default,
});

pub const std_options: std.Options = .{
Expand All @@ -14,12 +12,11 @@ pub const std_options: std.Options = .{
var std_log_fba_buffer: [4 * 4096]u8 = undefined;
var std_log_fba: std.heap.FixedBufferAllocator = .init(&std_log_fba_buffer);

pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var env = try std.process.getEnvMap(allocator);
defer env.deinit();
pub fn main(init: std.process.Init) !void {
return juicyMain(init.gpa, init.io, init.environ_map);
}

pub fn juicyMain(allocator: std.mem.Allocator, io: std.Io, env: *std.process.Environ.Map) !void {
var buffer: [256]u8 = undefined;

{
Expand All @@ -40,8 +37,8 @@ pub fn main() !void {
.quiet = false, // disable stderr logging, default is false
.mutex = .none, // none by default
});
var writer = std.fs.File.stdout().writer(&buffer);
try stdout_log.init(allocator, &.{&writer.interface}, &env);
var writer = std.Io.File.stdout().writer(io, &buffer);
try stdout_log.init(allocator, io, &.{&writer.interface}, env);
defer stdout_log.deinit(allocator);

// wait we actually don't want stderr logging let's disable it
Expand All @@ -57,7 +54,7 @@ pub fn main() !void {
// writers, it should be called at the very start of the program.
// std.log supports all the features of axe.Axe even additional writers,
// time or custom mutex.
try std_log.init(std_log_fba.allocator(), null, &env);
try std_log.init(std_log_fba.allocator(), io, null, env);
// defer std_log.deinit(allocator);

std.log.info("std.log.info with axe.Axe(.{{}})", .{});
Expand All @@ -70,8 +67,8 @@ pub fn main() !void {

{
// Custom writers:
var f = try std.fs.cwd().createFile("log.txt", .{});
defer f.close();
var f = try std.Io.Dir.cwd().createFile(io, "log.txt", .{});
defer f.close(io);
const log = axe.Axe(.{
.format = "%t %l%s%L %m\n",
.scope_format = "@%",
Expand All @@ -87,10 +84,10 @@ pub fn main() !void {
.info = "INFO",
.debug = "DEBUG",
},
.mutex = .default, // default to std.Thread.Mutex
.mutex = .default,
});
var writer = f.writer(&buffer);
try log.init(allocator, &.{&writer.interface}, &env);
var writer = f.writer(io, &buffer);
try log.init(allocator, io, &.{&writer.interface}, env);
defer log.deinit(allocator);

log.debug("Hello! This will have no color if NO_COLOR is defined or if piped", .{});
Expand All @@ -101,8 +98,8 @@ pub fn main() !void {

{
// JSON log:
var json_file = try std.fs.cwd().createFile("log.json", .{});
defer json_file.close();
var json_file = try std.Io.Dir.cwd().createFile(io, "log.json", .{});
defer json_file.close(io);
const json_log = axe.Axe(.{
.format =
\\{"level":"%l",%s"time":"%t","data":%m}
Expand All @@ -114,8 +111,8 @@ pub fn main() !void {
.time_format = .{ .gofmt = .rfc3339 }, // .rfc3339 is a preset but custom format is also possible
.color = .never,
});
var writer = json_file.writer(&buffer);
try json_log.init(allocator, &.{&writer.interface}, &env);
var writer = json_file.writer(io, &buffer);
try json_log.init(allocator, io, &.{&writer.interface}, env);
defer json_log.deinit(allocator);

json_log.debug("\"json log\"", .{});
Expand Down
70 changes: 36 additions & 34 deletions src/axe.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const std = @import("std");
const builtin = @import("builtin");
const zeit = @import("zeit");
const windows = std.os.windows;
const tty = std.Io.tty;
const tty = std.Io.Terminal;

pub const Config = struct {
/// The format to use for the log messages.
Expand Down Expand Up @@ -70,9 +70,10 @@ pub fn Axe(comptime config: Config) type {
var stderr_tty_config = defaultTtyConfig(config.color);
var writers_tty_config = defaultTtyConfig(config.color);
var timezone = if (config.time_format != .disabled) zeit.utc else {};
var io: std.Io = std.Io.Threaded.global_single_threaded.io();
var mutex = switch (config.mutex) {
.none, .function => {},
.default => if (builtin.single_threaded) {} else std.Thread.Mutex{},
.default => if (builtin.single_threaded) {} else std.Io.Mutex.init,
.custom => |T| T{},
};

Expand All @@ -85,8 +86,9 @@ pub fn Axe(comptime config: Config) type {
/// `env` is only used during initialization and is not stored.
pub fn init(
allocator: std.mem.Allocator,
_io: ?std.Io,
additional_writers: ?[]const *std.Io.Writer,
env: ?*const std.process.EnvMap,
env: ?*const std.process.Environ.Map,
) !void {
// Validate strftime format
// This used to be comptime but sadly it's not possible since zig
Expand All @@ -96,9 +98,13 @@ pub fn Axe(comptime config: Config) type {
var void_writer: std.Io.Writer.Discarding = .init(&.{});
try bogus.strftime(&void_writer.writer, config.time_format.strftime);
}

if (_io) |new_io|
io = new_io;
if (config.time_format != .disabled) {
timezone = try zeit.local(allocator, env);
timezone = try zeit.local(allocator, io, .{
.tz = if (env) |e| e.get("TZ") else null,
.tzdir = if (env) |e| e.get("TZDIR") else null,
});
}
if (additional_writers) |_writers| {
writers = try allocator.dupe(*std.Io.Writer, _writers);
Expand All @@ -125,11 +131,11 @@ pub fn Axe(comptime config: Config) type {
pub fn updateTtyConfig(color: Color) void {
writers_tty_config = defaultTtyConfig(color);
stderr_tty_config = switch (color) {
.auto => .detect(std.fs.File.stderr()),
.auto => tty.Mode.detect(io, std.Io.File.stderr(), false, false) catch .no_color,
.always => if (builtin.os.tag == .windows)
switch (tty.Config.detect(std.fs.File.stderr())) {
switch (tty.Mode.detect(io, std.Io.File.stderr(), false, false) catch .no_color) {
.no_color, .escape_codes => .escape_codes,
.windows_api => |ctx| .{ .windows_api = ctx },
.windows_api => |wa| .{ .windows_api = wa },
}
else
.escape_codes,
Expand All @@ -138,7 +144,7 @@ pub fn Axe(comptime config: Config) type {
}

/// Returns a scoped logging namespace that logs all messages using the scope provided.
pub fn scoped(comptime scope: @Type(.enum_literal)) type {
pub fn scoped(comptime scope: @EnumLiteral()) type {
return struct {
/// Log an error message. This log level is intended to be used
/// when something has gone wrong. This might be recoverable or might
Expand Down Expand Up @@ -246,7 +252,7 @@ pub fn Axe(comptime config: Config) type {
/// ```
pub fn log(
comptime level: Level,
comptime scope: @Type(.enum_literal),
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
Expand All @@ -256,7 +262,7 @@ pub fn Axe(comptime config: Config) type {
fn logAt(
comptime src: ?std.builtin.SourceLocation, // should this be comptime?
comptime level: Level,
comptime scope: @Type(.enum_literal),
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
Expand All @@ -267,18 +273,18 @@ pub fn Axe(comptime config: Config) type {
switch (config.mutex) {
.none => {},
.function => |f| f.lock(),
.default => if (!builtin.single_threaded) mutex.lock(),
.default => if (!builtin.single_threaded) mutex.lockUncancelable(io),
.custom => mutex.lock(),
}
defer switch (config.mutex) {
.none => {},
.function => |f| f.unlock(),
.default => if (!builtin.single_threaded) mutex.unlock(),
.default => if (!builtin.single_threaded) mutex.unlock(io),
.custom => mutex.unlock(),
};

const time = if (config.time_format != .disabled) t: {
const now = zeit.instant(.{ .timezone = &timezone }) catch unreachable;
const now = zeit.instant(io, .{ .timezone = &timezone }) catch unreachable;
break :t now.time();
} else {};

Expand All @@ -289,7 +295,7 @@ pub fn Axe(comptime config: Config) type {
}
if (!quiet) {
var buffer: [256]u8 = undefined;
var stderr = std.fs.File.stderr().writer(&buffer);
var stderr = std.Io.File.stderr().writer(io, &buffer);
print(src, &stderr.interface, stderr_tty_config, time, level, scope, format, args);
stderr.interface.flush() catch {};
}
Expand All @@ -299,10 +305,10 @@ pub fn Axe(comptime config: Config) type {
fn print(
comptime src: ?std.builtin.SourceLocation,
writer: *std.Io.Writer,
tty_config: tty.Config,
tty_config: tty.Mode,
time: if (config.time_format != .disabled) zeit.Time else void,
comptime level: Level,
comptime scope: @Type(.enum_literal),
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
Expand Down Expand Up @@ -543,8 +549,8 @@ pub const Style = union(enum) {
}
}

fn applyWindows(style: Style, ctx: tty.Config.WindowsContext) void {
const attributes = switch (style) {
fn applyWindows(style: Style, wa: tty.Mode.WindowsApi) void {
const attributes: windows.WORD = switch (style) {
.black => 0,
.red => windows.FOREGROUND_RED,
.green => windows.FOREGROUND_GREEN,
Expand All @@ -563,10 +569,11 @@ pub const Style = union(enum) {
.bright_white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
// "dim" is not supported using basic character attributes, but let's still make it do *something*.
.dim => windows.FOREGROUND_INTENSITY,
.reset => ctx.reset_attributes,
.reset => wa.reset_attributes,
else => return,
};
windows.SetConsoleTextAttribute(ctx.handle, attributes) catch unreachable;
var op = windows.CONSOLE.USER_IO.SET_TEXT_ATTRIBUTE(attributes);
_ = op.operate(wa.io, wa.file) catch {};
}
};

Expand Down Expand Up @@ -643,16 +650,11 @@ pub const GoTimeFormat = struct {
pub const FunctionMutex = struct {
lock: fn () void,
unlock: fn () void,

pub const progress_stderr: FunctionMutex = .{
.lock = std.Progress.lockStdErr,
.unlock = std.Progress.unlockStdErr,
};
};

inline fn callWithStyles(
writer: *std.Io.Writer,
tty_config: tty.Config,
tty_config: tty.Mode,
comptime styles: []const Style,
comptime callback: anytype,
args: anytype,
Expand All @@ -675,7 +677,7 @@ inline fn callWithStyles(
}
}

fn parseScopeFormat(comptime format: []const u8, comptime scope: @Type(.enum_literal)) []const u8 {
fn parseScopeFormat(comptime format: []const u8, comptime scope: @EnumLiteral()) []const u8 {
comptime {
var text: []const u8 = "";
var i: usize = 0;
Expand Down Expand Up @@ -730,7 +732,7 @@ fn writeLocation(
}
}

inline fn defaultTtyConfig(color: Color) tty.Config {
inline fn defaultTtyConfig(color: Color) tty.Mode {
return switch (color) {
.always => .escape_codes,
.auto, .never => .no_color,
Expand All @@ -746,7 +748,7 @@ test "log without styles" {
.styles = .none,
.quiet = true,
});
try log.init(std.testing.allocator, &.{&writer}, null);
try log.init(std.testing.allocator, std.testing.io, &.{&writer}, null);
defer log.deinit(std.testing.allocator);

log.info("Hello, {s}!", .{"world"});
Expand Down Expand Up @@ -787,7 +789,7 @@ test "log with complex config" {
.quiet = true,
.mutex = .default,
});
try log.init(std.testing.allocator, &.{&writer}, null);
try log.init(std.testing.allocator, std.testing.io, &.{&writer}, null);
defer log.deinit(std.testing.allocator);

log.info("Hello, {s}!", .{"world"});
Expand Down Expand Up @@ -824,7 +826,7 @@ test "time format" {
},
});
log.quiet = true;
try log.init(std.testing.allocator, &.{&dest.writer}, null);
try log.init(std.testing.allocator, std.testing.io, &.{&dest.writer}, null);
defer log.deinit(std.testing.allocator);

log.info("Hello {c}", .{'W'});
Expand Down Expand Up @@ -856,7 +858,7 @@ test "json log" {
.quiet = true,
.color = .never,
});
try log.init(std.testing.allocator, &.{&writer}, null);
try log.init(std.testing.allocator, std.testing.io, &.{&writer}, null);
defer log.deinit(std.testing.allocator);

log.debug("\"json log\"", .{});
Expand Down Expand Up @@ -890,7 +892,7 @@ test "updateTtyConfig" {
.color = .never,
.quiet = true,
});
try log.init(std.testing.allocator, &.{&writer}, null);
try log.init(std.testing.allocator, std.testing.io, &.{&writer}, null);
defer log.deinit(std.testing.allocator);

log.debug("No color", .{});
Expand Down
Loading