Stop Using “print” for Debugging: A 5 Minute Quickstart Guide to Python’s logging Module

One final tip: You can use the tail -f logfile.txt command to show a file as it is being written to. The -f stands for “follow”. Just leave a terminal/console window open with this command running, and new text in the log file will appear as it is written. This way, you don’t have to keep opening/reloading a text editor to view the latest text in the log file.

The tail command comes on Mac OS X and Linux OSes. On Windows, you can download the Cygwin project to get the tail command.

Page 2 of 2 | Previous page

19 comments on this post.
  1. Benjamin Jones:

    Thanks! That’s a nice short tutorial. I’m going to put logging into some of my code right now.

  2. LEW21:

    Yeah! Instead of using one simple line, copy & paste these 30 lines!

  3. diego:

    Thank you.

  4. Al Sweigart:

    If you’ll actually read the article, there’s only two lines to paste to have a logger that prints to the screen (just like print does). A small change and it can print to a file instead. The 12 lines (not 30) to do both isn’t that bad (especially since you can copy and paste it from this blog, and the 3 setLevel() lines aren’t even needed by default if you want to log everything.)

    The short-sightness and exaggeration are the exact same things that kept me from learning how to do things in a new (and more efficient) way. Grow out of it.

  5. David Avraamides:

    While I don’t agree with LEW21′s sarcasm, your third example – logging to a file and to the screen – underscores a design problem with the logging module. If you are logging to the screen OR a file, it takes one call to configure the logger. But if you are logging to both it’s quite a bit more complicated. It should only take two calls.

    I have my own log.py module to simplify my use of logging in my projects, but I think the need to wrap a standard library module in your own is a code smell. And that’s why I think it’s so common to see people using print instead of logging.

  6. Tim:

    This looks very useful, bookmarking immediately. I was just up against a problem the other day and a coworker and I were talking about adding logging to troubleshoot. I’ll be digging in to this over the weekend.

  7. Marcel Chastain:

    Great article! I must admit I’m guilty of littering my code with endless print statements which I later have to hastily remove before committing.

    This is quick, simple and concise. Thanks!

  8. jul:

    well, what if you are logging strings wich require a certain amount of CPU to format ? ( ex : logging( json.dumps(big_complex_dict))
    Will you get rid of this CPU burden by disabling the logging function ?

    I guess not.

    One should still use logging sparsely, and clean the code after debugging*.

    (*However, I personnaly keep silent logging function in some code as both a warning there is a caveat (and a practival HOWTO debug) and sometimes as a comment in tricky parts of code. )

  9. Stephen:

    @jul,

    “well, what if you are logging strings wich require a certain amount of CPU to format ? ( ex : logging( json.dumps(big_complex_dict))
    Will you get rid of this CPU burden by disabling the logging function ?”

    Yes, beyond a shadow a doubt. If it doesn’t you’ve misunderstood the meaning of the word “disable”.

    Debugging code is just a cost of doing business. Either write it perfectly the first time or get used to looking a debug log when you’re testing it.

  10. Richard Moore:

    I agree with David Avraamides that these examples illustrate that the API of the logging module right now isn’t very good. For example:

    import logging
    logging.debug(‘x’)
    logging.error(‘y’)

    This displays only the message from the error call (fair enough). But now I want to enable the debug messages. Can I simply do ‘logging.setLevel(Logging.DEBUG)’? Answer no.

    A good API should at least make the effort involved in moving from trivial stuff to a very slightly higher level proportionate with the change, rather than requiring you to understand a lot of implementation detail.

    Even the basic facility of timestamping is missing by default, the result is that using print is actually easier in general. A simple module that offers debug(), error() etc. methods actually feels much more attractive than the basic logging facility. At least if you avoid using print directly you can replace it with some magic from logging later.

    I’d say the basic message here is ‘use a wrapper function around print’, and maybe use logging later. That’s not how it should be of course, just how it seems to me right now.

  11. Anirban:

    But is there some way by which we can filter out the log messages from different modules, and allow only those from modules we want?

  12. Terry A. Davis:

    Logging to a file is practically impossible in LoseThos. When I made my native file system, my one goal was not to get sued, like FAT32. I did not make a fat table, just used an allocation bitmap. Files must be contiguous and you cannot grow them. Technically you can if you use a FAT file system, but I try to avoid anything which works on FAT and not my native system. The is no FPRINTF().

  13. Andre Ramaciotti:

    In your last example, are you sure you need to have `logger` as a global variable? It seems to me that the function `getLogger` returns the root logger of the application as a singleton (every time you call it, it’ll return a reference to the same object).

    >>> import logging
    >>> logging.getLogger()

    >>> logging.getLogger()

    `getLogger` documentation: http://docs.python.org/library/logging.html#logging.getLogger

  14. Nikhil Gopal:

    I knew there must have been a module for this! Thanks for sharing.

  15. Bastian:

    You can use
    if logger.isEnabledFor(logging.DEBUG):
    logger.debug(expensiveFormatter(foo))

    to avoid expensive computations in production

  16. Chris Graham:

    One way to reduce the amount of code you need to copy and paste in every new source code file is to use the logging.config functionality to create a logging settings file that is shared by all of your modules. Then you can setup as complicated of logging as you want, or edit the log settings once and have it be applied to everything you’ve already written with no code changes. Here is the code I use in my source to load the settings:

    if __name__==”__main__”:
    import logging.config
    logging.config.fileConfig(“log.ini”)
    import logging
    logger = logging.getLogger(__name__)

  17. [Python] An Introduction to logging | Lonely Coder:

    [...] Stop Using “print” for Debugging: A 5 Minute Quickstart Guide to Python’s logging Module [...]

  18. Vick Fisher:

    Thanks! I’ve always done logging to file and console with Java using log4j or similar, but since I’m having to write Python lately, it’s nice to make the code a little more professional. Timestamped, formatted logging is indispensable for production code.

  19. Jeremy:

    I’ve only just started working in python within the past 4-6 months, coming from a background in shell scripting and some reeeeeally old experience with C/C++. I started working on a project with a coworker, and noticed that import logging was the first line following the shebang in his first pass over the code.

    “That’s cool,” thinks myself. “I did want to implement some better logging into this program. I’ll look that up once I get done adding in the core functions.” And then I proceeded to write the majority of the core functionality of the project, using print statements to debug.

    I just spent about an hour rejiggering everything to only use print for – y’know – the stuff I want to print in production, and use logging for everything else. Bless you for this post – it made things make much more sense almost immediately.

    I’m still playing with it a bit – I’ve written a logging_init() function that I can port from file to file (eventually, I’ll write a module to import, but since I’m still tinkering with the code, a function has made more sense) to allow me to specify the log levels for file and console separately, so once I’m done testing a specific section, I just change a variable passed to the function and I’m no longer flooded with debug messages. I’m sure it’s just the beginning, so thanks for getting me rolling!

Leave a comment