aboutsummaryrefslogtreecommitdiff
path: root/lib/std/log.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-12-09 22:10:12 -0800
committerAndrew Kelley <andrew@ziglang.org>2025-12-23 22:15:09 -0800
commitffcbd48a1220ce6d652ee762001d88baa385de49 (patch)
treee1ea279b4cd0b8991df04d8a799589f72dbffd86 /lib/std/log.zig
parent78d262d96ee6200c7a6bc0a41fe536d263c24d92 (diff)
downloadzig-ffcbd48a1220ce6d652ee762001d88baa385de49.tar.gz
zig-ffcbd48a1220ce6d652ee762001d88baa385de49.zip
std: rework TTY detection and printing
This commit sketches an idea for how to deal with detection of file streams as being terminals. When a File stream is a terminal, writes through the stream should have their escapes stripped unless the programmer explicitly enables terminal escapes. Furthermore, the programmer needs a convenient API for intentionally outputting escapes into the stream. In particular it should be possible to set colors that are silently discarded when the stream is not a terminal. This commit makes `Io.File.Writer` track the terminal mode in the already-existing `mode` field, making it the appropriate place to implement escape stripping. `Io.lockStderrWriter` returns a `*Io.File.Writer` with terminal detection already done by default. This is a higher-level application layer stream for writing to stderr. Meanwhile, `std.debug.lockStderrWriter` also returns a `*Io.File.Writer` but a lower-level one that is hard-coded to use a static single-threaded `std.Io.Threaded` instance. This is the same instance that is used for collecting debug information and iterating the unwind info.
Diffstat (limited to 'lib/std/log.zig')
-rw-r--r--lib/std/log.zig64
1 files changed, 45 insertions, 19 deletions
diff --git a/lib/std/log.zig b/lib/std/log.zig
index 461dfca36e..029f724f44 100644
--- a/lib/std/log.zig
+++ b/lib/std/log.zig
@@ -15,7 +15,7 @@
//!
//! 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:
+//! supported. Its output looks like this:
//! ```
//! error: this is an error
//! error(scope): this is an error with a non-default scope
@@ -80,8 +80,6 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool {
return @intFromEnum(level) <= @intFromEnum(std.options.log_level);
}
-var static_threaded_io: std.Io.Threaded = .init_single_threaded;
-
/// The default implementation for the log function. Custom log functions may
/// forward log messages to this function.
///
@@ -93,36 +91,64 @@ pub fn defaultLog(
comptime format: []const u8,
args: anytype,
) void {
- return defaultLogIo(level, scope, format, args, static_threaded_io.io());
+ var buffer: [64]u8 = undefined;
+ const stderr = std.debug.lockStderrWriter(&buffer);
+ defer std.debug.unlockStderrWriter();
+ return defaultLogFileWriter(level, scope, format, args, stderr);
}
-pub fn defaultLogIo(
+pub fn defaultLogFileWriter(
comptime level: Level,
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
- io: std.Io,
+ fw: *std.Io.File.Writer,
) void {
- var buffer: [64]u8 = undefined;
- const stderr, const ttyconf = io.lockStderrWriter(&buffer);
- defer io.unlockStderrWriter();
- ttyconf.setColor(stderr, switch (level) {
+ fw.setColor(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 {};
+ fw.setColor(.bold) catch {};
+ fw.interface.writeAll(level.asText()) catch return;
+ fw.setColor(.reset) catch {};
+ fw.setColor(.dim) catch {};
+ fw.setColor(.bold) catch {};
if (scope != .default) {
- stderr.print("({s})", .{@tagName(scope)}) catch return;
+ fw.interface.print("({s})", .{@tagName(scope)}) catch return;
+ }
+ fw.interface.writeAll(": ") catch return;
+ fw.setColor(.reset) catch {};
+ fw.interface.print(format ++ "\n", decorateArgs(args, fw.mode)) catch return;
+}
+
+fn DecorateArgs(comptime Args: type) type {
+ const fields = @typeInfo(Args).@"struct".fields;
+ var new_fields: [fields.len]type = undefined;
+ for (fields, &new_fields) |old, *new| {
+ if (old.type == std.debug.FormatStackTrace) {
+ new.* = std.debug.FormatStackTrace.Decorated;
+ } else {
+ new.* = old.type;
+ }
+ }
+ return @Tuple(&new_fields);
+}
+
+fn decorateArgs(args: anytype, file_writer_mode: std.Io.File.Writer.Mode) DecorateArgs(@TypeOf(args)) {
+ var new_args: DecorateArgs(@TypeOf(args)) = undefined;
+ inline for (args, &new_args) |old, *new| {
+ if (@TypeOf(old) == std.debug.FormatStackTrace) {
+ new.* = .{
+ .stack_trace = old.stack_trace,
+ .file_writer_mode = file_writer_mode,
+ };
+ } else {
+ new.* = old;
+ }
}
- stderr.writeAll(": ") catch return;
- ttyconf.setColor(stderr, .reset) catch {};
- stderr.print(format ++ "\n", args) catch return;
+ return new_args;
}
/// Returns a scoped logging namespace that logs all messages using the scope