Use standard logging library
In Python there is only one proper pattern: use standard logging module according to official HowTo. Or, for Python 3, this standard logging module and this HowTo. There are no important differences between these versions¹.
If you have reasons to dislike the standard library, you can alternatively use one of the existing replacements. They have different configuration and some extensions, but the basic usage is similar or identical. I'll get to them later. It should be relatively easy to switch to them at any time, so
logging is at least a good starting point.
Usage in your module
Your code looks like this:
logger = logging.getLogger(__name__) # you can use other name
logger.info('Loading my cool module with cool logging')
logger.debug('Most of the time this message is really irrelevant')
logger.warning('Something suspicious happened: nothing broken yet, but you probably want to know')
logger.error('Everything is broken and this program just went nuts')
Or alternatively, you can use simpler:
instead of creating a logger, but it seems simple enough to add this single line everywhere.
If you are not sure what logging levels to use for different messages, you can refer to this SO question with some decent answers.
Then somewhere in your cofig you say:
to set the output and filter the errors appropriately. If you want to use stderr, say
Alternative handlers, logging to external services
There is also a number of libraries that you can but in place of basic logger, like raven for Sentry. You don't need to change anything in your code to tap into such a service, you just change the configuration.
There are some libraries that that you may want to use to completely replace
logging. They follow similar pattern, central logging configuration, then access a logger in each file and log using log levels. Logbook seems to be a mostly drop-in replacement for logging, with some nice features added. Some other are mentioned in this SO question.
Removing logging from optimized build
For extreme cases where debug logging might significantly affect performance or leak sensitive data in production environment, built-in
__debug__ constant can be used:
debug_info = some_complex_computation()
In this case, the whole statement would be erased when compiling the code with
-o option. However, this solution is ugly and somewhat brittle, it should be used with care and only where really necessary. For most applications that means "nowhere, never".
For other languages, it may look just the same (e.g. in Java) or slightly different, and vary somewhat (not every language has one standard way). But in general, you will find very similar patters, stuff like
LOG(DEBUG, "Your message")
LOG(INFO) << "Some people like C++ streams' syntax"
¹ AFAIK the only difference worth knowing about is that Python 3 has a "last resort" logger logging to stderror, so if you omit config completely, you still get something. In Python 2 you get just a warning about misconfigured logging.