Skip to content

Commit ee0b7f4

Browse files
authored
Make quiet & color options modifiable at runtime (#3)
* Make quiet option modifiable at runtime * Make color option modifiable at runtime
1 parent 9410e05 commit ee0b7f4

2 files changed

Lines changed: 83 additions & 32 deletions

File tree

example/example.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ pub fn main() !void {
3333
.err = "ErrOr",
3434
.debug = "DeBuG",
3535
},
36-
.quiet = true, // disable stderr logging, default is false
36+
.quiet = false, // disable stderr logging, default is false
3737
.mutex = .none, // none by default
3838
});
3939
var writer = std.fs.File.stdout().writer(&buffer);
4040
try stdout_log.init(allocator, &.{&writer.interface}, &env);
4141
defer stdout_log.deinit(allocator);
4242

43+
// wait we actually don't want stderr logging let's disable it
44+
stdout_log.quiet = true;
45+
4346
stdout_log.debug("Hello, stdout with no colors", .{});
4447
stdout_log.scoped(.main).err("scoped :)", .{});
4548
}
@@ -54,6 +57,10 @@ pub fn main() !void {
5457
defer std_log.deinit(allocator);
5558

5659
std.log.info("std.log.info with axe.Axe(.{{}})", .{});
60+
61+
// actually we want forced colors, try running with NO_COLOR=1
62+
std_log.updateTtyConfig(.always);
63+
5764
std.log.scoped(.main).warn("this is scoped", .{});
5865
}
5966

