Colors & Styling
Logly colors console output via tint.zig. Two rendering modes plus off:
var config = logly.Config.default();
config.colorMode = .horizontal; // whole record in level color (default)
config.colorMode = .vertical; // per-column colors (below)
config.colorMode = .none; // plain textHorizontal
One level color for the complete record (existing whole-line behavior).
[entire log record uses one level color]
2026-10-07 12:00:00 [INFO] Application startedVertical
Independent colors per field. Unset columns render uncolored; level falls back to the resolved level color.
config.columnColors.timestamp = logly.Color.Tint.color.ansi4.blue;
config.columnColors.message = logly.Color.Tint.color.ansi4.yellow;[timestamp: blue] [level: green] [message: yellow]
2026-10-07 12:00:00 [INFO] Application startedPrecedence: color/globalColorDisplay off wins first, then colorMode, then column colors, then level colors, then defaults. Multiline messages keep the color across lines with a trailing reset; every field reset prevents leakage into later output.
Format Color Policy
Serialization is always valid on its own; color is a presentation layer applied afterwards, never inside the data. All rendering goes through tint.zig — Logly keeps no second ANSI implementation.
| Format | No color | Horizontal | Vertical |
|---|---|---|---|
text, logfmt | plain | whole record in level color | per-field column colors |
json, ndjson | plain valid JSON | whole rendered line wrapped | JSON re-rendered with per-key colors |
syslog, syslog3164 | plain | whole rendered line wrapped | whole rendered line wrapped |
msgpack | raw bytes | unsupported (bytes stay raw) | unsupported (bytes stay raw) |
| custom template | plain | whole-line wrap | per-field spans |
Stripping ANSI from any colored text/JSON/syslog output recovers the exact uncolored bytes. msgpack output never contains color under any setting.
Per-sink selection: set .color = true to force presentation color even when piped, .color = false to disable, or leave null for automatic behavior (colored on interactive terminals, plain otherwise). A text console plus a plain JSON file plus a binary network sink can share one record without interfering, because presentation is computed per sink from the same record.
Platform Support
| Platform | Color Support | Notes |
|---|---|---|
| Linux | ✅ Native | Works out of the box |
| macOS | ✅ Native | Works out of the box |
| Windows 10+ | ✅ Requires init | Call Terminal.enableAnsiColors() |
| VS Code Terminal | ✅ Full | Works on all platforms |
| Windows Console | ⚠️ Legacy | May need VT processing enabled |
Enabling Colors
Windows Setup
On Windows, you must enable ANSI color support at the start of your program:
const logly = @import("logly");
pub fn main() !void {
// Enable ANSI colors on Windows (no-op on Linux/macOS)
_ = logly.Terminal.enableAnsiColors();
// ... rest of your code
}Check Color Support
if (logly.Terminal.supportsAnsiColors()) {
// Terminal supports colors
}Whole-Line Coloring
Logly colors the entire log line (timestamp, level, and message), not just the level tag. This provides better visual scanning:
[2024-01-15 10:30:45] [INFO] Application started <- Entire line white
[2024-01-15 10:30:46] [WARNING] Low disk space <- Entire line yellow
[2024-01-15 10:30:47] [ERROR] Connection failed <- Entire line redBuilt-in Level Colors
| Level | Priority | ANSI Code | Color | Preview |
|---|---|---|---|---|
| TRACE | 5 | 36 | Cyan | \x1b[36m |
| DEBUG | 10 | 34 | Blue | \x1b[34m |
| INFO | 20 | 37 | White | \x1b[37m |
| SUCCESS | 25 | 32 | Green | \x1b[32m |
| WARNING | 30 | 33 | Yellow | \x1b[33m |
| ERROR | 40 | 31 | Red | \x1b[31m |
| FAIL | 45 | 35 | Magenta | \x1b[35m |
| CRITICAL | 50 | 91 | Bright Red | \x1b[91m |
ANSI Color Code Reference
Basic Colors (Foreground)
| Code | Color |
|---|---|
| 30 | Black |
| 31 | Red |
| 32 | Green |
| 33 | Yellow |
| 34 | Blue |
| 35 | Magenta |
| 36 | Cyan |
| 37 | White |
Bright Colors (Foreground)
| Code | Color |
|---|---|
| 90 | Bright Black (Gray) |
| 91 | Bright Red |
| 92 | Bright Green |
| 93 | Bright Yellow |
| 94 | Bright Blue |
| 95 | Bright Magenta |
| 96 | Bright Cyan |
| 97 | Bright White |
Background Colors
| Code | Color |
|---|---|
| 40 | Black Background |
| 41 | Red Background |
| 42 | Green Background |
| 43 | Yellow Background |
| 44 | Blue Background |
| 45 | Magenta Background |
| 46 | Cyan Background |
| 47 | White Background |
Text Styles
| Code | Style |
|---|---|
| 0 | Reset |
| 1 | Bold |
| 2 | Dim |
| 3 | Italic |
| 4 | Underline |
| 5 | Blink |
| 7 | Reverse |
| 8 | Hidden |
| 9 | Strikethrough |
Combining Codes
Combine multiple codes with semicolons:
| Example | Description |
|---|---|
"31" | Red text |
"31;1" | Bold red text |
"31;4" | Underlined red text |
"31;1;4" | Bold underlined red |
"97;41" | White text on red background |
"36;1" | Bold cyan |
"33;7" | Yellow reverse (yellow background, black text) |
Custom Level Colors
Create custom log levels with your own colors:
const logly = @import("logly");
pub fn main() !void {
_ = logly.Terminal.enableAnsiColors();
var gpa = std.heap.DebugAllocator(.{}){};
const allocator = gpa.allocator();
const logger = try logly.Logger.init(allocator);
defer logger.deinit();
// Add custom levels with custom colors
try logger.addCustomLevel("AUDIT", 35, "35"); // Magenta
try logger.addCustomLevel("NOTICE", 22, "36;1"); // Bold Cyan
try logger.addCustomLevel("ALERT", 48, "91;1"); // Bold Bright Red
try logger.addCustomLevel("SECURITY", 55, "97;41"); // White on Red BG
try logger.addCustomLevel("METRIC", 15, "32;1"); // Bold Green
// Use custom levels
try logger.custom("AUDIT", "User login event", @src());
try logger.custom("NOTICE", "Important notice", @src());
try logger.custom("ALERT", "High CPU usage detected", @src());
try logger.custom("SECURITY", "Unauthorized access attempt", @src());
try logger.customf("METRIC", "Response time: {d}ms", .{42}, @src());
}Enhanced Color Options (v0.1.8)
Level Colors
Each log level has a default tint color. Level.defaultColor() returns a tint Color value; render it to an SGR sequence when writing output:
const Level = logly.Level;
// Default color as a tint Color value.
const traceColor = Level.trace.defaultColor();
// Render to an escape sequence for output.
const seq = logly.Color.sequence(traceColor, .trueColor);
// seq.slice() gives e.g. "\x1b[36m" for trace.tint Colors
All terminal colors come from tint.zig via logly.Color:
const tint = logly.Color.Tint;
// Named ANSI colors.
const red = tint.color.ansi4.red;
const brightRed = tint.color.ansi4.brightRed;
// 256-color palette.
const orange = tint.color.ansi256.index(208);
// RGB colors.
const coral = tint.color.rgb(255, 127, 80);
// Render any color to a sequence.
const seq = logly.Color.sequence(red, .trueColor);Parse user-supplied text (names, #rrggbb, or SGR params) with logly.Color.parse:
const magenta = logly.Color.parse("magenta").?;
const custom = logly.Color.parse("38;5;196").?;Theme Presets
Logly includes multiple color theme presets built on tint colors:
const Formatter = logly.Formatter;
// Use preset themes (each field is a tint Color).
const defaultTheme = Formatter.Theme{}; // Standard colors
const brightTheme = Formatter.Theme.bright(); // Bright colors
const dimTheme = Formatter.Theme.dim(); // Dim colors
const minimalTheme = Formatter.Theme.minimal(); // Subtle grays
const neonTheme = Formatter.Theme.neon(); // Vivid 256-colors
const pastelTheme = Formatter.Theme.pastel(); // Soft colors
const darkTheme = Formatter.Theme.dark(); // Dark terminal optimized
const lightTheme = Formatter.Theme.light(); // Light terminal optimized
// Apply theme to formatter
var formatter = Formatter.init(allocator);
formatter.setTheme(Formatter.Theme.neon());Custom Themes
Define per-level tint colors directly:
const Theme = logly.Formatter.Theme;
const custom = Theme{
.trace = logly.Color.parse("cyan").?,
.err = logly.Color.Tint.color.rgb(255, 80, 80),
};
formatter.setTheme(custom);Advanced CustomLevel Options
Create custom levels with full tint color control:
const CustomLevel = logly.CustomLevel;
// Basic custom level with a tint color
const audit = CustomLevel.init("AUDIT", 35, logly.Color.parse("cyan").?);
// Styled custom level (explicit tint style)
const styled = CustomLevel.initStyled("STYLED", 45, .{
.foreground = logly.Color.Tint.color.red,
.underline = true,
});
// With background color
const alert = CustomLevel.initWithBackground(
"ALERT", 50,
logly.Color.Tint.color.ansi4.brightWhite,
logly.Color.Tint.color.ansi4.red,
);Color Configuration
Global Color Control
var config = logly.Config.default();
// Master switch - disables colors everywhere
config.globalColorDisplay = false;
logger.configure(config);
// Re-enable colors
config.globalColorDisplay = true;
config.color = true;
logger.configure(config);Per-Sink Color Control
// Console sink with colors (default)
_ = try logger.addSink(.{
.color = true, // Explicitly enable colors
});
// File sink without colors (default for files)
_ = try logger.addSink(.{
.path = "logs/app.log",
.color = null, // Auto-detect: false for files
});
// File sink WITH colors (for terminals that read log files)
_ = try logger.addSink(.{
.path = "logs/colored.log",
.color = true, // Force colors in file
});Colors in Different Output Formats
Console Output (Default)
Colors are enabled by default for console output:
try logger.info("White text"); // \x1b[37m...\x1b[0m
try logger.warning("Yellow text"); // \x1b[33m...\x1b[0m
try logger.err("Red text"); // \x1b[31m...\x1b[0mJSON Output Is Never Colored
Serialization formats never contain ANSI sequences, even with colors enabled — color is a presentation concern for human-readable text only:
var config = logly.Config.default();
config.format = .json;
config.color = true; // Has no effect on JSON output
logger.configure(config);
try logger.info("Plain JSON object, no escape codes");Color applies to text and logfmt output. json, ndjson, syslog, syslog3164, and msgpack output is always plain.
File Output
By default, file sinks disable colors. To enable:
_ = try logger.addSink(.{
.path = "logs/colored.log",
.color = true, // Enable ANSI codes in file
});Note: Files with ANSI codes will display correctly in:
catcommand on Linux/macOSless -Rcommand- VS Code with ANSI color extensions
- Modern terminal emulators
Custom Format Strings with Colors
Custom format strings also support colors:
var config = logly.Config.default();
config.logFormat = "{time} | {level} | {message}";
config.color = true;
logger.configure(config);
// Output: \x1b[33m2024-01-15 10:30:45 | WARNING | Message\x1b[0m
try logger.warning("Formatted warning");Disabling Colors
For Production/Log Aggregation
// Disable colors globally
var config = logly.Config.default();
config.color = false;
config.globalColorDisplay = false;
logger.configure(config);For Specific Sinks
// JSON file without colors (for log aggregation)
_ = try logger.addSink(.{
.path = "logs/app.json",
.format = .json,
.color = false, // No ANSI codes
});Complete Example
const std = @import("std");
const logly = @import("logly");
pub fn main() !void {
// Enable Windows ANSI support
_ = logly.Terminal.enableAnsiColors();
var gpa = std.heap.DebugAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const logger = try logly.Logger.init(allocator);
defer logger.deinit();
// Add custom colored levels (tint colors)
try logger.addCustomLevel("AUDIT", 35, logly.Color.parse("magenta").?);
try logger.addCustomLevel("NOTICE", 22, logly.Color.parse("cyan").?);
try logger.addCustomLevel("HIGHLIGHT", 52, logly.Color.parse("yellow").?);
// Standard levels (all colored)
try logger.trace("Cyan trace message");
try logger.debug("Blue debug message");
try logger.info("White info message");
try logger.success("Green success message");
try logger.warning("Yellow warning message");
try logger.err("Red error message");
try logger.fail("Magenta fail message");
try logger.critical("Bright red critical message");
// Custom levels
try logger.custom("AUDIT", "Magenta audit message");
try logger.custom("NOTICE", "Bold cyan notice");
try logger.custom("HIGHLIGHT", "Yellow reverse highlight");
// Add file sink with colors
_ = try logger.addSink(.{
.path = "logs/colored.log",
.color = true,
});
// Add JSON sink without colors
_ = try logger.addSink(.{
.path = "logs/app.json",
.format = .json,
.color = false,
});
try logger.info("This goes to console (colored) and both files");
}Structured formats and presentation
Color never corrupts serialized data. For json, ndjson, syslog, and syslog3164 the record is serialized first and color is applied around the result, so stripping ANSI recovers the exact document — PRI, timestamp, hostname, and structured data included.
| Format | No color | Horizontal | Vertical | Custom / level colors |
|---|---|---|---|---|
text | yes | yes | yes | yes |
logfmt | yes | yes | yes | yes |
json | yes | yes | yes | yes |
ndjson | yes | yes | yes | yes |
syslog / syslog3164 | yes | yes | whole-record | yes |
msgpack | yes | presentation only | presentation only | presentation only |
A JSON file sink writes a single array document ([, records, ]), so a colored record is wrapped inside the array and the file stays valid JSON.
msgpack is never colored. Binary payloads stay byte-exact: no ANSI, no trailing newline, and length-prefixed framing is preserved.
Color is per sink
Each sink decides independently whether to present color, so enabling console color never injects ANSI into a file or network sink.
| Target | Default | How to enable |
|---|---|---|
| Console | Auto | Colored when stdout is a TTY; plain when piped, redirected, or in CI |
| File | Off | color = true |
| Network | Off | color = true |
| MessagePack | Never | Not supported |
_ = try logger.addSink(.{ .name = "console", .color = true });
_ = try logger.addSink(.{ .path = "logs/app.log", .color = false });
_ = try logger.addSink(.{ .path = "logs/app.json", .format = .json, .color = false });Async logging
Async mode queues the uncolored serialized record together with its resolved level color. Presentation is applied per sink at write time, which means background workers cannot push console color into files or network sinks, and ANSI sequences cannot interleave between records from different workers.
See Async for the queue and worker model.
