188

How to disable logging on the standard error stream in Python? This does not work:

import logging

logger = logging.getLogger()
logger.removeHandler(sys.stderr)
logger.warning('foobar')  # emits 'foobar' on sys.stderr
Maggyero
  • 4,569
  • 3
  • 30
  • 49
sorin
  • 149,293
  • 163
  • 498
  • 754
  • For those wondering why anyone would want to disable logging: You wouldn't want to log private data like passwords or API keys. – Stevoisiak Apr 27 '18 at 20:01
  • 8
    @StevenVascellaro. Why are those being sent to a logger in the first place then? That doesn't sound right... – Mad Physicist Jul 13 '18 at 16:47
  • 2
    @MadPhysicist I have an application which sends XML requests to an external API. By default, these requests are logged to a file. However, the initial login requires authentication with a username and password, which I don't want logged. – Stevoisiak Jul 13 '18 at 16:53
  • @StevenVascellaro. I see. Thanks for the explanation. – Mad Physicist Jul 13 '18 at 17:17
  • 2
    You do not show how/where you add your handlers. If they were added to the root logger this would prevent logging from adding default StreamHandler as described at https://docs.python.org/3/library/logging.html#logging.basicConfig Also, per linked description, the default StreamHandler is only added during first call emitting log message so when you print `logger.handlers` it should be empty (as it precedes `logger.debug()` call). The code in question displays only `[]` (empty list of handlers). Verified with Python 2.7.15 and Python 3.6.6. – Piotr Dobrogost Aug 22 '18 at 10:07
  • @StevenM.Vascellaro Another reason for disabling logging is if a third-party package is way too verbose, and is making it hard to find the relevant logs among all the others. – random_forest_fanatic Nov 19 '18 at 18:42

19 Answers19

214

I found a solution for this:

logger = logging.getLogger('my-logger')
logger.propagate = False
# now if you use logger it will not log to console.

This will prevent logging from being send to the upper logger that includes the console logging.

ereOn
  • 50,978
  • 36
  • 154
  • 231
sorin
  • 149,293
  • 163
  • 498
  • 754
  • 14
    I don't think this is a good solution. Not propagating to higher loggers could have other undesirable consequences. – lfk Aug 31 '17 at 06:20
  • 4
    If you wanted to only filter message below a certain log level (say, all `INFO` messages), you could change the second line to something like `logger.setLevel(logging.WARNING)` – Hartley Brody Jan 05 '18 at 16:48
  • 2
    How would you re-enable the log afterwards? – Stevoisiak Apr 27 '18 at 19:57
  • 9
    **Not an answer** as blocking propagation effectively disables all handlers of the root logger and the question clearly states *(…) but I may have other handlers there that I want to keep* which suggests the intention is to disable default StreamHandler of the root logger **only**. – Piotr Dobrogost Aug 22 '18 at 07:51
  • 1
    Stopping message propagation is not enough. [Since Python 3.2](https://docs.python.org/3/howto/logging.html#what-happens-if-no-configuration-is-provided), the `logging.lastResort` handler will still log messages of severity `logging.WARNING` and greater to `sys.stderr` in the absence of other handlers. [See my answer](https://stackoverflow.com/a/61333099/2326961). – Maggyero Apr 20 '20 at 22:34
129

I use:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
  • 1,757
  • 1
  • 14
  • 16
  • 16
    this also works at the `logging` module level to disable logging *entirely*, for example: `import logging; logging.disable(logging.CRITICAL);`: https://docs.python.org/2/library/logging.html#logging.disable – lsh May 09 '16 at 15:35
  • 1
    This is much better than disabling propagation. – Mátray Márk Aug 17 '18 at 12:38
  • 11
    **Not an answer** – the question asks how to disable default StreamHandler **only**. – Piotr Dobrogost Aug 22 '18 at 07:29
  • 1
    The `disabled` attribute is not part of the public API. See https://bugs.python.org/issue36318. – Maggyero Mar 28 '19 at 11:59
  • should this be enclosed in try / finally ? What happens if the code rises one exception ? Does the logger remain disabled ? – yucer Nov 25 '20 at 09:23
77

You can use:

logging.basicConfig(level=your_level)

where your_level is one of those:

'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL

So, if you set your_level to logging.CRITICAL, you will get only critical messages sent by:

logging.critical('This is a critical error message')

Setting your_level to logging.DEBUG will show all levels of logging.

For more details, please take a look at logging examples.

In the same manner to change level for each Handler use Handler.setLevel() function.

import logging
import logging.handlers

LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
          LOG_FILENAME, maxBytes=20, backupCount=5)

