From dee5701b4a32b34ca622cbbf66ce09290b03c294 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sat, 19 Apr 2025 21:41:27 +0100 Subject: [PATCH] Never sync stdout/stderr Detecting if stdout/stderr is tty seems to cause issues when running in Docker (in particular with fly.io) --- src/jetzig/Environment.zig | 27 ++++++++++++++------------ src/jetzig/loggers.zig | 5 +++++ src/jetzig/loggers/LogQueue.zig | 34 ++++++++++++++++++--------------- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/jetzig/Environment.zig b/src/jetzig/Environment.zig index 710b3a6..340b416 100644 --- a/src/jetzig/Environment.zig +++ b/src/jetzig/Environment.zig @@ -196,15 +196,15 @@ pub fn init(parent_allocator: std.mem.Allocator, env_options: EnvironmentOptions const env_file = std.fs.cwd().openFile(options.options.@"env-file", .{}) catch |err| switch (err) { - error.FileNotFound => null, - else => return err, - }; + error.FileNotFound => null, + else => return err, + }; const vars = try Vars.init(allocator, env_file); var launch_logger = LaunchLogger{ - .stdout = stdout, - .stderr = stderr, + .stdout = stdout.file, + .stderr = stderr.file, .silent = env_options.silent, }; @@ -213,8 +213,8 @@ pub fn init(parent_allocator: std.mem.Allocator, env_options: EnvironmentOptions .development_logger = jetzig.loggers.DevelopmentLogger.init( allocator, resolveLogLevel(options.options.@"log-level", jetzig.environment), - stdout, - stderr, + stdout.file, + stderr.file, ), }, .production => jetzig.loggers.Logger{ @@ -304,23 +304,26 @@ pub fn deinit(self: Environment) void { self.parent_allocator.destroy(self.arena); } -fn getLogFile(stream: enum { stdout, stderr }, options: Options) !std.fs.File { +fn getLogFile(stream: enum { stdout, stderr }, options: Options) !jetzig.loggers.LogFile { const path = switch (stream) { .stdout => options.log, .stderr => options.@"log-error", }; if (std.mem.eql(u8, path, "-")) return switch (stream) { - .stdout => std.io.getStdOut(), + .stdout => .{ .file = std.io.getStdOut(), .sync = false }, .stderr => if (std.mem.eql(u8, options.log, "-")) - std.io.getStdErr() + .{ .file = std.io.getStdErr(), .sync = false } else - try std.fs.createFileAbsolute(options.log, .{ .truncate = false }), + .{ + .file = try std.fs.createFileAbsolute(options.log, .{ .truncate = false }), + .sync = true, + }, }; const file = try std.fs.createFileAbsolute(path, .{ .truncate = false }); try file.seekFromEnd(0); - return file; + return .{ .file = file, .sync = true }; } fn getSecret( diff --git a/src/jetzig/loggers.zig b/src/jetzig/loggers.zig index 5153aea..d3a9f6b 100644 --- a/src/jetzig/loggers.zig +++ b/src/jetzig/loggers.zig @@ -12,6 +12,11 @@ pub const NullLogger = @import("loggers/NullLogger.zig"); pub const LogQueue = @import("loggers/LogQueue.zig"); +pub const LogFile = struct { + file: std.fs.File, + sync: bool = false, +}; + pub const LogLevel = enum(u4) { TRACE, DEBUG, INFO, WARN, ERROR, FATAL }; pub const LogFormat = enum { development, production, json, null }; diff --git a/src/jetzig/loggers/LogQueue.zig b/src/jetzig/loggers/LogQueue.zig index abd722c..f22b460 100644 --- a/src/jetzig/loggers/LogQueue.zig +++ b/src/jetzig/loggers/LogQueue.zig @@ -70,7 +70,11 @@ pub fn deinit(self: *LogQueue) void { } /// Set the stdout and stderr outputs. Must be called before `print`. -pub fn setFiles(self: *LogQueue, stdout_file: std.fs.File, stderr_file: std.fs.File) !void { +pub fn setFiles( + self: *LogQueue, + stdout_file: jetzig.loggers.LogFile, + stderr_file: jetzig.loggers.LogFile, +) !void { self.writer = Writer{ .queue = self, .mutex = std.Thread.Mutex{}, @@ -80,11 +84,11 @@ pub fn setFiles(self: *LogQueue, stdout_file: std.fs.File, stderr_file: std.fs.F .stderr_file = stderr_file, .queue = self, }; - self.stdout_is_tty = stdout_file.isTty(); - self.stderr_is_tty = stderr_file.isTty(); + self.stdout_is_tty = stdout_file.file.isTty(); + self.stderr_is_tty = stderr_file.file.isTty(); - self.stdout_colorize = std.io.tty.detectConfig(stdout_file) != .no_color; - self.stderr_colorize = std.io.tty.detectConfig(stderr_file) != .no_color; + self.stdout_colorize = std.io.tty.detectConfig(stdout_file.file) != .no_color; + self.stderr_colorize = std.io.tty.detectConfig(stderr_file.file) != .no_color; self.state = .ready; } @@ -147,8 +151,8 @@ pub const Writer = struct { /// Reader for `LogQueue`. Reads log events from the queue and writes them to the designated /// target (stdout or stderr). pub const Reader = struct { - stdout_file: std.fs.File, - stderr_file: std.fs.File, + stdout_file: jetzig.loggers.LogFile, + stderr_file: jetzig.loggers.LogFile, queue: *LogQueue, pub const PublishOptions = struct { @@ -160,8 +164,8 @@ pub const Reader = struct { pub fn publish(self: *Reader, options: PublishOptions) !void { std.debug.assert(self.queue.state == .ready); - const stdout_writer = self.stdout_file.writer(); - const stderr_writer = self.stderr_file.writer(); + const stdout_writer = self.stdout_file.file.writer(); + const stderr_writer = self.stderr_file.file.writer(); while (true) { self.queue.condition_mutex.lock(); @@ -181,13 +185,13 @@ pub const Reader = struct { .stdout => { stdout_written = true; if (builtin.os.tag == .windows) { - file = self.stdout_file; + file = self.stdout_file.file; } }, .stderr => { stderr_written = true; if (builtin.os.tag == .windows) { - file = self.stderr_file; + file = self.stderr_file.file; } }, } @@ -220,8 +224,8 @@ pub const Reader = struct { } } - if (stdout_written and !self.queue.stdout_is_tty) try self.stdout_file.sync(); - if (stderr_written and !self.queue.stderr_is_tty) try self.stderr_file.sync(); + if (stdout_written and self.stdout_file.sync) try self.stdout_file.file.sync(); + if (stderr_written and self.stderr_file.sync) try self.stderr_file.file.sync(); if (options.oneshot) break; } @@ -289,7 +293,7 @@ test "print to stdout and stderr" { const stderr = try tmp_dir.dir.createFile("stderr.log", .{ .read = true }); defer stderr.close(); - try log_queue.setFiles(stdout, stderr); + try log_queue.setFiles(.{ .file = stdout }, .{ .file = stderr }); try log_queue.print("foo {s}\n", .{"bar"}, .stdout); try log_queue.print("baz {s}\n", .{"qux"}, .stderr); try log_queue.print("quux {s}\n", .{"corge"}, .stdout); @@ -333,7 +337,7 @@ test "long messages" { const stderr = try tmp_dir.dir.createFile("stderr.log", .{ .read = true }); defer stderr.close(); - try log_queue.setFiles(stdout, stderr); + try log_queue.setFiles(.{ .file = stdout }, .{ .file = stderr }); try log_queue.print("foo" ** buffer_size, .{}, .stdout); try log_queue.reader.publish(.{ .oneshot = true });