hynek / structlog

Simple, powerful, and fast logging for Python.
https://www.structlog.org/
Other
3.48k stars 220 forks source link

The syntax to colorize the log message #646

Closed Pandede closed 1 month ago

Pandede commented 1 month ago

I'm wondering if there is any method to colorize the log message? For example, in loguru:

from loguru import logger

color_logger = logger.opt(colors=True)
color_logger.info("This is a <blue>message</>")
trim21 commented 1 month ago

you mayneed to use colorma and add color to message by yourself, don't think structlog support this

hynek commented 1 month ago

structlog defines a bunch of color codes for its own use:

https://github.com/hynek/structlog/blob/b265c7acbb700f7483f59b70c58855e59d9fc232/src/structlog/dev.py#L90-L103

So this is really easy with f-strings: logger.info(f"This is a {BLUE}message{RESET_ALL}.") and you don't need special syntax. Colorama is only necessary on Windows.

There's been some plans to deepen the integration with Rich and allow for its codes but since structlog's focus remains production systems, it hasn't become a priority yet.

Pandede commented 1 month ago

@hynek This wordaround is pretty implicit, should it be considered as a precessor? Just like KeyValueRenderer.

hynek commented 1 month ago

Processors are always a great idea! If you want the colors only in the event (and not the keys) it would be even trivial to implement since you would only have to replace strings with other strings.

Replacing a whole renderer is a bit more involved.

Pandede commented 1 month ago

Thanks for the quick response! I just follows the tutorial to create a custom KeyValueColumnFormatter:

from dataclasses import dataclass
from io import StringIO
from typing import Literal

from rich.console import Console

import structlog
from structlog.dev import Column

@dataclass
class RichColumnFormatter:
    color_system: Literal[
        "auto", "standard", "256", "truecolor", "windows"
    ] = "truecolor"
    emoji: bool | None = None
    markup: bool | None = None
    highlight: bool | None = None

    def __call__(self, key: str, value: object) -> str:
        with StringIO() as sio:
            Console(file=sio, color_system=self.color_system).print(
                value,
                emoji=self.emoji,
                markup=self.markup,
                highlight=self.highlight,
                end="",
            )
            return sio.getvalue()

def get_column_index(columns: list[Column], key: str) -> int:
    for i, c in enumerate(columns):
        if c.key == key:
            return i
    raise ValueError("No column found for key: {}".format(key))

# Replace the column formatter for the "event" column
cr = structlog.dev.ConsoleRenderer()
event_column_index = get_column_index(cr._columns, "event")
cr._columns[event_column_index].formatter = RichColumnFormatter()

# Follow the instructions in this tutorial
# https://www.structlog.org/en/stable/console-output.html#console-output-configuration
structlog.configure(
    processors=structlog.get_config()["processors"][:-1] + [cr]
)
logger: structlog.stdlib.BoundLogger = structlog.get_logger()

if __name__ == "__main__":
    logger.info("[blue]Hello, World![/]", foo=1, bar=2)

I'll close the issue with the workaround.