authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-10-24 13:13:06+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-24 15:04:29-04:00
logf7b090d7076a20a614bf20cac05e5e18c06ad18a
tree72bb252c5352e48ea6d60914debdf5ccc1821dff
parent6cf5305e47dd8382508f867b04067be615448b41

std.log: simplify to 4 distinct log levels

Over the last year of using std.log in practice, it has become clear to me that having the current 8 distinct log levels does more harm than good. It is too subjective which level a given message should have which makes filtering based on log level weaker as not all messages will have been assigned the log level one might expect. Instead, more granular filtering should be achieved by leveraging the logging scope feature. Filtering based on a combination of scope and log level should be sufficiently powerful for all use-cases. Note that the self hosted compiler has already limited itself to 4 distinct log levels for many months and implemented granular filtering based on both log scope and level. This has worked very well in practice while working on the self hosted compiler.

5 files changed, 59 insertions(+), 138 deletions(-)

lib/std/heap/logging_allocator.zig+2-6
......@@ -40,12 +40,8 @@ pub fn ScopedLoggingAllocator(
4040 // This function is required as the `std.log.log` function is not public
4141 inline fn logHelper(comptime log_level: std.log.Level, comptime format: []const u8, args: anytype) void {
4242 switch (log_level) {
43 .emerg => log.emerg(format, args),
44 .alert => log.alert(format, args),
45 .crit => log.crit(format, args),
4643 .err => log.err(format, args),
4744 .warn => log.warn(format, args),
48 .notice => log.notice(format, args),
4945 .info => log.info(format, args),
5046 .debug => log.debug(format, args),
5147 }
......@@ -120,6 +116,6 @@ pub fn ScopedLoggingAllocator(
120116/// This allocator is used in front of another allocator and logs to `std.log`
121117/// on every call to the allocator.
122118/// For logging to a `std.io.Writer` see `std.heap.LogToWriterAllocator`
123pub fn loggingAllocator(parent_allocator: *Allocator) LoggingAllocator(.debug, .crit) {
124 return LoggingAllocator(.debug, .crit).init(parent_allocator);
119pub fn loggingAllocator(parent_allocator: *Allocator) LoggingAllocator(.debug, .err) {
120 return LoggingAllocator(.debug, .err).init(parent_allocator);
125121}
lib/std/log.zig+42-90
......@@ -18,8 +18,8 @@
1818//! ```
1919//! const std = @import("std");
2020//!
21//! // Set the log level to warning
22//! pub const log_level: std.log.Level = .warn;
21//! // Set the log level to info
22//! pub const log_level: std.log.Level = .info;
2323//!
2424//! // Define root.log to override the std implementation
2525//! pub fn log(
......@@ -28,17 +28,17 @@
2828//! comptime format: []const u8,
2929//! args: anytype,
3030//! ) void {
31//! // Ignore all non-critical logging from sources other than
31//! // Ignore all non-error logging from sources other than
3232//! // .my_project, .nice_library and .default
3333//! const scope_prefix = "(" ++ switch (scope) {
3434//! .my_project, .nice_library, .default => @tagName(scope),
35//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
35//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.err))
3636//! @tagName(scope)
3737//! else
3838//! return,
3939//! } ++ "): ";
4040//!
41//! const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
41//! const prefix = "[" ++ level.asText() ++ "] " ++ scope_prefix;
4242//!
4343//! // Print the message to stderr, silently ignoring any errors
4444//! const held = std.debug.getStderrMutex().acquire();
......@@ -49,23 +49,23 @@
4949//!
5050//! pub fn main() void {
5151//! // Using the default scope:
52//! std.log.info("Just a simple informational log message", .{}); // Won't be printed as log_level is .warn
53//! std.log.warn("Flux capacitor is starting to overheat", .{});
52//! std.log.debug("A borderline useless debug log message", .{}); // Won't be printed as log_level is .info
53//! std.log.info("Flux capacitor is starting to overheat", .{});
5454//!
5555//! // Using scoped logging:
5656//! const my_project_log = std.log.scoped(.my_project);
5757//! const nice_library_log = std.log.scoped(.nice_library);
5858//! const verbose_lib_log = std.log.scoped(.verbose_lib);
5959//!
60//! my_project_log.info("Starting up", .{}); // Won't be printed as log_level is .warn
61//! nice_library_log.err("Something went very wrong, sorry", .{});
62//! verbose_lib_log.err("Added 1 + 1: {}", .{1 + 1}); // Won't be printed as it gets filtered out by our log function
60//! my_project_log.debug("Starting up", .{}); // Won't be printed as log_level is .info
61//! nice_library_log.warn("Something went very wrong, sorry", .{});
62//! verbose_lib_log.warn("Added 1 + 1: {}", .{1 + 1}); // Won't be printed as it gets filtered out by our log function
6363//! }
6464//! ```
6565//! Which produces the following output:
6666//! ```
67//! [warn] (default): Flux capacitor is starting to overheat
68//! [err] (nice_library): Something went very wrong, sorry
67//! [info] (default): Flux capacitor is starting to overheat
68//! [warning] (nice_library): Something went very wrong, sorry
6969//! ```
7070
7171const std = @import("std.zig");
......@@ -73,42 +73,29 @@ const builtin = @import("builtin");
7373const root = @import("root");
7474
7575pub const Level = enum {
76 /// Emergency: a condition that cannot be handled, usually followed by a
77 /// panic.
78 emerg,
79 /// Alert: a condition that should be corrected immediately (e.g. database
80 /// corruption).
81 alert,
82 /// Critical: A bug has been detected or something has gone wrong and it
83 /// will have an effect on the operation of the program.
84 crit,
85 /// Error: A bug has been detected or something has gone wrong but it is
86 /// recoverable.
76 /// Error: something has gone wrong. This might be recoverable or might
77 /// be followed by the program exiting.
8778 err,
8879 /// Warning: it is uncertain if something has gone wrong or not, but the
8980 /// circumstances would be worth investigating.
9081 warn,
91 /// Notice: non-error but significant conditions.
92 notice,
93 /// Informational: general messages about the state of the program.
82 /// Info: general messages about the state of the program.
9483 info,
9584 /// Debug: messages only useful for debugging.
9685 debug,
9786
9887 /// Returns a string literal of the given level in full text form.
9988 pub fn asText(comptime self: Level) switch (self) {
100 .emerg => @TypeOf("emergency"),
101 .crit => @TypeOf("critical"),
10289 .err => @TypeOf("error"),
10390 .warn => @TypeOf("warning"),
104 else => @TypeOf(@tagName(self)),
91 .info => @TypeOf("info"),
92 .debug => @TypeOf("debug"),
10593 } {
10694 return switch (self) {
107 .emerg => "emergency",
108 .crit => "critical",
10995 .err => "error",
11096 .warn => "warning",
111 else => @tagName(self),
97 .info => "info",
98 .debug => "debug",
11299 };
113100 }
114101};
......@@ -116,9 +103,8 @@ pub const Level = enum {
116103/// The default log level is based on build mode.
117104pub const default_level: Level = switch (builtin.mode) {
118105 .Debug => .debug,
119 .ReleaseSafe => .notice,
120 .ReleaseFast => .err,
121 .ReleaseSmall => .err,
106 .ReleaseSafe => .info,
107 .ReleaseFast, .ReleaseSmall => .err,
122108};
123109
124110/// The current log level. This is set to root.log_level if present, otherwise
......@@ -188,39 +174,18 @@ pub fn defaultLog(
188174/// provided here.
189175pub fn scoped(comptime scope: @Type(.EnumLiteral)) type {
190176 return struct {
191 /// Log an emergency message. This log level is intended to be used
192 /// for conditions that cannot be handled and is usually followed by a panic.
193 pub fn emerg(
194 comptime format: []const u8,
195 args: anytype,
196 ) void {
197 @setCold(true);
198 log(.emerg, scope, format, args);
199 }
177 /// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
178 pub const emerg = @This().err;
200179
201 /// Log an alert message. This log level is intended to be used for
202 /// conditions that should be corrected immediately (e.g. database corruption).
203 pub fn alert(
204 comptime format: []const u8,
205 args: anytype,
206 ) void {
207 @setCold(true);
208 log(.alert, scope, format, args);
209 }
180 /// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
181 pub const alert = @This().err;
210182
211 /// Log a critical message. This log level is intended to be used
212 /// when a bug has been detected or something has gone wrong and it will have
213 /// an effect on the operation of the program.
214 pub fn crit(
215 comptime format: []const u8,
216 args: anytype,
217 ) void {
218 @setCold(true);
219 log(.crit, scope, format, args);
220 }
183 /// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
184 pub const crit = @This().err;
221185
222 /// Log an error message. This log level is intended to be used when
223 /// a bug has been detected or something has gone wrong but it is recoverable.
186 /// Log an error message. This log level is intended to be used
187 /// when something has gone wrong. This might be recoverable or might
188 /// be followed by the program exiting.
224189 pub fn err(
225190 comptime format: []const u8,
226191 args: anytype,
......@@ -239,14 +204,8 @@ pub fn scoped(comptime scope: @Type(.EnumLiteral)) type {
239204 log(.warn, scope, format, args);
240205 }
241206
242 /// Log a notice message. This log level is intended to be used for
243 /// non-error but significant conditions.
244 pub fn notice(
245 comptime format: []const u8,
246 args: anytype,
247 ) void {
248 log(.notice, scope, format, args);
249 }
207 /// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
208 pub const notice = @This().info;
250209
251210 /// Log an info message. This log level is intended to be used for
252211 /// general messages about the state of the program.
......@@ -271,24 +230,18 @@ pub fn scoped(comptime scope: @Type(.EnumLiteral)) type {
271230/// The default scoped logging namespace.
272231pub const default = scoped(.default);
273232
274/// Log an emergency message using the default scope. This log level is
275/// intended to be used for conditions that cannot be handled and is usually
276/// followed by a panic.
277pub const emerg = default.emerg;
233/// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
234pub const emerg = default.err;
278235
279/// Log an alert message using the default scope. This log level is intended to
280/// be used for conditions that should be corrected immediately (e.g. database
281/// corruption).
282pub const alert = default.alert;
236/// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
237pub const alert = default.err;
283238
284/// Log a critical message using the default scope. This log level is intended
285/// to be used when a bug has been detected or something has gone wrong and it
286/// will have an effect on the operation of the program.
287pub const crit = default.crit;
239/// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
240pub const crit = default.err;
288241
289242/// Log an error message using the default scope. This log level is intended to
290/// be used when a bug has been detected or something has gone wrong but it is
291/// recoverable.
243/// be used when something has gone wrong. This might be recoverable or might
244/// be followed by the program exiting.
292245pub const err = default.err;
293246
294247/// Log a warning message using the default scope. This log level is intended
......@@ -296,9 +249,8 @@ pub const err = default.err;
296249/// the circumstances would be worth investigating.
297250pub const warn = default.warn;
298251
299/// Log a notice message using the default scope. This log level is intended to
300/// be used for non-error but significant conditions.
301pub const notice = default.notice;
252/// Deprecated. TODO: replace with @compileError() after 0.9.0 is released
253pub const notice = default.info;
302254
303255/// Log an info message using the default scope. This log level is intended to
304256/// be used for general messages about the state of the program.
lib/std/zig/parser_test.zig+1-1
......@@ -4398,7 +4398,7 @@ test "zig fmt: regression test for #5722" {
43984398 \\ while (it.next()) |node|
43994399 \\ view_tags.append(node.view.current_tags) catch {
44004400 \\ c.wl_resource_post_no_memory(self.wl_resource);
4401 \\ log.crit(.river_status, "out of memory", .{});
4401 \\ log.err(.river_status, "out of memory", .{});
44024402 \\ return;
44034403 \\ };
44044404 \\}
src/main.zig+3-10
......@@ -27,7 +27,7 @@ const crash_report = @import("crash_report.zig");
2727pub usingnamespace crash_report.root_decls;
2828
2929pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
30 std.log.emerg(format, args);
30 std.log.err(format, args);
3131 process.exit(1);
3232}
3333
......@@ -94,7 +94,7 @@ const usage = if (debug_extensions_enabled) debug_usage else normal_usage;
9494pub const log_level: std.log.Level = switch (builtin.mode) {
9595 .Debug => .debug,
9696 .ReleaseSafe, .ReleaseFast => .info,
97 .ReleaseSmall => .crit,
97 .ReleaseSmall => .err,
9898};
9999
100100var log_scopes: std.ArrayListUnmanaged([]const u8) = .{};
......@@ -120,14 +120,7 @@ pub fn log(
120120 } else return;
121121 }
122122
123 // We only recognize 4 log levels in this application.
124 const level_txt = switch (level) {
125 .emerg, .alert, .crit, .err => "error",
126 .warn => "warning",
127 .notice, .info => "info",
128 .debug => "debug",
129 };
130 const prefix1 = level_txt;
123 const prefix1 = comptime level.asText();
131124 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
132125
133126 // Print the message to stderr, silently ignoring any errors
test/compare_output.zig+11-31
......@@ -435,8 +435,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
435435 \\pub const log_level: std.log.Level = .debug;
436436 \\
437437 \\pub const scope_levels = [_]std.log.ScopeLevel{
438 \\ .{ .scope = .a, .level = .alert },
439 \\ .{ .scope = .c, .level = .emerg },
438 \\ .{ .scope = .a, .level = .warn },
439 \\ .{ .scope = .c, .level = .err },
440440 \\};
441441 \\
442442 \\const loga = std.log.scoped(.a);
......@@ -452,10 +452,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
452452 \\ logb.info("", .{});
453453 \\ logc.info("", .{});
454454 \\
455 \\ loga.notice("", .{});
456 \\ logb.notice("", .{});
457 \\ logc.notice("", .{});
458 \\
459455 \\ loga.warn("", .{});
460456 \\ logb.warn("", .{});
461457 \\ logc.warn("", .{});
......@@ -463,18 +459,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
463459 \\ loga.err("", .{});
464460 \\ logb.err("", .{});
465461 \\ logc.err("", .{});
466 \\
467 \\ loga.crit("", .{});
468 \\ logb.crit("", .{});
469 \\ logc.crit("", .{});
470 \\
471 \\ loga.alert("", .{});
472 \\ logb.alert("", .{});
473 \\ logc.alert("", .{});
474 \\
475 \\ loga.emerg("", .{});
476 \\ logb.emerg("", .{});
477 \\ logc.emerg("", .{});
478462 \\}
479463 \\pub fn log(
480464 \\ comptime level: std.log.Level,
......@@ -483,22 +467,18 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
483467 \\ args: anytype,
484468 \\) void {
485469 \\ const level_txt = comptime level.asText();
486 \\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
470 \\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "):";
487471 \\ const stdout = std.io.getStdOut().writer();
488472 \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
489473 \\}
490474 ,
491 \\debug(b):
492 \\info(b):
493 \\notice(b):
494 \\warning(b):
495 \\error(b):
496 \\critical(b):
497 \\alert(a):
498 \\alert(b):
499 \\emergency(a):
500 \\emergency(b):
501 \\emergency(c):
475 \\debug(b):
476 \\info(b):
477 \\warning(a):
478 \\warning(b):
479 \\error(a):
480 \\error(b):
481 \\error(c):
502482 \\
503483 );
504484
......@@ -534,7 +514,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
534514 ,
535515 \\debug: alloc - success - len: 10, ptr_align: 1, len_align: 0
536516 \\debug: shrink - success - 10 to 5, len_align: 0, buf_align: 1
537 \\critical: expand - failure: OutOfMemory - 5 to 20, len_align: 0, buf_align: 1
517 \\error: expand - failure: OutOfMemory - 5 to 20, len_align: 0, buf_align: 1
538518 \\debug: free - success - len: 5
539519 \\
540520 );