blob: 4a597a1627f0b88b120e56302f9fa77d02f13a51 [file] [log] [blame]
Guido van Rossum57102f82002-11-13 16:15:58 +00001# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
2#
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"""
18Logging package for Python. Based on PEP 282 and comments thereto in
19comp.lang.python, and influenced by Apache's log4j system.
20
21Should work under Python versions >= 1.5.2, except that source line
22information is not available unless 'inspect' is.
23
24Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
25
26To use, simply 'import logging' and log away!
27"""
28
Neal Norwitzf297bd12003-04-23 03:49:43 +000029import sys, logging, socket, types, os, string, cPickle, struct, time
Guido van Rossum57102f82002-11-13 16:15:58 +000030
31from SocketServer import ThreadingTCPServer, StreamRequestHandler
32
33#
34# Some constants...
35#
36
37DEFAULT_TCP_LOGGING_PORT = 9020
38DEFAULT_UDP_LOGGING_PORT = 9021
39DEFAULT_HTTP_LOGGING_PORT = 9022
40DEFAULT_SOAP_LOGGING_PORT = 9023
41SYSLOG_UDP_PORT = 514
42
43
44class RotatingFileHandler(logging.FileHandler):
45 def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
46 """
47 Open the specified file and use it as the stream for logging.
48
49 By default, the file grows indefinitely. You can specify particular
50 values of maxBytes and backupCount to allow the file to rollover at
51 a predetermined size.
52
53 Rollover occurs whenever the current log file is nearly maxBytes in
54 length. If backupCount is >= 1, the system will successively create
55 new files with the same pathname as the base file, but with extensions
56 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
57 and a base file name of "app.log", you would get "app.log",
58 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
59 written to is always "app.log" - when it gets filled up, it is closed
60 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
61 exist, then they are renamed to "app.log.2", "app.log.3" etc.
62 respectively.
63
64 If maxBytes is zero, rollover never occurs.
65 """
66 logging.FileHandler.__init__(self, filename, mode)
67 self.maxBytes = maxBytes
68 self.backupCount = backupCount
69 if maxBytes > 0:
70 self.mode = "a"
71
72 def doRollover(self):
73 """
74 Do a rollover, as described in __init__().
75 """
76
77 self.stream.close()
78 if self.backupCount > 0:
79 for i in range(self.backupCount - 1, 0, -1):
80 sfn = "%s.%d" % (self.baseFilename, i)
81 dfn = "%s.%d" % (self.baseFilename, i + 1)
82 if os.path.exists(sfn):
83 #print "%s -> %s" % (sfn, dfn)
84 if os.path.exists(dfn):
85 os.remove(dfn)
86 os.rename(sfn, dfn)
87 dfn = self.baseFilename + ".1"
88 if os.path.exists(dfn):
89 os.remove(dfn)
90 os.rename(self.baseFilename, dfn)
91 #print "%s -> %s" % (self.baseFilename, dfn)
92 self.stream = open(self.baseFilename, "w")
93
94 def emit(self, record):
95 """
96 Emit a record.
97
98 Output the record to the file, catering for rollover as described
99 in setRollover().
100 """
101 if self.maxBytes > 0: # are we rolling over?
102 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000103 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000104 if self.stream.tell() + len(msg) >= self.maxBytes:
105 self.doRollover()
106 logging.FileHandler.emit(self, record)
107
108
109class SocketHandler(logging.Handler):
110 """
111 A handler class which writes logging records, in pickle format, to
112 a streaming socket. The socket is kept open across logging calls.
113 If the peer resets it, an attempt is made to reconnect on the next call.
114 Note that the very simple wire protocol used means that packet sizes
115 are expected to be encodable within 16 bits (i.e. < 32767 bytes).
116 """
117
118 def __init__(self, host, port):
119 """
120 Initializes the handler with a specific host address and port.
121
122 The attribute 'closeOnError' is set to 1 - which means that if
123 a socket error occurs, the socket is silently closed and then
124 reopened on the next logging call.
125 """
126 logging.Handler.__init__(self)
127 self.host = host
128 self.port = port
129 self.sock = None
130 self.closeOnError = 0
131
132 def makeSocket(self):
133 """
134 A factory method which allows subclasses to define the precise
135 type of socket they want.
136 """
137 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
138 s.connect((self.host, self.port))
139 return s
140
141 def send(self, s):
142 """
143 Send a pickled string to the socket.
144
145 This function allows for partial sends which can happen when the
146 network is busy.
147 """
Neal Norwitzf297bd12003-04-23 03:49:43 +0000148 if hasattr(self.sock, "sendall"):
Guido van Rossum57102f82002-11-13 16:15:58 +0000149 self.sock.sendall(s)
150 else:
151 sentsofar = 0
152 left = len(s)
153 while left > 0:
154 sent = self.sock.send(s[sentsofar:])
155 sentsofar = sentsofar + sent
156 left = left - sent
157
158 def makePickle(self, record):
159 """
160 Pickles the record in binary format with a length prefix, and
161 returns it ready for transmission across the socket.
162 """
163 s = cPickle.dumps(record.__dict__, 1)
164 #n = len(s)
165 #slen = "%c%c" % ((n >> 8) & 0xFF, n & 0xFF)
166 slen = struct.pack(">L", len(s))
167 return slen + s
168
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000169 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000170 """
171 Handle an error during logging.
172
173 An error has occurred during logging. Most likely cause -
174 connection lost. Close the socket so that we can retry on the
175 next event.
176 """
177 if self.closeOnError and self.sock:
178 self.sock.close()
179 self.sock = None #try to reconnect next time
180 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000181 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000182
183 def emit(self, record):
184 """
185 Emit a record.
186
187 Pickles the record and writes it to the socket in binary format.
188 If there is an error with the socket, silently drop the packet.
189 If there was a problem with the socket, re-establishes the
190 socket.
191 """
192 try:
193 s = self.makePickle(record)
194 if not self.sock:
195 self.sock = self.makeSocket()
196 self.send(s)
197 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000198 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000199
200 def close(self):
201 """
202 Closes the socket.
203 """
204 if self.sock:
205 self.sock.close()
206 self.sock = None
207
208class DatagramHandler(SocketHandler):
209 """
210 A handler class which writes logging records, in pickle format, to
211 a datagram socket. Note that the very simple wire protocol used means
212 that packet sizes are expected to be encodable within 16 bits
213 (i.e. < 32767 bytes).
214
215 """
216 def __init__(self, host, port):
217 """
218 Initializes the handler with a specific host address and port.
219 """
220 SocketHandler.__init__(self, host, port)
221 self.closeOnError = 0
222
223 def makeSocket(self):
224 """
225 The factory method of SocketHandler is here overridden to create
226 a UDP socket (SOCK_DGRAM).
227 """
228 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
229 return s
230
231 def send(self, s):
232 """
233 Send a pickled string to a socket.
234
235 This function no longer allows for partial sends which can happen
236 when the network is busy - UDP does not guarantee delivery and
237 can deliver packets out of sequence.
238 """
239 #old code
240 #sentsofar = 0
241 #left = len(s)
242 #addr = (self.host, self.port)
243 #while left > 0:
244 # sent = self.sock.sendto(s[sentsofar:], addr)
245 # sentsofar = sentsofar + sent
246 # left = left - sent
247 self.sock.sendto(s, (self.host, self.port))
248
249class SysLogHandler(logging.Handler):
250 """
251 A handler class which sends formatted logging records to a syslog
252 server. Based on Sam Rushing's syslog module:
253 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
254 Contributed by Nicolas Untz (after which minor refactoring changes
255 have been made).
256 """
257
258 # from <linux/sys/syslog.h>:
259 # ======================================================================
260 # priorities/facilities are encoded into a single 32-bit quantity, where
261 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
262 # facility (0-big number). Both the priorities and the facilities map
263 # roughly one-to-one to strings in the syslogd(8) source code. This
264 # mapping is included in this file.
265 #
266 # priorities (these are ordered)
267
268 LOG_EMERG = 0 # system is unusable
269 LOG_ALERT = 1 # action must be taken immediately
270 LOG_CRIT = 2 # critical conditions
271 LOG_ERR = 3 # error conditions
272 LOG_WARNING = 4 # warning conditions
273 LOG_NOTICE = 5 # normal but significant condition
274 LOG_INFO = 6 # informational
275 LOG_DEBUG = 7 # debug-level messages
276
277 # facility codes
278 LOG_KERN = 0 # kernel messages
279 LOG_USER = 1 # random user-level messages
280 LOG_MAIL = 2 # mail system
281 LOG_DAEMON = 3 # system daemons
282 LOG_AUTH = 4 # security/authorization messages
283 LOG_SYSLOG = 5 # messages generated internally by syslogd
284 LOG_LPR = 6 # line printer subsystem
285 LOG_NEWS = 7 # network news subsystem
286 LOG_UUCP = 8 # UUCP subsystem
287 LOG_CRON = 9 # clock daemon
288 LOG_AUTHPRIV = 10 # security/authorization messages (private)
289
290 # other codes through 15 reserved for system use
291 LOG_LOCAL0 = 16 # reserved for local use
292 LOG_LOCAL1 = 17 # reserved for local use
293 LOG_LOCAL2 = 18 # reserved for local use
294 LOG_LOCAL3 = 19 # reserved for local use
295 LOG_LOCAL4 = 20 # reserved for local use
296 LOG_LOCAL5 = 21 # reserved for local use
297 LOG_LOCAL6 = 22 # reserved for local use
298 LOG_LOCAL7 = 23 # reserved for local use
299
300 priority_names = {
301 "alert": LOG_ALERT,
302 "crit": LOG_CRIT,
303 "critical": LOG_CRIT,
304 "debug": LOG_DEBUG,
305 "emerg": LOG_EMERG,
306 "err": LOG_ERR,
307 "error": LOG_ERR, # DEPRECATED
308 "info": LOG_INFO,
309 "notice": LOG_NOTICE,
310 "panic": LOG_EMERG, # DEPRECATED
311 "warn": LOG_WARNING, # DEPRECATED
312 "warning": LOG_WARNING,
313 }
314
315 facility_names = {
316 "auth": LOG_AUTH,
317 "authpriv": LOG_AUTHPRIV,
318 "cron": LOG_CRON,
319 "daemon": LOG_DAEMON,
320 "kern": LOG_KERN,
321 "lpr": LOG_LPR,
322 "mail": LOG_MAIL,
323 "news": LOG_NEWS,
324 "security": LOG_AUTH, # DEPRECATED
325 "syslog": LOG_SYSLOG,
326 "user": LOG_USER,
327 "uucp": LOG_UUCP,
328 "local0": LOG_LOCAL0,
329 "local1": LOG_LOCAL1,
330 "local2": LOG_LOCAL2,
331 "local3": LOG_LOCAL3,
332 "local4": LOG_LOCAL4,
333 "local5": LOG_LOCAL5,
334 "local6": LOG_LOCAL6,
335 "local7": LOG_LOCAL7,
336 }
337
338 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
339 """
340 Initialize a handler.
341
342 If address is specified as a string, UNIX socket is used.
343 If facility is not specified, LOG_USER is used.
344 """
345 logging.Handler.__init__(self)
346
347 self.address = address
348 self.facility = facility
349 if type(address) == types.StringType:
Neal Norwitzd89c4062003-01-26 02:14:23 +0000350 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
Neal Norwitzf4cdb472003-01-26 16:15:24 +0000351 # syslog may require either DGRAM or STREAM sockets
352 try:
353 self.socket.connect(address)
354 except socket.error:
355 self.socket.close()
356 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000357 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000358 self.unixsocket = 1
359 else:
360 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
361 self.unixsocket = 0
362
363 self.formatter = None
364
365 # curious: when talking to the unix-domain '/dev/log' socket, a
366 # zero-terminator seems to be required. this string is placed
367 # into a class variable so that it can be overridden if
368 # necessary.
369 log_format_string = '<%d>%s\000'
370
371 def encodePriority (self, facility, priority):
372 """
373 Encode the facility and priority. You can pass in strings or
374 integers - if strings are passed, the facility_names and
375 priority_names mapping dictionaries are used to convert them to
376 integers.
377 """
378 if type(facility) == types.StringType:
379 facility = self.facility_names[facility]
380 if type(priority) == types.StringType:
381 priority = self.priority_names[priority]
382 return (facility << 3) | priority
383
384 def close (self):
385 """
386 Closes the socket.
387 """
388 if self.unixsocket:
389 self.socket.close()
390
391 def emit(self, record):
392 """
393 Emit a record.
394
395 The record is formatted, and then sent to the syslog server. If
396 exception information is present, it is NOT sent to the server.
397 """
398 msg = self.format(record)
399 """
400 We need to convert record level to lowercase, maybe this will
401 change in the future.
402 """
403 msg = self.log_format_string % (
404 self.encodePriority(self.facility,
405 string.lower(record.levelname)),
406 msg)
407 try:
408 if self.unixsocket:
409 self.socket.send(msg)
410 else:
411 self.socket.sendto(msg, self.address)
412 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000413 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000414
415class SMTPHandler(logging.Handler):
416 """
417 A handler class which sends an SMTP email for each logging event.
418 """
419 def __init__(self, mailhost, fromaddr, toaddrs, subject):
420 """
421 Initialize the handler.
422
423 Initialize the instance with the from and to addresses and subject
424 line of the email. To specify a non-standard SMTP port, use the
425 (host, port) tuple format for the mailhost argument.
426 """
427 logging.Handler.__init__(self)
428 if type(mailhost) == types.TupleType:
429 host, port = mailhost
430 self.mailhost = host
431 self.mailport = port
432 else:
433 self.mailhost = mailhost
434 self.mailport = None
435 self.fromaddr = fromaddr
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000436 if type(toaddrs) == types.StringType:
437 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000438 self.toaddrs = toaddrs
439 self.subject = subject
440
441 def getSubject(self, record):
442 """
443 Determine the subject for the email.
444
445 If you want to specify a subject line which is record-dependent,
446 override this method.
447 """
448 return self.subject
449
Neal Norwitzf297bd12003-04-23 03:49:43 +0000450 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
451
452 monthname = [None,
453 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
454 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
455
456 def date_time(self):
457 """Return the current date and time formatted for a MIME header."""
458 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())
459 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
460 self.weekdayname[wd],
461 day, self.monthname[month], year,
462 hh, mm, ss)
463 return s
464
Guido van Rossum57102f82002-11-13 16:15:58 +0000465 def emit(self, record):
466 """
467 Emit a record.
468
469 Format the record and send it to the specified addressees.
470 """
471 try:
472 import smtplib
473 port = self.mailport
474 if not port:
475 port = smtplib.SMTP_PORT
476 smtp = smtplib.SMTP(self.mailhost, port)
477 msg = self.format(record)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000478 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 +0000479 self.fromaddr,
480 string.join(self.toaddrs, ","),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000481 self.getSubject(record),
482 self.date_time(), msg)
Guido van Rossum57102f82002-11-13 16:15:58 +0000483 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
484 smtp.quit()
485 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000486 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000487
488class NTEventLogHandler(logging.Handler):
489 """
490 A handler class which sends events to the NT Event Log. Adds a
491 registry entry for the specified application name. If no dllname is
492 provided, win32service.pyd (which contains some basic message
493 placeholders) is used. Note that use of these placeholders will make
494 your event logs big, as the entire message source is held in the log.
495 If you want slimmer logs, you have to pass in the name of your own DLL
496 which contains the message definitions you want to use in the event log.
497 """
498 def __init__(self, appname, dllname=None, logtype="Application"):
499 logging.Handler.__init__(self)
500 try:
501 import win32evtlogutil, win32evtlog
502 self.appname = appname
503 self._welu = win32evtlogutil
504 if not dllname:
505 dllname = os.path.split(self._welu.__file__)
506 dllname = os.path.split(dllname[0])
507 dllname = os.path.join(dllname[0], r'win32service.pyd')
508 self.dllname = dllname
509 self.logtype = logtype
510 self._welu.AddSourceToRegistry(appname, dllname, logtype)
511 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
512 self.typemap = {
513 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
514 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000515 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000516 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
517 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
518 }
519 except ImportError:
520 print "The Python Win32 extensions for NT (service, event "\
521 "logging) appear not to be available."
522 self._welu = None
523
524 def getMessageID(self, record):
525 """
526 Return the message ID for the event record. If you are using your
527 own messages, you could do this by having the msg passed to the
528 logger being an ID rather than a formatting string. Then, in here,
529 you could use a dictionary lookup to get the message ID. This
530 version returns 1, which is the base message ID in win32service.pyd.
531 """
532 return 1
533
534 def getEventCategory(self, record):
535 """
536 Return the event category for the record.
537
538 Override this if you want to specify your own categories. This version
539 returns 0.
540 """
541 return 0
542
543 def getEventType(self, record):
544 """
545 Return the event type for the record.
546
547 Override this if you want to specify your own types. This version does
548 a mapping using the handler's typemap attribute, which is set up in
549 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000550 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000551 either need to override this method or place a suitable dictionary in
552 the handler's typemap attribute.
553 """
554 return self.typemap.get(record.levelno, self.deftype)
555
556 def emit(self, record):
557 """
558 Emit a record.
559
560 Determine the message ID, event category and event type. Then
561 log the message in the NT event log.
562 """
563 if self._welu:
564 try:
565 id = self.getMessageID(record)
566 cat = self.getEventCategory(record)
567 type = self.getEventType(record)
568 msg = self.format(record)
569 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
570 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000571 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000572
573 def close(self):
574 """
575 Clean up this handler.
576
577 You can remove the application name from the registry as a
578 source of event log entries. However, if you do this, you will
579 not be able to see the events as you intended in the Event Log
580 Viewer - it needs to be able to access the registry to get the
581 DLL name.
582 """
583 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
584 pass
585
586class HTTPHandler(logging.Handler):
587 """
588 A class which sends records to a Web server, using either GET or
589 POST semantics.
590 """
591 def __init__(self, host, url, method="GET"):
592 """
593 Initialize the instance with the host, the request URL, and the method
594 ("GET" or "POST")
595 """
596 logging.Handler.__init__(self)
597 method = string.upper(method)
598 if method not in ["GET", "POST"]:
599 raise ValueError, "method must be GET or POST"
600 self.host = host
601 self.url = url
602 self.method = method
603
Neal Norwitzf297bd12003-04-23 03:49:43 +0000604 def mapLogRecord(self, record):
605 """
606 Default implementation of mapping the log record into a dict
607 that is send as the CGI data. Overwrite in your class.
608 Contributed by Franz Glasner.
609 """
610 return record.__dict__
611
Guido van Rossum57102f82002-11-13 16:15:58 +0000612 def emit(self, record):
613 """
614 Emit a record.
615
616 Send the record to the Web server as an URL-encoded dictionary
617 """
618 try:
619 import httplib, urllib
620 h = httplib.HTTP(self.host)
621 url = self.url
Neal Norwitzf297bd12003-04-23 03:49:43 +0000622 data = urllib.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +0000623 if self.method == "GET":
624 if (string.find(url, '?') >= 0):
625 sep = '&'
626 else:
627 sep = '?'
628 url = url + "%c%s" % (sep, data)
629 h.putrequest(self.method, url)
630 if self.method == "POST":
631 h.putheader("Content-length", str(len(data)))
632 h.endheaders()
633 if self.method == "POST":
634 h.send(data)
635 h.getreply() #can't do anything with the result
636 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000637 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000638
639class BufferingHandler(logging.Handler):
640 """
641 A handler class which buffers logging records in memory. Whenever each
642 record is added to the buffer, a check is made to see if the buffer should
643 be flushed. If it should, then flush() is expected to do what's needed.
644 """
645 def __init__(self, capacity):
646 """
647 Initialize the handler with the buffer size.
648 """
649 logging.Handler.__init__(self)
650 self.capacity = capacity
651 self.buffer = []
652
653 def shouldFlush(self, record):
654 """
655 Should the handler flush its buffer?
656
657 Returns true if the buffer is up to capacity. This method can be
658 overridden to implement custom flushing strategies.
659 """
660 return (len(self.buffer) >= self.capacity)
661
662 def emit(self, record):
663 """
664 Emit a record.
665
666 Append the record. If shouldFlush() tells us to, call flush() to process
667 the buffer.
668 """
669 self.buffer.append(record)
670 if self.shouldFlush(record):
671 self.flush()
672
673 def flush(self):
674 """
675 Override to implement custom flushing behaviour.
676
677 This version just zaps the buffer to empty.
678 """
679 self.buffer = []
680
681class MemoryHandler(BufferingHandler):
682 """
683 A handler class which buffers logging records in memory, periodically
684 flushing them to a target handler. Flushing occurs whenever the buffer
685 is full, or when an event of a certain severity or greater is seen.
686 """
687 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
688 """
689 Initialize the handler with the buffer size, the level at which
690 flushing should occur and an optional target.
691
692 Note that without a target being set either here or via setTarget(),
693 a MemoryHandler is no use to anyone!
694 """
695 BufferingHandler.__init__(self, capacity)
696 self.flushLevel = flushLevel
697 self.target = target
698
699 def shouldFlush(self, record):
700 """
701 Check for buffer full or a record at the flushLevel or higher.
702 """
703 return (len(self.buffer) >= self.capacity) or \
704 (record.levelno >= self.flushLevel)
705
706 def setTarget(self, target):
707 """
708 Set the target handler for this handler.
709 """
710 self.target = target
711
712 def flush(self):
713 """
714 For a MemoryHandler, flushing means just sending the buffered
715 records to the target, if there is one. Override if you want
716 different behaviour.
717 """
718 if self.target:
719 for record in self.buffer:
720 self.target.handle(record)
721 self.buffer = []
722
723 def close(self):
724 """
725 Flush, set the target to None and lose the buffer.
726 """
727 self.flush()
728 self.target = None
729 self.buffer = []