First, a little primer. The logging module is essentially a singleton that you fetch (not instantiate) and configure with pluggable handlers for log message routing. You can set any logging level (DEBUG, INFO, WARNING, ERROR, and CRITICAL) you want for each handler, and you can set a global level that encompasses all handlers.
Now you have logging, but it doesn't really go anywhere. This will anger the admins, I know, I have worked with a lot of them. Difficulty tracking down errors is one of the number reasons why you end up fixing it yourself. If you give them the heads up, and a way to look inside to see what's happening, then they will do what they do best: fixing things. Remember, maintenance staff are users to. Fortunately, those great logging handlers come in lots of different flavors. By combining the right ones together you can get all the functionality you ever dreamed off:
- StreamHandler instances send error messages to streams (file-like objects).
- FileHandler instances send error messages to disk files.
- BaseRotatingHandler is the base class for handlers that rotate log files at a certain point. It is not meant to be instantiated directly. Instead, use RotatingFileHandler or TimedRotatingFileHandler.
- RotatingFileHandler instances send error messages to disk files, with support for maximum log file sizes and log file rotation.
- TimedRotatingFileHandler instances send error messages to disk files rotating the log file at certain timed intervals.
- SocketHandler instances send error messages to TCP/IP sockets.
- DatagramHandler instances send error messages to UDP sockets.
- SMTPHandler instances send error messages to a designated email address.
- SysLogHandler instances send error messages to a Unix syslog daemon, possibly on a remote machine.
- NTEventLogHandler instances send error messages to a Windows NT/2000/XP event log.
- MemoryHandler instances send error messages to a buffer in memory, which is flushed whenever specific criteria are met.
- HTTPHandler instances send error messages to an HTTP server using either "GET" or "POST” semantics.
- Logging of errors.
- A feed of debugging information.
- Notifications of serious errors.
- Don't fill the disk with logs!
Now all the serious errors get routed to a permanent file and mailed to operations staff, and debugging info goes to a size managed log that admins can follow (tail –f):
Uh oh, looks like we have a problem. Someone changed the hostname of a service. Go-go Unix beard!
“Now, what about Django? The title has Django in it, and I know it has a built in error mailer?” Well, it does, but it just mails you the exception and gives the user a 500 error. If you want to give your users' nicer errors, or if you are exposing an XML-RPC/SOAP/REST API, then you are going to want to handle it better than a 500 page. You need to trap and log your exceptions, and throw out there something that is prettier, like an appropriate SOAP fault if this were a SOAP server.
This is done best in two ways: right in your views or by setting up some middleware with a process_exception() method. For our purposes, I will keep it simple and do it right in the view. You do exactly the same as you would above, with one exception. Django is multithreaded, while the logging module behaves like a singleton. This means you can end up setting too many handlers and getting duplicate log entries. The best way around this is to take advantage of the log modules singleton behavior:
Another Django tip: add a console handler so you can get a tail like feed of what is happening during development right in the development server console.
Finally, one the major advantages of using the logging module is that you not only see the messages from your code, but all the Python modules you import. This is due to the logging modules previously mentioned singleton behavior and “batteries included” approach of Python. A lot modules (native and third party) use it, so you see their errors show up in your logs to, and you get a much better clue of what is going wrong.