aboutsummaryrefslogtreecommitdiff
path: root/lib/std/log.zig
blob: 0cc2b544522cbb108481ec952f5c8d60157d3070 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2020 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("std.zig");
const builtin = std.builtin;
const root = @import("root");

//! std.log is a standardized interface for logging which allows for the logging
//! of programs and libraries using this interface to be formatted and filtered
//! by the implementer of the root.log function.
//!
//! Each log message has an associated scope enum, which can be used to give
//! context to the logging. The logging functions in std.log implicitly use a
//! scope of .default.
//!
//! A logging namespace using a custom scope can be created using the
//! std.log.scoped function, passing the scope as an argument; the logging
//! functions in the resulting struct use the provided scope parameter.
//! For example, a library called 'libfoo' might use
//! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its
//! log messages.
//!
//! An example root.log might look something like this:
//!
//! ```
//! const std = @import("std");
//!
//! // Set the log level to warning
//! pub const log_level: std.log.Level = .warn;
//!
//! // Define root.log to override the std implementation
//! pub fn log(
//!     comptime level: std.log.Level,
//!     comptime scope: @TypeOf(.EnumLiteral),
//!     comptime format: []const u8,
//!     args: anytype,
//! ) void {
//!     // Ignore all non-critical logging from sources other than
//!     // .my_project, .nice_library and .default
//!     const scope_prefix = "(" ++ switch (scope) {
//!         .my_project, .nice_library, .default => @tagName(scope),
//!         else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
//!             @tagName(scope)
//!         else
//!             return,
//!     } ++ "): ";
//!
//!     const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
//!
//!     // Print the message to stderr, silently ignoring any errors
//!     const held = std.debug.getStderrMutex().acquire();
//!     defer held.release();
//!     const stderr = std.io.getStdErr().writer();
//!     nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
//! }
//!
//! pub fn main() void {
//!     // Using the default scope:
//!     std.log.info("Just a simple informational log message", .{}); // Won't be printed as log_level is .warn
//!     std.log.warn("Flux capacitor is starting to overheat", .{});
//!
//!     // Using scoped logging:
//!     const my_project_log = std.log.scoped(.my_project);
//!     const nice_library_log = std.log.scoped(.nice_library);
//!     const verbose_lib_log = std.log.scoped(.verbose_lib);
//!
//!     my_project_log.info("Starting up", .{}); // Won't be printed as log_level is .warn
//!     nice_library_log.err("Something went very wrong, sorry", .{});
//!     verbose_lib_log.err("Added 1 + 1: {}", .{1 + 1}); // Won't be printed as it gets filtered out by our log function
//! }
//! ```
//! Which produces the following output:
//! ```
//! [warn] (default): Flux capacitor is starting to overheat
//! [err] (nice_library): Something went very wrong, sorry
//! ```

pub const Level = enum {
    /// Emergency: a condition that cannot be handled, usually followed by a
    /// panic.
    emerg,
    /// Alert: a condition that should be corrected immediately (e.g. database
    /// corruption).
    alert,
    /// Critical: A bug has been detected or something has gone wrong and it
    /// will have an effect on the operation of the program.
    crit,
    /// Error: A bug has been detected or something has gone wrong but it is
    /// recoverable.
    err,
    /// Warning: it is uncertain if something has gone wrong or not, but the
    /// circumstances would be worth investigating.
    warn,
    /// Notice: non-error but significant conditions.
    notice,
    /// Informational: general messages about the state of the program.
    info,
    /// Debug: messages only useful for debugging.
    debug,
};

/// The default log level is based on build mode.
pub const default_level: Level = switch (builtin.mode) {
    .Debug => .debug,
    .ReleaseSafe => .notice,
    .ReleaseFast => .err,
    .ReleaseSmall => .err,
};

/// The current log level. This is set to root.log_level if present, otherwise
/// log.default_level.
pub const level: Level = if (@hasDecl(root, "log_level"))
    root.log_level
else
    default_level;

