Back to Blog
Python

Python Rich Logging: Tracebacks and Syntax Highlighting

python rich logging tracebacks and syntax highlighting: Learn how to use Python's Rich library to add traceback and syntax highlighting to logging output, making logs...

Richloggingtracebackssyntax highlightingpython-logging
A terminal window with a highlighted Python traceback and colored log lines, illustrating Rich logging output.

When Python's standard logging output meets a long traceback, the terminal becomes a wall of plain text. The Rich library changes that by adding syntax highlighting and structured traceback formatting to logging output. This article shows how to configure python rich logging tracebacks and syntax highlighting in your own projects.

Setting Up Rich Logging

Rich provides a RichHandler that plugs directly into Python's standard logging module. Install Rich first:

pip install rich

Then replace the default handler with RichHandler:

import logging from rich.logging import RichHandler logging.basicConfig( level=logging.INFO, handlers=[RichHandler()] ) log = logging.getLogger("example") log.info("Hello, Rich logging")

RichHandler writes log records to the console using Rich's rendering engine. The output is colorized by default: timestamps, level names, and messages get distinct colors. This alone makes logs easier to scan, but the real value appears when an exception occurs.

Traceback Formatting with Rich

When an uncaught exception reaches the logging system, RichHandler automatically formats the traceback using Rich's Traceback class. The result is a syntax-highlighted, indented traceback with clear frame separation. Consider this example:

import logging from rich.logging import RichHandler logging.basicConfig(level=logging.INFO, handlers=[RichHandler()]) log = logging.getLogger("example") def divide(a, b): return a / b log.info("Starting division") print(divide(10, 0))

Running this script produces a traceback where each frame is colorized, the offending line is highlighted, and the error message is shown in a contrasting color. This is a direct improvement over the default traceback, which uses monochrome text and makes it hard to spot the root cause quickly.

Syntax Highlighting in Log Messages

Tracebacks are not the only place where syntax highlighting helps. You can embed highlighted code in log messages using Rich's Syntax object. This is useful when logging a configuration snippet, a query, or any code that should be visually distinct:

import logging from rich.logging import RichHandler from rich.syntax import Syntax logging.basicConfig(level=logging.INFO, handlers=[RichHandler()]) log = logging.getLogger("example") code = "def greet(name):\n return f'Hello, {name}'" syntax = Syntax(code, "python", theme="monokai", line_numbers=True) log.info("Generated function:\n%s", syntax)

The Syntax object is rendered as a rich text block inside the log message. The theme parameter controls the color scheme, and line_numbers adds line numbers to the output. This works because RichHandler accepts any object that Rich can render, not just strings.

Controlling Traceback Detail

RichHandler exposes parameters to control how much traceback information is shown. The most useful ones are tracebacks_show_locals and tracebacks_max_frames:

import logging from rich.logging import RichHandler logging.basicConfig( level=logging.INFO, handlers=[RichHandler( tracebacks_show_locals=True, tracebacks_max_frames=5 )] )

tracebacks_show_locals=True includes the values of local variables in each frame of the traceback. This can be extremely helpful during debugging, but it may expose sensitive data in logs. tracebacks_max_frames limits how many frames are displayed, preventing extremely long tracebacks from flooding the console.

Customizing the Console and Theme

Rich's Console object controls the output stream and theme. You can pass a custom Console to RichHandler to change colors, width, or output target:

import logging from rich.console import Console from rich.logging import RichHandler from rich.theme import Theme custom_theme = Theme({ "logging.level.info": "cyan", "logging.level.warning": "yellow", "logging.level.error": "bold red" }) console = Console(theme=custom_theme, width=120) logging.basicConfig(level=logging.INFO, handlers=[RichHandler(console=console)])

This is useful when you want to match the log colors to your application's branding or when you need to control the console width for better readability in narrow terminals.

Performance and Production Considerations

Rich logging adds overhead compared to plain text logging. Syntax highlighting and traceback rendering require parsing and colorization, which takes CPU time. In development this is usually negligible, but in high-throughput production systems it can become noticeable.

If you need Rich's readability in production, consider using it only for warning and error levels, while keeping info and debug messages plain. You can achieve this by setting a lower-level handler for info and a separate RichHandler for warnings and above:

import logging from rich.logging import RichHandler logger = logging.getLogger("app") logger.setLevel(logging.INFO) plain = logging.StreamHandler() plain.setLevel(logging.INFO) rich = RichHandler(level=logging.WARNING) logger.addHandler(plain) logger.addHandler(rich)

This way, routine info logs use the fast plain handler, while errors and warnings get the full Rich treatment. You can also disable syntax highlighting entirely by setting highlight=False on the RichHandler if you only need traceback formatting.

Integrating with Existing Logging Configuration

If your application already uses a logging configuration file or a custom formatter, you can replace the handler without changing the rest of the setup. RichHandler works with any Logger instance and respects the log level hierarchy. For example, if you use logging.config.dictConfig, you can specify RichHandler as the handler class:

import logging.config LOGGING_CONFIG = { "version": 1, "handlers": { "rich": { "class": "rich.logging.RichHandler", "level": "INFO" } }, "root": { "handlers": ["rich"], "level": "INFO" } } logging.config.dictConfig(LOGGING_CONFIG)

Because RichHandler inherits from logging.Handler, it can be used anywhere a standard handler is expected. The only requirement is that the Rich package is installed in the environment where the logging runs.

Handling Edge Cases in Rich Logging

Rich's traceback rendering relies on the traceback module and may not capture every exception type perfectly. For example, exceptions raised in C extensions or during interpreter shutdown may not render with full syntax highlighting. In those cases, Rich falls back to a plain text traceback. This is a known limitation and not a bug in your code.

Another edge case is logging from multiple threads. RichHandler is thread-safe, but if you write to the same console from multiple threads, the output may interleave. To avoid this, pass a dedicated Console per thread or use a lock around log writes. For most applications, the default behavior is acceptable.

Finally, remember that Rich's syntax highlighting depends on the Pygments library. If you use a custom lexer or a language that Pygments does not support, the highlighting will be absent. You can still use the Syntax object with a custom lexer, but it requires additional setup. In practice, the built-in lexers cover all common languages used in log messages.

python rich logging tracebacks and syntax highlighting: Prac | RYUSLOG DEV