handler.setLevel(logging.CRITICAL)

my_logger.addHandler(handler)
vallentin
  • 21,302
  • 6
  • 52
  • 73
Vadikus
  • 981
  • 5
  • 5
  • 8
    This is generally useful info, but the question asked how t o disable console logging, not how to add an additional handler. if you were to examine my_logger.handlers with the above code applied to the original example, you'd see two handlers -- your new file handler and the original stream handler. – Joe Jul 12 '17 at 18:07
  • 1
    **CRITICAL** was the word I was looking for. Thanks. – Nishant Apr 29 '19 at 12:59
  • I would love to see a debug level of OFF. It is unambiguous and simple. – Not a machine Aug 21 '19 at 19:53
  • logging.CRITICAL+1 – Piotr Dec 20 '21 at 17:00
55

Using Context manager - [ most simple ]

import logging 

class DisableLogger():
    def __enter__(self):
       logging.disable(logging.CRITICAL)
    def __exit__(self, exit_type, exit_value, exit_traceback):
       logging.disable(logging.NOTSET)

Example of use:

with DisableLogger():
    do_something()

If you need a [more COMPLEX] fine-grained solution you can look at AdvancedLogger

AdvancedLogger can be used for fine grained logging temporary modifications

How it works:
Modifications will be enabled when context_manager/decorator starts working and be reverted after

Usage:
AdvancedLogger can be used
- as decorator `@AdvancedLogger()`
- as context manager `with  AdvancedLogger():`

It has three main functions/features:
- disable loggers and it's handlers by using disable_logger= argument
- enable/change loggers and it's handlers by using enable_logger= argument
- disable specific handlers for all loggers, by using  disable_handler= argument

All features they can be used together

Use cases for AdvancedLogger

# Disable specific logger handler, for example for stripe logger disable console
AdvancedLogger(disable_logger={"stripe": "console"})
AdvancedLogger(disable_logger={"stripe": ["console", "console2"]})

# Enable/Set loggers
# Set level for "stripe" logger to 50
AdvancedLogger(enable_logger={"stripe": 50})
AdvancedLogger(enable_logger={"stripe": {"level": 50, "propagate": True}})

