Confusing Panic when migrating to 0.15

In my main hobby project, I handle error logging by creating a file and writing to it. I am having trouble with migrating this to zig 0.15.1 however, because for some reason, in this particular part of the code, when I flush the write there is always a WriteFailed error.

logging.zig

const std = @import("std");
const fs = std.fs;
const Allocator = std.mem.Allocator;

const c = @cImport({
    @cInclude("time.h");
});

pub var log_file: ?fs.File = null;

fn init() !fs.File {
    var path_buffer: [1024]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&path_buffer);
    const alloc = fba.allocator();

    const log_dir = try getLogDir(alloc);
    const time_stamp = try getTimeStamp(alloc);
    const log_name = try std.mem.join(alloc, "", &.{ time_stamp, ".log" });

    return log_dir.createFile(log_name, .{});
}

fn getLogDir(alloc: Allocator) !fs.Dir {
    const data_dir = try fs.getAppDataDir(alloc, "zepto");
    _ = fs.openDirAbsolute(data_dir, .{}) catch {
        try fs.makeDirAbsolute(data_dir);
    };

    const log_dir_path = try fs.path.join(alloc, &.{ data_dir, "logs" });

    const file_dir = fs.openDirAbsolute(log_dir_path, .{}) catch {
        try fs.makeDirAbsolute(log_dir_path);
        return fs.openDirAbsolute(log_dir_path, .{});
    };
    return file_dir;
}

fn getTimeStamp(alloc: Allocator) ![]const u8 {
    var now: c.time_t = undefined;
    _ = c.time(&now);
    const time_info = c.localtime(&now);
    const date = c.asctime(time_info);
    const time_stamp = try std.fmt.allocPrint(alloc, "{s}", .{date});
    return time_stamp[0 .. time_stamp.len - 1]; //remove trailing newLine
}

pub fn log(
    comptime level: std.log.Level,
    comptime scope: @TypeOf(.EnumLiteral),
    comptime format: []const u8,
    args: anytype,
) void {
    log_file = log_file orelse init() catch |err| @panic(@errorName(err));

    var buff: [1024]u8 = undefined;
    var writer = log_file.?.writer(&buff).interface;

    _ = writer.print("[{s}] {s}: ", .{ @tagName(level), @tagName(scope) }) catch |err|
        @panic(@errorName(err));

    _ = writer.print(format, args) catch |err| @panic(@errorName(err));

    writer.flush() catch |err| @panic(@errorName(err)); //This is the source of the panic

    _ = writer.writeAll("\n") catch |err| @panic(@errorName(err));
}

If I don’t flush the contents of the log file is never written. Can anyone help me fix this bug?

I believe it is this:

var writer = &log_file.?.writer(&buff).interface; // use &

or more simple

var writer = log_file.?.writer(&buff);
writer.interface.print(...);
writer.interface.flush();

This is still not correct, as writer(&buff) becomes a const temporary here.

Best to just put writer into a var.

1 Like

Ow… but the error is in that line, isn’t it? Ok now I will check it first…

Yeah I was right.

var log_file: std.fs.File = std.fs.createFileAbsolute("C:/Tmp/hello.txt", .{}) catch |err| @panic(@errorName(err));
var buff: [1024]u8 = undefined;
var writer = log_file.writer(&buff);

_ = writer.interface.print("writer.interface", .{}) catch |err| @panic(@errorName(err));
writer.interface.flush() catch |err| @panic(@errorName(err));
_ = writer.interface.writeAll("\n") catch |err| @panic(@errorName(err));

var intf = &writer.interface; // Omit the & and you will crash from here.

_ = intf.print("intf", .{}) catch |err| @panic(@errorName(err));
intf.flush() catch |err| @panic(@errorName(err));
_ = intf.writeAll("\n") catch |err| @panic(@errorName(err));

Yeah, interface copying is a commonly encountered footgun. I was just referring to an issue with your one-liner.

3 Likes