Twiggy 0.4.4

A more Pythonic logger
Twiggy is an early-stage project to build a more Pythonic logging package.

Usage:

Make display work:

>>> import sys; sys.stderr = sys.stdout

Setup is simple

In your main.py:

>>> import twiggy
>>> twiggy.quick_setup()


Logging Messages

>>> from twiggy import *

Main interface is the the magic log:

>>> log #doctest:+ELLIPSIS
< twiggy.Logger.Logger object at 0x... >


It works out of the box, using standard levels:

>>> log.debug('You may not care')
DEBUG:You may not care
>>> log.error('OMFG! Pants on fire!')
ERROR:OMFG! Pants on fire!


It supports a variety of format strings, defaulting to new-style:

>>> log.info('I wear {0} on my {where}', 'pants', where='legs')
INFO:I wear pants on my legs


Old style works fine though:

>>> log.options(style='percent').info('I like %s', "bikes")
INFO:I like bikes


As do templates:

>>> log.options(style='dollar').info('$what kill', what='Cars')
INFO:Cars kill


You can name your loggers:

>>> mylog = log.name('alfredo')
>>> mylog.debug('hello')
DEBUG:alfredo:hello


But the name has no relation to the object; it's just for human use:

>>> mylog is log.name('alfredo')
False


Emitting Messages

Emitters are loosely coupled:

>>> twiggy.emitters #doctest:+ELLIPSIS
{'*': < twiggy.Emitter.Emitter object at 0x... >}


You can set a min_level on Emitters:

>>> twiggy.emitters['*'].min_level = twiggy.Levels.INFO
>>> log.debug("Help, help I'm being suppressed")
>>> log.info("I'm not quite dead yet")
INFO:I'm not quite dead yet


You can filter on regexes, or with arbitrary functions:

>>> twiggy.emitters['*'].filter = ".*pants.*"
>>> log.info("Got my {0} on", "pants")
INFO:Got my pants on
>>> log.info("Got my {0} on", "shirt")


Let's reset all that:

>>> twiggy.emitters['*'].filter = True
>>> twiggy.emitters['*'].min_level = twiggy.Levels.DEBUG


Better output

Newlines are suppressed by default; that can be turned off per-message:

>>> log.info('user\ninput\nannoys\nus')
INFO:user\ninput\nannoys\nus

>>> log.options(suppress_newlines=False).info('we\ndeal')
INFO:we
deal


Exceptions are prefixed. Can also pass exc_info. Use '\n' as a prefix to fold into a single line:

>>> try:
... 1/0
... except:
... log.trace('error').warning('oh noes') #doctest:+ELLIPSIS
WARNING:oh noes
TRACE Traceback (most recent call last):
TRACE File "< doctest notes.txt[...] >", line 2, in
TRACE 1/0
TRACE ZeroDivisionError: integer division or modulo by zero


Method Chaining

I like this chained style a lot.

>>> log.name('benito').info('hi there')
INFO:benito:hi there


It makes structured logging easy:

>>> log.fields(paths=42).info('Going for a walk')
INFO:paths=42:Going for a walk


Short cut. Great for runtime statistics gathering.

>>> log.struct(paths=42, dolphins='thankful')
INFO:dolphins=thankful:paths=42:


Partial binding can be useful for webapps:

>>> per_request_log = log.fields(request_id='12345')
>>> per_request_log.fields(rows=100, user='frank').info('frobnicating database')
INFO:request_id=12345:rows=100:user=frank:frobnicating database
>>> per_request_log.fields(bytes=5678).info('sending page over tubes')
INFO:bytes=5678:request_id=12345:sending page over tubes


Chained style is awesome:

>>> log.name('donjuan').fields(pants='sexy').info("hello, {who} want to {what}?", who='ladies', what='dance')
INFO:donjuan:pants=sexy:hello, ladies want to dance?


Dynamic!

Any functions in args/fields are called and the value substitued:

>>> import os
>>> from twiggy.lib import thread_name
>>> thread_name()
'MainThread'
>>> log.fields(pid=os.getpid).info("I'm in thread {0}", thread_name) #doctest:+ELLIPSIS
INFO:pid=1076:I'm in thread MainThread


This can be useful with partially-bound loggers, which let's us do some cool stuff:

>>> class ThreadTracker(object):
... def __init__(self, obj):
... self.__obj = obj
... # a partially bound logger
... self.__log = log.name("tracker").fields(obj_id=id(obj), thread=thread_name)
... self.__log.debug("started tracking")
... def __getattr__(self, attr):
... self.__log.debug("accessed {0}", attr)
... return getattr(self.__obj, attr)
...
>>> class Bunch(object):
... pass
...
>>> foo = Bunch()
>>> foo.bar = 42
>>> tracked = ThreadTracker(foo)
DEBUG:tracker:obj_id=14063980:thread=MainThread:started tracking
>>> tracked.bar
DEBUG:tracker:obj_id=14063980:thread=MainThread:accessed bar
42
>>> import threading
>>> t=threading.Thread(target = lambda: tracked.bar * 2, name = "TheDoubler")
>>> t.start()
DEBUG:tracker:obj_id=14063980:thread=TheDoubler:accessed bar


If you really want to log a callable, repr() it or wrap it in lambda.

Optimizations

As an optimization, a min_level can be set on loggers:

>>> mylog.min_level = twiggy.Levels.INFO
>>> mylog.info("You see this")
INFO:alfredo:You see this
>>> mylog.debug("This is hidden")


They also take a filter that operates on format_spec. The use case is efficiently shutting off specific messages in a library which is doing something stupid:

>>> mylog.filter = lambda s: "shenanigans" not in s
>>> mylog.info("Starting silliness")
INFO:alfredo:Starting silliness
>>> for i in xrange(3): # for large values of 3
... mylog.info("I call shenanigans!")
>>> mylog.info("End silliness")
INFO:alfredo:End silliness

last updated on:
July 14th, 2011, 10:45 GMT
price:
FREE!
homepage:
code.google.com
license type:
BSD License 
developed by:
Peter Fein
category:
ROOT \ System \ Logging
Twiggy
Download Button

In a hurry? Add it to your Download Basket!

user rating

UNRATED
0.0/5
 

0/5

Rate it!

Add your review!

SUBMIT