# Adjust already registered handlers
AdvancedLogger(enable_logger={"stripe": {"handlers": "console"}
pymen
  • 4,609
  • 38
  • 30
  • I really like this idiom, but I would rather be able to disable a particular namespace. For example, I just want the root logger temporarily disabled. Although using this idiom, we should be able to just temporarily add/remove handlers and the such. – Chris Feb 12 '14 at 18:21
  • 2
    The question asks how to disable default StreamHandler **only**. – Piotr Dobrogost Aug 21 '18 at 14:06
  • 1
    You don’t need to roll your own class, you can use @contextmanager from contextlib and write a yielding function – KristianR Oct 24 '18 at 23:24
  • 2
    If you are into exotic fruits on your pizza. Sure. – user3504575 Apr 27 '19 at 22:55
  • @PiotrDobrogost i have added a link to AdvancedLogger which allows to temporary disable output to console (StreamHandler) – pymen Aug 16 '20 at 21:37
48

(long dead question, but for future searchers)

Closer to the original poster's code/intent, this works for me under python 2.6

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger

lhStdout = logger.handlers[0]  # stdout is the only handler initially

# ... here I add my own handlers 
f = open("/tmp/debug","w")          # example handler
lh = logging.StreamHandler(f)
logger.addHandler(lh)

logger.removeHandler(lhStdout)

logger.debug("bla bla")

The gotcha I had to work out was to remove the stdout handler after adding a new one; the logger code appears to automatically re-add the stdout if no handlers are present.

IndexOutOfBound Fix: If you get a IndexOutOfBound Error while instantiating lhStdout, move the instantiation to after adding your file handler i.e.

...
logger.addHandler(lh)

lhStdout = logger.handlers[0]
logger.removeHandler(lhStdout)
l82ky
  • 513
  • 4
  • 2
  • 4
    The sequence `logger = logging.getLogger(); lhStdout = logger.handlers[0]` is wrong as the root logger initially has no handlers – `python -c "import logging; assert not logging.getLogger().handlers"`. Verified with Python 2.7.15 and Python 3.6.6. – Piotr Dobrogost Aug 21 '18 at 14:31
41

To fully disable logging:

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

To enable logging:

logging.disable(logging.NOTSET)

Other answers provide work arounds which don't fully solve the problem, such as

logging.getLogger().disabled = True

and, for some n greater than 50,

logging.disable(n)

The problem with the first solution is that it only works for the root logger. Other loggers created using, say, logging.getLogger(__name__) are not disabled by this method.

The second solution does affect all logs. But it limits output to levels above that given, so one could override it by logging with a level greater than 50.

That can be prevented by

logging.disable(sys.maxint)

which as far as I can tell (after reviewing the source) is the only way to fully disable logging.

townie
  • 184
  • 2
  • 9
starfry
  • 8,525
  • 6
  • 60
  • 88
27

There are some really nice answers here, but apparently the simplest is not taken too much in consideration (only from infinito).

root_logger = logging.getLogger()
root_logger.disabled = True

This disables the root logger, and thus all the other loggers. I haven't really tested but it should be also the fastest.

From the logging code in python 2.7 I see this

def handle(self, record):
    """
    Call the handlers for the specified record.

    This method is used for unpickled records received from a socket, as
    well as those created locally. Logger-level filtering is applied.
    """
    if (not self.disabled) and self.filter(record):
        self.callHandlers(record)

Which means that when it's disabled no handler is called, and it should be more efficient that filtering to a very high value or setting a no-op handler for example.

andrea_crotti
  • 2,862
  • 2
  • 27
  • 33
  • 1
    Unless I am doing something wrong, this only disables the root logger and not any created like `log = logging.getLogger(__name__)` – starfry May 21 '17 at 16:03
  • 2
    This could be problematic if you're dealing with multiple loggers or multiple handlers. If, for example, you still want to log to a file but want to disable the stream handler in a specific case. – Joe Jul 12 '17 at 18:10
  • 2
    *This disables the root logger, and thus all the other loggers* – strictly speaking disabling the root logger does not disable any other loggers. Besides the question asks about disabling default StreamHandler **only**. – Piotr Dobrogost Aug 21 '18 at 14:03
  • 1
    The `disabled` attribute is not part of the public API. See https://bugs.python.org/issue36318. – Maggyero Mar 28 '19 at 12:00
15

Logging has the following structure:

  • loggers are arranged according to a namespace hierarchy with dot separators;
  • each logger has a level (logging.WARNING by default for the root logger and logging.NOTSET by default for non-root loggers) and an effective level (the effective level of the parent logger for non-root loggers with a level logging.NOTSET and the level of the logger otherwise);
  • each logger has a list of filters;
  • each logger has a list of handlers;
  • each handler has a level (logging.NOTSET by default);
  • each handler has a list of filters.

Logging has the following process (represented by a flowchart):

Logging flow.

Therefore to disable a particular logger you can adopt one of the following strategies:

  1. Set the level of the logger to logging.CRITICAL + 1.

    • Using the main API:

      import logging
      
      logger = logging.getLogger("foo")
      logger.setLevel(logging.CRITICAL + 1)
      
    • Using the config API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "loggers": {
              "foo": {
                  "level": logging.CRITICAL + 1
              }
          }
      })
      
  2. Add a filter lambda record: False to the logger.

    • Using the main API:

      import logging
      
      logger = logging.getLogger("foo")
      logger.addFilter(lambda record: False)
      
    • Using the config API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "filters": {
              "all": {
                  "()": lambda: (lambda record: False)
              }
          },
          "loggers": {
              "foo": {
                  "filters": ["all"]
              }
          }
      })
      
  3. Remove the existing handlers of the logger, add a handler logging.NullHandler() to the logger (to prevent events from being handled by the handler logging.lastResort which is a logging.StreamHandler using the current stream sys.stderr and a level logging.WARNING) and set the attribute propagate of the logger to False (to prevent events from being handled by the handlers of the ancestor loggers of the logger).

    • Using the main API:

      import logging
      
      logger = logging.getLogger("foo")
      for handler in logger.handlers.copy():
          try:
              logger.removeHandler(handler)
          except ValueError:  # in case another thread has already removed it
              pass
      logger.addHandler(logging.NullHandler())
      logger.propagate = False
      
    • Using the config API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
      

Warning. — Contrary to strategies 1 and 2 which only prevent events logged by the logger from being emitted by the handlers of the logger and its ancestor loggers, strategy 3 also prevents events logged by the descendant loggers of the logger (e.g. logging.getLogger("foo.bar")) to be emitted by the handlers of the logger and its ancestor loggers.

