#Adding dynamic log levels to std.log (aka another noob question about comptime)

1 messages · Page 1 of 1 (latest)

brazen cloak
#

Just starting with zig I thought a trivial but real-world exercise would be adding dynamic log levels to std.log. I added a global level easily enough by filtering inside a custom logFn, but what if I want per-scope dynamic levels? I tried adding an array of ScopeLevels, but the compiler tells me it has to be const or comptime because of the @cosmic crescent(.EnumLiteral) param in the logFn. My understanding of comptime is still very vague, why is a global dynamic level acceptable but not an array of them?

brazen cloak
#
    comptime message_level: std.log.Level,
    comptime scope: @Type(.EnumLiteral),
    comptime format: []const u8,
    args: anytype,
) void {
    // error: variable of type '[1]log.ScopeLevel' must be const or comptime
    for (scoped_runtime_log_levels) |scope_level| {
        if (scope_level.scope == scope) return @intFromEnum(message_level) <= @intFromEnum(scope_level.level);
    }

    // this works
    if (@intFromEnum(message_level) > @intFromEnum(global_runtime_log_level)) {
        return;
    }

    std.log.defaultLog(message_level, scope, format, args);
}

pub var global_runtime_log_level: std.log.Level = .warn;

pub var scoped_runtime_log_levels = [_]std.log.ScopeLevel {
    .{ .scope = .customLog, .level = .debug },
};```
#

ultimately it's not practical anyway, since I don't think I have a reasonable way of altering scoped_runtime_log_levels at runtime. In which case the question becomes how else can I associate data with the scope type passed in as a parameter?

storm hornet
#

bumping up. I'm trying to create a simple CLI tool with --verbose flag, and being able to alter log-level in runtime is sort of a must-have. Have run into same issue as OP, any solutions yet?

placid whale
#

What is the error you get?

#

Ah, I guess the @Type(.EnumLiteral) can't be used with runtime variables. In that case you'll have to use a different type for scope levels. Easiest would either be use a []const u8 for the scope and test against @tagName(scope) in the log function, or make an enum of all the scopes you use.

storm hornet
#

yeah, it's the runtime thing. a narrowed down code goes like this:

const std = @import("std");

pub const std_options = struct {
    pub const log_level = .debug;
};

pub fn main() !void {
    var opt = try simargs.parse(allocator, struct {
        verbose: ?bool,
    }, "[file]", null);
    
    if (opt.args.verbose.?) {
        std_options.log_level = .debug;
    }
}

and then:

$ zig build run -- --verbose
src/main.zig:41:20: error: cannot assign to constant
        std_options.log_level = .debug;
        ~~~~~~~~~~~^~~~~~~~~~
#

As far as I understand scopes, different log levels per scope would also have to be defined in std_options at comptime and as OP said, I don't seem to be able to create a global log_level variable, because enum literal needs to be const

#

So if I do anything like:

    pub const log_level = .debug;
    pub const logFn = myLogFn;
};

var global_log_level: @TypeOf(.enum_literal) = .info;

pub fn myLogFn(
    comptime level: std.log.Level,
    comptime scope: @TypeOf(.EnumLiteral),
    comptime format: []const u8,
    args: anytype,
) void {
    if (@intFromEnum(level) < @intFromEnum(global_log_level)) {
        return;
    }

    std.log.defaultLog(level, scope, format, args);
}```

i get:

$ zig build run -- --verbose
src/main.zig:12:23: error: variable of type '@TypeOf(.enum_literal)' must be const or comptime
var global_log_level: @TypeOf(.enum_literal) = .info;
^~~~~~~~~~~~~~~~~~~~~~```

#

I guess, from what you're saying the way to go would be to abandon the levels as defined in std.log and go with a custom enum of sorts