diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-09 22:10:12 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:09 -0800 |
| commit | ffcbd48a1220ce6d652ee762001d88baa385de49 (patch) | |
| tree | e1ea279b4cd0b8991df04d8a799589f72dbffd86 /lib/std/log.zig | |
| parent | 78d262d96ee6200c7a6bc0a41fe536d263c24d92 (diff) | |
| download | zig-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.zig | 64 |
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 |
