Configuration

Sawmill comes with some default configurators that are simple helper functions for configuring the system. Each one is registered in the sawmill.configurators dictionary and can be called through the sawmill.configure() function.

The default configurator is the classic one which you would have used in the Tutorial.

Note

You can find reference for each configurator that comes with Sawmill in the sawmill.configurator section.

You don’t have to use a configurator though (or you might want to create your own) so let’s take a look at manually configuring the system.

First, decide what you want to handle, where it should go and how it should be formatted. For our purposes, we will configure Sawmill to:

  • Send all logs to sys.stderr that have a level greater than ‘warning’ or have been tagged with a ‘user’ key. The format will be just the level and message.
  • Send all logs to a file displaying all available fields verbosely starting with date, level, name, message.
  • Send a formatted email for any log with a level of ‘error’ or higher to our support team including the traceback and a buffer of recent logs.
  • Redirect standard library logging to Sawmill.

Before you start make sure you import all the necessary modules:

import sys
import tempfile
from datetime import datetime

import sawmill
import sawmill.handler.stream
import sawmill.handler.email
import sawmill.handler.buffer
import sawmill.formatter.template
import sawmill.formatter.field
import sawmill.filterer.level
import sawmill.filterer.item
import sawmill.compatibility

User Visible Or High Level To Standard Error

First up we need a Stream handler to direct output to sys.stderr:

    stderr_handler = sawmill.handler.stream.Stream(sys.stderr)

We want to use a simple Template formatter to display each log as a string of level and message:

    stderr_formatter = sawmill.formatter.template.Template(
        '{level}:{message}\n'
    )

And attach that as the formatter for the stderr_handler:

    stderr_handler.formatter = stderr_formatter

Now we need to filter all logs that don’t meet the level requirement unless they are tagged with a ‘user’ key:

    stderr_filterer = sawmill.filterer.level.Level(min=level, max=None)
    stderr_filterer |= sawmill.filterer.item.Item('user', True)

And attach as the filterer for the stderr_handler:

    stderr_handler.filterer = stderr_filterer

Next we just need to register this handler under a sensible name like stderr:

    sawmill.root.handlers['stderr'] = stderr_handler

All To File

Logging everything to a file means we need another Stream handler, but pointing at a file this time:

    # Output to log file
    if filepath is None:
        prefix = datetime.now().strftime('sawmill-%Y_%m_%d-%H_%M_%S-')
        _, filepath = tempfile.mkstemp(prefix=prefix, suffix='.log')

    file_stream = open(filepath, 'a')
    file_handler = sawmill.handler.stream.Stream(file_stream)

We don’t need any filterer as all logs should go to the file, but we do want a specific formatter to try and capture as much information as the logs can provide:

    file_formatter = sawmill.formatter.field.Field([
        'timestamp', '*', 'name', 'level', 'message', 'traceback'
    ])
    file_handler.formatter = file_formatter

Note

The ‘*’ is special and means capture all other fields present ordered alphabetically by key.

And register that one as well:

    sawmill.root.handlers['file'] = file_handler

Errors To Email

Next, create an email handler that will send any errors to a predefined email address, including a buffer of recent messages. First setup the email handler with the default template:

    # Send email on errors.
    email_handler = sawmill.handler.email.Email(
        'smtp.example.com', 587,
        secure=(),
        formatter=sawmill.formatter.email.Email(
            'Error Report',
            email_sender,
            email_recipients
        )
    )

Next, we need a buffer so that errors will have context. This buffer will wrap the email handler and only pass on messages when the set trigger is activated:

    def check_for_error(logs, buffer):
        '''Return True if any of the recent logs was an error message.'''
        for log in logs:
            if log.get('level') == 'error':
                return True

        return False

    email_buffer_handler = sawmill.handler.buffer.Buffer(
        email_handler,
        check_for_error,
        limit=30
    )

And register the buffer handler as the email handler:

    sawmill.root.handlers['email'] = email_buffer_handler

Integrate standard library logging

Finally, redirect all standard library logging to Sawmill (and this configuration) using the supplied helper function:

    if redirect_standard_logging:
        sawmill.compatibility.redirect_standard_library_logging()

See also

The alpha configurator used for this example.