const std = @import("std"); const fs = std.fs; var log_level: Level = .Debug; var writer: ?std.fs.File.Writer = null; const Level = enum { Debug, Info, Warn, Error, Disable, }; pub fn init(log_file: []const u8, level: Level) !void { log_level = level; const file = try fs.cwd().createFile(log_file, .{}); writer = file.writer(); } pub fn setLevel(level: Level) void { log_level = level; } pub fn deinit() void { if (writer) |w| { w.context.close(); } } pub fn debug(comptime format: []const u8, args: anytype) void { if (@intFromEnum(log_level) <= @intFromEnum(Level.Debug)) { if (writer) |w| { _ = w.write("[Debug] ") catch {}; std.fmt.format(w, format, args) catch {}; _ = w.writeByte('\n') catch {}; } } } pub fn info(comptime format: []const u8, args: anytype) void { if (@intFromEnum(log_level) <= @intFromEnum(Level.Info)) { if (writer) |w| { _ = w.write("\x1b[1m[Info] ") catch {}; std.fmt.format(w, format, args) catch {}; _ = w.write("\x1b[0m\n") catch {}; } } } pub fn warn(comptime format: []const u8, args: anytype) void { if (@intFromEnum(log_level) <= @intFromEnum(Level.Warn)) { if (writer) |w| { _ = w.write("\x1b[1;33m[Warn] ") catch {}; std.fmt.format(w, format, args) catch {}; _ = w.write("\x1b[0m\n") catch {}; } } } pub fn err(comptime format: []const u8, args: anytype) void { if (@intFromEnum(log_level) <= @intFromEnum(Level.Error)) { if (writer) |w| { _ = w.write("\x1b[1;31m[Error] ") catch {}; std.fmt.format(w, format, args) catch {}; _ = w.write("\x1b[0m\n") catch {}; } } } test "debug" { try init("test.log", .Debug); setLevel(.Debug); debug("Hello", .{}); info("Hello", .{}); warn("Hello", .{}); err("Hello", .{}); deinit(); const file = try fs.cwd().openFile("test.log", .{}); defer file.close(); const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024); defer std.testing.allocator.free(contents); try std.testing.expectEqualStrings("[Debug] Hello\n\x1b[1m[Info] Hello\x1b[0m\n\x1b[1;33m[Warn] Hello\x1b[0m\n\x1b[1;31m[Error] Hello\x1b[0m\n", contents); try fs.cwd().deleteFile("test.log"); } test "info" { try init("test.log", .Info); setLevel(.Info); debug("Hello", .{}); info("Hello", .{}); warn("Hello", .{}); err("Hello", .{}); deinit(); const file = try fs.cwd().openFile("test.log", .{}); defer file.close(); const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024); defer std.testing.allocator.free(contents); try std.testing.expectEqualStrings("\x1b[1m[Info] Hello\x1b[0m\n\x1b[1;33m[Warn] Hello\x1b[0m\n\x1b[1;31m[Error] Hello\x1b[0m\n", contents); try fs.cwd().deleteFile("test.log"); } test "warn" { try init("test.log", .Warn); setLevel(.Warn); debug("Hello", .{}); info("Hello", .{}); warn("Hello", .{}); err("Hello", .{}); deinit(); const file = try fs.cwd().openFile("test.log", .{}); defer file.close(); const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024); defer std.testing.allocator.free(contents); try std.testing.expectEqualStrings("\x1b[1;33m[Warn] Hello\x1b[0m\n\x1b[1;31m[Error] Hello\x1b[0m\n", contents); try fs.cwd().deleteFile("test.log"); } test "error" { try init("test.log", .Error); setLevel(.Error); debug("Hello", .{}); info("Hello", .{}); warn("Hello", .{}); err("Hello", .{}); deinit(); const file = try fs.cwd().openFile("test.log", .{}); defer file.close(); const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024); defer std.testing.allocator.free(contents); try std.testing.expectEqualStrings("\x1b[1;31m[Error] Hello\x1b[0m\n", contents); try fs.cwd().deleteFile("test.log"); }