19

I've written a python cli with the 'click' library. I'd like to also use python's built-in logging module for logging to the console. But I've struggled getting the logging messages to the console. I tried a very simple approach:

logger = logging.getLogger(__name__)

@click.command()
def cli():
    logger.setLevel("INFO")
    logger.info("Does this work?")
    print("done.")

The logger content doesn't appear in my console. Maybe it needs a handler to explicitly send log messages to stdout?

logger = logging.getLogger(__name__)

@click.command()
def cli():
    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
    handler.setLevel("INFO")
    logger.addHandler(handler)
    logger.info("Does this work?")
    print("done.")

Unfortunately this also doesn't work.

A third option--creating a handler and setting loglevels for the handler and the logger-- works:

logger = logging.getLogger(__name__)

@click.command()
def cli():
    logger.setLevel("INFO")
    handler = logging.StreamHandler(sys.stderr)
    handler.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
    handler.setLevel("INFO")
    logger.addHandler(handler)
    logger.info("Does this work?")
    print("done.")

It seems like:

  • if creating a logger with logging.getLogger, I have to create a handler for my logger explicitly.
  • and I have to set a loglevel on both the logger and the handler?

Is that right? It seems silly to set the level twice. What's the point?

Or am I still misunderstanding the right way to do this?

Thanks for your help!

0

3 Answers 3

10

I had to manually set

logging.basicConfig(level=logging.INFO)

Example

import click
import logging

logging.basicConfig(level=logging.INFO)

@click.command()
def cli():
    logging.info("it works")

gives

$ mycli
INFO:root:it works
Sign up to request clarification or add additional context in comments.

1 Comment

Use logging.basicConfig(level=logging.INFO, format='%(message)s') to get output without "INFO:root:"
9

I personally like the loguru library for handling logs. I think it's simpler.

Here's an example of how I usually do it:

import click
from loguru import logger


@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name', help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        logger.info(f"That's it, beautiful and simple logging! - Counter: {x}")
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()
❯ python loguru-click-cli-test.py --count=3
Your name: Geraldo
2020-06-10 20:02:48.297 | INFO     | __main__:hello:11 - That's it, beautiful and simple logging! - Counter: 0
Hello Geraldo!
2020-06-10 20:02:48.297 | INFO     | __main__:hello:11 - That's it, beautiful and simple logging! - Counter: 1
Hello Geraldo!
2020-06-10 20:02:48.297 | INFO     | __main__:hello:11 - That's it, beautiful and simple logging! - Counter: 2
Hello Geraldo!

Loguru: https://github.com/Delgan/loguru

Comments

2

I was looking to get logs when a click handler is called (command name and parameters). I end up with this snippet:

from functools import wraps
import logging
import click

logger = logging.getLogger(__name__)

def click_log(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        context = click.get_current_context()
        logger.info(f"{context.command.name}(**{context.params})")
        return fn(*args, **kwargs)
    return wrapper

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
@click_log
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo(f"Hello {name}!")  # you can use logger.info


def configure_logger():
    logging.basicConfig(level="DEBUG")

configure_logger()

if __name__ == '__main__':
    hello()

It gives you the following output:

python test_click.py --count 3
Your name: ok
INFO:__main__:hello(**{'count': 3, 'name': 'ok'})
Hello ok!
Hello ok!
Hello ok!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.