| ... | ... | @@ -13,63 +13,15 @@ |
| 13 | 13 | //! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its |
| 14 | 14 | //! log messages. |
| 15 | 15 | //! |
| 16 | | //! An example `logFn` might look something like this: |
| 17 | | //! |
| 18 | | //! ``` |
| 19 | | //! const std = @import("std"); |
| 20 | | //! |
| 21 | | //! pub const std_options: std.Options = .{ |
| 22 | | //! // Set the log level to info |
| 23 | | //! .log_level = .info, |
| 24 | | //! |
| 25 | | //! // Define logFn to override the std implementation |
| 26 | | //! .logFn = myLogFn, |
| 27 | | //! }; |
| 28 | | //! |
| 29 | | //! pub fn myLogFn( |
| 30 | | //! comptime level: std.log.Level, |
| 31 | | //! comptime scope: @Type(.enum_literal), |
| 32 | | //! comptime format: []const u8, |
| 33 | | //! args: anytype, |
| 34 | | //! ) void { |
| 35 | | //! // Ignore all non-error logging from sources other than |
| 36 | | //! // .my_project, .nice_library and the default |
| 37 | | //! const scope_prefix = "(" ++ switch (scope) { |
| 38 | | //! .my_project, .nice_library, std.log.default_log_scope => @tagName(scope), |
| 39 | | //! else => if (@intFromEnum(level) <= @intFromEnum(std.log.Level.err)) |
| 40 | | //! @tagName(scope) |
| 41 | | //! else |
| 42 | | //! return, |
| 43 | | //! } ++ "): "; |
| 44 | | //! |
| 45 | | //! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix; |
| 46 | | //! |
| 47 | | //! // Print the message to stderr, silently ignoring any errors |
| 48 | | //! std.debug.lockStdErr(); |
| 49 | | //! defer std.debug.unlockStdErr(); |
| 50 | | //! var stderr = std.fs.File.stderr().writer(&.{}); |
| 51 | | //! nosuspend stderr.interface.print(prefix ++ format ++ "\n", args) catch return; |
| 52 | | //! } |
| 53 | | //! |
| 54 | | //! pub fn main() void { |
| 55 | | //! // Using the default scope: |
| 56 | | //! std.log.debug("A borderline useless debug log message", .{}); // Won't be printed as log_level is .info |
| 57 | | //! std.log.info("Flux capacitor is starting to overheat", .{}); |
| 58 | | //! |
| 59 | | //! // Using scoped logging: |
| 60 | | //! const my_project_log = std.log.scoped(.my_project); |
| 61 | | //! const nice_library_log = std.log.scoped(.nice_library); |
| 62 | | //! const verbose_lib_log = std.log.scoped(.verbose_lib); |
| 63 | | //! |
| 64 | | //! my_project_log.debug("Starting up", .{}); // Won't be printed as log_level is .info |
| 65 | | //! nice_library_log.warn("Something went very wrong, sorry", .{}); |
| 66 | | //! verbose_lib_log.warn("Added 1 + 1: {}", .{1 + 1}); // Won't be printed as it gets filtered out by our log function |
| 67 | | //! } |
| 16 | //! For an example implementation of the `logFn` function, see `defaultLog`, |
| 17 | //! which is the default implementation. It outputs to stderr, using color if |
| 18 | //! the detected `std.Io.tty.Config` supports it. Its output looks like this: |
| 68 | 19 | //! ``` |
| 69 | | //! Which produces the following output: |
| 70 | | //! ``` |
| 71 | | //! [info] (default): Flux capacitor is starting to overheat |
| 72 | | //! [warning] (nice_library): Something went very wrong, sorry |
| 20 | //! error: this is an error |
| 21 | //! error(scope): this is an error with a non-default scope |
| 22 | //! warning: this is a warning |
| 23 | //! info: this is an informative message |
| 24 | //! debug: this is a debugging message |
| 73 | 25 | //! ``` |
| 74 | 26 | |
| 75 | 27 | const std = @import("std.zig"); |
| ... | ... | @@ -104,37 +56,28 @@ pub const default_level: Level = switch (builtin.mode) { |
| 104 | 56 | .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info, |
| 105 | 57 | }; |
| 106 | 58 | |
| 107 | | const level = std.options.log_level; |
| 108 | | |
| 109 | 59 | pub const ScopeLevel = struct { |
| 110 | 60 | scope: @Type(.enum_literal), |
| 111 | 61 | level: Level, |
| 112 | 62 | }; |
| 113 | 63 | |
| 114 | | const scope_levels = std.options.log_scope_levels; |
| 115 | | |
| 116 | 64 | fn log( |
| 117 | | comptime message_level: Level, |
| 65 | comptime level: Level, |
| 118 | 66 | comptime scope: @Type(.enum_literal), |
| 119 | 67 | comptime format: []const u8, |
| 120 | 68 | args: anytype, |
| 121 | 69 | ) void { |
| 122 | | if (comptime !logEnabled(message_level, scope)) return; |
| 70 | if (comptime !logEnabled(level, scope)) return; |
| 123 | 71 | |
| 124 | | std.options.logFn(message_level, scope, format, args); |
| 72 | std.options.logFn(level, scope, format, args); |
| 125 | 73 | } |
| 126 | 74 | |
| 127 | 75 | /// Determine if a specific log message level and scope combination are enabled for logging. |
| 128 | | pub fn logEnabled(comptime message_level: Level, comptime scope: @Type(.enum_literal)) bool { |
| 129 | | inline for (scope_levels) |scope_level| { |
| 130 | | if (scope_level.scope == scope) return @intFromEnum(message_level) <= @intFromEnum(scope_level.level); |
| 76 | pub fn logEnabled(comptime level: Level, comptime scope: @Type(.enum_literal)) bool { |
| 77 | inline for (std.options.log_scope_levels) |scope_level| { |
| 78 | if (scope_level.scope == scope) return @intFromEnum(level) <= @intFromEnum(scope_level.level); |
| 131 | 79 | } |
| 132 | | return @intFromEnum(message_level) <= @intFromEnum(level); |
| 133 | | } |
| 134 | | |
| 135 | | /// Determine if a specific log message level using the default log scope is enabled for logging. |
| 136 | | pub fn defaultLogEnabled(comptime message_level: Level) bool { |
| 137 | | return comptime logEnabled(message_level, default_log_scope); |
| 80 | return @intFromEnum(level) <= @intFromEnum(std.options.log_level); |
| 138 | 81 | } |
| 139 | 82 | |
| 140 | 83 | /// The default implementation for the log function. Custom log functions may |
| ... | ... | @@ -143,17 +86,31 @@ pub fn defaultLogEnabled(comptime message_level: Level) bool { |
| 143 | 86 | /// Uses a 64-byte buffer for formatted printing which is flushed before this |
| 144 | 87 | /// function returns. |
| 145 | 88 | pub fn defaultLog( |
| 146 | | comptime message_level: Level, |
| 89 | comptime level: Level, |
| 147 | 90 | comptime scope: @Type(.enum_literal), |
| 148 | 91 | comptime format: []const u8, |
| 149 | 92 | args: anytype, |
| 150 | 93 | ) void { |
| 151 | | const level_txt = comptime message_level.asText(); |
| 152 | | const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; |
| 153 | 94 | var buffer: [64]u8 = undefined; |
| 154 | | const stderr, _ = std.debug.lockStderrWriter(&buffer); |
| 95 | const stderr, const ttyconf = std.debug.lockStderrWriter(&buffer); |
| 155 | 96 | defer std.debug.unlockStderrWriter(); |
| 156 | | nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; |
| 97 | ttyconf.setColor(stderr, switch (level) { |
| 98 | .err => .red, |
| 99 | .warn => .yellow, |
| 100 | .info => .green, |
| 101 | .debug => .magenta, |
| 102 | }) catch {}; |
| 103 | ttyconf.setColor(stderr, .bold) catch {}; |
| 104 | stderr.writeAll(level.asText()) catch return; |
| 105 | ttyconf.setColor(stderr, .reset) catch {}; |
| 106 | ttyconf.setColor(stderr, .dim) catch {}; |
| 107 | ttyconf.setColor(stderr, .bold) catch {}; |
| 108 | if (scope != .default) { |
| 109 | stderr.print("({s})", .{@tagName(scope)}) catch return; |
| 110 | } |
| 111 | stderr.writeAll(": ") catch return; |
| 112 | ttyconf.setColor(stderr, .reset) catch {}; |
| 113 | stderr.print(format ++ "\n", args) catch return; |
| 157 | 114 | } |
| 158 | 115 | |
| 159 | 116 | /// Returns a scoped logging namespace that logs all messages using the scope |