Configuring Output¶
This part discusses how to configure twiggy’s output of messages. You should do this once, near the start of your application’s __main__
.
Quick Setup¶
To quickly configure output, use the quick_setup
function. Quick setup is limited to sending all messages to a file or sys.stderr
. A timestamp will be prefixed when logging to a file.
-
twiggy.
quick_setup
(min_level=<LogLevel DEBUG>, file=None, msg_buffer=0) Quickly set up
emitters
.Parameters: - min_level (LogLevel) – lowest message level to cause output
- file (string) – filename to log to, or
sys.stdout
, orsys.stderr
.None
means standard error. - msg_buffer (int) – number of messages to buffer, see
outputs.AsyncOutput.msg_buffer
twiggy_setup.py¶
Twiggy’s output side features modern, loosely coupled design.
By convention, your configuration lives in a file in your application called twiggy_setup.py
, in a function called twiggy_setup()
. You can of course put your configuration elsewhere, but using a separate module makes integration with configuration management systems easy. You should import and run twiggy_setup
near the top of your application. It’s particularly important to set up Twiggy before spawning new processes.
A twiggy_setup
function should create ouputs and use the add_emitters()
convenience function to link those outputs to the log
.
from twiggy import add_emitters, outputs, levels, filters, formats, emitters # import * is also ok
def twiggy_setup():
alice_output = outputs.FileOutput("alice.log", format=formats.line_format)
bob_output = outputs.FileOutput("bob.log", format=formats.line_format)
add_emitters(
# (name, min_level, filter, output),
("alice", levels.DEBUG, None, alice_output),
("betty", levels.INFO, filters.names("betty"), bob_output),
("brian.*", levels.DEBUG, filters.glob_names("brian.*"), bob_output),
)
# near the top of your __main__
twiggy_setup()
add_emitters()
populates the emitters
dictionary:
>>> sorted(emitters.keys())
['alice', 'betty', 'brian.*']
In this example, we create two log destinations: alice.log
and bob.log
. alice will receive all messages, and bob will receive two sets of messages:
- messages with the name field equal to
betty
and level >=INFO
- messages with the name field glob-matching
brian.*
Emitters
can be removed by deleting them from this dict. filter
and min_level
may be modified during the running of the application, but outputs cannot be changed. Instead, remove the emitter and re-add it.
>>> # bump level
... emitters['alice'].min_level = levels.WARNING
>>> # change filter
... emitters['alice'].filter = filters.names('alice', 'andy')
>>> # remove entirely
... del emitters['alice']
We’ll examine the various parts in more detail.
Outputs¶
Outputs are the destinations to which log messages are written (files, databases, etc.). Several implementations
are provided. Once created, outputs cannot be modified. Each output has an associated format
.
Asynchronous Logging¶
Many outputs can be configured to use a separate, dedicated process to log messages. This is known as asynchronous logging and is enabled with the msg_buffer
argument. Asynchronous mode dramatically reduces the cost of logging, as expensive formatting and writing operations are moved out of the main thread of control.
Formats¶
Formats
transform a log message into a form that can be written by an output. The result of formatting is output dependent; for example, an output that posts to an HTTP server may take a format that provides JSON, whereas an output that writes to a file may produce text.
Line-oriented formatting¶
LineFormat
formats messages for text-oriented outputs such as a file or standard error. It uses a ConversionTable
to stringify the arbitrary fields in a message. To customize, copy the default line_format
and modify:
# in your twiggy_setup
import copy
my_format = copy.copy(formats.line_format)
my_format.conversion.add(key = 'address', # name of the field
convert_value = hex, # gets original value
convert_item = "{0}={1}".format, # gets called with: key, converted_value
required = True)
# output messages with name 'memory' to stderr
add_emitters(('memory', levels.DEBUG, filters.names('memory'), outputs.StreamOutput(format = my_format)))
Filtering Output¶
The messages output by an emitter are determined by its min_level
and filter (a function
which take a Message
and returns bool). These attributes may be changed while the application is running. The filter
attribute of emitters is intelligent
; you may assign strings, bools or functions and it will magically do the right thing. Assigning a list indicates that all of the filters must pass for the message to be output.
e = emitters['memory']
e.min_level = levels.WARNING
# True allows all messages through (None works as well)
e.filter = True
# False blocks all messages
e.filter = False
# Strings are interpreted as regexes (regex objects ok too)
e.filter = "^mem.*y$"
# functions are passed the message; return True to emit
e.filter = lambda msg: msg.fields['address'] > 0xDECAF
# lists are all()'d
e.filter = ["^mem.y$", lambda msg: msg.fields['address'] > 0xDECAF]
See also
Available filters