Using logging's root logger before app.run() breaks Flask logging
See original GitHub issueOn latest PyPI’s release (1.0.2), using logging
’s root logger before running Flask breaks its logging.
For example, with such a run.py
file:
import logging
import flask
logging.info("it breaks")
flask.Flask(__name__).run()
python run.py
outputs this:
* Serving Flask app "run" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
and then stops any kind of logging.
The application is running and answering, but you don’t get the Running on http://...
line, nor any request logging.
It’s worth noting that using the root logger inside a route doesn’t break Flask.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:8 (4 by maintainers)
Top Results From Across the Web
How can I log outside of main Flask module? - Stack Overflow
So if your main module is called 'my_tool', you would want to do logger = logging.getLogger('my_tool') in the Service module. To add onto...
Read more >Logging — Flask Documentation (2.2.x)
The simplest way to do this is to add handlers to the root logger instead of only the app logger. Depending on your...
Read more >Getting Started Quickly With Flask Logging - SentinelOne
Rerun the application and make a request. Your log message went to the console. If you don't configure logging yourself, Flask adds a...
Read more >How to Get Started with Logging in Flask - Better Stack
Learn how to start logging with Flask and go from basics to best practices in no time. ... If the app.logger is accessed...
Read more >Learn the Working of Flask logging with Examples - eduCBA
1. Logger: This is the entry point of any logging system which provides an interface to the Flask application for logging events for...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
TL;DR: This behavior is caused by
flask.logging.has_level_handler()
andlogging.basicConfig()
. Specifically, using the root logger (i.e. callinglogging.<function()>
) before callingapp.logger
will add a handler to the root logger and causeflask.logging.has_level_handler()
to return True when it should return False.When you use any function from the logging module such as
logging.info('message')
orlogging.basicConfig(filename='some_log_file')
, you work with the root logger. The root logger is a Logger object that sits inside the logging module with a default level oflogging.WARNING
. The root logger isn’t created with any handlers attached, but when you use a module-level function like those I offered previously (specifically any function that feeds intobasicConfig()
), they will attach a file handler or a stream handler to the root logger for you.From the logging module (3.7):
In the scenario laid out by @ramnes, the root logger is used first, will have a default level of warning, and will have a stream handler attached to it. Now if we go to
flask.logging.create_logger()
(which is where all of the calls to the application-level logger will feed into some time after the call tologging.info()
), we see the following in theflask.logging
module:The primary issue lies with
has_level_handler()
:has_level_handler()
has three lines that cause the problem if used after a root logger is configured.First, in our example,
getEffectiveLevel()
will return a value of 30 (warning). The application logger begins its life with a level of 0, sogetEffectiveLevel()
will defer to the nearest level in the logging tree. In our case, that’s the root logger with a level of 30 (the 30 is returned). Next, we enter the loop and check the application logger for any handlers with a level of 30 or below. We don’t find any, so we get the logger’s parent (the root logger) and loop again. This time, we do find a handler: the handler logging attached to the root logger for us. All handlers begin their life with a level of 0, so this incidental handler fits the the conditionalif any(handler.level <= level for handler in current.handlers)
and hashas_level_handler()
return True when it should return False. Hopping back tocreate_logger()
, this means that the application logger will never be given the default handler.Now, whenever the application logger is asked to emit a message, it will have to defer to the nearest handler in the logging tree, which just so happens to be the incidental handler that we attached the root logger when we used it at the very beginning. In our example, this is a stream handler, which happens to default to stderr. Recall that the root logger defaults to a level of warning. This means that any messages with a debug- or info-level messages that the application logger attempts to emit will be suppressed. In our example, warning messages and above will still be output to stderr. If you call
basicConfig(filename='file.log')
, then warning messages and above will be appended tofile.log
. Notice that the werkzeug logger will still emit some warning messages if you haven’t touched with it.There are a couple of potential quick fixes (these won’t solve the problem, only mask it)
app.logger.<something>
) before the root logger so that it uses its own default handler (recommended).For a longer-fix, I would look at patching
flask.logging.has_level_handler()
, although I’m not sure how because I don’t understand its intentions.Whether this behavior is the resultant of an intentional implementation or not is not the point here. I do agree that #2436 make things much better than just removing any existing handler as Flask used to do, but please understand that the current behavior is very developer-hostile in that particular case. No one, beside someone that perfectly knows Flask internals, would consider
logging.info("it breaks")
as a way of configuring (or just altering) Flask’s logging. And yet, the root logger is mostly used by beginners, and developers that just want to throw some logging somewhere quickly for debugging purposes. I don’t know any library other than Flask which has its logging altered by a simple use of the root logger.