fn log(
    comptime message_level: Level,
    comptime scope: @Type(.EnumLiteral),
    comptime format: []const u8,
    args: anytype,
) void {
    if (@enumToInt(message_level) <= @enumToInt(level)) {
        if (@hasDecl(root, "log")) {
            root.log(message_level, scope, format, args);
        } else if (std.Target.current.os.tag == .freestanding) {
            // On freestanding one must provide a log function; we do not have
            // any I/O configured.
            return;
        } else {
            const level_txt = switch (message_level) {
                .emerg => "emergency",
                .alert => "alert",
                .crit => "critical",
                .err => "error",
                .warn => "warning",
                .notice => "notice",
                .info => "info",
                .debug => "debug",
            };
            const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
            const stderr = std.io.getStdErr().writer();
            const held = std.debug.getStderrMutex().acquire();
            defer held.release();
            nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
        }
    }
}

/// Returns a scoped logging namespace that logs all messages using the scope
/// provided here.
pub fn scoped(comptime scope: @Type(.EnumLiteral)) type {
    return struct {
        /// Log an emergency message. This log level is intended to be used
        /// for conditions that cannot be handled and is usually followed by a panic.
        pub fn emerg(
            comptime format: []const u8,
            args: anytype,
        ) void {
            @setCold(true);
            log(.emerg, scope, format, args);
        }

        /// Log an alert message. This log level is intended to be used for
        /// conditions that should be corrected immediately (e.g. database corruption).
        pub fn alert(
            comptime format: []const u8,
            args: anytype,
        ) void {
            @setCold(true);
            log(.alert, scope, format, args);
        }

        /// Log a critical message. This log level is intended to be used
        /// when a bug has been detected or something has gone wrong and it will have
        /// an effect on the operation of the program.
        pub fn crit(
            comptime format: []const u8,
            args: anytype,
        ) void {
            @setCold(true);
            log(.crit, scope, format, args);
        }

        /// Log an error message. This log level is intended to be used when
        /// a bug has been detected or something has gone wrong but it is recoverable.
        pub fn err(
            comptime format: []const u8,
            args: anytype,
        ) void {
            @setCold(true);
            log(.err, scope, format, args);
        }

        /// Log a warning message. This log level is intended to be used if
        /// it is uncertain whether something has gone wrong or not, but the
        /// circumstances would be worth investigating.
        pub fn warn(
            comptime format: []const u8,
            args: anytype,
        ) void {
            log(.warn, scope, format, args);
        }

        /// Log a notice message. This log level is intended to be used for
        /// non-error but significant conditions.
        pub fn notice(
            comptime format: []const u8,
            args: anytype,
        ) void {
            log(.notice, scope, format, args);
        }

        /// Log an info message. This log level is intended to be used for
        /// general messages about the state of the program.
        pub fn info(
            comptime format: []const u8,
            args: anytype,
        ) void {
            log(.info, scope, format, args);
        }

        /// Log a debug message. This log level is intended to be used for
        /// messages which are only useful for debugging.
        pub fn debug(
            comptime format: []const u8,
            args: anytype,
        ) void {
            log(.debug, scope, format, args);
        }
    };
}

/// The default scoped logging namespace.
pub const default = scoped(.default);

/// Log an emergency message using the default scope. This log level is
/// intended to be used for conditions that cannot be handled and is usually
/// followed by a panic.
pub const emerg = default.emerg;

/// Log an alert message using the default scope. This log level is intended to
/// be used for conditions that should be corrected immediately (e.g. database
/// corruption).
pub const alert = default.alert;

/// Log a critical message using the default scope. This log level is intended
/// to be used when a bug has been detected or something has gone wrong and it
/// will have an effect on the operation of the program.
pub const crit = default.crit;

/// Log an error message using the default scope. This log level is intended to
/// be used when a bug has been detected or something has gone wrong but it is
/// recoverable.
pub const err = default.err;

/// Log a warning message using the default scope. This log level is intended
/// to be used if it is uncertain whether something has gone wrong or not, but
/// the circumstances would be worth investigating.
pub const warn = default.warn;

/// Log a notice message using the default scope. This log level is intended to
/// be used for non-error but significant conditions.
pub const notice = default.notice;

/// Log an info message using the default scope. This log level is intended to
/// be used for general messages about the state of the program.
pub const info = default.info;

/// Log a debug message using the default scope. This log level is intended to
/// be used for messages which are only useful for debugging.
pub const debug = default.debug;