Skip to content

Introduce std.log #5348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lib/std/debug.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ pub const LineInfo = struct {

var stderr_mutex = std.Mutex.init();

/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
pub fn warn(comptime fmt: []const u8, args: var) void {
/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
/// "printf debugging".
pub const warn = print;

/// Print to stderr, unbuffered, and silently returning on failure. Intended
/// for use in "printf debugging." Use `std.log` functions for proper logging.
pub fn print(comptime fmt: []const u8, args: var) void {
const held = stderr_mutex.acquire();
defer held.release();
const stderr = io.getStdErr().writer();
Expand Down
202 changes: 202 additions & 0 deletions lib/std/log.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
const std = @import("std.zig");
const builtin = std.builtin;
const root = @import("root");

//! std.log is 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.
//!
//! The scope parameter should be used to give context to the logging. For
//! example, a library called 'libfoo' might use .libfoo as its scope.
//!
//! 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: var,
//! ) void {
//! // Ignore all non-critical logging from sources other than
//! // .my_project and .nice_library
//! const scope_prefix = "(" ++ switch (scope) {
//! .my_project, .nice_library => @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.debug.getStderrStream();
//! nosuspend stderr.print(prefix ++ format, args) catch return;
//! }
//!
//! pub fn main() void {
//! // Won't be printed as log_level is .warn
//! std.log.info(.my_project, "Starting up.\n", .{});
//! std.log.err(.nice_library, "Something went very wrong, sorry.\n", .{});
//! // Won't be printed as it gets filtered out by our log function
//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1\n", .{});
//! }
//! ```
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't sure how example code should be formatted in doc comments, so I just defaulted to some markdown backticks

//! Which produces the following output:
//! ```
//! [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. Note that in ReleaseSmall
/// builds the default level is emerg but no messages will be stored/logged
/// by the default logger to save space.
pub const default_level: Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe => .notice,
.ReleaseFast => .err,
.ReleaseSmall => .emerg,
};

/// 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: var,
) void {
if (@enumToInt(message_level) <= @enumToInt(level)) {
if (@hasDecl(root, "log")) {
root.log(message_level, scope, format, args);
} else if (builtin.mode != .ReleaseSmall) {
const held = std.debug.getStderrMutex().acquire();
defer held.release();
const stderr = io.getStdErr().writer();
nosuspend stderr.print(format, args) catch return;
}
}
}

/// Log an emergency message to stderr. 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 scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.emerg, scope, format, args);
}

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

/// Log a critical message to stderr. 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 scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.crit, scope, format, args);
}

/// Log an error message to stderr. 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 scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.err, scope, format, args);
}

/// Log a warning message to stderr. 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 scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
log(.warn, scope, format, args);
}

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

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

/// Log a debug message to stderr. This log level is intended to be used for
/// messages which are only useful for debugging.
pub fn debug(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
log(.debug, scope, format, args);
}
1 change: 1 addition & 0 deletions lib/std/std.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub const heap = @import("heap.zig");
pub const http = @import("http.zig");
pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const log = @import("log.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math.zig");
pub const mem = @import("mem.zig");
Expand Down