How do I silence a class's logging without knowing the name of its logger? Class in question is qualysconnect.
import logging
import qualysconnect.util
# Set log options. This is my attempt to silence it.
logger_qc = logging.getLogger('qualysconnect')
logger_qc.setLevel(logging.ERROR)
# Define a Handler which writes WARNING messages or higher to the sys.stderr
logger_console = logging.StreamHandler()
logger_console.setLevel(logging.ERROR)
# Set a format which is simpler for console use.
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# Tell the handler to use this format.
logger_console.setFormatter(formatter)
# Add the handler to the root logger
logging.getLogger('').addHandler(logger_console)
# 'application' code
logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')
Output when import qualysconnect.util is commented out:
root : ERROR error message
root : CRITICAL critical message
Output when import qualysconnect.util is kept in:
WARNING:root:warn message
ERROR:root:error message
root : ERROR error message
CRITICAL:root:critical message
root : CRITICAL critical message
Sadly, as they did not define a name for their logger, and as in qualysconnect.util they don't even do a getLogger call or a getChild call, you can't do something on one that will not affect the whole module's logging behavior without getting dirty.
The only clean option I can think of is to report the way they handle logging as a bug, and submit a patch request where you modify qualysconnect.util logging statement with something like:
import logging
logger = logging.getLogger('qualysconnect').getChild('util')
and replace all logging.info(), logging.debug()... into logger.info(), logger.debug()...
The dirty option: You can monkey patch the qualysconnect.util module so you replace its logging object with a logger object:
import qualysconnect.util
logger_qc = logging.getLogger('qualysconnect')
logger_qc.setLevel(logging.ERROR)
qualysconnect.util.logging = logger_qc.getLogger('qualysconnect').getChild('util')
qualysconnect.util.logging.disable(logging.CRITICAL) # will disable all logging for CRITICAL and below
That can be a working solution for the time you're sending a patch request to the upstream project, but certainly not a long-term working solution.
Or you can simply shut all logging off from the whole qualysconnect module, but I don't think that's what you want.
Related
The fastAPI library that I import for an API I have written, writes many logging.INFO level messages to the console, which I would like either to redirect to a file-based log, or ideally, to both console and file. Here is an example of fastAPI module logging events in my console:
So I've tried to implement this Stack Overflow answer ("Easy-peasy with Python 3.3 and above"), but the log file it creates ("api_screen.log") is always empty....
# -------------------------- logging ----------------------------
logging_file = "api_screen.log"
logging_level = logging.INFO
logging_format = ' %(message)s'
logging_handlers = [logging.FileHandler(logging_file), logging.StreamHandler()]
logging.basicConfig(level = logging_level, format = logging_format, handlers = logging_handlers)
logging.info("------logging test------")
Even though my own "------logging test------" message does appear on console within the other fastAPI logs:
As you can see here it's created the file, but it has size zero.
So what do I need to do also to get the file logging working?
There are multiple issues here. First and most importantly: basicConfig does nothing if a logger is already configured, which fastAPI does. So the handlers you are creating are never used. When you call logging.info() you are sending a log to the root logger which is printed because the fastAPI has added a handler to it. You are also not setting the level on your handlers. Try this code instead of what you currently have:
logging_file = "api_screen.log"
logging_level = logging.INFO
logging_fh = logging.FileHandler(logging_file)
logging_sh = logging.StreamHandler()
logging_fh.setLevel(logging_level)
logging_sh.setLevel(logging_level)
root_logger = logging.getLogger()
root_logger.addHandler(logging_fh)
root_logger.addHandler(logging_sh)
logging.info('--test--')
I am having trouble finding the bug in my code when I try to log using the logging library. Even though I set the minimum log-level to DEBUG, the logger creates a log-file that starts at WARNING.
import logging
my_logger = logging.getLogger("our logger")
# Remove all handlers associated with my_logger object.
# This is only done so that we can run this block mult. times.
for handler in my_logger.handlers[:]:
my_logger.removeHandler(handler)
# let's create a handler and set the logging level
my_handler_for_file = logging.FileHandler("custom logging.log", mode='w')
my_handler_for_file.setLevel(logging.DEBUG)
# try to set it to logging.CRITICAL and it will only log CRITICAL,
# so it does work but neither for DEBUG nor INFO!
# add the handlers to our custom logger
my_logger.addHandler(my_handler_for_file)
# let's create some logs
my_logger.debug('This is a debug message')
my_logger.info('This is an info message')
my_logger.warning('This is a warning message')
my_logger.error('This is an error message')
my_logger.critical('This is a critical message')
This is the output in the log-file:
This is a warning message
This is an error message
This is a critical message
And this is what I expect it to be:
This is a debug message
This is an info message
This is a warning message
This is an error message
This is a critical message
Does anyone have an idea what is wrong here?
You need to set the level on the logger too.
my_logger.setLevel(logging.DEBUG)
This is because both the loggers and the handlers filter messages based on their level.
I'd like to test logger messages without printing them to the screen in my unittests. Given this code:
import logging
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger('test')
logger.warning('this is a warning')
# How do I see that there was a warning?
How do I look at the log records in the logger to see that there was a warning? I cannot find an iterator in Logger that would do the job.
You may need to use TestCase.assertLogs() context manager in such case. The documentation provides a pretty good example of what can be done with it:
with self.assertLogs('foo', level='INFO') as cm:
logging.getLogger('foo').info('first message')
logging.getLogger('foo.bar').error('second message')
self.assertEqual(cm.output, ['INFO:foo:first message',
'ERROR:foo.bar:second message'])
Inside the context manager, you can access cm.records for a list of LogRecord instances, or cm.output for a list of formatted messages
I try to use logging in Python to write some log, but strangely, only the error will be logged, the info will be ignored no matter whichn level I set.
code:
import logging
import logging.handlers
if __name__ == "__main__":
logger = logging.getLogger()
fh = logging.handlers.RotatingFileHandler('./logtest.log', maxBytes=10240, backupCount=5)
fh.setLevel(logging.DEBUG)#no matter what level I set here
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.info('INFO')
logger.error('ERROR')
The result is:
2014-01-14 11:47:38,990 - root - ERROR - ERROR
According to http://docs.python.org/2/library/logging.html#logging-levels
The INFO should be logged too.
The problem is that the logger's level is still set to the default. So the logger discards the message before it even gets to the handlers. The fact that the handler would have accepted the message if it received it doesn't matter, because it never receives it.
So, just add this:
logger.setLevel(logging.INFO)
As the docs explain, the logger's default level is NOTSET, which means it checks with its parent, which is the root, which has a default of WARNING.
And you can probably leave the handler at its default of NOTSET, which means it defers to the logger's filtering.
I think you might have to set the correct threshold.
logger.setLevel(logging.INFO)
I got a logger using logging.getLogger(__name__). I tried setting the log level to logging.INFO as mentioned in other answers, but that didn't work.
A quick check on both the created logger and its parent (root) logger showed it did not have any handlers (using hasHandler()). The documentation states that the handler should've been created upon first call to any of logging functions debug, info etc.,
The functions debug(), info(), warning(), error() and critical() will
call basicConfig() automatically if no handlers are defined for the
root logger.
But it did not. All I had to do was call basicConfig() manually.
Solution:
import logging
logging.basicConfig() # Add logging level here if you plan on using logging.info() instead of my_logger as below.
my_logger = logging.getLogger(__name__)
my_logger .setLevel(logging.INFO)
my_logger .info("Hi")
INFO:__main__:Hi
I have used the following code to get warnings to be logged:
import logging
logging.captureWarnings(True)
formatter = logging.Formatter('%(asctime)s\t%(levelname)s\t%(message)s')
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)
This works, however, my logging formatter is not applied, and the warnings come out looking like this:
WARNING:py.warnings:/home/joakim/.virtualenvs/masterload/local/lib/python2.7/site-packages/MySQL_python-1.2.3c1-py2.7-linux-x86_64.egg/MySQLdb/cursors.py:100: Warning:
InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_per_table.
instead of the expected format:
2012-11-12 18:19:44,421 INFO START updating products
How can I apply my normal formatting to captured warning messages?
You created a handler, but never configured the logging module to use it:
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)
You need to add this handler to a logger; the root logger for example:
logging.getLogger().addHandler(console_handler)
Alternatively, you can add the handler to the warnings logger only; the captureWarnings() documentation states that it uses py.warnings for captured warnings:
logging.getLogger('py.warnings').addHandler(console_handler)
Instead of creating a handler and formatter explicitly, you can also just call basicConfig() to configure the root logger:
logging.basicConfig(format='%(asctime)s\t%(levelname)s\t%(message)s', level=logging.DEBUG)
The above basic configuration is the moral equivalent of the handler configuration you set up.
logging.captureWarnings logs to a logger named py.warnings, so you need to add your handler to that logger:
import logging
logging.captureWarnings(True)
formatter = logging.Formatter('%(asctime)s\t%(levelname)s\t%(message)s')
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)
py_warnings_logger = logging.getLogger('py.warnings')
py_warnings_logger.addHandler(console_handler)
The documentation says that If capture is True, warnings issued by the warnings module will be redirected to the logging system. Specifically, a warning will be formatted using warnings.formatwarning() and the resulting string logged to a logger named 'py.warnings' with a severity of WARNING.
Therefore I would try to
# get the 'py.warnings' logger
log = logging.getLogger('py.warnings')
# assign the handler to it
log.addHandler(console_handler)