0

I have a Python script and I want that the info method write the messages in the console. But the warning, critical or error writes the messages to a file. How can I do that?

I tried this:

import logging

console_log = logging.getLogger("CONSOLE")
console_log.setLevel(logging.INFO)

stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
console_log.addHandler(stream_handler)

file_log = logging.getLogger("FILE")
file_log.setLevel(logging.WARNING)

file_handler = logging.FileHandler('log.txt')
file_handler.setLevel(logging.WARNING)
file_log.addHandler(file_handler)

def log_to_console(message):
  console_log.info(message)  

def log_to_file(message):
  file_log.warning(message)

log_to_console("THIS SHOULD SHOW ONLY IN CONSOLE")
log_to_file("THIS SHOULD SHOW ONLY IN FILE")

but the message that should be only in the file is going to the console too, and the message that should be in the console, is duplicating. What am I doing wrong here?

enter image description here

vvvvv
  • 25,404
  • 19
  • 49
  • 81
Tiago Silveira
  • 267
  • 4
  • 14
  • https://stackoverflow.com/questions/58977892/prevent-python-logger-from-printing-to-console – Andrew Ryan Dec 16 '22 at 21:27
  • This solution does not work for me. I Tried to basicConfig at the start of the script. Here's a example notebook https://colab.research.google.com/drive/1UWeHdU0eP8ReCAHZS3xRNpBKzAWLnuX9?authuser=1#scrollTo=LCRGs6IJOsSH – Tiago Silveira Dec 16 '22 at 21:49
  • 1
    https://stackoverflow.com/questions/2266646/how-to-disable-logging-on-the-standard-error-stream – Andrew Ryan Dec 16 '22 at 21:59
  • It solves the problem. But why the File handler propagates to console? – Tiago Silveira Dec 16 '22 at 23:02
  • Before your solution, yes log_to_file() was showing the message in console. After set the property propagate to False, it solves the problem. – Tiago Silveira Dec 19 '22 at 03:24

1 Answers1

2

What happens is that the two loggers you created propagated the log upwards to the root logger. The root logger does not have any handlers by default, but will use the lastResort handler if needed:

A "handler of last resort" is available through this attribute. This is a StreamHandler writing to sys.stderr with a level of WARNING, and is used to handle logging events in the absence of any logging configuration. The end result is to just print the message to sys.stderr.

Source from the Python documentation.

Inside the Python source code, you can see where the call is done.

Therefore, to solve your problem, you could set the console_log and file_log loggers' propagate attribute to False.


On another note, I think you should refrain from instantiating several loggers for you use case. Just use one custom logger with 2 different handlers that will each log to a different destination.

Create a custom StreamHandler to log only the specified level:

import logging

class MyStreamHandler(logging.StreamHandler):
    def emit(self, record):
        if record.levelno == self.level:
            # this ensures this handler will print only for the specified level
            super().emit(record)

Then, use it:

my_custom_logger = logging.getLogger("foobar")
my_custom_logger.propagate = False
my_custom_logger.setLevel(logging.INFO)

stream_handler = MyStreamHandler()
stream_handler.setLevel(logging.INFO)

file_handler = logging.FileHandler("log.txt")
file_handler.setLevel(logging.WARNING)

my_custom_logger.addHandler(stream_handler)
my_custom_logger.addHandler(file_handler)

my_custom_logger.info("THIS SHOULD SHOW ONLY IN CONSOLE")
my_custom_logger.warning("THIS SHOULD SHOW ONLY IN FILE")

And it works without duplicate and without misplaced log.

vvvvv
  • 25,404
  • 19
  • 49
  • 81