| ... | @@ -13,63 +13,15 @@ | ... | @@ -13,63 +13,15 @@ |
| 13 | //! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its | 13 | //! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its |
| 14 | //! log messages. | 14 | //! log messages. |
| 15 | //! | 15 | //! |
| 16 | //! An example `logFn` might look something like this: | 16 | //! For an example implementation of the `logFn` function, see `defaultLog`, |
| 17 | //! | 17 | //! which is the default implementation. It outputs to stderr, using color if |
| 18 | //! ``` | 18 | //! the detected `std.Io.tty.Config` supports it. Its output looks like this: |
| 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 | //! } | | |
| 68 | //! ``` | 19 | //! ``` |
| 69 | //! Which produces the following output: | 20 | //! error: this is an error |
| 70 | //! ``` | 21 | //! error(scope): this is an error with a non-default scope |
| 71 | //! [info] (default): Flux capacitor is starting to overheat | 22 | //! warning: this is a warning |
| 72 | //! [warning] (nice_library): Something went very wrong, sorry | 23 | //! info: this is an informative message |
| | 24 | //! debug: this is a debugging message |
| 73 | //! ``` | 25 | //! ``` |
| 74 | | 26 | |
| 75 | const std = @import("std.zig"); | 27 | const std = @import("std.zig"); |
| ... | @@ -104,37 +56,28 @@ pub const default_level: Level = switch (builtin.mode) { | ... | @@ -104,37 +56,28 @@ pub const default_level: Level = switch (builtin.mode) { |
| 104 | .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info, | 56 | .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info, |
| 105 | }; | 57 | }; |
| 106 | | 58 | |
| 107 | const level = std.options.log_level; | | |
| 108 | | | |
| 109 | pub const ScopeLevel = struct { | 59 | pub const ScopeLevel = struct { |
| 110 | scope: @Type(.enum_literal), | 60 | scope: @Type(.enum_literal), |
| 111 | level: Level, | 61 | level: Level, |
| 112 | }; | 62 | }; |
| 113 | | 63 | |
| 114 | const scope_levels = std.options.log_scope_levels; | | |
| 115 | | | |
| 116 | fn log( | 64 | fn log( |
| 117 | comptime message_level: Level, | 65 | comptime level: Level, |
| 118 | comptime scope: @Type(.enum_literal), | 66 | comptime scope: @Type(.enum_literal), |
| 119 | comptime format: []const u8, | 67 | comptime format: []const u8, |
| 120 | args: anytype, | 68 | args: anytype, |
| 121 | ) void { | 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 | /// Determine if a specific log message level and scope combination are enabled for logging. | 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 { | 76 | pub fn logEnabled(comptime level: Level, comptime scope: @Type(.enum_literal)) bool { |
| 129 | inline for (scope_levels) |scope_level| { | 77 | inline for (std.options.log_scope_levels) |scope_level| { |
| 130 | if (scope_level.scope == scope) return @intFromEnum(message_level) <= @intFromEnum(scope_level.level); | 78 | if (scope_level.scope == scope) return @intFromEnum(level) <= @intFromEnum(scope_level.level); |
| 131 | } | 79 | } |
| 132 | return @intFromEnum(message_level) <= @intFromEnum(level); | 80 | return @intFromEnum(level) <= @intFromEnum(std.options.log_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); | | |
| 138 | } | 81 | } |
| 139 | | 82 | |
| 140 | /// The default implementation for the log function. Custom log functions may | 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,17 +86,31 @@ pub fn defaultLogEnabled(comptime message_level: Level) bool { |
| 143 | /// Uses a 64-byte buffer for formatted printing which is flushed before this | 86 | /// Uses a 64-byte buffer for formatted printing which is flushed before this |
| 144 | /// function returns. | 87 | /// function returns. |
| 145 | pub fn defaultLog( | 88 | pub fn defaultLog( |
| 146 | comptime message_level: Level, | 89 | comptime level: Level, |
| 147 | comptime scope: @Type(.enum_literal), | 90 | comptime scope: @Type(.enum_literal), |
| 148 | comptime format: []const u8, | 91 | comptime format: []const u8, |
| 149 | args: anytype, | 92 | args: anytype, |
| 150 | ) void { | 93 | ) void { |
| 151 | const level_txt = comptime message_level.asText(); | | |
| 152 | const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; | | |
| 153 | var buffer: [64]u8 = undefined; | 94 | var buffer: [64]u8 = undefined; |
| 154 | const stderr, _ = std.debug.lockStderrWriter(&buffer); | 95 | const stderr, const ttyconf = std.debug.lockStderrWriter(&buffer); |
| 155 | defer std.debug.unlockStderrWriter(); | 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 | /// Returns a scoped logging namespace that logs all messages using the scope | 116 | /// Returns a scoped logging namespace that logs all messages using the scope |