blob: 36225b9a2da84d548ad1c6bbb77e138a67ea14d7 [file] [log] [blame]
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +00002#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose and without fee is hereby granted,
5# provided that the above copyright notice appear in all copies and that
6# both that copyright notice and this permission notice appear in
7# supporting documentation, and that the name of Vinay Sajip
8# not be used in advertising or publicity pertaining to distribution
9# of the software without specific, written prior permission.
10# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
11# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
13# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Guido van Rossum57102f82002-11-13 16:15:58 +000016
17"""
Vinay Sajip3f742842004-02-28 16:07:46 +000018Additional handlers for the logging package for Python. The core package is
19based on PEP 282 and comments thereto in comp.lang.python, and influenced by
20Apache's log4j system.
Guido van Rossum57102f82002-11-13 16:15:58 +000021
22Should work under Python versions >= 1.5.2, except that source line
Vinay Sajip48cfe382004-02-20 13:17:27 +000023information is not available unless 'sys._getframe()' is.
Guido van Rossum57102f82002-11-13 16:15:58 +000024
Vinay Sajip48cfe382004-02-20 13:17:27 +000025Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000026
27To use, simply 'import logging' and log away!
28"""
29
Neal Norwitzf297bd12003-04-23 03:49:43 +000030import sys, logging, socket, types, os, string, cPickle, struct, time
Guido van Rossum57102f82002-11-13 16:15:58 +000031
Guido van Rossum57102f82002-11-13 16:15:58 +000032#
33# Some constants...
34#
35
36DEFAULT_TCP_LOGGING_PORT = 9020
37DEFAULT_UDP_LOGGING_PORT = 9021
38DEFAULT_HTTP_LOGGING_PORT = 9022
39DEFAULT_SOAP_LOGGING_PORT = 9023
40SYSLOG_UDP_PORT = 514
41
42
43class RotatingFileHandler(logging.FileHandler):
44 def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
45 """
46 Open the specified file and use it as the stream for logging.
47
48 By default, the file grows indefinitely. You can specify particular
49 values of maxBytes and backupCount to allow the file to rollover at
50 a predetermined size.
51
52 Rollover occurs whenever the current log file is nearly maxBytes in
53 length. If backupCount is >= 1, the system will successively create
54 new files with the same pathname as the base file, but with extensions
55 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
56 and a base file name of "app.log", you would get "app.log",
57 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
58 written to is always "app.log" - when it gets filled up, it is closed
59 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
60 exist, then they are renamed to "app.log.2", "app.log.3" etc.
61 respectively.
62
63 If maxBytes is zero, rollover never occurs.
64 """
65 logging.FileHandler.__init__(self, filename, mode)
66 self.maxBytes = maxBytes
67 self.backupCount = backupCount
68 if maxBytes > 0:
69 self.mode = "a"
70
71 def doRollover(self):
72 """
73 Do a rollover, as described in __init__().
74 """
75
76 self.stream.close()
77 if self.backupCount > 0:
78 for i in range(self.backupCount - 1, 0, -1):
79 sfn = "%s.%d" % (self.baseFilename, i)
80 dfn = "%s.%d" % (self.baseFilename, i + 1)
81 if os.path.exists(sfn):
82 #print "%s -> %s" % (sfn, dfn)
83 if os.path.exists(dfn):
84 os.remove(dfn)
85 os.rename(sfn, dfn)
86 dfn = self.baseFilename + ".1"
87 if os.path.exists(dfn):
88 os.remove(dfn)
89 os.rename(self.baseFilename, dfn)
90 #print "%s -> %s" % (self.baseFilename, dfn)
91 self.stream = open(self.baseFilename, "w")
92
93 def emit(self, record):
94 """
95 Emit a record.
96
97 Output the record to the file, catering for rollover as described
Raymond Hettingere21f6062003-11-08 11:40:03 +000098 in doRollover().
Guido van Rossum57102f82002-11-13 16:15:58 +000099 """
100 if self.maxBytes > 0: # are we rolling over?
101 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000102 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000103 if self.stream.tell() + len(msg) >= self.maxBytes:
104 self.doRollover()
105 logging.FileHandler.emit(self, record)
106
107
108class SocketHandler(logging.Handler):
109 """
110 A handler class which writes logging records, in pickle format, to
111 a streaming socket. The socket is kept open across logging calls.
112 If the peer resets it, an attempt is made to reconnect on the next call.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000113 The pickle which is sent is that of the LogRecord's attribute dictionary
114 (__dict__), so that the receiver does not need to have the logging module
115 installed in order to process the logging event.
116
117 To unpickle the record at the receiving end into a LogRecord, use the
118 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000119 """
120
121 def __init__(self, host, port):
122 """
123 Initializes the handler with a specific host address and port.
124
125 The attribute 'closeOnError' is set to 1 - which means that if
126 a socket error occurs, the socket is silently closed and then
127 reopened on the next logging call.
128 """
129 logging.Handler.__init__(self)
130 self.host = host
131 self.port = port
132 self.sock = None
133 self.closeOnError = 0
Vinay Sajip48cfe382004-02-20 13:17:27 +0000134 self.retryTime = None
135 #
136 # Exponential backoff parameters.
137 #
138 self.retryStart = 1.0
139 self.retryMax = 30.0
140 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000141
142 def makeSocket(self):
143 """
144 A factory method which allows subclasses to define the precise
145 type of socket they want.
146 """
147 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
148 s.connect((self.host, self.port))
149 return s
150
Vinay Sajip48cfe382004-02-20 13:17:27 +0000151 def createSocket(self):
152 """
153 Try to create a socket, using an exponential backoff with
154 a max retry time. Thanks to Robert Olson for the original patch
155 (SF #815911) which has been slightly refactored.
156 """
157 now = time.time()
158 # Either retryTime is None, in which case this
159 # is the first time back after a disconnect, or
160 # we've waited long enough.
161 if self.retryTime is None:
162 attempt = 1
163 else:
164 attempt = (now >= self.retryTime)
165 if attempt:
166 try:
167 self.sock = self.makeSocket()
168 self.retryTime = None # next time, no delay before trying
169 except:
170 #Creation failed, so set the retry time and return.
171 if self.retryTime is None:
172 self.retryPeriod = self.retryStart
173 else:
174 self.retryPeriod = self.retryPeriod * self.retryFactor
175 if self.retryPeriod > self.retryMax:
176 self.retryPeriod = self.retryMax
177 self.retryTime = now + self.retryPeriod
178
Guido van Rossum57102f82002-11-13 16:15:58 +0000179 def send(self, s):
180 """
181 Send a pickled string to the socket.
182
183 This function allows for partial sends which can happen when the
184 network is busy.
185 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000186 if self.sock is None:
187 self.createSocket()
188 #self.sock can be None either because we haven't reached the retry
189 #time yet, or because we have reached the retry time and retried,
190 #but are still unable to connect.
191 if self.sock:
192 try:
193 if hasattr(self.sock, "sendall"):
194 self.sock.sendall(s)
195 else:
196 sentsofar = 0
197 left = len(s)
198 while left > 0:
199 sent = self.sock.send(s[sentsofar:])
200 sentsofar = sentsofar + sent
201 left = left - sent
202 except socket.error:
203 self.sock.close()
204 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000205
206 def makePickle(self, record):
207 """
208 Pickles the record in binary format with a length prefix, and
209 returns it ready for transmission across the socket.
210 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000211 ei = record.exc_info
212 if ei:
213 dummy = self.format(record) # just to get traceback text into record.exc_text
214 record.exc_info = None # to avoid Unpickleable error
Guido van Rossum57102f82002-11-13 16:15:58 +0000215 s = cPickle.dumps(record.__dict__, 1)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000216 if ei:
217 record.exc_info = ei # for next handler
Guido van Rossum57102f82002-11-13 16:15:58 +0000218 slen = struct.pack(">L", len(s))
219 return slen + s
220
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000221 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000222 """
223 Handle an error during logging.
224
225 An error has occurred during logging. Most likely cause -
226 connection lost. Close the socket so that we can retry on the
227 next event.
228 """
229 if self.closeOnError and self.sock:
230 self.sock.close()
231 self.sock = None #try to reconnect next time
232 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000233 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000234
235 def emit(self, record):
236 """
237 Emit a record.
238
239 Pickles the record and writes it to the socket in binary format.
240 If there is an error with the socket, silently drop the packet.
241 If there was a problem with the socket, re-establishes the
242 socket.
243 """
244 try:
245 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000246 self.send(s)
247 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000248 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000249
250 def close(self):
251 """
252 Closes the socket.
253 """
254 if self.sock:
255 self.sock.close()
256 self.sock = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000257 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000258
259class DatagramHandler(SocketHandler):
260 """
261 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000262 a datagram socket. The pickle which is sent is that of the LogRecord's
263 attribute dictionary (__dict__), so that the receiver does not need to
264 have the logging module installed in order to process the logging event.
265
266 To unpickle the record at the receiving end into a LogRecord, use the
267 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000268
269 """
270 def __init__(self, host, port):
271 """
272 Initializes the handler with a specific host address and port.
273 """
274 SocketHandler.__init__(self, host, port)
275 self.closeOnError = 0
276
277 def makeSocket(self):
278 """
279 The factory method of SocketHandler is here overridden to create
280 a UDP socket (SOCK_DGRAM).
281 """
282 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
283 return s
284
285 def send(self, s):
286 """
287 Send a pickled string to a socket.
288
289 This function no longer allows for partial sends which can happen
290 when the network is busy - UDP does not guarantee delivery and
291 can deliver packets out of sequence.
292 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000293 self.sock.sendto(s, (self.host, self.port))
294
295class SysLogHandler(logging.Handler):
296 """
297 A handler class which sends formatted logging records to a syslog
298 server. Based on Sam Rushing's syslog module:
299 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
300 Contributed by Nicolas Untz (after which minor refactoring changes
301 have been made).
302 """
303
304 # from <linux/sys/syslog.h>:
305 # ======================================================================
306 # priorities/facilities are encoded into a single 32-bit quantity, where
307 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
308 # facility (0-big number). Both the priorities and the facilities map
309 # roughly one-to-one to strings in the syslogd(8) source code. This
310 # mapping is included in this file.
311 #
312 # priorities (these are ordered)
313
314 LOG_EMERG = 0 # system is unusable
315 LOG_ALERT = 1 # action must be taken immediately
316 LOG_CRIT = 2 # critical conditions
317 LOG_ERR = 3 # error conditions
318 LOG_WARNING = 4 # warning conditions
319 LOG_NOTICE = 5 # normal but significant condition
320 LOG_INFO = 6 # informational
321 LOG_DEBUG = 7 # debug-level messages
322
323 # facility codes
324 LOG_KERN = 0 # kernel messages
325 LOG_USER = 1 # random user-level messages
326 LOG_MAIL = 2 # mail system
327 LOG_DAEMON = 3 # system daemons
328 LOG_AUTH = 4 # security/authorization messages
329 LOG_SYSLOG = 5 # messages generated internally by syslogd
330 LOG_LPR = 6 # line printer subsystem
331 LOG_NEWS = 7 # network news subsystem
332 LOG_UUCP = 8 # UUCP subsystem
333 LOG_CRON = 9 # clock daemon
334 LOG_AUTHPRIV = 10 # security/authorization messages (private)
335
336 # other codes through 15 reserved for system use
337 LOG_LOCAL0 = 16 # reserved for local use
338 LOG_LOCAL1 = 17 # reserved for local use
339 LOG_LOCAL2 = 18 # reserved for local use
340 LOG_LOCAL3 = 19 # reserved for local use
341 LOG_LOCAL4 = 20 # reserved for local use
342 LOG_LOCAL5 = 21 # reserved for local use
343 LOG_LOCAL6 = 22 # reserved for local use
344 LOG_LOCAL7 = 23 # reserved for local use
345
346 priority_names = {
347 "alert": LOG_ALERT,
348 "crit": LOG_CRIT,
349 "critical": LOG_CRIT,
350 "debug": LOG_DEBUG,
351 "emerg": LOG_EMERG,
352 "err": LOG_ERR,
353 "error": LOG_ERR, # DEPRECATED
354 "info": LOG_INFO,
355 "notice": LOG_NOTICE,
356 "panic": LOG_EMERG, # DEPRECATED
357 "warn": LOG_WARNING, # DEPRECATED
358 "warning": LOG_WARNING,
359 }
360
361 facility_names = {
362 "auth": LOG_AUTH,
363 "authpriv": LOG_AUTHPRIV,
364 "cron": LOG_CRON,
365 "daemon": LOG_DAEMON,
366 "kern": LOG_KERN,
367 "lpr": LOG_LPR,
368 "mail": LOG_MAIL,
369 "news": LOG_NEWS,
370 "security": LOG_AUTH, # DEPRECATED
371 "syslog": LOG_SYSLOG,
372 "user": LOG_USER,
373 "uucp": LOG_UUCP,
374 "local0": LOG_LOCAL0,
375 "local1": LOG_LOCAL1,
376 "local2": LOG_LOCAL2,
377 "local3": LOG_LOCAL3,
378 "local4": LOG_LOCAL4,
379 "local5": LOG_LOCAL5,
380 "local6": LOG_LOCAL6,
381 "local7": LOG_LOCAL7,
382 }
383
384 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
385 """
386 Initialize a handler.
387
388 If address is specified as a string, UNIX socket is used.
389 If facility is not specified, LOG_USER is used.
390 """
391 logging.Handler.__init__(self)
392
393 self.address = address
394 self.facility = facility
395 if type(address) == types.StringType:
Neal Norwitzd89c4062003-01-26 02:14:23 +0000396 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
Neal Norwitzf4cdb472003-01-26 16:15:24 +0000397 # syslog may require either DGRAM or STREAM sockets
398 try:
399 self.socket.connect(address)
400 except socket.error:
401 self.socket.close()
402 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000403 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000404 self.unixsocket = 1
405 else:
406 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
407 self.unixsocket = 0
408
409 self.formatter = None
410
411 # curious: when talking to the unix-domain '/dev/log' socket, a
412 # zero-terminator seems to be required. this string is placed
413 # into a class variable so that it can be overridden if
414 # necessary.
415 log_format_string = '<%d>%s\000'
416
417 def encodePriority (self, facility, priority):
418 """
419 Encode the facility and priority. You can pass in strings or
420 integers - if strings are passed, the facility_names and
421 priority_names mapping dictionaries are used to convert them to
422 integers.
423 """
424 if type(facility) == types.StringType:
425 facility = self.facility_names[facility]
426 if type(priority) == types.StringType:
427 priority = self.priority_names[priority]
428 return (facility << 3) | priority
429
430 def close (self):
431 """
432 Closes the socket.
433 """
434 if self.unixsocket:
435 self.socket.close()
Vinay Sajip48cfe382004-02-20 13:17:27 +0000436 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000437
438 def emit(self, record):
439 """
440 Emit a record.
441
442 The record is formatted, and then sent to the syslog server. If
443 exception information is present, it is NOT sent to the server.
444 """
445 msg = self.format(record)
446 """
447 We need to convert record level to lowercase, maybe this will
448 change in the future.
449 """
450 msg = self.log_format_string % (
451 self.encodePriority(self.facility,
452 string.lower(record.levelname)),
453 msg)
454 try:
455 if self.unixsocket:
456 self.socket.send(msg)
457 else:
458 self.socket.sendto(msg, self.address)
459 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000460 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000461
462class SMTPHandler(logging.Handler):
463 """
464 A handler class which sends an SMTP email for each logging event.
465 """
466 def __init__(self, mailhost, fromaddr, toaddrs, subject):
467 """
468 Initialize the handler.
469
470 Initialize the instance with the from and to addresses and subject
471 line of the email. To specify a non-standard SMTP port, use the
472 (host, port) tuple format for the mailhost argument.
473 """
474 logging.Handler.__init__(self)
475 if type(mailhost) == types.TupleType:
476 host, port = mailhost
477 self.mailhost = host
478 self.mailport = port
479 else:
480 self.mailhost = mailhost
481 self.mailport = None
482 self.fromaddr = fromaddr
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000483 if type(toaddrs) == types.StringType:
484 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000485 self.toaddrs = toaddrs
486 self.subject = subject
487
488 def getSubject(self, record):
489 """
490 Determine the subject for the email.
491
492 If you want to specify a subject line which is record-dependent,
493 override this method.
494 """
495 return self.subject
496
Neal Norwitzf297bd12003-04-23 03:49:43 +0000497 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
498
499 monthname = [None,
500 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
501 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
502
503 def date_time(self):
504 """Return the current date and time formatted for a MIME header."""
505 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())
506 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
507 self.weekdayname[wd],
508 day, self.monthname[month], year,
509 hh, mm, ss)
510 return s
511
Guido van Rossum57102f82002-11-13 16:15:58 +0000512 def emit(self, record):
513 """
514 Emit a record.
515
516 Format the record and send it to the specified addressees.
517 """
518 try:
519 import smtplib
520 port = self.mailport
521 if not port:
522 port = smtplib.SMTP_PORT
523 smtp = smtplib.SMTP(self.mailhost, port)
524 msg = self.format(record)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000525 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
Guido van Rossum57102f82002-11-13 16:15:58 +0000526 self.fromaddr,
527 string.join(self.toaddrs, ","),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000528 self.getSubject(record),
529 self.date_time(), msg)
Guido van Rossum57102f82002-11-13 16:15:58 +0000530 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
531 smtp.quit()
532 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000533 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000534
535class NTEventLogHandler(logging.Handler):
536 """
537 A handler class which sends events to the NT Event Log. Adds a
538 registry entry for the specified application name. If no dllname is
539 provided, win32service.pyd (which contains some basic message
540 placeholders) is used. Note that use of these placeholders will make
541 your event logs big, as the entire message source is held in the log.
542 If you want slimmer logs, you have to pass in the name of your own DLL
543 which contains the message definitions you want to use in the event log.
544 """
545 def __init__(self, appname, dllname=None, logtype="Application"):
546 logging.Handler.__init__(self)
547 try:
548 import win32evtlogutil, win32evtlog
549 self.appname = appname
550 self._welu = win32evtlogutil
551 if not dllname:
552 dllname = os.path.split(self._welu.__file__)
553 dllname = os.path.split(dllname[0])
554 dllname = os.path.join(dllname[0], r'win32service.pyd')
555 self.dllname = dllname
556 self.logtype = logtype
557 self._welu.AddSourceToRegistry(appname, dllname, logtype)
558 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
559 self.typemap = {
560 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
561 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000562 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000563 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
564 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
565 }
566 except ImportError:
567 print "The Python Win32 extensions for NT (service, event "\
568 "logging) appear not to be available."
569 self._welu = None
570
571 def getMessageID(self, record):
572 """
573 Return the message ID for the event record. If you are using your
574 own messages, you could do this by having the msg passed to the
575 logger being an ID rather than a formatting string. Then, in here,
576 you could use a dictionary lookup to get the message ID. This
577 version returns 1, which is the base message ID in win32service.pyd.
578 """
579 return 1
580
581 def getEventCategory(self, record):
582 """
583 Return the event category for the record.
584
585 Override this if you want to specify your own categories. This version
586 returns 0.
587 """
588 return 0
589
590 def getEventType(self, record):
591 """
592 Return the event type for the record.
593
594 Override this if you want to specify your own types. This version does
595 a mapping using the handler's typemap attribute, which is set up in
596 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000597 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000598 either need to override this method or place a suitable dictionary in
599 the handler's typemap attribute.
600 """
601 return self.typemap.get(record.levelno, self.deftype)
602
603 def emit(self, record):
604 """
605 Emit a record.
606
607 Determine the message ID, event category and event type. Then
608 log the message in the NT event log.
609 """
610 if self._welu:
611 try:
612 id = self.getMessageID(record)
613 cat = self.getEventCategory(record)
614 type = self.getEventType(record)
615 msg = self.format(record)
616 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
617 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000618 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000619
620 def close(self):
621 """
622 Clean up this handler.
623
624 You can remove the application name from the registry as a
625 source of event log entries. However, if you do this, you will
626 not be able to see the events as you intended in the Event Log
627 Viewer - it needs to be able to access the registry to get the
628 DLL name.
629 """
630 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000631 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000632
633class HTTPHandler(logging.Handler):
634 """
635 A class which sends records to a Web server, using either GET or
636 POST semantics.
637 """
638 def __init__(self, host, url, method="GET"):
639 """
640 Initialize the instance with the host, the request URL, and the method
641 ("GET" or "POST")
642 """
643 logging.Handler.__init__(self)
644 method = string.upper(method)
645 if method not in ["GET", "POST"]:
646 raise ValueError, "method must be GET or POST"
647 self.host = host
648 self.url = url
649 self.method = method
650
Neal Norwitzf297bd12003-04-23 03:49:43 +0000651 def mapLogRecord(self, record):
652 """
653 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +0000654 that is sent as the CGI data. Overwrite in your class.
Neal Norwitzf297bd12003-04-23 03:49:43 +0000655 Contributed by Franz Glasner.
656 """
657 return record.__dict__
658
Guido van Rossum57102f82002-11-13 16:15:58 +0000659 def emit(self, record):
660 """
661 Emit a record.
662
663 Send the record to the Web server as an URL-encoded dictionary
664 """
665 try:
666 import httplib, urllib
667 h = httplib.HTTP(self.host)
668 url = self.url
Neal Norwitzf297bd12003-04-23 03:49:43 +0000669 data = urllib.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +0000670 if self.method == "GET":
671 if (string.find(url, '?') >= 0):
672 sep = '&'
673 else:
674 sep = '?'
675 url = url + "%c%s" % (sep, data)
676 h.putrequest(self.method, url)
677 if self.method == "POST":
678 h.putheader("Content-length", str(len(data)))
679 h.endheaders()
680 if self.method == "POST":
681 h.send(data)
682 h.getreply() #can't do anything with the result
683 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000684 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000685
686class BufferingHandler(logging.Handler):
687 """
688 A handler class which buffers logging records in memory. Whenever each
689 record is added to the buffer, a check is made to see if the buffer should
690 be flushed. If it should, then flush() is expected to do what's needed.
691 """
692 def __init__(self, capacity):
693 """
694 Initialize the handler with the buffer size.
695 """
696 logging.Handler.__init__(self)
697 self.capacity = capacity
698 self.buffer = []
699
700 def shouldFlush(self, record):
701 """
702 Should the handler flush its buffer?
703
704 Returns true if the buffer is up to capacity. This method can be
705 overridden to implement custom flushing strategies.
706 """
707 return (len(self.buffer) >= self.capacity)
708
709 def emit(self, record):
710 """
711 Emit a record.
712
713 Append the record. If shouldFlush() tells us to, call flush() to process
714 the buffer.
715 """
716 self.buffer.append(record)
717 if self.shouldFlush(record):
718 self.flush()
719
720 def flush(self):
721 """
722 Override to implement custom flushing behaviour.
723
724 This version just zaps the buffer to empty.
725 """
726 self.buffer = []
727
Vinay Sajipf42d95e2004-02-21 22:14:34 +0000728 def close(self):
729 """
730 Close the handler.
731
732 This version just flushes and chains to the parent class' close().
733 """
734 self.flush()
735 logging.Handler.close(self)
736
Guido van Rossum57102f82002-11-13 16:15:58 +0000737class MemoryHandler(BufferingHandler):
738 """
739 A handler class which buffers logging records in memory, periodically
740 flushing them to a target handler. Flushing occurs whenever the buffer
741 is full, or when an event of a certain severity or greater is seen.
742 """
743 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
744 """
745 Initialize the handler with the buffer size, the level at which
746 flushing should occur and an optional target.
747
748 Note that without a target being set either here or via setTarget(),
749 a MemoryHandler is no use to anyone!
750 """
751 BufferingHandler.__init__(self, capacity)
752 self.flushLevel = flushLevel
753 self.target = target
754
755 def shouldFlush(self, record):
756 """
757 Check for buffer full or a record at the flushLevel or higher.
758 """
759 return (len(self.buffer) >= self.capacity) or \
760 (record.levelno >= self.flushLevel)
761
762 def setTarget(self, target):
763 """
764 Set the target handler for this handler.
765 """
766 self.target = target
767
768 def flush(self):
769 """
770 For a MemoryHandler, flushing means just sending the buffered
771 records to the target, if there is one. Override if you want
772 different behaviour.
773 """
774 if self.target:
775 for record in self.buffer:
776 self.target.handle(record)
777 self.buffer = []
778
779 def close(self):
780 """
781 Flush, set the target to None and lose the buffer.
782 """
783 self.flush()
784 self.target = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000785 BufferingHandler.close(self)