Fix development logger colors on Windows

This commit is contained in:
Bob Farrell 2024-11-12 08:52:32 +00:00
parent e4da5bc9c8
commit bed91b2131
3 changed files with 40 additions and 32 deletions

View File

@ -29,7 +29,7 @@ pub const codes = .{
}; };
/// Map color codes generated by `std.io.tty.Config.setColor` back to `std.io.tty.Color`. Used by /// Map color codes generated by `std.io.tty.Config.setColor` back to `std.io.tty.Color`. Used by
/// `jetzig.loggers.LogQueue.writeWindows` to parse escape codes so they can be passed to /// `jetzig.util.writeAnsi` to parse escape codes so they can be passed to
/// `std.io.tty.Config.setColor` (using Windows API to set console color mode). /// `std.io.tty.Config.setColor` (using Windows API to set console color mode).
const ansi_colors = .{ const ansi_colors = .{
.{ "30", .black }, .{ "30", .black },

View File

@ -57,7 +57,8 @@ pub fn log(
const formatted_level = colorizedLogLevel(level); const formatted_level = colorizedLogLevel(level);
try self.logWriter(level).print( try self.print(
level,
"{s: >5} [{s}] {s}\n", "{s: >5} [{s}] {s}\n",
.{ formatted_level, iso8601, output }, .{ formatted_level, iso8601, output },
); );
@ -93,7 +94,7 @@ pub fn logRequest(self: DevelopmentLogger, request: *const jetzig.http.Request)
const formatted_level = if (self.stdout_colorized) colorizedLogLevel(.INFO) else @tagName(.INFO); const formatted_level = if (self.stdout_colorized) colorizedLogLevel(.INFO) else @tagName(.INFO);
try self.logWriter(.INFO).print("{s: >5} [{s}] [{s}/{s}/{s}]{s}{s}{s}{s}{s}{s}{s}{s}{s}{s} {s}\n", .{ try self.print(.INFO, "{s: >5} [{s}] [{s}/{s}/{s}]{s}{s}{s}{s}{s}{s}{s}{s}{s}{s} {s}\n", .{
formatted_level, formatted_level,
iso8601, iso8601,
formatted_duration, formatted_duration,
@ -118,7 +119,7 @@ pub fn logSql(self: *const DevelopmentLogger, event: jetzig.jetquery.events.Even
// from multiple threads. JSON logger etc. write in one call and the log queue prevents // from multiple threads. JSON logger etc. write in one call and the log queue prevents
// clobbering, but this is not the case here. // clobbering, but this is not the case here.
const formatted_level = if (self.stdout_colorized) colorizedLogLevel(.INFO) else @tagName(.INFO); const formatted_level = if (self.stdout_colorized) colorizedLogLevel(.INFO) else @tagName(.INFO);
try self.logWriter(.INFO).print("{s} [database] ", .{formatted_level}); try self.print(.INFO, "{s} [database] ", .{formatted_level});
try self.printSql(event.sql orelse ""); try self.printSql(event.sql orelse "");
var duration_buf: [256]u8 = undefined; var duration_buf: [256]u8 = undefined;
@ -128,7 +129,8 @@ pub fn logSql(self: *const DevelopmentLogger, event: jetzig.jetquery.events.Even
self.stdout_colorized, self.stdout_colorized,
) else ""; ) else "";
try self.logWriter(.INFO).print( try self.print(
.INFO,
std.fmt.comptimePrint(" [{s}]\n", .{jetzig.colors.cyan("{s}")}), std.fmt.comptimePrint(" [{s}]\n", .{jetzig.colors.cyan("{s}")}),
.{formatted_duration}, .{formatted_duration},
); );
@ -233,7 +235,7 @@ fn printSql(self: *const DevelopmentLogger, sql: []const u8) !void {
}, },
} }
} }
try self.logWriter(.INFO).print("{s}", .{stream.getWritten()}); try self.print(.INFO, "{s}", .{stream.getWritten()});
} }
pub fn logError(self: *const DevelopmentLogger, err: anyerror) !void { pub fn logError(self: *const DevelopmentLogger, err: anyerror) !void {
@ -249,12 +251,41 @@ pub fn logError(self: *const DevelopmentLogger, err: anyerror) !void {
try self.log(.ERROR, "Encountered Error: {s}", .{@errorName(err)}); try self.log(.ERROR, "Encountered Error: {s}", .{@errorName(err)});
} }
fn logWriter(self: DevelopmentLogger, comptime level: jetzig.loggers.LogLevel) std.fs.File.Writer { fn logFile(self: DevelopmentLogger, comptime level: jetzig.loggers.LogLevel) std.fs.File {
const target = comptime jetzig.loggers.logTarget(level); const target = comptime jetzig.loggers.logTarget(level);
return switch (target) { return switch (target) {
.stdout => self.stdout, .stdout => self.stdout,
.stderr => self.stderr, .stderr => self.stderr,
}.writer(); };
}
fn logWriter(self: DevelopmentLogger, comptime level: jetzig.loggers.LogLevel) std.fs.File.Writer {
return self.logFile(level).writer();
}
fn print(
self: DevelopmentLogger,
comptime level: jetzig.loggers.LogLevel,
comptime template: []const u8,
args: anytype,
) !void {
const log_writer = self.logWriter(level);
const count = std.fmt.count(template, args);
const buf_size = 4096;
if (count <= buf_size) {
var buf: [buf_size]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf);
const writer = stream.writer();
try writer.print(template, args);
try jetzig.util.writeAnsi(self.logFile(level), log_writer, stream.getWritten());
} else {
const buf = try self.allocator.alloc(u8, count);
defer self.allocator.free(buf);
var stream = std.io.fixedBufferStream(buf);
const writer = stream.writer();
try writer.print(template, args);
try jetzig.util.writeAnsi(self.logFile(level), log_writer, stream.getWritten());
}
} }
inline fn colorizedLogLevel(comptime level: LogLevel) []const u8 { inline fn colorizedLogLevel(comptime level: LogLevel) []const u8 {

View File

@ -164,7 +164,6 @@ pub const Reader = struct {
var stdout_written = false; var stdout_written = false;
var stderr_written = false; var stderr_written = false;
var file: std.fs.File = undefined; var file: std.fs.File = undefined;
var colorize = false;
while (try self.queue.popFirst()) |event| { while (try self.queue.popFirst()) |event| {
self.queue.writer.mutex.lock(); self.queue.writer.mutex.lock();
@ -175,14 +174,12 @@ pub const Reader = struct {
stdout_written = true; stdout_written = true;
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
file = self.stdout_file; file = self.stdout_file;
colorize = self.queue.stdout_colorize;
} }
}, },
.stderr => { .stderr => {
stderr_written = true; stderr_written = true;
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
file = self.stderr_file; file = self.stderr_file;
colorize = self.queue.stderr_colorize;
} }
}, },
} }
@ -199,7 +196,7 @@ pub const Reader = struct {
continue; continue;
} }
try jetzig.util.writeAnsi(file, writer, event.message[0..event.len]); try writer.writeAll(event.message[0..event.len]);
self.queue.writer.position -= 1; self.queue.writer.position -= 1;
@ -270,26 +267,6 @@ fn initPool(allocator: std.mem.Allocator, T: type) std.heap.MemoryPool(T) {
return std.heap.MemoryPool(T).initPreheated(allocator, max_pool_len) catch @panic("OOM"); return std.heap.MemoryPool(T).initPreheated(allocator, max_pool_len) catch @panic("OOM");
} }
fn writeWindows(file: std.fs.File, writer: anytype, event: Event) !void {
var info: std.os.windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
_ = std.os.windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info);
var it = std.mem.tokenizeSequence(u8, event.message[0..event.len], "\x1b[");
while (it.next()) |token| {
if (std.mem.indexOfScalar(u8, token, 'm')) |index| {
if (index > 0 and index + 1 < token.len) {
if (jetzig.colors.windows_map.get(token[0..index])) |color| {
try std.os.windows.SetConsoleTextAttribute(file.handle, color);
try writer.writeAll(token[index + 1 ..]);
continue;
}
}
}
// Fallback
try writer.writeAll(token);
}
}
test "print to stdout and stderr" { test "print to stdout and stderr" {
var log_queue = LogQueue.init(std.testing.allocator); var log_queue = LogQueue.init(std.testing.allocator);
defer log_queue.deinit(); defer log_queue.deinit();