How spdlog Handles Colored Output on Different Terminals

spdlog abstracts terminal differences through platform-specific sinks—using ANSI escape codes on POSIX systems and WinAPI console attributes on Windows—while supporting automatic terminal capability detection and configurable color modes.

The gabime/spdlog library provides robust spdlog colored output capabilities that work seamlessly across Linux, macOS, and Windows without requiring conditional compilation in your application code. By implementing separate sink classes for each platform's color model, spdlog ensures that log levels appear in distinct colors whether you're running in a terminal that supports ANSI codes or the legacy Windows console.

Platform-Specific Color Sink Architecture

spdlog implements colored output through two distinct sink templates that share a common interface but handle color application differently based on the underlying operating system APIs.

ANSI Color Sink for POSIX Systems

For Unix-like systems, spdlog uses ansicolor_sink, implemented in include/spdlog/sinks/ansicolor_sink-inl.h. This sink inserts standard ANSI escape sequences (such as \x1b[32m for green) directly into the output stream around formatted log messages. When color mode is enabled, the sink wraps log content with the appropriate escape codes defined in its internal colors_ array, then appends a reset sequence to clear formatting.

Windows Console Sink

On Windows platforms, spdlog employs wincolor_sink from include/spdlog/sinks/wincolor_sink-inl.h. Instead of emitting escape sequences, this sink calls the Windows API functions SetConsoleTextAttribute and WriteConsoleA/W to modify the console's foreground color attributes dynamically. The implementation stores the original console attributes before setting the color, writes the message fragment, then restores the original attributes to prevent color bleeding into subsequent output.

Automatic Terminal Detection and Color Modes

Both color sinks accept a color_mode argument that controls when colors are applied, allowing you to override automatic detection based on your deployment environment.

Supported Color Modes

  • automatic (default): The sink detects at runtime whether the output stream is attached to a color-capable terminal
  • always: Forces color output regardless of whether the stream is a terminal or redirected to a file
  • never: Disables color output entirely, emitting plain text only

Runtime Detection Logic

In automatic mode, each sink performs platform-specific checks:

  • POSIX: ansicolor_sink queries details::os::in_terminal(target_file_) and details::os::is_color_terminal() (defined in include/spdlog/details/os.h) to verify that the file descriptor refers to a TTY and supports color sequences
  • Windows: wincolor_sink calls GetConsoleMode on the output handle; if the call succeeds, the handle represents a real console window rather than a redirected pipe or file

Mapping Log Levels to Colors

Each color sink maintains a colors_ array that maps spdlog::level::level_enum values to platform-specific color representations.

ANSI Color Codes

The ANSI sink stores string literals representing escape sequences:

  • Trace: white
  • Debug: cyan
  • Info: green
  • Warn: yellow_bold
  • Error: red_bold
  • Critical: bold_on_red
  • Reset: reset (clears all formatting)

These strings are emitted by the private print_ccode_ method before and after the message content.

Windows Color Attributes

The Windows sink uses FOREGROUND_* bit-mask constants from the Win32 API:

  • Trace: FOREGROUND_RED | FOREGROUND_GREEN | FOREROUND_BLUE (white)
  • Debug: FOREGROUND_GREEN | FOREGROUND_BLUE (cyan)
  • Info: FOREGROUND_GREEN (green)
  • Warn: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY (yellow bold)
  • Error: FOREGROUND_RED | FOREGROUND_INTENSITY (red bold)
  • Critical: Background red with white text

The sink saves the original console attributes via GetConsoleScreenBufferInfo, applies the level-specific attributes, writes the output, then restores the original state.

Pattern-Based Color Ranges

spdlog supports selective coloring through pattern formatters using the %^ (start color) and %$ (end color) markers. When parsing the pattern string, the formatter records byte positions in msg.color_range_start and msg.color_range_end. During the log() operation, the sink prints the message prefix, injects the color code (or sets the console attribute), prints the colored range, then resets the color before emitting the remainder of the message.

This allows precise control over which portions of a log line receive color treatment, such as highlighting only the log level or message text while leaving timestamps and source locations in the default terminal color.

Creating Color Loggers with Factory Functions

The public API in src/color_sinks.cpp provides convenience factory functions that instantiate the appropriate sink type based on your compilation platform. These functions automatically forward the color_mode parameter to the underlying implementation.

// Automatic detection (default behavior)
auto logger = spdlog::stdout_color_mt("console");
logger->info("Info message");      // green on most terminals
logger->warn("Warning message");   // yellow (bold) on most terminals

// Force colors even when output is redirected to a file
auto forced_logger = spdlog::stdout_color_mt("always", spdlog::color_mode::always);
forced_logger->error("Error – always coloured");

// Disable colors entirely
auto plain_logger = spdlog::stdout_color_mt("no_color", spdlog::color_mode::never);
plain_logger->debug("Debug – plain text");

// Custom color range in pattern
auto custom_logger = spdlog::stdout_color_mt("custom");
custom_logger->set_pattern("%^[%L] %v%$");   // brackets and message colored, timestamp plain
custom_logger->info("Custom coloured line");

The stdout_color_mt and stderr_color_st functions (along with their _mt and _st variants for thread-safe and single-threaded modes) include stdout_color_sinks.h and delegate platform selection to the preprocessor, ensuring your code remains portable without #ifdef blocks.

Summary

  • spdlog colored output relies on two platform-specific implementations: ansicolor_sink for POSIX systems and wincolor_sink for Windows
  • Automatic terminal detection occurs at runtime using is_color_terminal() on POSIX and GetConsoleMode on Windows
  • Color modes (automatic, always, never) provide explicit control over color emission regardless of terminal capabilities
  • Log levels map to ANSI escape strings or FOREGROUND_* bit-masks depending on the platform
  • Pattern markers %^ and %$ enable selective coloring of specific message ranges
  • Factory functions in color_sinks.cpp abstract platform differences behind a unified API

Frequently Asked Questions

How do I force colors when redirecting output to a file?

Pass spdlog::color_mode::always as the second argument to stdout_color_mt or stderr_color_st. This bypasses the automatic terminal detection in include/spdlog/details/os.h and forces the sink to emit ANSI codes or set console attributes regardless of whether the stream is a TTY.

What terminal capabilities does spdlog check on POSIX systems?

According to include/spdlog/details/os.h, spdlog verifies two conditions: first, that the file descriptor is a terminal using isatty(), and second, that the terminal supports color by checking the TERM environment variable against a whitelist of color-capable terminals (including xterm, screen, vt100, and others supporting ANSI sequences).

How does Windows color handling differ from ANSI terminals?

Windows uses console API calls rather than escape sequences. The wincolor_sink in include/spdlog/sinks/wincolor_sink-inl.h manipulates console attributes directly through SetConsoleTextAttribute, whereas POSIX systems receive byte sequences like \x1b[31m that the terminal emulator interprets. Windows 10 and later do support ANSI escape codes, but spdlog maintains the WinAPI approach for backward compatibility with older Windows versions.

Can I disable colors entirely without changing pattern strings?

Yes. Construct your logger with spdlog::color_mode::never to suppress all color output while preserving your existing format patterns. This setting prevents the sink from emitting ANSI codes or modifying console attributes, outputting plain text suitable for log files or environments where color codes would appear as garbled text.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →