Colour-coded clarity: writing a Python module for log level output
Anyone who has spent a late arvo staring at a wall of grey log lines knows the pain of scanning for the one error that matters. When I was maintaining DenyHosts, a Python-based blocker for SSH brute-force attempts, I would sift through thousands of identical-looking entries trying to spot the IP that triggered a critical rule. My eyes would glaze over, and I would invariably miss the warning buried between routine info messages. That frustration became the seed for a small utility that paints log levels in distinct hues so the important lines jump straight off the screen.
The idea sounded straightforward at first, but anyone who has tinkered with terminal escape codes knows the rabbit hole runs deep. Different shells interpret sequences differently, Windows handles ANSI in its own quirky way, and screen readers obviously cannot see colour at all. I wanted something that felt natural on a developer's machine in Sydney or a sysadmin's server rack in Adelaide, without forcing anyone to install heavy dependencies. The result was a standalone Python module that slots neatly into the standard library's logging framework and stays out of the way when output is redirected to a file.
This piece walks through the design decisions, the ANSI quirks, and the integration tricks that made the module work in practice. Along the way I lean on a few Aussie sensibilities, because honest engineering down here tends to favour pragmatic tools over flashy frameworks, and that ethos shaped the final shape of the code.
Why a dedicated module was worth the effort
The Python ecosystem already offers libraries like colorama and coloredlogs, so the obvious question is why roll another one. For my purposes, those packages pulled in extra dependencies that clashed with the minimalist philosophy of DenyHosts, which aims to run quietly on a server with nothing more than a stock Python install. A bespoke module keeps the dependency footprint at zero and gives full control over the colour palette. It also lets me match the visual style to the rest of my tools, which matters when logs from different utilities stream into the same terminal window during a post-mortem.
Another motivation came from watching colleagues at a Brisbane meetup struggle to interpret log dumps during a live demo. The presenter would narrate "this should be red" while the audience squinted at monochrome output piped through tmux. A lightweight, dependency-free colouriser would have spared everyone the eye strain. Once that realisation landed, the path forward was obvious, and I sketched the module's interface on the back of a tram ticket during the commute home from the CBD.
Mapping severity to hue
Choosing which colour represents which severity sounds trivial until you sit down and try it. Red feels right for errors, yellow or amber for warnings, green for success, and cyan or white for routine information. The trick is balancing contrast against accessibility. Pure red text on a black background can be jarring during a long debugging session, while soft pastel shades wash out on cheap laptop panels. I settled on a palette inspired by the colour schemes used in PyCon AU talk recordings, where the speaker's slides remained readable even in the back row of a dim auditorium.
The module exposes a simple dictionary mapping the standard logging levels to ANSI colour codes. Developers can override individual entries if their team has a convention, say, making every warning magenta to match an internal dashboard. That flexibility matters in Australian workplaces where teams are often distributed across time zones from Perth to the east coast, and a shared visual language cuts down on Slack threads asking "what does this colour mean again?".
ANSI escape sequences and the Windows quirk
ANSI escape codes are the lingua franca of terminal colour, but they are a twentieth-century standard grafted onto twenty-first-century operating systems. The basic sequence looks like \x1b[31m for foreground red, \x1b[0m to reset, and similar codes for the rest of the spectrum. On macOS and Linux, terminals handle these codes natively, which is why most Australian developers working on Unix-like systems never give it a thought.
Windows is the odd one out. Older versions of cmd.exe ignored ANSI altogether, which forced module authors to ship conditional code paths. Python 10 introduced the os.system call to enable virtual terminal processing, but the module needed to support older interpreters too. I added a graceful fallback that probes for ANSI support at import time and disables colour when running in a non-tty stream. The detection logic uses sys.stdout.isatty, which catches the common case of piped output during a cron job running on a Perth data-centre server, where colour would only confuse log aggregators downstream.
Hooking into the standard logging module
The real value of a colourising module comes when it slots into code that already exists. Python's logging framework accepts custom formatter classes, so the module subclasses LogFormatter and overrides the format method. Inside that override, the level name is replaced with a wrapped string that toggles colour on and off around the original text. The reset code at the end prevents bleed-through into the next line, which is a subtle bug that haunts many naive implementations.
Performance was a pleasant surprise. The extra string concatenation and conditional checks added maybe a microsecond per log record, well below the noise floor of a typical SSH authentication check. For a tool like DenyHosts that processes hundreds of events per second on a busy server, that overhead is essentially nothing. The module ships as a single file, documented in plain English with examples that reflect how engineers down here talk about logging in stand-ups: "just chuck a colour on it so we can spot the errors at a glance".
Putting it all together and sharing the code
The finished module lives alongside the other utilities on my personal site, tucked between DenyHosts and Kodos in the downloads section. It carries a permissive licence so anyone can fork it, tweak the palette, and ship it inside their own projects. Documentation includes a quick-start snippet, a section on accessibility considerations for screen reader users, and notes on testing under tmux, screen, and Windows Terminal.
Looking back, the most rewarding part was not the ANSI codes themselves but the conversations that followed. A mate in Hobart mailed me to say he had wired the module into his home automation logs and finally stopped missing critical alerts from his solar inverter. Another developer in Melbourne dropped it into a continuous integration pipeline to highlight failing tests in red during code review. That kind of organic reuse is what makes open source worthwhile, and it reminds me that a small, sharp utility often outlasts a sprawling framework. The next time a log file feels like a wall of grey, a few lines of colour can turn noise into signal.
