Skip to content

Basic Usage

The API for containerlog is fairly sparse and there is very little configuration that can be done for it. As such, it is simple to get started with and quickly integrate into your application.

Create a Logger

Similar to the Python standard logger, a containerlog logger is initialized, typically at the module level, using get_logger.

The logger name can be passed in explicitly

logger = containerlog.get_logger('my-logger')

or, it can use the module name if no name is explicitly provided.

logger = containerlog.get_logger()

Configuration

There are only a handful of options that can be configured for a logger.

Log Level

Log levels are defined at the containerlog module level. The supported levels are:

  • containerlog.TRACE (0)
  • containerlog.DEBUG (1)
  • containerlog.INFO (2)
  • containerlog.WARN (3)
  • containerlog.ERROR (4)
  • containerlog.CRITICAL (5)

These are internally mapped as integers, so the corresponding int may also be used to designate the log level.

Log levels may be set on a single logger via the level attribute.

logger.level = containerlog.INFO

Or, the log level can be set globally for all loggers

containerlog.set_level(containerlog.INFO)

Design Choices

  • If no logging level is set, it will default to debug. This does not follow other loggers which may choose to default to an info level. The rationale behind this is that for a default use case, it is better to display too much information rather than too little.

    As such, you may find yourself needing to configuring the log level in an inverse fashion from what you may be used to.

    if not config.DEBUG:
      containerlog.set_level(containerlog.INFO)
    

Log Output

Loggers can also be configured to change the location of where logs are written to. In general, this should not need to be configured, though it can be useful when writing tests and needing to capture log output.

By default, logs at level ERROR or greater are written to stderr, while logs at WARN or less are written to stdout.

The target for error output can be set via the Logger's writeerr attribute, e.g.

err_stream = io.StringIO()
logger.writeerr = err_stream.write

For normal output, set the writeout attribute.

out_stream = io.StringIO()
logger.writeout = out_stream.write

Optimization

The writeerr and writeout attributes should reference the write function for an IO implementation, not the object which implements the IO interface itself. This is an optimization, as it localizes the function for faster loading.

If you do not wish to log to two different streams, you can set one stream equal to the other.

logger.writeout = logger.writeerr

Logging

Once you have a logger, you can log a message at trace, debug, info, warn, error, or critical level.

# Log at trace level
logger.trace('message to log')

# Log at debug level
logger.debug('message to log')

# Log at info level
logger.info('message to log')

# Log at warn level
logger.warn('message to log')
logger.warning('message to log')

# Log at error level
logger.error('message to log')

# Log at critical level
logger.critical('message to log')

Data passed in as keyword arguments to any of the logging methods gets rendered as key-value pairs for the structured log.

logger.info('connected to remote server', ip=server_ip, port=server_port)

Tip

Since this library is intended to be used for structured logging, avoid logging formatted messages to minimize the performance penalty from the additional string format.

If a logger is configured at INFO level, a debug message

logger.debug(f'got a value: {value}')

would not get logged, but the string formatting woulds still happen since it is done upfront, not within the log function. Instead, pass values as keyword arguments.

logger.debug('got a value', value=value)

Logger Behavior

Disable a Logger

Internally, log levels are compared to determine what level something should be logged at. For example, if logging a message at debug (1) level, but the logger is configured at info (2) level, 1 < 2, so the message does not get logged.

To disable a logger, the log level is just set to a value higher than any of the supported log levels. Canonically, this is 99, but could be anything higher than critical.

To disable a logger, simply call the disable() method.

logger.disable()

this will prevent it from emitting any logs.

To check whether a logger is disabled, check the disabled property.

if logger.disabled:
    ...

Enable a Logger

If a logger was previously disabled, it can be re-enabled by calling the Logger's enable() method.

logger.enable()

This will restore its log level to its previously known level, prior to having disable() called.

If a logger is already enabled, this call will do nothing.

Disable Loggers Globally

Instead of disabling a single logger, you may want to disable all loggers. This can be done be calling the package's disable function.

containerlog.disable()

This could be useful in tests to suppress log output.

Disabling all loggers can be heavy-handed at times. For more granular control, you can pass the name(s) of the loggers to disable, globs to match against logger names, or a combination of both.

containerlog.disable('my-app.secrets*', 'third-party-logger')

Enable Loggers Globally

Calling the module level enable performs the inverse of the global disable. It can re-enable all loggers if no arguments are provided

containerlog.enable()

Or it can selectively re-enable based on string or glob match to the logger name.

containerlog.enable('my-app.secrets*', 'third-party-logger')