2024-11-30 23:50:33 +00:00
|
|
|
const std = @import("std");
|
|
|
|
|
const cham = @import("chameleon");
|
|
|
|
|
|
2024-12-07 02:29:20 +00:00
|
|
|
const SmartString = @import("../util/utils.zig").SmartString;
|
2024-12-06 06:31:55 +00:00
|
|
|
|
2024-11-30 23:50:33 +00:00
|
|
|
const log = std.log;
|
|
|
|
|
|
|
|
|
|
pub const Level = log.Level;
|
|
|
|
|
pub const Scope = @Type(.EnumLiteral);
|
|
|
|
|
|
2024-12-01 02:43:08 +00:00
|
|
|
pub const Color = enum {
|
|
|
|
|
red,
|
|
|
|
|
green,
|
|
|
|
|
yellow,
|
|
|
|
|
blue,
|
|
|
|
|
magenta,
|
|
|
|
|
cyan,
|
|
|
|
|
white,
|
|
|
|
|
default,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub const ScopeModifier = struct {
|
2024-12-07 02:29:20 +00:00
|
|
|
scope: SmartString,
|
2024-12-01 02:43:08 +00:00
|
|
|
color: ?Color = .default,
|
|
|
|
|
bright: bool = false,
|
2024-12-07 02:29:20 +00:00
|
|
|
rename: ?SmartString = null,
|
2024-12-01 02:43:08 +00:00
|
|
|
};
|
|
|
|
|
|
2024-11-30 23:50:33 +00:00
|
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
|
|
|
|
|
|
const Globals = struct {
|
|
|
|
|
allocator: Allocator,
|
|
|
|
|
|
|
|
|
|
enableFileOutput: bool = false,
|
|
|
|
|
outputFile: ?std.fs.File = null,
|
|
|
|
|
|
2024-12-01 02:43:08 +00:00
|
|
|
additionalScopes: std.ArrayList(ScopeModifier),
|
|
|
|
|
|
|
|
|
|
fn initOrGetFile(self: *Globals) !std.fs.File {
|
|
|
|
|
if (self.outputFile) |file| {
|
|
|
|
|
return file;
|
|
|
|
|
} else {
|
|
|
|
|
return error.NoFileSet;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-30 23:50:33 +00:00
|
|
|
fn init(allocator: Allocator) !Globals {
|
|
|
|
|
return .{
|
2024-12-01 02:43:08 +00:00
|
|
|
.allocator = allocator,
|
|
|
|
|
|
2024-12-02 02:50:58 +00:00
|
|
|
.enableFileOutput = false,
|
|
|
|
|
.outputFile = null,
|
|
|
|
|
|
2024-12-01 02:43:08 +00:00
|
|
|
.additionalScopes = std.ArrayList(ScopeModifier).init(allocator),
|
2024-11-30 23:50:33 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn deinit(self: *Globals) void {
|
|
|
|
|
if (self.outputFile) |file| {
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-06 06:31:55 +00:00
|
|
|
for (self.additionalScopes.items) |*modifier| {
|
|
|
|
|
if (modifier.*.rename) |*value| {
|
|
|
|
|
value.deinit();
|
2024-12-02 07:16:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-06 06:31:55 +00:00
|
|
|
modifier.*.scope.deinit();
|
2024-12-02 07:16:32 +00:00
|
|
|
}
|
2024-12-01 02:43:08 +00:00
|
|
|
self.additionalScopes.deinit();
|
|
|
|
|
|
2024-11-30 23:50:33 +00:00
|
|
|
self.* = undefined;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var core: ?Globals = null;
|
|
|
|
|
|
|
|
|
|
pub const config = struct {
|
|
|
|
|
pub fn enableFileOutput(value: bool) void {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
globals.enableFileOutput = value;
|
|
|
|
|
} else {
|
|
|
|
|
unreachable; // logging is not initialized
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn isFileOutput() bool {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
return globals.enableFileOutput;
|
|
|
|
|
} else {
|
|
|
|
|
unreachable; // logging is not initialized
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn setOutputFile(file: std.fs.File) !void {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
globals.outputFile = file;
|
|
|
|
|
} else {
|
|
|
|
|
unreachable; // logging is not initialized
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn getOutputFile() ?*const std.fs.File {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
return &globals.outputFile;
|
|
|
|
|
} else {
|
|
|
|
|
unreachable; // logging is not initialized
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-01 02:43:08 +00:00
|
|
|
|
|
|
|
|
pub fn addScope(modifier: ScopeModifier) !void {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
try globals.additionalScopes.append(modifier);
|
|
|
|
|
} else {
|
|
|
|
|
unreachable; // logging is not initialized
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-30 23:50:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub fn init(allocator: Allocator) !void {
|
2024-12-02 02:50:58 +00:00
|
|
|
if (core) |_| {
|
|
|
|
|
return error.AlreadyInitialized;
|
|
|
|
|
}
|
2024-11-30 23:50:33 +00:00
|
|
|
core = try Globals.init(allocator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn deinit() void {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
globals.deinit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// If using this log function, you *must* call `init` before any logging occurs.
|
|
|
|
|
/// Otherwise, it will complain. A lot.
|
|
|
|
|
pub fn logFn(comptime level: Level, comptime scope: Scope, comptime format: []const u8, args: anytype) void {
|
|
|
|
|
nosuspend logFnImpl(level, scope, format, args) catch |err| {
|
|
|
|
|
std.debug.print("lys: error while logging: {s}\n", .{@errorName(err)});
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-02 02:50:58 +00:00
|
|
|
fn get() *Globals {
|
|
|
|
|
if (core) |*globals| {
|
|
|
|
|
return globals;
|
2024-12-02 02:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unreachable; // logging is not initialized
|
2024-12-01 02:43:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-30 23:50:33 +00:00
|
|
|
fn logFnImpl(comptime level: Level, comptime scope: Scope, comptime format: []const u8, args: anytype) !void {
|
2024-12-02 02:50:58 +00:00
|
|
|
const globals = get();
|
2024-12-02 03:16:30 +00:00
|
|
|
var arena = std.heap.ArenaAllocator.init(globals.allocator);
|
2024-12-02 02:50:58 +00:00
|
|
|
var c = cham.initRuntime(.{
|
2024-11-30 23:50:33 +00:00
|
|
|
.allocator = arena.allocator(),
|
|
|
|
|
});
|
2024-12-01 02:43:08 +00:00
|
|
|
defer {
|
|
|
|
|
c.deinit();
|
|
|
|
|
arena.deinit();
|
|
|
|
|
}
|
2024-11-30 23:50:33 +00:00
|
|
|
|
2024-12-02 02:54:13 +00:00
|
|
|
const scopeText = scopeTextBlk: {
|
|
|
|
|
switch (scope) {
|
|
|
|
|
.default => break :scopeTextBlk "main",
|
|
|
|
|
.gpa => {
|
2024-12-06 06:39:02 +00:00
|
|
|
const gpa = "GPAlloc";
|
2024-12-02 02:54:13 +00:00
|
|
|
|
|
|
|
|
break :scopeTextBlk try c.redBright().fmt("{s}", .{gpa});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
else => {
|
|
|
|
|
for (globals.additionalScopes.items) |modifier| {
|
2024-12-06 06:39:02 +00:00
|
|
|
if (std.mem.eql(u8, modifier.scope.data, @tagName(scope))) {
|
2024-12-02 02:54:13 +00:00
|
|
|
const text = blk: {
|
|
|
|
|
if (modifier.rename) |rename| {
|
2024-12-06 06:39:02 +00:00
|
|
|
break :blk rename.data;
|
2024-12-02 02:54:13 +00:00
|
|
|
} else {
|
|
|
|
|
break :blk @tagName(scope);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (modifier.color) |color| {
|
|
|
|
|
switch (color) {
|
|
|
|
|
.default => break :scopeTextBlk text,
|
|
|
|
|
.blue => break :scopeTextBlk try c.blue().fmt("{s}", .{text}),
|
|
|
|
|
.green => break :scopeTextBlk try c.green().fmt("{s}", .{text}),
|
|
|
|
|
.red => break :scopeTextBlk try c.red().fmt("{s}", .{text}),
|
|
|
|
|
.white => break :scopeTextBlk try c.white().fmt("{s}", .{text}),
|
|
|
|
|
.yellow => break :scopeTextBlk try c.yellow().fmt("{s}", .{text}),
|
|
|
|
|
.magenta => break :scopeTextBlk try c.magenta().fmt("{s}", .{text}),
|
|
|
|
|
.cyan => break :scopeTextBlk try c.cyan().fmt("{s}", .{text}),
|
|
|
|
|
}
|
2024-12-01 02:43:08 +00:00
|
|
|
} else {
|
2024-12-02 02:54:13 +00:00
|
|
|
break :scopeTextBlk text;
|
2024-12-01 02:43:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-12-02 02:54:52 +00:00
|
|
|
} else {
|
|
|
|
|
break :scopeTextBlk @tagName(scope);
|
2024-12-01 02:43:08 +00:00
|
|
|
}
|
2024-12-02 02:54:13 +00:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unreachable;
|
2024-12-01 02:43:08 +00:00
|
|
|
};
|
2024-11-30 23:50:33 +00:00
|
|
|
|
2024-12-01 02:43:08 +00:00
|
|
|
const levelText = switch (level) {
|
|
|
|
|
.debug => try c.gray().fmt("{s: >5}", .{"DEBUG"}),
|
|
|
|
|
.info => try c.white().fmt("{s: >5}", .{"INFO"}),
|
|
|
|
|
.warn => try c.yellow().fmt("{s: >5}", .{"WARN"}),
|
|
|
|
|
.err => try c.red().fmt("{s: >5}", .{"ERROR"}),
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-06 06:39:02 +00:00
|
|
|
const prefix = try std.fmt.allocPrint(arena.allocator(), "[{s}] {s}:", .{
|
2024-12-01 02:43:08 +00:00
|
|
|
levelText,
|
|
|
|
|
scopeText,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const message = try std.fmt.allocPrint(arena.allocator(), format, args);
|
|
|
|
|
|
2024-12-02 02:50:58 +00:00
|
|
|
if (globals.enableFileOutput and globals.outputFile != null) {
|
2024-12-01 02:43:08 +00:00
|
|
|
var file = try globals.initOrGetFile();
|
|
|
|
|
var writer = file.writer().any();
|
|
|
|
|
|
|
|
|
|
nosuspend try writer.print("{s} {s}\n", .{
|
|
|
|
|
prefix,
|
|
|
|
|
message,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
nosuspend std.debug.print("{s} {s}\n", .{
|
|
|
|
|
prefix,
|
|
|
|
|
message,
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-11-30 23:50:33 +00:00
|
|
|
}
|
2024-12-02 02:50:58 +00:00
|
|
|
|
2024-12-06 06:39:02 +00:00
|
|
|
const t = std.testing;
|
|
|
|
|
|
|
|
|
|
test "logFn works" {
|
2024-12-02 02:50:58 +00:00
|
|
|
try init(t.allocator);
|
|
|
|
|
defer deinit();
|
|
|
|
|
|
2024-12-06 06:39:02 +00:00
|
|
|
try config.addScope(.{
|
2024-12-07 02:29:20 +00:00
|
|
|
.scope = SmartString.constant("someScope"),
|
|
|
|
|
.rename = SmartString.constant("some rename"),
|
2024-12-06 06:39:02 +00:00
|
|
|
.color = .green,
|
|
|
|
|
});
|
|
|
|
|
try config.addScope(.{
|
2024-12-07 02:29:20 +00:00
|
|
|
.scope = SmartString.constant("other"),
|
|
|
|
|
.rename = SmartString.constant("other rename"),
|
2024-12-06 06:39:02 +00:00
|
|
|
.color = .blue,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try logFnImpl(.err, .default, "hello world", .{});
|
|
|
|
|
try logFnImpl(.info, .someScope, "hello world", .{});
|
|
|
|
|
try logFnImpl(.warn, .other, "hello world", .{});
|
2024-12-02 02:50:58 +00:00
|
|
|
}
|