So far, I've been using logging only in main()
mostly. I do not log anything in my functions or classes but sometimes I feel like it would be helpful.
What I would do now:
def init_logger():
# set up logging to file
logging.basicConfig(filename=LOG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('testlog')
h = logging.handlers.SysLogHandler()
h.setLevel(logging.ERROR)
logger.addHandler(h)
return logger
def x():
return 5+5
def main():
logger = init_logger()
logger.info('starting function x')
x()
logger.info('x returned successfully')
But sometimes I want/need to do something like:
def x():
logger.info('starting x')
...
logger.info('running x')
...
logger.info('finished x')
return value
But this means that I would need to set up logging at the module level, right? E.g.
#!/bin/python
import logging
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
And now I can use logging anywhere in my script.
Also, doing so raises other points of consideration such as making sure logging is set up properly if somebody imports my module etc.
So basically, my main question is:
Is it Pythonic to use logging inside classes/methods/functions or should it be used in outer functions such as main()
- as I do now?
And my subquestion would be:
Should logging be initialized at the module level or inside a function as I've done in my example in init_logger()
?
-
Anyone who imports your module can set up their own logging preferences after the import runs, so you don't need to worry about affecting client modules.Kilian Foth– Kilian Foth2020年12月18日 07:44:20 +00:00Commented Dec 18, 2020 at 7:44
-
2Asking if something is "good practice" or even "Pythonic" is pretty meaningless. Asking if something is a good idea for reaching a specific goal makes way more sense. For example, in this case, you should ask if usage of logging can affect or restrict reusability of a module - and the question title and wording should put that goal into focus. Otherwise, this question is prone to get close votes for not being focussed enough.Doc Brown– Doc Brown2020年12月18日 08:26:50 +00:00Commented Dec 18, 2020 at 8:26
-
Did my post answer your question?lennon310– lennon3102021年01月12日 14:00:35 +00:00Commented Jan 12, 2021 at 14:00
3 Answers 3
Since the module is the unit of Python software, a good convention to use when naming loggers is to use a module-level logger, in each module which uses logging, add
logger = logging.getLogger(__name__)
on top of each module right after import
statements. This is a singleton so we don't pass it around functions. And of course you can use logger.info
or debug/error/warn inside any function/class then.
Configuring logging has several possible ways, Programmers can configure logging in three ways:
Creating loggers, handlers, and formatters explicitly using Python code that calls the configuration methods listed above.
Creating a logging config file and reading it using the fileConfig() function.
Creating a dictionary of configuration information and passing it to the dictConfig() function.
Refer to Configuring Logging section in Python doc for more examples.
AFAIK it is not a bad practice to log in a function or a class method.
Is it Pythonic to use logging inside classes/methods/functions or should it be used in outer functions such as main() - as I do now?
I don't know whether is it Pythonic or not but I guess it is ok ;). Anyway it doesn't make your code more unreadable.
Should logging be initialized at the module level or inside a function as I've done in my example in init_logger()?
I suggest to init logger at the module level as it is basically like importing and other such stuff.
Do bear in mind that logging.getLogger(name_of_logger)
will return the exact same logger object as long as name_of_logger is the same.
So you can let the invoking module sets up the logging as they like, and just use a fetched logger object in your functions/classes.
To adopt your example code:
def x():
logger = logging.getLogger("xlog")
logger.info('starting x')
...
logger.info('running x')
...
logger.info('finished x')
return value
This frees up the need to initialize the logging in your code. If the user code does not need logging, then user code can just invoke your code. If the user code actually needs logging, then it's the responsibility of the user code to setup all the handlers and formatters for the "xlog"
logger object.
You can put the information about the "xlog"
logger in the documentation to your code, like in the docstring of the x()
function.