blob: 48e14b6c15cef8e627a5cc7e176ce409a3ba6ddd [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
29import sys, logging, socket, types, os, string, cPickle, struct
30
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 Norwitz6fa635d2003-02-18 14:20:07 +0000148 v = logging._verinfo
149 if v and (v[0] >= 2) and (v[1] >= 2):
Guido van Rossum57102f82002-11-13 16:15:58 +0000150 self.sock.sendall(s)
151 else:
152 sentsofar = 0
153 left = len(s)
154 while left > 0:
155 sent = self.sock.send(s[sentsofar:])
156 sentsofar = sentsofar + sent
157 left = left - sent
158
159 def makePickle(self, record):
160 """
161 Pickles the record in binary format with a length prefix, and
162 returns it ready for transmission across the socket.
163 """
164 s = cPickle.dumps(record.__dict__, 1)
165 #n = len(s)
166 #slen = "%c%c" % ((n >> 8) & 0xFF, n & 0xFF)
167 slen = struct.pack(">L", len(s))
168 return slen + s
169
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000170 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000171 """
172 Handle an error during logging.
173
174 An error has occurred during logging. Most likely cause -
175 connection lost. Close the socket so that we can retry on the
176 next event.
177 """
178 if self.closeOnError and self.sock:
179 self.sock.close()
180 self.sock = None #try to reconnect next time
181 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000182 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000183
184 def emit(self, record):
185 """
186 Emit a record.
187
188 Pickles the record and writes it to the socket in binary format.
189 If there is an error with the socket, silently drop the packet.
190 If there was a problem with the socket, re-establishes the
191 socket.
192 """
193 try:
194 s = self.makePickle(record)
195 if not self.sock:
196 self.sock = self.makeSocket()
197 self.send(s)
198 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000199 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000200
201 def close(self):
202 """
203 Closes the socket.
204 """
205 if self.sock:
206 self.sock.close()
207 self.sock = None
208
209class DatagramHandler(SocketHandler):
210 """
211 A handler class which writes logging records, in pickle format, to
212 a datagram socket. Note that the very simple wire protocol used means
213 that packet sizes are expected to be encodable within 16 bits
214 (i.e. < 32767 bytes).
215
216 """
217 def __init__(self, host, port):
218 """
219 Initializes the handler with a specific host address and port.
220 """
221 SocketHandler.__init__(self, host, port)
222 self.closeOnError = 0
223
224 def makeSocket(self):
225 """
226 The factory method of SocketHandler is here overridden to create
227 a UDP socket (SOCK_DGRAM).
228 """
229 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
230 return s
231
232 def send(self, s):
233 """
234 Send a pickled string to a socket.
235
236 This function no longer allows for partial sends which can happen
237 when the network is busy - UDP does not guarantee delivery and
238 can deliver packets out of sequence.
239 """
240 #old code
241 #sentsofar = 0
242 #left = len(s)
243 #addr = (self.host, self.port)
244 #while left > 0:
245 # sent = self.sock.sendto(s[sentsofar:], addr)
246 # sentsofar = sentsofar + sent
247 # left = left - sent
248 self.sock.sendto(s, (self.host, self.port))
249
250class SysLogHandler(logging.Handler):
251 """
252 A handler class which sends formatted logging records to a syslog
253 server. Based on Sam Rushing's syslog module:
254 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
255 Contributed by Nicolas Untz (after which minor refactoring changes
256 have been made).
257 """
258
259 # from <linux/sys/syslog.h>:
260 # ======================================================================
261 # priorities/facilities are encoded into a single 32-bit quantity, where
262 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
263 # facility (0-big number). Both the priorities and the facilities map
264 # roughly one-to-one to strings in the syslogd(8) source code. This
265 # mapping is included in this file.
266 #
267 # priorities (these are ordered)
268
269 LOG_EMERG = 0 # system is unusable
270 LOG_ALERT = 1 # action must be taken immediately
271 LOG_CRIT = 2 # critical conditions
272 LOG_ERR = 3 # error conditions
273 LOG_WARNING = 4 # warning conditions
274 LOG_NOTICE = 5 # normal but significant condition
275 LOG_INFO = 6 # informational
276 LOG_DEBUG = 7 # debug-level messages
277
278 # facility codes
279 LOG_KERN = 0 # kernel messages
280 LOG_USER = 1 # random user-level messages
281 LOG_MAIL = 2 # mail system
282 LOG_DAEMON = 3 # system daemons
283 LOG_AUTH = 4 # security/authorization messages
284 LOG_SYSLOG = 5 # messages generated internally by syslogd
285 LOG_LPR = 6 # line printer subsystem
286 LOG_NEWS = 7 # network news subsystem
287 LOG_UUCP = 8 # UUCP subsystem
288 LOG_CRON = 9 # clock daemon
289 LOG_AUTHPRIV = 10 # security/authorization messages (private)
290
291 # other codes through 15 reserved for system use
292 LOG_LOCAL0 = 16 # reserved for local use
293 LOG_LOCAL1 = 17 # reserved for local use
294 LOG_LOCAL2 = 18 # reserved for local use
295 LOG_LOCAL3 = 19 # reserved for local use
296 LOG_LOCAL4 = 20 # reserved for local use
297 LOG_LOCAL5 = 21 # reserved for local use
298 LOG_LOCAL6 = 22 # reserved for local use
299 LOG_LOCAL7 = 23 # reserved for local use
300
301 priority_names = {
302 "alert": LOG_ALERT,
303 "crit": LOG_CRIT,
304 "critical": LOG_CRIT,
305 "debug": LOG_DEBUG,
306 "emerg": LOG_EMERG,
307 "err": LOG_ERR,
308 "error": LOG_ERR, # DEPRECATED
309 "info": LOG_INFO,
310 "notice": LOG_NOTICE,
311 "panic": LOG_EMERG, # DEPRECATED
312 "warn": LOG_WARNING, # DEPRECATED
313 "warning": LOG_WARNING,
314 }
315
316 facility_names = {
317 "auth": LOG_AUTH,
318 "authpriv": LOG_AUTHPRIV,
319 "cron": LOG_CRON,
320 "daemon": LOG_DAEMON,
321 "kern": LOG_KERN,
322 "lpr": LOG_LPR,
323 "mail": LOG_MAIL,
324 "news": LOG_NEWS,
325 "security": LOG_AUTH, # DEPRECATED
326 "syslog": LOG_SYSLOG,
327 "user": LOG_USER,
328 "uucp": LOG_UUCP,
329 "local0": LOG_LOCAL0,
330 "local1": LOG_LOCAL1,
331 "local2": LOG_LOCAL2,
332 "local3": LOG_LOCAL3,
333 "local4": LOG_LOCAL4,
334 "local5": LOG_LOCAL5,
335 "local6": LOG_LOCAL6,
336 "local7": LOG_LOCAL7,
337 }
338
339 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
340 """
341 Initialize a handler.
342
343 If address is specified as a string, UNIX socket is used.
344 If facility is not specified, LOG_USER is used.
345 """
346 logging.Handler.__init__(self)
347
348 self.address = address
349 self.facility = facility
350 if type(address) == types.StringType:
Neal Norwitzd89c4062003-01-26 02:14:23 +0000351 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
Neal Norwitzf4cdb472003-01-26 16:15:24 +0000352 # syslog may require either DGRAM or STREAM sockets
353 try:
354 self.socket.connect(address)
355 except socket.error:
356 self.socket.close()
357 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000358 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000359 self.unixsocket = 1
360 else:
361 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
362 self.unixsocket = 0
363
364 self.formatter = None
365
366 # curious: when talking to the unix-domain '/dev/log' socket, a
367 # zero-terminator seems to be required. this string is placed
368 # into a class variable so that it can be overridden if
369 # necessary.
370 log_format_string = '<%d>%s\000'
371
372 def encodePriority (self, facility, priority):
373 """
374 Encode the facility and priority. You can pass in strings or
375 integers - if strings are passed, the facility_names and
376 priority_names mapping dictionaries are used to convert them to
377 integers.
378 """
379 if type(facility) == types.StringType:
380 facility = self.facility_names[facility]
381 if type(priority) == types.StringType:
382 priority = self.priority_names[priority]
383 return (facility << 3) | priority
384
385 def close (self):
386 """
387 Closes the socket.
388 """
389 if self.unixsocket:
390 self.socket.close()
391
392 def emit(self, record):
393 """
394 Emit a record.
395
396 The record is formatted, and then sent to the syslog server. If
397 exception information is present, it is NOT sent to the server.
398 """
399 msg = self.format(record)
400 """
401 We need to convert record level to lowercase, maybe this will
402 change in the future.
403 """
404 msg = self.log_format_string % (
405 self.encodePriority(self.facility,
406 string.lower(record.levelname)),
407 msg)
408 try:
409 if self.unixsocket:
410 self.socket.send(msg)
411 else:
412 self.socket.sendto(msg, self.address)
413 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000414 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000415
416class SMTPHandler(logging.Handler):
417 """
418 A handler class which sends an SMTP email for each logging event.
419 """
420 def __init__(self, mailhost, fromaddr, toaddrs, subject):
421 """
422 Initialize the handler.
423
424 Initialize the instance with the from and to addresses and subject
425 line of the email. To specify a non-standard SMTP port, use the
426 (host, port) tuple format for the mailhost argument.
427 """
428 logging.Handler.__init__(self)
429 if type(mailhost) == types.TupleType:
430 host, port = mailhost
431 self.mailhost = host
432 self.mailport = port
433 else:
434 self.mailhost = mailhost
435 self.mailport = None
436 self.fromaddr = fromaddr
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000437 if type(toaddrs) == types.StringType:
438 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000439 self.toaddrs = toaddrs
440 self.subject = subject
441
442 def getSubject(self, record):
443 """
444 Determine the subject for the email.
445
446 If you want to specify a subject line which is record-dependent,
447 override this method.
448 """
449 return self.subject
450
451 def emit(self, record):
452 """
453 Emit a record.
454
455 Format the record and send it to the specified addressees.
456 """
457 try:
458 import smtplib
459 port = self.mailport
460 if not port:
461 port = smtplib.SMTP_PORT
462 smtp = smtplib.SMTP(self.mailhost, port)
463 msg = self.format(record)
464 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (
465 self.fromaddr,
466 string.join(self.toaddrs, ","),
467 self.getSubject(record), msg
468 )
469 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
470 smtp.quit()
471 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000472 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000473
474class NTEventLogHandler(logging.Handler):
475 """
476 A handler class which sends events to the NT Event Log. Adds a
477 registry entry for the specified application name. If no dllname is
478 provided, win32service.pyd (which contains some basic message
479 placeholders) is used. Note that use of these placeholders will make
480 your event logs big, as the entire message source is held in the log.
481 If you want slimmer logs, you have to pass in the name of your own DLL
482 which contains the message definitions you want to use in the event log.
483 """
484 def __init__(self, appname, dllname=None, logtype="Application"):
485 logging.Handler.__init__(self)
486 try:
487 import win32evtlogutil, win32evtlog
488 self.appname = appname
489 self._welu = win32evtlogutil
490 if not dllname:
491 dllname = os.path.split(self._welu.__file__)
492 dllname = os.path.split(dllname[0])
493 dllname = os.path.join(dllname[0], r'win32service.pyd')
494 self.dllname = dllname
495 self.logtype = logtype
496 self._welu.AddSourceToRegistry(appname, dllname, logtype)
497 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
498 self.typemap = {
499 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
500 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000501 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000502 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
503 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
504 }
505 except ImportError:
506 print "The Python Win32 extensions for NT (service, event "\
507 "logging) appear not to be available."
508 self._welu = None
509
510 def getMessageID(self, record):
511 """
512 Return the message ID for the event record. If you are using your
513 own messages, you could do this by having the msg passed to the
514 logger being an ID rather than a formatting string. Then, in here,
515 you could use a dictionary lookup to get the message ID. This
516 version returns 1, which is the base message ID in win32service.pyd.
517 """
518 return 1
519
520 def getEventCategory(self, record):
521 """
522 Return the event category for the record.
523
524 Override this if you want to specify your own categories. This version
525 returns 0.
526 """
527 return 0
528
529 def getEventType(self, record):
530 """
531 Return the event type for the record.
532
533 Override this if you want to specify your own types. This version does
534 a mapping using the handler's typemap attribute, which is set up in
535 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000536 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000537 either need to override this method or place a suitable dictionary in
538 the handler's typemap attribute.
539 """
540 return self.typemap.get(record.levelno, self.deftype)
541
542 def emit(self, record):
543 """
544 Emit a record.
545
546 Determine the message ID, event category and event type. Then
547 log the message in the NT event log.
548 """
549 if self._welu:
550 try:
551 id = self.getMessageID(record)
552 cat = self.getEventCategory(record)
553 type = self.getEventType(record)
554 msg = self.format(record)
555 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
556 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000557 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000558
559 def close(self):
560 """
561 Clean up this handler.
562
563 You can remove the application name from the registry as a
564 source of event log entries. However, if you do this, you will
565 not be able to see the events as you intended in the Event Log
566 Viewer - it needs to be able to access the registry to get the
567 DLL name.
568 """
569 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
570 pass
571
572class HTTPHandler(logging.Handler):
573 """
574 A class which sends records to a Web server, using either GET or
575 POST semantics.
576 """
577 def __init__(self, host, url, method="GET"):
578 """
579 Initialize the instance with the host, the request URL, and the method
580 ("GET" or "POST")
581 """
582 logging.Handler.__init__(self)
583 method = string.upper(method)
584 if method not in ["GET", "POST"]:
585 raise ValueError, "method must be GET or POST"
586 self.host = host
587 self.url = url
588 self.method = method
589
590 def emit(self, record):
591 """
592 Emit a record.
593
594 Send the record to the Web server as an URL-encoded dictionary
595 """
596 try:
597 import httplib, urllib
598 h = httplib.HTTP(self.host)
599 url = self.url
600 data = urllib.urlencode(record.__dict__)
601 if self.method == "GET":
602 if (string.find(url, '?') >= 0):
603 sep = '&'
604 else:
605 sep = '?'
606 url = url + "%c%s" % (sep, data)
607 h.putrequest(self.method, url)
608 if self.method == "POST":
609 h.putheader("Content-length", str(len(data)))
610 h.endheaders()
611 if self.method == "POST":
612 h.send(data)
613 h.getreply() #can't do anything with the result
614 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000615 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000616
617class BufferingHandler(logging.Handler):
618 """
619 A handler class which buffers logging records in memory. Whenever each
620 record is added to the buffer, a check is made to see if the buffer should
621 be flushed. If it should, then flush() is expected to do what's needed.
622 """
623 def __init__(self, capacity):
624 """
625 Initialize the handler with the buffer size.
626 """
627 logging.Handler.__init__(self)
628 self.capacity = capacity
629 self.buffer = []
630
631 def shouldFlush(self, record):
632 """
633 Should the handler flush its buffer?
634
635 Returns true if the buffer is up to capacity. This method can be
636 overridden to implement custom flushing strategies.
637 """
638 return (len(self.buffer) >= self.capacity)
639
640 def emit(self, record):
641 """
642 Emit a record.
643
644 Append the record. If shouldFlush() tells us to, call flush() to process
645 the buffer.
646 """
647 self.buffer.append(record)
648 if self.shouldFlush(record):
649 self.flush()
650
651 def flush(self):
652 """
653 Override to implement custom flushing behaviour.
654
655 This version just zaps the buffer to empty.
656 """
657 self.buffer = []
658
659class MemoryHandler(BufferingHandler):
660 """
661 A handler class which buffers logging records in memory, periodically
662 flushing them to a target handler. Flushing occurs whenever the buffer
663 is full, or when an event of a certain severity or greater is seen.
664 """
665 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
666 """
667 Initialize the handler with the buffer size, the level at which
668 flushing should occur and an optional target.
669
670 Note that without a target being set either here or via setTarget(),
671 a MemoryHandler is no use to anyone!
672 """
673 BufferingHandler.__init__(self, capacity)
674 self.flushLevel = flushLevel
675 self.target = target
676
677 def shouldFlush(self, record):
678 """
679 Check for buffer full or a record at the flushLevel or higher.
680 """
681 return (len(self.buffer) >= self.capacity) or \
682 (record.levelno >= self.flushLevel)
683
684 def setTarget(self, target):
685 """
686 Set the target handler for this handler.
687 """
688 self.target = target
689
690 def flush(self):
691 """
692 For a MemoryHandler, flushing means just sending the buffered
693 records to the target, if there is one. Override if you want
694 different behaviour.
695 """
696 if self.target:
697 for record in self.buffer:
698 self.target.handle(record)
699 self.buffer = []
700
701 def close(self):
702 """
703 Flush, set the target to None and lose the buffer.
704 """
705 self.flush()
706 self.target = None
707 self.buffer = []