1//! std.log is a standardized interface for logging which allows for the logging
2//! of programs and libraries using this interface to be formatted and filtered
3//! by the implementer of the `std.options.logFn` function.
4//!
5//! Each log message has an associated scope enum, which can be used to give
6//! context to the logging. The logging functions in std.log implicitly use a
7//! scope of .default.
8//!
9//! A logging namespace using a custom scope can be created using the
10//! std.log.scoped function, passing the scope as an argument; the logging
11//! functions in the resulting struct use the provided scope parameter.
12//! For example, a library called 'libfoo' might use
13//! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its
14//! log messages.
15//!
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//! supported. Its output looks like this:
19//! ```
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
25//! ```
26
27const std = @import("std.zig");
28const builtin = @import("builtin");
29
30pub const Level = enum {
31 /// Error: something has gone wrong. This might be recoverable or might
32 /// be followed by the program exiting.
33 err,
34 /// Warning: it is uncertain if something has gone wrong or not, but the
35 /// circumstances would be worth investigating.
36 warn,
37 /// Info: general messages about the state of the program.
38 info,
39 /// Debug: messages only useful for debugging.
40 debug,
41
42 /// Returns a string literal of the given level in full text form.
43 pub fn asText(comptime self: Level) []const u8 {
44 return switch (self) {
45 .err => "error",
46 .warn => "warning",
47 .info => "info",
48 .debug => "debug",
49 };
50 }
51};
52
53/// The default log level is based on build mode.
54pub const default_level: Level = switch (builtin.mode) {
55 .debug => .debug,
56 .safe, .fast, .small => .info,
57};
58
59pub const ScopeLevel = struct {
60 scope: @EnumLiteral(),
61 level: Level,
62};
63
64fn log(
65 comptime level: Level,
66 comptime scope: @EnumLiteral(),
67 comptime format: []const u8,
68 args: anytype,
69) void {
70 if (comptime !logEnabled(level, scope)) return;
71
72 std.options.logFn(level, scope, format, args);
73}
74
75/// Determine if a specific log message level and scope combination are enabled for logging.
76pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool {
77 inline for (std.options.log_scope_levels) |scope_level| {
78 if (scope_level.scope == scope) return @backingInt(level) <= @backingInt(scope_level.level);
79 }
80 return @backingInt(level) <= @backingInt(std.options.log_level);
81}
82
83pub const terminalMode = std.Options.logTerminalMode;
84
85pub fn defaultTerminalMode() std.Io.Terminal.Mode {
86 const stderr = std.debug.lockStderr(&.{}).terminal();
87 std.debug.unlockStderr();
88 return stderr.mode;
89}
90
91/// The default implementation for the log function. Custom log functions may
92/// forward log messages to this function.
93///
94/// Uses a 64-byte buffer for formatted printing which is flushed before this
95/// function returns.
96pub fn defaultLog(
97 comptime level: Level,
98 comptime scope: @EnumLiteral(),
99 comptime format: []const u8,
100 args: anytype,
101) void {
102 const io = std.Options.debug_io;
103 const prev = io.swapCancelProtection(.blocked);
104 defer _ = io.swapCancelProtection(prev);
105 var buffer: [64]u8 = undefined;
106 const stderr = std.debug.lockStderr(&buffer).terminal();
107 defer std.debug.unlockStderr();
108 return defaultLogFileTerminal(level, scope, format, args, stderr) catch {};
109}
110
111pub fn defaultLogFileTerminal(
112 comptime level: Level,
113 comptime scope: @EnumLiteral(),
114 comptime format: []const u8,
115 args: anytype,
116 t: std.Io.Terminal,
117) std.Io.Writer.Error!void {
118 t.setColor(switch (level) {
119 .err => .red,
120 .warn => .yellow,
121 .info => .green,
122 .debug => .magenta,
123 }) catch {};
124 t.setColor(.bold) catch {};
125 try t.writer.writeAll(level.asText());
126 t.setColor(.reset) catch {};
127 t.setColor(.dim) catch {};
128 t.setColor(.bold) catch {};
129 if (scope != .default) try t.writer.print("({t})", .{scope});
130 try t.writer.writeAll(": ");
131 t.setColor(.reset) catch {};
132 try t.writer.print(format ++ "\n", args);
133}
134
135/// Returns a scoped logging namespace that logs all messages using the scope
136/// provided here.
137pub fn scoped(comptime scope: @EnumLiteral()) type {
138 return struct {
139 /// Log an error message. This log level is intended to be used
140 /// when something has gone wrong. This might be recoverable or might
141 /// be followed by the program exiting.
142 pub fn err(
143 comptime format: []const u8,
144 args: anytype,
145 ) void {
146 @branchHint(.cold);
147 log(.err, scope, format, args);
148 }
149
150 /// Log a warning message. This log level is intended to be used if
151 /// it is uncertain whether something has gone wrong or not, but the
152 /// circumstances would be worth investigating.
153 pub fn warn(
154 comptime format: []const u8,
155 args: anytype,
156 ) void {
157 log(.warn, scope, format, args);
158 }
159
160 /// Log an info message. This log level is intended to be used for
161 /// general messages about the state of the program.
162 pub fn info(
163 comptime format: []const u8,
164 args: anytype,
165 ) void {
166 log(.info, scope, format, args);
167 }
168
169 /// Log a debug message. This log level is intended to be used for
170 /// messages which are only useful for debugging.
171 pub fn debug(
172 comptime format: []const u8,
173 args: anytype,
174 ) void {
175 log(.debug, scope, format, args);
176 }
177 };
178}
179
180pub const default_log_scope = .default;
181
182/// The default scoped logging namespace.
183pub const default = scoped(default_log_scope);
184
185/// Log an error message using the default scope. This log level is intended to
186/// be used when something has gone wrong. This might be recoverable or might
187/// be followed by the program exiting.
188pub const err = default.err;
189
190/// Log a warning message using the default scope. This log level is intended
191/// to be used if it is uncertain whether something has gone wrong or not, but
192/// the circumstances would be worth investigating.
193pub const warn = default.warn;
194
195/// Log an info message using the default scope. This log level is intended to
196/// be used for general messages about the state of the program.
197pub const info = default.info;
198
199/// Log a debug message using the default scope. This log level is intended to
200/// be used for messages which are only useful for debugging.
201pub const debug = default.debug;