Note. — Setting the attribute disabled of the logger to True is not yet another strategy, as it is not part of the public API (cf. https://bugs.python.org/issue36318):

import logging

logger = logging.getLogger("foo")
logger.disabled = True  # DO NOT DO THIS
Maggyero
  • 4,569
  • 3
  • 30
  • 49
  • Excellent, but the question also asked how to re-enable it? For example, would you do a removeFilter and how? – NealWalters Aug 29 '20 at 19:53
  • 1
    @NealWalters For the 1st solution, you would create a handler: `handler = logging.NullHandler()`, add it to the logger and disable propagation to disable logging: `logger.addHandler(handler); logger.propagate = False`, and remove it from the logger and re-enable propagation to re-enable logging: `logger.removeHandler(handler); logger.propagate = True`. For the 2nd solution, you would create a filter: `def filter(record): return False`, add it to the logger to disable logging: `logger.addFilter(filter)`, and remove it from the logger to re-enable logging: `logger.removeFilter(filter)`. – Maggyero Aug 29 '20 at 21:26
11

No need to divert stdout. Here is better way to do it:

import logging
class MyLogHandler(logging.Handler):
    def emit(self, record):
        pass

logging.getLogger().addHandler(MyLogHandler())

An even simpler way is:

logging.getLogger().setLevel(100)
Ehsan Foroughi
  • 2,900
  • 2
  • 17
  • 19
  • 5
    In Python 2.7+ this is available as [NullHandler()](https://docs.python.org/2/library/logging.handlers.html#nullhandler) – Pierre May 20 '14 at 17:19
  • 2
    The reason why this works (disables default StreamHandler) can be seen when reading description of `logging.basicConfig()` function (emphasis mine): *Does basic configuration for the logging system by creating a StreamHandler with a default Formatter and adding it to the root logger. The functions debug(), info(), warning(), error() and critical() will call basicConfig() automatically **if no handlers are defined for the root logger**.* – https://docs.python.org/3/library/logging.html#logging.basicConfig – Piotr Dobrogost Aug 21 '18 at 13:42
2

This will prevent all logging from a third library which it used as decribed here https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library

logging.getLogger('somelogger').addHandler(logging.NullHandler())
1
import logging

log_file = 'test.log'
info_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'info_format': {
            'format': info_format
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'info_format'
        },
        'info_log_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'INFO',
            'filename': log_file,
            'formatter': 'info_format'
        }
    },
    'loggers': {
        '': {
            'handlers': [
                'console',
                'info_log_file'
            ],
            'level': 'INFO'
        }
    }
})


class A:

    def __init__(self):
        logging.info('object created of class A')

        self.logger = logging.getLogger()
        self.console_handler = None

    def say(self, word):
        logging.info('A object says: {}'.format(word))

    def disable_console_log(self):
        if self.console_handler is not None:
            # Console log has already been disabled
            return

        for handler in self.logger.handlers:
            if type(handler) is logging.StreamHandler:
                self.console_handler = handler
                self.logger.removeHandler(handler)

    def enable_console_log(self):
        if self.console_handler is None:
            # Console log has already been enabled
            return

        self.logger.addHandler(self.console_handler)
        self.console_handler = None


if __name__ == '__main__':
    a = A()
    a.say('111')
    a.disable_console_log()
    a.say('222')
    a.enable_console_log()
    a.say('333')

Console output:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,358 - INFO - A object says: 333

test.log file content:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,357 - INFO - A object says: 222
2018-09-15 15:22:23,358 - INFO - A object says: 333
Shawn Hu
  • 189
  • 1
  • 7
1

Considering you have created your own handlers, then right before you add them to the logger, you can do:

logger.removeHandler(logger.handlers[0])

Which will remove the default StreamHandler. This worked for me on Python 3.8 after encountering unwanted emitting of logs to stderr, when they should have been only recorded onto a file.

elbud
  • 25
  • 6
0

I don't know the logging module very well, but I'm using it in the way that I usually want to disable only debug (or info) messages. You can use Handler.setLevel() to set the logging level to CRITICAL or higher.

Also, you could replace sys.stderr and sys.stdout by a file open for writing. See http://docs.python.org/library/sys.html#sys.stdout. But I wouldn't recommend that.

Krab
  • 2,068
  • 14
  • 23
0

