zig/lib/std/log.zig
Matthew Lugg 9215121688
std.log: colorize output in default implementation
Also remove the example implementation from the file doc comment; it's
better to just link to `defaultLog` as an example, since this avoids
writing the example implementation twice and prevents the example from
bitrotting.
2025-10-30 09:31:30 +00:00

183 lines
6.5 KiB
Zig

//! std.log is a standardized interface for logging which allows for the logging
//! of programs and libraries using this interface to be formatted and filtered
//! by the implementer of the `std.options.logFn` function.
//!
//! Each log message has an associated scope enum, which can be used to give
//! context to the logging. The logging functions in std.log implicitly use a
//! scope of .default.
//!
//! A logging namespace using a custom scope can be created using the
//! std.log.scoped function, passing the scope as an argument; the logging
//! functions in the resulting struct use the provided scope parameter.
//! For example, a library called 'libfoo' might use
//! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its
//! log messages.
//!
//! For an example implementation of the `logFn` function, see `defaultLog`,
//! which is the default implementation. It outputs to stderr, using color if
//! the detected `std.Io.tty.Config` supports it. Its output looks like this:
//! ```
//! error: this is an error
//! error(scope): this is an error with a non-default scope
//! warning: this is a warning
//! info: this is an informative message
//! debug: this is a debugging message
//! ```
const std = @import("std.zig");
const builtin = @import("builtin");
pub const Level = enum {
/// Error: something has gone wrong. This might be recoverable or might
/// be followed by the program exiting.
err,
/// Warning: it is uncertain if something has gone wrong or not, but the
/// circumstances would be worth investigating.
warn,
/// Info: general messages about the state of the program.
info,
/// Debug: messages only useful for debugging.
debug,
/// Returns a string literal of the given level in full text form.
pub fn asText(comptime self: Level) []const u8 {
return switch (self) {
.err => "error",
.warn => "warning",
.info => "info",
.debug => "debug",
};
}
};
/// The default log level is based on build mode.
pub const default_level: Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info,
};
pub const ScopeLevel = struct {
scope: @Type(.enum_literal),
level: Level,
};
fn log(
comptime level: Level,
comptime scope: @Type(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
if (comptime !logEnabled(level, scope)) return;
std.options.logFn(level, scope, format, args);
}
/// Determine if a specific log message level and scope combination are enabled for logging.
pub fn logEnabled(comptime level: Level, comptime scope: @Type(.enum_literal)) bool {
inline for (std.options.log_scope_levels) |scope_level| {
if (scope_level.scope == scope) return @intFromEnum(level) <= @intFromEnum(scope_level.level);
}
return @intFromEnum(level) <= @intFromEnum(std.options.log_level);
}
/// The default implementation for the log function. Custom log functions may
/// forward log messages to this function.
///
/// Uses a 64-byte buffer for formatted printing which is flushed before this
/// function returns.
pub fn defaultLog(
comptime level: Level,
comptime scope: @Type(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
var buffer: [64]u8 = undefined;
const stderr, const ttyconf = std.debug.lockStderrWriter(&buffer);
defer std.debug.unlockStderrWriter();
ttyconf.setColor(stderr, switch (level) {
.err => .red,
.warn => .yellow,
.info => .green,
.debug => .magenta,
}) catch {};
ttyconf.setColor(stderr, .bold) catch {};
stderr.writeAll(level.asText()) catch return;
ttyconf.setColor(stderr, .reset) catch {};
ttyconf.setColor(stderr, .dim) catch {};
ttyconf.setColor(stderr, .bold) catch {};
if (scope != .default) {
stderr.print("({s})", .{@tagName(scope)}) catch return;
}
stderr.writeAll(": ") catch return;
ttyconf.setColor(stderr, .reset) catch {};
stderr.print(format ++ "\n", args) catch return;
}
/// Returns a scoped logging namespace that logs all messages using the scope
/// provided here.
pub fn scoped(comptime scope: @Type(.enum_literal)) 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
/// be followed by the program exiting.
pub fn err(
comptime format: []const u8,
args: anytype,
) void {
@branchHint(.cold);
log(.err, scope, format, args);
}
/// Log a warning message. This log level is intended to be used if
/// it is uncertain whether something has gone wrong or not, but the
/// circumstances would be worth investigating.
pub fn warn(
comptime format: []const u8,
args: anytype,
) void {
log(.warn, scope, format, args);
}
/// Log an info message. This log level is intended to be used for
/// general messages about the state of the program.
pub fn info(
comptime format: []const u8,
args: anytype,
) void {
log(.info, scope, format, args);
}
/// Log a debug message. This log level is intended to be used for
/// messages which are only useful for debugging.
pub fn debug(
comptime format: []const u8,
args: anytype,
) void {
log(.debug, scope, format, args);
}
};
}
pub const default_log_scope = .default;
/// The default scoped logging namespace.
pub const default = scoped(default_log_scope);
/// Log an error message using the default scope. This log level is intended to
/// be used when something has gone wrong. This might be recoverable or might
/// be followed by the program exiting.
pub const err = default.err;
/// Log a warning message using the default scope. This log level is intended
/// to be used if it is uncertain whether something has gone wrong or not, but
/// the circumstances would be worth investigating.
pub const warn = default.warn;
/// Log an info message using the default scope. This log level is intended to
/// be used for general messages about the state of the program.
pub const info = default.info;
/// Log a debug message using the default scope. This log level is intended to
/// be used for messages which are only useful for debugging.
pub const debug = default.debug;