structlog icon indicating copy to clipboard operation
structlog copied to clipboard

KeyValueRenderer with optional colors

Open rhymes opened this issue 6 years ago • 5 comments

I gave an implementation of a color keyvaluerenderer a shot:

import structlog
try:
    import colorama
except ImportError:
    colorama = None


class ColorKeyValueRenderer(structlog.processors.KeyValueRenderer):
    def __init__(self, colors=True, force_colors=False, **kwargs):
        super().__init__(self, **kwargs)

        if colors and colorama:
            if force_colors:
                colorama.deinit()
                colorama.init(strip=False)
            else:
                colorama.init()
            styles = structlog.dev._ColorfulStyles
        else:
            styles = structlog.dev._PlainStyles

        self._styles = styles

        self._level_to_color = structlog.dev.ConsoleRenderer.get_default_level_styles(
            colors)

        for key in self._level_to_color.keys():
            self._level_to_color[key] += styles.bright

    def __call__(self, _, __, event_dict):
        sio = StringIO()

        for key, value in self._ordered_items(event_dict):
            if key == 'level':
                sio.write(
                    self._level_to_color[value] + key +
                    '=' + str(value) + self._styles.reset + ' '
                )
            elif key in ('time', 'timestamp'):
                sio.write(
                    self._styles.timestamp + key + '=' +
                    str(value) + self._styles.reset + ' '
                )
            else:
                # sio.write(self._styles.kv_key + key + '=' +
                #           self._repr(value) + self._styles.reset + ' ')
                sio.write(
                    self._styles.kv_key + key + self._styles.reset +
                    "=" +
                    self._styles.kv_value + self._repr(value) +
                    self._styles.reset + ' '
                )

        return sio.getvalue()

Would render the following:

screen shot 2018-03-28 at 9 17 36 pm

As you can see is a giant hack around KeyValueRenderer and ConsoleRenderer :-)

What do you think?

rhymes avatar Mar 28 '18 17:03 rhymes

I made another version. This one uses an infinite color iterator to colorize key value pairs, I don't distinguish between the color of the key and the value:

LEVEL_COLORS = {
    "critical": colorama.Fore.RED,
    "exception": colorama.Fore.RED,
    "error": colorama.Fore.RED,
    "warn": colorama.Fore.YELLOW,
    "warning": colorama.Fore.YELLOW,
    "info": colorama.Fore.GREEN,
    "debug": colorama.Fore.BLUE,
    "notset": colorama.Back.RED,
}

COLORS = {
    'timestamp': colorama.Style.DIM,
    'time': colorama.Style.DIM,
    'event': colorama.Fore.BLUE + colorama.Style.BRIGHT
}

# all ANSI colors, except black and white
KV_COLORS = (
    colorama.Fore.RED, colorama.Fore.GREEN,
    colorama.Fore.YELLOW, colorama.Fore.BLUE, colorama.Fore.MAGENTA,
    colorama.Fore.CYAN)


class ColorKeyValueRenderer(structlog.processors.KeyValueRenderer):
    def __init__(self, force_colors=False, **kwargs):
        super().__init__(self, **kwargs)

        if force_colors:
            colorama.deinit()
            colorama.init(strip=False)
        else:
            colorama.init()

    def __call__(self, _, __, event_dict):
        colors_iterator = itertools.cycle(reversed(KV_COLORS))
        buffer = StringIO()

        for key, value in self._ordered_items(event_dict):
            if key == 'level':
                buffer.write(
                    LEVEL_COLORS[value] + key +
                    '=' + str(value) + colorama.Style.RESET_ALL + ' '
                )
            else:
                buffer.write(
                    self.color(key, colors_iterator) + key + '=' +
                    self._repr(value) + colorama.Style.RESET_ALL + ' '
                )

        return buffer.getvalue()

    def color(self, key, colors):
        return COLORS.get(key, next(colors))

Result:

screen shot 2018-03-28 at 9 57 22 pm

rhymes avatar Mar 28 '18 20:03 rhymes

What is it you’re trying to achieve here? A colorful ConsoleRenderer without field alignments? I’m not sure I quite understand the point, you wouldn’t put it into prod anyway?

hynek avatar Apr 05 '18 05:04 hynek

@hynek just an experiment. I just added colors to the KV renderer basically. I'm not particularly interested in the alignment part of the ConsoleRenderer.

Why, apart from the fact that's an experiment, shouldn't I use it in production?

Thanks

rhymes avatar Apr 05 '18 07:04 rhymes

Well I don’t know your setup, but I don’t want to have ANSI color sequences in my log files? :)

hynek avatar Apr 06 '18 10:04 hynek

@hynek oh yes, that obviously :-)

rhymes avatar Apr 06 '18 18:04 rhymes

While I don't want to have random colors around...I DO think it's useful to colorize the event dicts, is there a feature for this?

snackattas avatar Sep 06 '22 15:09 snackattas

No, this is not something I'm willing to spend my time on, but it's absolutely something I encourage people to write and share. It's a matter of taste anyway and it's impossible for me to make everybody happy; especially given I don't care about it myself.

hynek avatar Sep 10 '22 08:09 hynek