question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. Itย collects links to all the places you might be looking at while hunting down a tough bug.

And, if youโ€™re still stuck at the end, weโ€™re happy to hop on a call to see how we can help out.

[BUG] RichHandler: jumbled output with existing module logger and logging config from file

See original GitHub issue

Read the issues I read through the existing issues and found some that might be related, but not quite and a bit outdated: #69, #87 and bonus points module name (in the example below "this.module") #850.

update: Just noticed that the module name can be included if e.g. the simple formatter is used for the handler rich (see log_rich.yml below).

Describe the bug The logging and progress bar updates get jumbled in the console when using an existing module logger which is configured externally to use the rich.logging.RichHandler, see the output:

image

To Reproduce Adapted from the packaged python -m rich.progress plus external logging config file log_rich.yml:

version: 1
formatters:
  simple:
    format: '%(asctime)s %(name)s %(levelname)s %(message)s'
    datefmt: '%Y-%m-%dT%H:%M:%S'
handlers:
  console:
    class: logging.StreamHandler
    formatter: simple
    stream: ext://sys.stdout
  rich:
    class: rich.logging.RichHandler
  file:
    class: logging.FileHandler
    formatter: simple
    filename: this.log
    mode: a
loggers:
  this.module:
    level: DEBUG
    handlers: [ rich, file ]

The reproducer manual_logging_rich.py:

import logging
from logging.config import dictConfig
import random
import time

from rich.console import Console
from rich.panel import Panel
from rich.progress import (
    BarColumn,
    Progress,
    SpinnerColumn,
    TextColumn,
    TimeElapsedColumn,
    TimeRemainingColumn,
)
from rich.rule import Rule
from rich.syntax import Syntax
from rich.table import Table
import yaml

LOGGER = logging.getLogger("this.module")


if __name__ == "__main__":
    _log = "log_rich.yml"
    try:
        with open(_log, "rt") as f:
            config = yaml.load(f.read(), yaml.FullLoader)
            dictConfig(config)
    except FileNotFoundError:
        LOGGER = logging.getLogger()
        LOGGER.setLevel(logging.DEBUG)

    syntax = Syntax(
        '''def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
    """Iterate and generate a tuple with a flag for last value."""
    iter_values = iter(values)
    try:
        previous_value = next(iter_values)
    except StopIteration:
        return
    for value in iter_values:
        yield False, previous_value
        previous_value = value
    yield True, previous_value''',
        "python",
        line_numbers=True,
    )

    table = Table("foo", "bar", "baz")
    table.add_row("1", "2", "3")

    progress_renderables = [
        "Text may be printed while the progress bars are rendering.",
        Panel("In fact, [i]any[/i] renderable will work"),
        "Such as [magenta]tables[/]...",
        table,
        "Pretty printed structures...",
        {"type": "example", "text": "Pretty printed"},
        "Syntax...",
        syntax,
        Rule("Give it a try!"),
    ]

    from itertools import cycle

    examples = cycle(progress_renderables)

    console = Console(record=True)

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        BarColumn(),
        TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
        TimeRemainingColumn(),
        TimeElapsedColumn(),
        console=console,
        transient=True,
    ) as progress:
        LOGGER.debug("existing log message")

        task1 = progress.add_task("[red]Downloading", total=1000)
        task2 = progress.add_task("[green]Processing", total=1000)
        task3 = progress.add_task("[yellow]Thinking", total=1000, start=False)

        LOGGER.debug("existing log message")

        while not progress.finished:
            progress.update(task1, advance=0.5)
            progress.update(task2, advance=0.3)
            LOGGER.debug("existing log message")
            time.sleep(0.01)
            if random.randint(0, 100) < 1:
                progress.log(next(examples))

Platform Running on Windows 10.18363, PowerShell 6.2.2 within Windows Terminal 1.9.1942.0.

Diagnose