src/axe.zig

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,14 @@ pub const Config = struct {
4242
strftime: []const u8,
4343
} = .disabled,
4444
/// Whether to enable color output.
45-
color: enum {
46-
/// Check for NO_COLOR, CLICOLOR_FORCE and tty support on stderr.
47-
/// Color output is disabled on other writers.
48-
auto,
49-
/// Enable color output on every writers.
50-
always,
51-
/// Disable color output on every writers.
52-
never,
53-
} = .auto,
45+
/// This can be modified at runtime using `updateTtyConfig`.
46+
color: Color = .auto,
5447
/// Set to `.none` to disable all styles.
5548
styles: Styles = .{},
5649
/// The text to display for each log level.
5750
level_text: LevelText = .{},
5851
/// Whether to write log messages to stderr.
52+
/// This is modifiable at runtime.
5953
quiet: bool = false,
6054
/// The mutex interface to use for the log messages.
6155
mutex: union(enum) {
@@ -68,15 +62,13 @@ pub const Config = struct {
6862

6963
/// Create a new logger based on the given configuration.
7064
pub fn Axe(comptime config: Config) type {
71-
const writers_tty_config: tty.Config = switch (config.color) {
72-
.always => .escape_codes,
73-
.auto, .never => .no_color,
74-
};
75-
7665
return struct {
66+
/// Whether to write log messages to stderr.
67+
pub var quiet = config.quiet;
68+
7769
var writers: []*std.Io.Writer = &.{};
78-
// zig/llvm can't handle this without explicit type
79-
var stderr_tty_config: if (config.quiet) void else tty.Config = if (config.quiet) {} else .no_color;
70+
var stderr_tty_config = defaultTtyConfig(config.color);
71+
var writers_tty_config = defaultTtyConfig(config.color);
8072
var timezone = if (config.time_format != .disabled) zeit.utc else {};
8173
var mutex = switch (config.mutex) {
8274
.none, .function => {},
@@ -111,16 +103,7 @@ pub fn Axe(comptime config: Config) type {
111103
if (additional_writers) |_writers| {
112104
writers = try allocator.dupe(*std.Io.Writer, _writers);
113105
}
114-
if (!config.quiet) {
115-
stderr_tty_config = switch (config.color) {
116-
.auto => .detect(std.fs.File.stderr()),
117-
.always => if (builtin.os.tag == .windows) switch (.detect(std.fs.File.stderr())) {
118-
.no_color, .escape_codes => .escape_codes,
119-
.windows_api => |ctx| .{ .windows_api = ctx },
120-
} else .escape_codes,
121-
.never => .no_color,
122-
};
123-
}
106+
updateTtyConfig(config.color);
124107
}
125108

126109
/// Deinitialize the logger.
@@ -131,6 +114,22 @@ pub fn Axe(comptime config: Config) type {
131114
allocator.free(writers);
132115
}
133116

117+
/// Update tty configuration for stderr and additional writers.
118+
pub fn updateTtyConfig(color: Color) void {
119+
writers_tty_config = defaultTtyConfig(color);
120+
stderr_tty_config = switch (color) {
121+
.auto => .detect(std.fs.File.stderr()),
122+
.always => if (builtin.os.tag == .windows)
123+
switch (tty.Config.detect(std.fs.File.stderr())) {
124+
.no_color, .escape_codes => .escape_codes,
125+
.windows_api => |ctx| .{ .windows_api = ctx },
126+
}
127+
else
128+
.escape_codes,
129+
.never => .no_color,
130+
};
131+
}
132+
134133
/// Returns a scoped logging namespace that logs all messages using the scope provided.
135134
pub fn scoped(comptime scope: @Type(.enum_literal)) type {
136135
return struct {
@@ -281,7 +280,7 @@ pub fn Axe(comptime config: Config) type {
281280
print(src, writer, writers_tty_config, time, level, scope, format, args);
282281
writer.flush() catch {};
283282
}
284-
if (!config.quiet) {
283+
if (!quiet) {
285284
var buffer: [256]u8 = undefined;
286285
var stderr = std.fs.File.stderr().writer(&buffer);
287286
print(src, &stderr.interface, stderr_tty_config, time, level, scope, format, args);
@@ -417,6 +416,7 @@ pub const Style = union(enum) {
417416
bg_rgb: struct { r: u8, g: u8, b: u8 },
418417
/// #RRGGBB, #RGB, RRGGBB, RGB
419418
hex: []const u8,
419+
/// #RRGGBB, #RGB, RRGGBB, RGB
420420
bg_hex: []const u8,
421421

422422
inline fn fmt(comptime styles: []const Style, comptime text: []const u8) []const u8 {
@@ -563,6 +563,16 @@ pub const Style = union(enum) {
563563
}
564564
};
565565

566+
pub const Color = enum {
567+
/// Check for NO_COLOR, CLICOLOR_FORCE and tty support on stderr.
568+
/// Color output is disabled on other writers.
569+
auto,
570+
/// Enable color output on every writers.
571+
always,
572+
/// Disable color output on every writers.
573+
never,
574+
};
575+
566576
pub const Styles = struct {
567577
// levels
568578
err: []const Style = &.{ .bold, .red },
@@ -713,6 +723,13 @@ fn writeLocation(
713723
}
714724
}
715725

726+
inline fn defaultTtyConfig(color: Color) tty.Config {
727+
return switch (color) {
728+
.always => .escape_codes,
729+
.auto, .never => .no_color,
730+
};
731+
}
732+
716733
test "log without styles" {
717734
const expectEqualStrings = std.testing.expectEqualStrings;
718735
var buffer: [256]u8 = undefined;
@@ -782,6 +799,7 @@ test "log with complex config" {
782799
}
783800

784801
test "time format" {
802+
const expectEqual = std.testing.expectEqual;
785803
var dest: std.Io.Writer.Allocating = .init(std.testing.allocator);
786804
defer dest.deinit();
787805

@@ -790,7 +808,6 @@ test "time format" {
790808
.scope_format = "|%",
791809
.time_format = .{ .strftime = "%Y-%m-%d %H:%M:%S" },
792810
.loc_format = "|%m",
793-
.quiet = true,
794811
.color = .never,
795812
.level_text = .{
796813
.debug = "DBG",
@@ -799,20 +816,21 @@ test "time format" {
799816
.err = "ERR",
800817
},
801818
});
819+
log.quiet = true;
802820
try log.init(std.testing.allocator, &.{&dest.writer}, null);
803821
defer log.deinit(std.testing.allocator);
804822

805823
log.info("Hello {c}", .{'W'});
806824
// [YYYY-mm-dd HH:MM:SS|INF] Hello W
807-
try std.testing.expectEqual(34, dest.written().len);
825+
try expectEqual(34, dest.written().len);
808826
dest.writer.end = 0;
809827
log.scoped(.foo).warn("Hi", .{});
810828
// [YYYY-mm-dd HH:MM:SS|WRN|foo] Hi
811-
try std.testing.expectEqual(33, dest.written().len);
829+
try expectEqual(33, dest.written().len);
812830
dest.writer.end = 0;
813831
log.errAt(@src(), "Bye {}", .{'*'});
814832
// [YYYY-mm-dd HH:MM:SS|ERR|root] Bye 42
815-
try std.testing.expectEqual(38, dest.written().len);
833+
try expectEqual(38, dest.written().len);
816834
}
817835

818836
test "json log" {
@@ -855,3 +873,29 @@ test "json log" {
855873
\\
856874
, writer.buffered());
857875
}
876+
877+
test "updateTtyConfig" {
878+
const expectEqualStrings = std.testing.expectEqualStrings;
879+
var buffer: [256]u8 = undefined;
880+
var writer = std.Io.Writer.fixed(&buffer);
881+
882+
const log = Axe(.{
883+
.color = .never,
884+
.quiet = true,
885+
});
886+
try log.init(std.testing.allocator, &.{&writer}, null);
887+
defer log.deinit(std.testing.allocator);
888+
889+
log.debug("No color", .{});
890+
try expectEqualStrings("debug: No color\n", writer.buffered());
891+
writer.end = 0;
892+
893+
log.updateTtyConfig(.always);
894+
log.debug("With color", .{});
895+
try expectEqualStrings(Style.fmt(&.{ .bold, .cyan }, "debug") ++ ": With color\n", writer.buffered());
896+
writer.end = 0;
897+
898+
log.updateTtyConfig(.never);
899+
log.debug("No color again", .{});
900+
try expectEqualStrings("debug: No color again\n", writer.buffered());
901+
}

0 commit comments

Comments
 (0)