
I Wear Pants
Now with Stuff and Things!
A blog by Peter Fein.
Follow @wearpants
The views expressed here do not represent my past, present or future employers, collectives, family, nation-state or houseplants. They are mine alone. Who's else would they be?
Twiggy Now Supports Python 2.6
July 12, 2011 at 06:40 PM | Tags: python, twiggy, loggingJust pushed out version 0.4.4 of Twiggy, a more Pythonic logger. This release adds support for Python 2.6. Get it from the usual place. Please let me know if you have any problems. I'll be giving an invited talk at the 2011 Scipy Conference as part of the Core Python track. Hope to see you there!
Note: for my readers more interested in activism than code, I'll be creating separate channels real soon now.
log.name("twiggy").info("What's new, what's next")
November 09, 2010 at 11:00 AM | Tags: python, release, logging, twiggyThis post was import from an earlier version of this blog. Original here.
An update about Twiggy, my new Pythonic logger.
What’s New
Yesterday I released a new version 0.4.1 of Twiggy. This release adds full test coverage (over 1000 lines, nearly twice the lines of actual code). I’ve fixed a number of important bugs in the process, so you’re encouraged to upgrade.
The features system is currently deprecated, pending a reimplementation in version 0.5. Features are currently global (shared by all log instances); they really should be per-object so libraries can use them without stepping on each other. Expect some clever metaprogramming voodoo to make this work while keeping things running fast.
What’s Next
Here’s a little preview of what you can expect over the next few weeks:
Be the best, steal from the rest
I’ll be adding support for context fields, a feature inspired by Logbook’s stacks. This allows an application to add fields to all log messages on a per-thread or per-process basis.
>>> from twiggy import * >>> quickSetup() >>> log.process(x=42) >>> log.thread(y=100) >>> log.debug('yo') DEBUG:x=42:y=100:yo >>> def doit(): ... log.debug('no y') ... log.thread(y=999) ... log.debug('different y') ... >>> import threading >>> t = threading.Thread(target=doit) >>> t.start(); t.join() DEBUG:x=42:no y DEBUG:x=42:y=999:different y
This is a killer feature for logging/debugging in webapps. One often wants to inject the request ID into all messages, including libraries that don’t know/care that they’re running on the web. There’ll be methods for clearing these contexts, as well as context managers to use with the with: statement.
Stdlib compatibility layer
0.5 will improve compatibility with the standard library’s logging package. This compatiblity will be two-way. You’ll be able to:
- configure twiggy to use stdlib logging as an output backend
- inject an API shim that emulates basic logging functionality
The later requires some explanation. 90-plus percent of the logging code I’ve ever seen only uses the most basic functionality: creating loggers, logging messages and capturing tracebacks. For such code, it should be possible to do:
from twiggy import logging_compat as logging log = logging.getLogger("oldcode") log.info("Shh, don't tell")
Even better, twiggy will provide a logging_compat.hijack() method to inject itself into sys.modules so that no modification to old code is needed at all.
I don’t expect this compatibility layer to work for everyone – notably, custom handlers won’t be supported (the underlying models are just too different), but this should ease the transition pain for many people.
Indentation
Also planned for 0.5 is support for user-defined counters. This feature is still taking shape, but it’ll look something like:
>>> def deep(): ... with log.increment('depth'): ... log.info("it's dark") ... abyss() ... log.warning("coming back up") ... >>> def abyss(): ... with log.increment('depth'): ... log.info("it's cold") ... >>> deep() INFO:depth=1:it's dark INFO:depth=2:it's cold WARNING:depth=1:coming back up
Outputs will be able to transform the depth field into useful visual formatting – for example, by using indentation to group lines together in a console app, or by setting a CSS class in HTML. Hell yeah, structured logging.
Etc.
Other forthcoming changes include: a port to Python 3, PEP-8 compliance, rewriting the features system, support for the warnings module and various minor enhancements. I’ll continue to support Python 2.7 using 3to2
N+1
I should probably stop there, but I’m excited by what’s further down the road. That includes:
- lazy logging: an output backend that groups messages together by a key, and only outputs them if some condition is met. For example, capture messages by request ID, and output all of them together if any one message is ERROR or higher.
- cluster logging: Twiggy will support easily settting up a master logging daemon to receive messages from multiple processes on a machine or across your cluster.
- unittest support: stuff the expected log output in your test docstring, apply a decorator, and Twiggy will add additional asserts to ensure your logs come out right.
- backends, backends, backends: email, HTTP, SQL, CouchDB, syslog, NT event log… Maybe even backends that open tickets in your bug tracker or stream live logs to your browser. Yeah.
What do you want?
Now is your opportunity to let me know what you want in a logger. Got a feature I haven’t thought of? Crazy idea? Think I should implement your favorite backend sooner? Tell me in the comments below.
Pete cooks, rides bikes and hacks Python. Maybe for you?. Don’t worry, he wears pants.
Meet Twiggy
October 21, 2010 at 07:45 PM | Tags: python, myeyesitsfullofjava, logging, twiggyThis post was import from an earlier version of this blog. Original here.
Date bumped so this post gets picked up by Planet Python (this blog was just added). Originally published October 18, 2010 at 3:25 PM.
Meet Twiggy
Twiggy is a new Pythonic logger.
>>> log.name('frank').fields(number=42).info("hello {who}, it's a {} day", 'sunny', who='world') INFO:frank:number=42:hello world, it's a sunny day
I started the project at Pycon. I was suffering from burnout, and looking to rekindle my interest in programming. I whined about the standard library’s logging package on IRC and Jesse Noller “invited” me to do something about it. I’m developing Twiggy because I want to give something back to the Python community, of which it’s been an honor and pleasure to be a member of these past eight years. I don’t have any immediate need for such a thing in a larger project- heck, I’m not even working right now.
This post is intended to give an overview of Twiggy, and persuade you that it should be your new logger. For a more complete introduction, please see the documentation.
Why Logging Matters
When we write code, logging is often an afterthought. I think this is a mistake. Logging is:
- your only view into a running program
- your only view of past execution
- your data for post-mortem analysis and domain-specific measurement
Given that, I think we should be logging more than we do. A lot more. Though given logging’s history as being slow, error-prone and generally unfun, it’s excusable that we don’t.
Want to know what your code is doing without dropping into a debugger or cluttering up with print statements? Logging. Need to figure out why that daemon keeps crashing? Logging. Business guys want to know what the customers bought and why? Logging.
Logging. We can’t live without it. So let’s do it better.
What’s Wrong with the Standard Library’s logging
Let me begin by expressing my sincere gratitude to Vinay Sajip for developing and maintaining the standard lib’s logging package since 2002. I mean that. In the numerous applications I’ve used it in, I’ve found it to be useful, featureful and very well documented. You have my thanks.
When talking to folks in the community, I heard vague displeasure with the standard lib’s logging.
- It’s complicated.
- It’s slow.
- 3rd place in poll of modules needing a redesign.
- People are flaming mad.
Folks had some pet peeves too.
- newlines in output
- unhandled exceptions during logging bring down the whole program
- only supports tuples for format strings
- too much locking

