9

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()?

Kilian Foth
111k45 gold badges301 silver badges323 bronze badges
asked Dec 18, 2020 at 7:36
3
  • 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. Commented Dec 18, 2020 at 7:44
  • 2
    Asking 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. Commented Dec 18, 2020 at 8:26
  • Did my post answer your question? Commented Jan 12, 2021 at 14:00

3 Answers 3

8

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.

answered Dec 18, 2020 at 14:40
2

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.

answered Dec 18, 2020 at 12:05
1

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.

answered Jan 18, 2021 at 15:43

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.