โฏ python -m rich.diagnose
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ <class 'rich.console.Console'> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ A high level console interface.                                                  โ”‚
โ”‚                                                                                  โ”‚
โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ”‚
โ”‚ โ”‚ <console width=209 ColorSystem.TRUECOLOR>                                    โ”‚ โ”‚
โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚
โ”‚                                                                                  โ”‚
โ”‚     color_system = 'truecolor'                                                   โ”‚
โ”‚         encoding = 'utf-8'                                                       โ”‚
โ”‚             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> โ”‚
โ”‚           height = 28                                                            โ”‚
โ”‚    is_alt_screen = False                                                         โ”‚
โ”‚ is_dumb_terminal = False                                                         โ”‚
โ”‚   is_interactive = True                                                          โ”‚
โ”‚       is_jupyter = False                                                         โ”‚
โ”‚      is_terminal = True                                                          โ”‚
โ”‚   legacy_windows = False                                                         โ”‚
โ”‚         no_color = False                                                         โ”‚
โ”‚          options = ConsoleOptions(                                               โ”‚
โ”‚                        size=ConsoleDimensions(width=209, height=28),             โ”‚
โ”‚                        legacy_windows=False,                                     โ”‚
โ”‚                        min_width=1,                                              โ”‚
โ”‚                        max_width=209,                                            โ”‚
โ”‚                        is_terminal=True,                                         โ”‚
โ”‚                        encoding='utf-8',                                         โ”‚
โ”‚                        max_height=28,                                            โ”‚
โ”‚                        justify=None,                                             โ”‚
โ”‚                        overflow=None,                                            โ”‚
โ”‚                        no_wrap=False,                                            โ”‚
โ”‚                        highlight=None,                                           โ”‚
โ”‚                        markup=None,                                              โ”‚
โ”‚                        height=None                                               โ”‚
โ”‚                    )                                                             โ”‚
โ”‚            quiet = False                                                         โ”‚
โ”‚           record = False                                                         โ”‚
โ”‚         safe_box = True                                                          โ”‚
โ”‚             size = ConsoleDimensions(width=209, height=28)                       โ”‚
โ”‚        soft_wrap = False                                                         โ”‚
โ”‚           stderr = False                                                         โ”‚
โ”‚            style = None                                                          โ”‚
โ”‚         tab_size = 8                                                             โ”‚
โ”‚            width = 209                                                           โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
โฏ python -m rich._windows
platform="Windows"
WindowsConsoleFeatures(vt=True, truecolor=True)
โฏ conda env export | grep rich
  - rich=10.6.0=py38haa244fe_0

Did I help?

If I was able to resolve your problem, consider sponsoring my work on Rich, or buy me a coffee to say thanks.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8

github_iconTop GitHub Comments

1reaction
fleimgrubercommented, Aug 4, 2021

Thanks for your thoughts, much appreciated.

Iโ€™m guessing where the config says โ€œclassโ€ you can supply a callable that returns a RichHandler constructed with your console object.

In this case I am not sure where to instantiate the console initially. I guess in the mentioned callable? But then how to pass that to Progress? It would have to be picked out of the RichHandler in turn, I guess. A better approach might be to define a console object on module level (where the callable is defined) and that same console object could be imported and used for Progress. I think the latter option would work and be cleanest?

Failing that you could extend RichHandler and cusomize init to set the console there.

Ah I guess this extended handler class would also import a shared module scope console and use that in the init? And the extended class would then be used in the dictconfig?

I will experiment some more and try one of the suggested approaches, thanks!

And also thanks for your awesome work and commitment on all your public libraries!

0reactions
willmcgugancommented, Aug 5, 2021

Yeah, record=True is only necessary if you want to export text / html.

As long as the handler and progress use the same console instance it should keep the output looking nice.

Read more comments on GitHub >

github_iconTop Results From Across the Web

[REQUEST] A way to show the logger name when ... - GitHub
I would add a way to show the logger name in RichHandler . While you can use a logging.Formatter that won't allow you...
Read more >
logging.config โ€” Logging configuration โ€” Python 3.11.1 ...
This section describes the API for configuring the logging module. ... This behaviour is to disable any existing non-root loggers unless they or...
Read more >
Logging Handler โ€” Rich 12.6.0 documentation
Rich supplies a logging handler which will format and colorize text written by Python's logging module. Here's an example of how to set...
Read more >
log messages appearing twice with Python Logging
This solution fixed the problem for log messages generated in the main flask app, but my app cals functions in a library module,...
Read more >
Logging - Django documentation
A Python logging configuration consists of four parts: ... For example, you could write a filter that downgrades ERROR log records to WARNING...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found