| ... | @@ -0,0 +1,202 @@ |
| 1 | const std = @import("std.zig"); |
| 2 | const builtin = std.builtin; |
| 3 | const root = @import("root"); |
| 4 | |
| 5 | //! std.log is standardized interface for logging which allows for the logging |
| 6 | //! of programs and libraries using this interface to be formatted and filtered |
| 7 | //! by the implementer of the root.log function. |
| 8 | //! |
| 9 | //! The scope parameter should be used to give context to the logging. For |
| 10 | //! example, a library called 'libfoo' might use .libfoo as its scope. |
| 11 | //! |
| 12 | //! An example root.log might look something like this: |
| 13 | //! |
| 14 | //! ``` |
| 15 | //! const std = @import("std"); |
| 16 | //! |
| 17 | //! // Set the log level to warning |
| 18 | //! pub const log_level: std.log.Level = .warn; |
| 19 | //! |
| 20 | //! // Define root.log to override the std implementation |
| 21 | //! pub fn log( |
| 22 | //! comptime level: std.log.Level, |
| 23 | //! comptime scope: @TypeOf(.EnumLiteral), |
| 24 | //! comptime format: []const u8, |
| 25 | //! args: var, |
| 26 | //! ) void { |
| 27 | //! // Ignore all non-critical logging from sources other than |
| 28 | //! // .my_project and .nice_library |
| 29 | //! const scope_prefix = "(" ++ switch (scope) { |
| 30 | //! .my_project, .nice_library => @tagName(scope), |
| 31 | //! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit)) |
| 32 | //! @tagName(scope) |
| 33 | //! else |
| 34 | //! return, |
| 35 | //! } ++ "): "; |
| 36 | //! |
| 37 | //! const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix; |
| 38 | //! |
| 39 | //! // Print the message to stderr, silently ignoring any errors |
| 40 | //! const held = std.debug.getStderrMutex().acquire(); |
| 41 | //! defer held.release(); |
| 42 | //! const stderr = std.debug.getStderrStream(); |
| 43 | //! nosuspend stderr.print(prefix ++ format, args) catch return; |
| 44 | //! } |
| 45 | //! |
| 46 | //! pub fn main() void { |
| 47 | //! // Won't be printed as log_level is .warn |
| 48 | //! std.log.info(.my_project, "Starting up.\n", .{}); |
| 49 | //! std.log.err(.nice_library, "Something went very wrong, sorry.\n", .{}); |
| 50 | //! // Won't be printed as it gets filtered out by our log function |
| 51 | //! std.log.err(.lib_that_logs_too_much, "Added 1 + 1\n", .{}); |
| 52 | //! } |
| 53 | //! ``` |
| 54 | //! Which produces the following output: |
| 55 | //! ``` |
| 56 | //! [err] (nice_library): Something went very wrong, sorry. |
| 57 | //! ``` |
| 58 | |
| 59 | pub const Level = enum { |
| 60 | /// Emergency: a condition that cannot be handled, usually followed by a |
| 61 | /// panic. |
| 62 | emerg, |
| 63 | /// Alert: a condition that should be corrected immediately (e.g. database |
| 64 | /// corruption). |
| 65 | alert, |
| 66 | /// Critical: A bug has been detected or something has gone wrong and it |
| 67 | /// will have an effect on the operation of the program. |
| 68 | crit, |
| 69 | /// Error: A bug has been detected or something has gone wrong but it is |
| 70 | /// recoverable. |
| 71 | err, |
| 72 | /// Warning: it is uncertain if something has gone wrong or not, but the |
| 73 | /// circumstances would be worth investigating. |
| 74 | warn, |
| 75 | /// Notice: non-error but significant conditions. |
| 76 | notice, |
| 77 | /// Informational: general messages about the state of the program. |
| 78 | info, |
| 79 | /// Debug: messages only useful for debugging. |
| 80 | debug, |
| 81 | }; |
| 82 | |
| 83 | /// The default log level is based on build mode. Note that in ReleaseSmall |
| 84 | /// builds the default level is emerg but no messages will be stored/logged |
| 85 | /// by the default logger to save space. |
| 86 | pub const default_level: Level = switch (builtin.mode) { |
| 87 | .Debug => .debug, |
| 88 | .ReleaseSafe => .notice, |
| 89 | .ReleaseFast => .err, |
| 90 | .ReleaseSmall => .emerg, |
| 91 | }; |
| 92 | |
| 93 | /// The current log level. This is set to root.log_level if present, otherwise |
| 94 | /// log.default_level. |
| 95 | pub const level: Level = if (@hasDecl(root, "log_level")) |
| 96 | root.log_level |
| 97 | else |
| 98 | default_level; |
| 99 | |
| 100 | fn log( |
| 101 | comptime message_level: Level, |
| 102 | comptime scope: @Type(.EnumLiteral), |
| 103 | comptime format: []const u8, |
| 104 | args: var, |
| 105 | ) void { |
| 106 | if (@enumToInt(message_level) <= @enumToInt(level)) { |
| 107 | if (@hasDecl(root, "log")) { |
| 108 | root.log(message_level, scope, format, args); |
| 109 | } else if (builtin.mode != .ReleaseSmall) { |
| 110 | const held = std.debug.getStderrMutex().acquire(); |
| 111 | defer held.release(); |
| 112 | const stderr = io.getStdErr().writer(); |
| 113 | nosuspend stderr.print(format, args) catch return; |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | /// Log an emergency message to stderr. This log level is intended to be used |
| 119 | /// for conditions that cannot be handled and is usually followed by a panic. |
| 120 | pub fn emerg( |
| 121 | comptime scope: @Type(.EnumLiteral), |
| 122 | comptime format: []const u8, |
| 123 | args: var, |
| 124 | ) void { |
| 125 | @setCold(true); |
| 126 | log(.emerg, scope, format, args); |
| 127 | } |
| 128 | |
| 129 | /// Log an alert message to stderr. This log level is intended to be used for |
| 130 | /// conditions that should be corrected immediately (e.g. database corruption). |
| 131 | pub fn alert( |
| 132 | comptime scope: @Type(.EnumLiteral), |
| 133 | comptime format: []const u8, |
| 134 | args: var, |
| 135 | ) void { |
| 136 | @setCold(true); |
| 137 | log(.alert, scope, format, args); |
| 138 | } |
| 139 | |
| 140 | /// Log a critical message to stderr. This log level is intended to be used |
| 141 | /// when a bug has been detected or something has gone wrong and it will have |
| 142 | /// an effect on the operation of the program. |
| 143 | pub fn crit( |
| 144 | comptime scope: @Type(.EnumLiteral), |
| 145 | comptime format: []const u8, |
| 146 | args: var, |
| 147 | ) void { |
| 148 | @setCold(true); |
| 149 | log(.crit, scope, format, args); |
| 150 | } |
| 151 | |
| 152 | /// Log an error message to stderr. This log level is intended to be used when |
| 153 | /// a bug has been detected or something has gone wrong but it is recoverable. |
| 154 | pub fn err( |
| 155 | comptime scope: @Type(.EnumLiteral), |
| 156 | comptime format: []const u8, |
| 157 | args: var, |
| 158 | ) void { |
| 159 | @setCold(true); |
| 160 | log(.err, scope, format, args); |
| 161 | } |
| 162 | |
| 163 | /// Log a warning message to stderr. This log level is intended to be used if |
| 164 | /// it is uncertain whether something has gone wrong or not, but the |
| 165 | /// circumstances would be worth investigating. |
| 166 | pub fn warn( |
| 167 | comptime scope: @Type(.EnumLiteral), |
| 168 | comptime format: []const u8, |
| 169 | args: var, |
| 170 | ) void { |
| 171 | log(.warn, scope, format, args); |
| 172 | } |
| 173 | |
| 174 | /// Log a notice message to stderr. This log level is intended to be used for |
| 175 | /// non-error but significant conditions. |
| 176 | pub fn notice( |
| 177 | comptime scope: @Type(.EnumLiteral), |
| 178 | comptime format: []const u8, |
| 179 | args: var, |
| 180 | ) void { |
| 181 | log(.notice, scope, format, args); |
| 182 | } |
| 183 | |
| 184 | /// Log an info message to stderr. This log level is intended to be used for |
| 185 | /// general messages about the state of the program. |
| 186 | pub fn info( |
| 187 | comptime scope: @Type(.EnumLiteral), |
| 188 | comptime format: []const u8, |
| 189 | args: var, |
| 190 | ) void { |
| 191 | log(.info, scope, format, args); |
| 192 | } |
| 193 | |
| 194 | /// Log a debug message to stderr. This log level is intended to be used for |
| 195 | /// messages which are only useful for debugging. |
| 196 | pub fn debug( |
| 197 | comptime scope: @Type(.EnumLiteral), |
| 198 | comptime format: []const u8, |
| 199 | args: var, |
| 200 | ) void { |
| 201 | log(.debug, scope, format, args); |
| 202 | } |