You could also:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
  • 1,946
  • 3
  • 17
  • 29
  • Why are you using `app.logger` which you don't even specify instead of the root logger explicitly mentioned in the question (`logging.getLogger()`) and most answers? How do you know you can safely modify `handlers` property instead of calling `Logger.addHandler` method? – Piotr Dobrogost Aug 21 '18 at 13:58
0

By changing one level in the "logging.config.dictConfig" you'll be able to take the whole logging level to a new level.

logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'console': {
        'format': '%(name)-12s %(levelname)-8s %(message)s'
    },
    'file': {
        'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
    }
},
'handlers': {
    'console': {
        'class': 'logging.StreamHandler',
        'formatter': 'console'
    },
#CHANGE below level from DEBUG to THE_LEVEL_YOU_WANT_TO_SWITCH_FOR
#if we jump from DEBUG to INFO
# we won't be able to see the DEBUG logs in our logging.log file
    'file': {
        'level': 'DEBUG',
        'class': 'logging.FileHandler',
        'formatter': 'file',
        'filename': 'logging.log'
    },
},
'loggers': {
    '': {
        'level': 'DEBUG',
        'handlers': ['console', 'file'],
        'propagate': False,
    },
}

})

0

Found an elegant solution using decorators, which addresses the following problem: what if you are writing a module with several functions, each of them with several debugging messages, and you want to disable logging in all functions but the one you are currently focusing on?

You can do it using decorators:

import logging, sys
logger = logging.getLogger()
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)


def disable_debug_messages(func):
    def wrapper(*args, **kwargs):
        prev_state = logger.disabled
        logger.disabled = True
        result = func(*args, **kwargs)
        logger.disabled = prev_state
        return result
    return wrapper

Then, you can do:

@disable_debug_messages
def function_already_debugged():
    ...
    logger.debug("This message won't be showed because of the decorator")
    ...

def function_being_focused():
    ...
    logger.debug("This message will be showed")
    ...

Even if you call function_already_debugged from within function_being_focused, debug messages from function_already_debugged won't be showed. This ensures you will see only the debug messages from the function you are focusing on.

Hope it helps!

Carlos Souza
  • 193
  • 1
  • 11
0

You can change the level of debug mode for specific handler instead of completely disable it.

So if you have a case you want to stop the debug mode for console only but you still need to keep the other levels like the Error. you can do this like the following

# create logger
logger = logging.getLogger(__name__)

def enableConsoleDebug (debug = False):
    #Set level to logging.DEBUG to see CRITICAL, ERROR, WARNING, INFO and DEBUG statements
    #Set level to logging.ERROR to see the CRITICAL & ERROR statements only
    logger.setLevel(logging.DEBUG)

    debugLevel = logging.ERROR
    if debug:
        debugLevel = logging.DEBUG

    for handler in logger.handlers:
        if type(handler) is logging.StreamHandler:
            handler.setLevel (debugLevel)

aibrahim
  • 199
  • 1
  • 4
0

It is not 100% solution, but none of the answers here solved my issue. I have custom logging module which outputs colored text according to severity. I needed to disable stdout output since it was duplicating my logs. I'm OK with critical logs being outputted to console since I almost don't use it. I didn't test it for stderr since I don't use it in my logging, but should work same way as stdout. It sets CRITICAL as minimal severity just for stdout (stderr if requested).

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# disable terminal output - it is handled by this module
stdout_handler = logging.StreamHandler(sys.stdout)

# set terminal output to critical only - won't output lower levels
stdout_handler.setLevel(logging.CRITICAL)

# add adjusted stream handler
logger.addHandler(stdout_handler)
-2

subclass the handler you want to be able to disable temporarily:

class ToggledHandler(logging.StreamHandler):
"""A handler one can turn on and off"""

def __init__(self, args, kwargs):
    super(ToggledHandler, self).__init__(*args, **kwargs)
    self.enabled = True  # enabled by default

def enable(self):
    """enables"""
    self.enabled = True

def disable(self):
    """disables"""
    self.enabled = False

def emit(self, record):
    """emits, if enabled"""
    if self.enabled:
        # this is taken from the super's emit, implement your own
        try:
            msg = self.format(record)
            stream = self.stream
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)

finding the handler by name is quite easy:

_handler = [x for x in logging.getLogger('').handlers if x.name == your_handler_name]
if len(_handler) == 1:
    _handler = _handler[0]
else:
    raise Exception('Expected one handler but found {}'.format(len(_handler))

once found:

_handler.disable()
doStuff()
_handler.enable()
jake77
  • 1,584
  • 2
  • 12
  • 21