Whatever. The big problem in my opinion is that it’s full of Java. The standard lib’s logging is a port of log4j, just like PEP-282 says.
Twiggy: More Pythonic
As near as I can tell, Twiggy is the first totally new design for a logger since log4j was developed in 1996. Let me say that again: Twiggy is the first new logger in 15 years. We’ve learned a lot about how to build software in that time. Let’s make use of that knowledge.
Logging Should be Fun
Let’s start with messages. Twiggy uses new-style format strings by default. Way nicer than %s (printf).
>>> from twiggy import log >>> log.name('twiggy').info('I wear {} on my {where}', 'pants', where='legs') INFO:twiggy:I wear pants on my legs
Output is better. No more hard-to-grep traceback lines cluttering up your logs.
>>> try: ... 1/0 ... except: ... log.trace('error').warning('oh noes') WARNING:oh noes TRACE Traceback (most recent call last): TRACE File "<meet_twiggy.py>", line 2, in <module> TRACE ZeroDivisionError: integer division or modulo by zero
Twiggy includes easy support for structured logging. In the past, we stuffed key-value data into our human readable messages.
>>> log = logging.getLogger("stdlib.logging") >>> log.info('Going for a walk. path: %s roads: %d', "less traveled", 42) INFO:stdlib.logging:Going for a walk. path: less traveled roads: 42
Twiggy preserves the structure in such messages, making parsing and sophisticated formatting possible.
>>> log.name('twiggy').fields(path="less traveled", roads=42).info('Going for a walk') INFO:twiggy:path=less traveled:roads=42:Going for a walk
Modern Configuration
Twiggy uses loose coupling between loggers and outputs for configuration. This approach should look familiar to anyone who’s used Django’s URLconfs.
from twiggy import addEmitters, 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) addEmitters( # (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()
Filtering in Twiggy is smart. You can use builtin types as filters and Twiggy will just do the right thing. Strings are treated as regexps on message text.
emitters['alice'].filter = ".*pants.*" # alice only gets messages with pants
So Fast it’s Free
Outputs in Twiggy support asynchronous logging using the multiprocessing module. Twiggy can move the operation of writing to a file, database or server to a separate process and out of your application’s critical path. That makes logging basically free. And the best part is that Twiggy handles this for you, which means any outputs you write can take advantage of asynchronous support with no additional work.
Solves Your Problems. Pets Your Puppy.
A common problem in logging is the need to maintain context across several messages. This often comes up in webapps, where you’re shuttling request objects around. You can extract that context each time, but that quickly gets tiresome and may be impossible if it was created somewhere else. Twiggy makes this easy. Each call to fields() creates a new, partially-bound logger that can be passed around.
>>> ## an application-level log ... webapp_log = log.name("myblog") >>> ## a log for the individual request ... some_request.log = webapp_log.fields(request_id='12345') >>> some_request.log.fields(rows=100, user='frank').info('frobnicating database') INFO:myblog:request_id=12345:rows=100:user=frank:frobnicating database >>> some_request.log.fields(bytes=5678).info('sending page over tubes') INFO:myblog:bytes=5678:request_id=12345:sending page over tubes >>> ## a log for a different request ... other_request.log = webapp_log.fields(request_id='67890') >>> other_request.log.debug('Client connected') DEBUG:myblog:request_id=67890:Client connected
And we haven’t even gotten to the cool stuff or the features.
The Future

Twiggy works well now – you can start using it today. Since it’s core infrastructure, I believe a logger should be absolutely bulletproof. Twiggy’s not there yet. I’ll be focusing on getting it into rock solid shape over the next few weeks. I’ll also be porting to Python 3.x, mainly for its saner unicode support (I’ll maintain a 2.x branch if there’s sufficient interest).
Output backends are one of Twiggy’s weak spots. Currently, there’s only support for files. Future outputs will likely include: email, SQL database, syslog/NT event log, JSON/HTTP (CouchDB anyone?), message queues, etc.. Really, the sky/boredom’s the limit. ;–)
I’ll be adding some features to support common use cases – timing context managers, argument inspection decorators, that sort of thing. Also in the works is unittesting support – the ability to ensure that particular paths through your code produce the correct log output.
I’m planning support for a standard library logging compatibility mode. Ideally, one should be able have 90% of code that uses logging work out of the box.
from twiggy import logging_compat as logging log = logging.getLogger("oldcode") log.info("Shh, don't tell")
Even better, Twiggy could inject the compatibility layer into sys.modules, meaning no modification to old code at all.
# in your twiggy_setup: from twiggy import logging_compat logging_compat.hijack() # take over!
Way further down the road, I’ve got ideas for a zero-configuration log analysis tool called hatchet. But for now, I’m excited about Twiggy – I hope you are too.
See also: Discussion on reddit
10/19: Update import to make names in use clearer
These comments were imported from an earlier version of this blog.
Dag 2010/10/18 13:43:12 -0700
What about http://packages.python.org/Logbook/ ?
Vinay Sajip 2010/10/18 15:04:20 -0700
Hi Peter,
I just saw this post and will be taking a closer look at twiggy when I get the time. I notice that you refer to displeasure other people have expressed with the stdlib logging package, but you don't explore yourself whether those comments had any merit. Too bad -
"it's complicated" - no backup for this, simple usage is no harder than twiggy from my initial impressions.
"it's slow" - sure, a single system call will be faster, but so what? That's an apples-to-oranges comparison. In Robert Brewer's article, he says that syslog is faster because it uses C rather than Python - if that's such a major problem then twiggy will presumably suffer from it too.
I did a simple order-of-magnitude benchmark which shows that logging calls are of the order of a few 10s of MICROSECONDS, which I don't see as being too slow when compared with database query times, network latencies etc.
"3rd place in a poll" - sure. You know that phrase, "haters gonna hate"? Is there a corresponding one "lovers gonna love"? No? Perhaps that's because people love to moan and criticise, more than they go out of their way to praise.
"Flaming mad" - that's not "people", that's Armin Ronacher, Logbook developer and not exactly without an axe to grind. If you actually read that Reddit thread, you would see that I answered most of his complaints.
According to you the big problem is that in your opinion, "it's full of Java". I say, no way! And PEP 282 does not say that Python logging is a port of log4j - it merely cites log4j as an influence. That's not the same thing!
I'll reiterate what I said here in the next paragraph: http://wiki.python.org/moin/LoggingPackage
Also, anyone who bothers to look at log4j in detail will see that Python logging is not a mindless translation - it's fairly Pythonic. Beyond the basic abstractions of "What? Where? How Important? Who Needs To Know?", there's no real correspondence between the Java artifacts and the Python ones. Using David A. Wheeler's SLOCCount, log4j 1.2.15 = 168 source files, around 16K SLOC; Python 2.6 logging = 3 source files, < 1.5K SLOC. To me the Java connection and inferences that people draw from the "Java heritage" is bordering on FUD a lot of the time, I have to say. But feel free to put me right with specific comments rather than vague arm-waving, and you'll find me receptive.
By the way, you perhaps haven't noticed that Python logging easily supports logging via multiprocessing too, I posted about that on plumberjack.blogspot.com.
Anyway, I welcome the fact that other people like you and Armin are thinking about logging, and hopefully, new ideas will make the Python ecosystem better. But please, do your research before posting things that are imprecise or hard to substantiate. That doesn't help anyone, as far as I can see.
None 2010/10/19 09:50:08 -0700
@Vinay:
I notice that you refer to displeasure other people have expressed with the stdlib logging package, but you don't explore yourself whether those comments had any merit. Too bad
When I started the project at Pycon, I asked around the hallway track and these were the responses I got. Clearly, there's a need. It also jives with my experience.
"it's complicated" - no backup for this, simple usage is no harder than twiggy from my initial impressions.
Exactly - the problem is that stdlib logging is complicated for anything besides simple usage.
"it's slow" - sure, a single system call will be faster, but so what? That's an apples-to-oranges comparison. In Robert Brewer's article, he says that syslog is faster because it uses C rather than Python - if that's such a major problem then twiggy will presumably suffer from it too.
Actually, I suspect a lot of the performance win is that the I/O is being done by the syslog daemon, rather than by Python. Twiggy can do that for any backend using asynchronous logging.
By the way, you perhaps haven't noticed that Python logging easily supports logging via multiprocessing too, I posted about that on plumberjack.blogspot.com.
Compared to the single parameter that Twiggy uses, I'd hardly call that easy.
I did a simple order-of-magnitude benchmark which shows that logging calls are of the order of a few 10s of MICROSECONDS, which I don't see as being too slow when compared with database query times, network latencies etc.
The speed issue is not just how fast logging itself is, but the impact it has on the performance of the program it's used in. Stdlib's logging heavy locking hurts the performance of a multithreaded app pretty badly.
"3rd place in a poll" - sure. You know that phrase, "haters gonna hate"? Is there a corresponding one "lovers gonna love"? No? Perhaps that's because people love to moan and criticise, more than they go out of their way to praise.
That doesn't invalidate the results. I'm not sure how one is supposed to measure design. You're welcome to start your own poll on "module least needing a redesign" or "modules people love because they're lovers", of course.
According to you the big problem is that in your opinion, "it's full of Java". I say, no way! And PEP 282 does not say that Python logging is a port of log4j - it merely cites log4j as an influence. That's not the same thing!
By "port" I meant "largely modelled after", not "line for line translation." I don't know anyone who uses it that way. You might want to update the wikipedia page for log4j, which lists it under "Ports" while you're at it.
I'll reiterate what I said here in the next paragraph: http://wiki.python.org/moin/LoggingPackage
Also, anyone who bothers to look at log4j in detail will see that Python logging is not a mindless translation - it's fairly Pythonic. Beyond the basic abstractions of "What? Where? How Important? Who Needs To Know?", there's no real correspondence between the Java artifacts and the Python ones. Using David A. Wheeler's SLOCCount, log4j 1.2.15 = 168 source files, around 16K SLOC; Python 2.6 logging = 3 source files, < 1.5K SLOC.
My $DIETY! Python is more concise than Java! Who knew? ;-) Really, I don't see what SLOC tells about design. But since we're measuring, Twiggy comes in at 536. For 2.7 logging, I get 1965 (including 605 for handlers - logging's got a lot more backends than Twiggy).
To me the Java connection and inferences that people draw from the "Java heritage" is bordering on FUD a lot of the time, I have to say. But feel free to put me right with specific comments rather than vague arm-waving, and you'll find me receptive.
From your own page on logging:
This package owes its greatest debt to Apache log4j. Due notice was also taken of log4j's comprehensive critique (no longer online) of JSR47. This package bears a close resemblance to log4j, but is not a close translation. I have attempted to be more minimalist (and hopefully more Pythonic) in my approach. You be the judge!
Since you asked, I did a cursory analysis of the source of 2.7's logging package. I would have looked more closely, but I'm trying to save my eyes.
Setters & Getters
This is the easy one. I count eight such beasties (being generous). At least two are single-statement assignments. Classic Java. Not Pythonic.
Functors
Filters are a classic functor. Formatters are pretty close. Pythonic code uses callables.
Java Idioms
Explicit type checking, adapters, tight coupling, deep call stacks, object hierarchies for structure... really, all we're missing is a big heap of XML.
None 2010/10/19 10:05:34 -0700
@Dag:
I've glanced at Logbook, but I admit I'm having trouble getting my head around it. The big feature seems to be stacks. This seems in some way broadly similar to logging (in that they're both hierarchical), in contrast to Twiggy's Django-inspired loose coupling. I'm not a huge fan of the implicitness of the stacks or the need manage/think about them - a call stack is enough for my brain. ;-) I really don't like having to restructure one's code (using with: blocks) to add context.
That said, we're clearly aiming at some of the same problems, and Logbook's solutions merit a closer look. I also like the wider variety of modern output backends (queues, growl, twitter, databases).
I hope to put heads together with Armin at some point...
Vinay Sajip 2010/10/19 11:05:02 -0700
@Peter:
"the problem is that stdlib logging is complicated for anything besides simple usage."
That's pretty vague. For example, you can easily set up a fairly complex configuration using YAML and effect it in a one-liner or so, as described in PEP 391. For non-simple usage, the work required to set up logging is typically small compared with the other non-simple work being done. If you don't agree, please post info about a configuration you think is particularly hard to set up.
"Compared to the single parameter that Twiggy uses, I'd hardly call that easy."
Sure, but it doesn't force any particular solution. The QueueHandler allows working with threads, processes or ZeroMQ sockets without too much trouble. Worth the extra few lines, I'd say. You'd hardly call it easy, but would you call it hard? really?
"The speed issue is not just how fast logging itself is, but the impact it has on the performance of the program it's used in. Stdlib's logging heavy locking hurts the performance of a multithreaded app pretty badly."
Do you have any numbers for this? I'd be really interested. At least I gave some numbers.
"That doesn't invalidate the results."
Some people have told me that they were turned off logging at the outset because of prejudice - they'd heard it was not Pythonic, a Java port etc. and didn't give it a fair shake. It's not easy to say how many people fall into the same boat. However, that poll doesn't say anything about the people who are perfectly satisfied with how logging works, but are not so much in love with it that they'll jump up and give three cheers whenever it's mentioned. After all, it's just a component in one's toolkit, not a religion ;-) I've had plenty of positive feedback about logging, so I know there are a fair number people who don't have issues with it. They just wouldn't show up on any poll.
Why, you yourself expressed sincere gratitude and said it was featureful, useful and well documented.
"By "port" I meant "largely modelled after", not "line for line translation." I don't know anyone who uses it that way. You might want to update the wikipedia page for log4j, which lists it under"Ports" while you're at it."
In my logging 101 post I mentioned that logging was based on "what happened? where did it happen? how important is it? and who wants to know", which are the basis for the design, and I don't fully see what's so un-Pythonic or specifically Java-like about that.
Although the package does appear in the "Ports" section, it does say "inspired by". I don't suppose it's worth having a separate section in that document entitled "Not actual ports, but inspired by".
"Really, I don't see what SLOC tells about design."
Sure. I only compared SLOCs to highlight that one is not a port of the other. I'm not hung up on SLOC.
"Since you asked, I did a cursory analysis of the source of 2.7's logging package. I would have looked more closely, but I'm trying to save my eyes."
I'm sorry your eyes are so sensitive.
"Setters & Getters - This is the easy one. I count eight such beasties (being generous). At least two are single-statement assignments. Classic Java. Not Pythonic.
Python nowadays has properties, but it wasn't always so; logging dates back to 1.5.2, though it was brought into Python in 2.3. In the absence of properties, getters and setters were used to give me freedom to change implementation details without clients having to change their code. So I don't apologise for those, though I wouldn't use them now that properties are available.
"Functors - Filters are a classic functor. Formatters are pretty close. Pythonic code uses callables."
I agree with that point, but it's a minor one. As it stands, logging could change to support callables as well as filter instances without impacting existing client code. If it was a problem for you, you could have filed an enhancement request on the bug tracker and the change might have been made already.
"Explicit type checking" - only in a few places and for what I consider acceptable reasons. Please point out where you think it's been misused.
"Adapters" - just one class, LoggerAdapter, or are there some I've missed? I don't believe adapters are un-Pythonic. For example, proxies with __getattr__ are analogous to adapters. Is it the fact that I've called it an Adapter that's bugging you?
"tight coupling" - where/how has this hurt you? It's not especially a Java idiom, you can have tight coupling in any language.
"deep call stacks" - ditto.
"object hierarchies for structure" - not quite sure what you mean by this, please clarify.
"really, all we're missing is a big heap of XML."
Heh.
I've never claimed that logging's design was perfect. Do you claim this for twiggy? Or anything else you've written? No software is perfect. I'm quite happy with "featureful, useful and well documented" but I'm not complacent; when people suggest things I generally do give them a hearing. When I try and use someone else's software and I can't get it to do something that I want to, or have some other issue with it, my first impulse is to report an issue or file an enhancement request. Have you ever done anything like that with Python logging? For example, posted some performance problems you've encountered in practice with how locking is hurting performance? I'd definitely look into anything I could readily reproduce.
Regards,
Vinay Sajip
None 2010/10/19 12:38:36 -0700
One of the first thoughts I had when seeing Logbook was that if you're going to reimplement logging, why not rethink it as well? I'm happy that someone has tried :)
Matthew Scott 2010/10/31 00:16:45 -0700
I haven't yet looked at logbook, but honestly I'm quite pleased that twiggy treats human-readable key=value pairs as first class citizens, and I think that demonstrating that in the twiggy docs' "hello world" snippet is very wise.
I haven't deeply evaluated the Splunk product itself, but the chief architect recently gave this talk at Pivotal Labs and made a strong case for embracing key=value pairs since they are useful both during manual inspection of the actual log message, and for analytical processing.
http://pivotallabs.com/talks/110-semantic-logging-with-splunk
Vinay Sajip 2010/10/31 04:43:42 -0700
@Matthew Scott: It's perfectly easy to output key=value pairs using stdlib logging too, just by specifying suitable format strings or an appropriate Formatter class. Python logging and Splunk work fine together IIRC from some Q & A on the Splunk answers site.
Marko Zavodjenje 2010/12/13 02:14:24 -0800
Very useful info, but you scared the s**t out of me with yours Java eyeballs :)
Matt Chaput 2010/12/20 11:57:38 -0800
This looks cool. Sorry Vinay, but the design just seems more appealing to me. But I wish it was backwards compatible with 2.5 (it's amazing how many people are still stuck on that) and used PEP 8 naming instead of camelCase (what was all that about Java?).
Also, I think an awesome name for a logging module would be Bark.
Matthew Wilson 2011/03/06 12:54:47 -0800
Good luck with this project! I spent a lot of time studying the standard library logging module and now, I'm pretty happy with it, but it does have a pretty serious learning curve.
However, I think that's inevitable. A really good logging system is going to have a serious learning curve. For a serious production system, you're going to want logs going to lots of different places. Some stuff must be emailed, other stuff can be sent to a file, other stuff needs to go to some remote logging system, etc.
Just a suggestion -- add in support for different logging handlers ASAP. Writing to a file vs sending email vs throwing UDP packets all involve weird little idosyncracies.