1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 Additional handlers for the logging package for Python. The core package is
19 based on PEP 282 and comments thereto in comp.lang.python, and influenced by
20 Apache's log4j system.
21
22 Should work under Python versions >= 1.5.2, except that source line
23 information is not available unless 'sys._getframe()' is.
24
25 Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
26
27 To use, simply 'import logging' and log away!
28 """
29
30 import sys, socket, types, os, string, cPickle, struct, time, glob
31
32
33 import Ganga.Utility.external.logging as logging
34
35
36
37
38
39
40 DEFAULT_TCP_LOGGING_PORT = 9020
41 DEFAULT_UDP_LOGGING_PORT = 9021
42 DEFAULT_HTTP_LOGGING_PORT = 9022
43 DEFAULT_SOAP_LOGGING_PORT = 9023
44 SYSLOG_UDP_PORT = 514
45
47 """
48 Base class for handlers that rotate log files at a certain point.
49 Not meant to be instantiated directly. Instead, use RotatingFileHandler
50 or TimedRotatingFileHandler.
51 """
57
58 - def emit(self, record):
71
73 """
74 Handler for logging to a set of files, which switches from one file
75 to the next when the current file reaches a certain size.
76 """
77 - def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
78 """
79 Open the specified file and use it as the stream for logging.
80
81 By default, the file grows indefinitely. You can specify particular
82 values of maxBytes and backupCount to allow the file to rollover at
83 a predetermined size.
84
85 Rollover occurs whenever the current log file is nearly maxBytes in
86 length. If backupCount is >= 1, the system will successively create
87 new files with the same pathname as the base file, but with extensions
88 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
89 and a base file name of "app.log", you would get "app.log",
90 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
91 written to is always "app.log" - when it gets filled up, it is closed
92 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
93 exist, then they are renamed to "app.log.2", "app.log.3" etc.
94 respectively.
95
96 If maxBytes is zero, rollover never occurs.
97 """
98 self.mode = mode
99 if maxBytes > 0:
100 self.mode = "a"
101 BaseRotatingHandler.__init__(self, filename, self.mode)
102 self.maxBytes = maxBytes
103 self.backupCount = backupCount
104
106 """
107 Do a rollover, as described in __init__().
108 """
109
110 self.stream.close()
111 if self.backupCount > 0:
112 for i in range(self.backupCount - 1, 0, -1):
113 sfn = "%s.%d" % (self.baseFilename, i)
114 dfn = "%s.%d" % (self.baseFilename, i + 1)
115 if os.path.exists(sfn):
116
117 if os.path.exists(dfn):
118 os.remove(dfn)
119 os.rename(sfn, dfn)
120 dfn = self.baseFilename + ".1"
121 if os.path.exists(dfn):
122 os.remove(dfn)
123 os.rename(self.baseFilename, dfn)
124
125 self.stream = open(self.baseFilename, "w")
126
128 """
129 Determine if rollover should occur.
130
131 Basically, see if the supplied record would cause the file to exceed
132 the size limit we have.
133 """
134 if self.maxBytes > 0:
135 msg = "%s\n" % self.format(record)
136 self.stream.seek(0, 2)
137 if self.stream.tell() + len(msg) >= self.maxBytes:
138 return 1
139 return 0
140
142 """
143 Handler for logging to a file, rotating the log file at certain timed
144 intervals.
145
146 If backupCount is > 0, when rollover is done, no more than backupCount
147 files are kept - the oldest ones are deleted.
148 """
149 - def __init__(self, filename, when='h', interval=1, backupCount=0):
150 BaseRotatingHandler.__init__(self, filename, 'a')
151 self.when = string.upper(when)
152 self.backupCount = backupCount
153
154
155
156
157
158
159
160
161
162
163
164
165 currentTime = int(time.time())
166 if self.when == 'S':
167 self.interval = 1
168 self.suffix = "%Y-%m-%d_%H-%M-%S"
169 elif self.when == 'M':
170 self.interval = 60
171 self.suffix = "%Y-%m-%d_%H-%M"
172 elif self.when == 'H':
173 self.interval = 60 * 60
174 self.suffix = "%Y-%m-%d_%H"
175 elif self.when == 'D' or self.when == 'MIDNIGHT':
176 self.interval = 60 * 60 * 24
177 self.suffix = "%Y-%m-%d"
178 elif self.when.startswith('W'):
179 self.interval = 60 * 60 * 24 * 7
180 if len(self.when) != 2:
181 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
182 if self.when[1] < '0' or self.when[1] > '6':
183 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
184 self.dayOfWeek = int(self.when[1])
185 self.suffix = "%Y-%m-%d"
186 else:
187 raise ValueError("Invalid rollover interval specified: %s" % self.when)
188
189 self.interval = self.interval * interval
190 self.rolloverAt = currentTime + self.interval
191
192
193
194
195
196
197
198
199 if self.when == 'MIDNIGHT' or self.when.startswith('W'):
200
201 t = time.localtime(currentTime)
202 currentHour = t[3]
203 currentMinute = t[4]
204 currentSecond = t[5]
205
206 r = (24 - currentHour) * 60 * 60
207 r = r + (59 - currentMinute) * 60
208 r = r + (59 - currentSecond)
209 self.rolloverAt = currentTime + r
210
211
212
213
214
215
216
217
218
219
220
221
222 if when.startswith('W'):
223 day = t[6]
224 if day > self.dayOfWeek:
225 daysToWait = (day - self.dayOfWeek) - 1
226 self.rolloverAt = self.rolloverAt + (daysToWait * (60 * 60 * 24))
227 if day < self.dayOfWeek:
228 daysToWait = (6 - self.dayOfWeek) + day
229 self.rolloverAt = self.rolloverAt + (daysToWait * (60 * 60 * 24))
230
231
232
234 """
235 Determine if rollover should occur
236
237 record is not used, as we are just comparing times, but it is needed so
238 the method siguratures are the same
239 """
240 t = int(time.time())
241 if t >= self.rolloverAt:
242 return 1
243
244 return 0
245
247 """
248 do a rollover; in this case, a date/time stamp is appended to the filename
249 when the rollover happens. However, you want the file to be named for the
250 start of the interval, not the current time. If there is a backup count,
251 then we have to get a list of matching filenames, sort them and remove
252 the one with the oldest suffix.
253 """
254 self.stream.close()
255
256 t = self.rolloverAt - self.interval
257 timeTuple = time.localtime(t)
258 dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
259 if os.path.exists(dfn):
260 os.remove(dfn)
261 os.rename(self.baseFilename, dfn)
262 if self.backupCount > 0:
263
264 s = glob.glob(self.baseFilename + ".20*")
265 if len(s) > self.backupCount:
266 s.sort()
267 os.remove(s[0])
268
269 self.stream = open(self.baseFilename, "w")
270 self.rolloverAt = int(time.time()) + self.interval
271
273 """
274 A handler class which writes logging records, in pickle format, to
275 a streaming socket. The socket is kept open across logging calls.
276 If the peer resets it, an attempt is made to reconnect on the next call.
277 The pickle which is sent is that of the LogRecord's attribute dictionary
278 (__dict__), so that the receiver does not need to have the logging module
279 installed in order to process the logging event.
280
281 To unpickle the record at the receiving end into a LogRecord, use the
282 makeLogRecord function.
283 """
284
286 """
287 Initializes the handler with a specific host address and port.
288
289 The attribute 'closeOnError' is set to 1 - which means that if
290 a socket error occurs, the socket is silently closed and then
291 reopened on the next logging call.
292 """
293 logging.Handler.__init__(self)
294 self.host = host
295 self.port = port
296 self.sock = None
297 self.closeOnError = 0
298 self.retryTime = None
299
300
301
302 self.retryStart = 1.0
303 self.retryMax = 30.0
304 self.retryFactor = 2.0
305
307 """
308 A factory method which allows subclasses to define the precise
309 type of socket they want.
310 """
311 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
312 s.connect((self.host, self.port))
313 return s
314
316 """
317 Try to create a socket, using an exponential backoff with
318 a max retry time. Thanks to Robert Olson for the original patch
319 (SF #815911) which has been slightly refactored.
320 """
321 now = time.time()
322
323
324
325 if self.retryTime is None:
326 attempt = 1
327 else:
328 attempt = (now >= self.retryTime)
329 if attempt:
330 try:
331 self.sock = self.makeSocket()
332 self.retryTime = None
333 except:
334
335 if self.retryTime is None:
336 self.retryPeriod = self.retryStart
337 else:
338 self.retryPeriod = self.retryPeriod * self.retryFactor
339 if self.retryPeriod > self.retryMax:
340 self.retryPeriod = self.retryMax
341 self.retryTime = now + self.retryPeriod
342
344 """
345 Send a pickled string to the socket.
346
347 This function allows for partial sends which can happen when the
348 network is busy.
349 """
350 if self.sock is None:
351 self.createSocket()
352
353
354
355 if self.sock:
356 try:
357 if hasattr(self.sock, "sendall"):
358 self.sock.sendall(s)
359 else:
360 sentsofar = 0
361 left = len(s)
362 while left > 0:
363 sent = self.sock.send(s[sentsofar:])
364 sentsofar = sentsofar + sent
365 left = left - sent
366 except socket.error:
367 self.sock.close()
368 self.sock = None
369
371 """
372 Pickles the record in binary format with a length prefix, and
373 returns it ready for transmission across the socket.
374 """
375 ei = record.exc_info
376 if ei:
377 dummy = self.format(record)
378 record.exc_info = None
379 s = cPickle.dumps(record.__dict__, 1)
380 if ei:
381 record.exc_info = ei
382 slen = struct.pack(">L", len(s))
383 return slen + s
384
386 """
387 Handle an error during logging.
388
389 An error has occurred during logging. Most likely cause -
390 connection lost. Close the socket so that we can retry on the
391 next event.
392 """
393 if self.closeOnError and self.sock:
394 self.sock.close()
395 self.sock = None
396 else:
397 logging.Handler.handleError(self, record)
398
399 - def emit(self, record):
400 """
401 Emit a record.
402
403 Pickles the record and writes it to the socket in binary format.
404 If there is an error with the socket, silently drop the packet.
405 If there was a problem with the socket, re-establishes the
406 socket.
407 """
408 try:
409 s = self.makePickle(record)
410 self.send(s)
411 except:
412 self.handleError(record)
413
415 """
416 Closes the socket.
417 """
418 if self.sock:
419 self.sock.close()
420 self.sock = None
421 logging.Handler.close(self)
422
424 """
425 A handler class which writes logging records, in pickle format, to
426 a datagram socket. The pickle which is sent is that of the LogRecord's
427 attribute dictionary (__dict__), so that the receiver does not need to
428 have the logging module installed in order to process the logging event.
429
430 To unpickle the record at the receiving end into a LogRecord, use the
431 makeLogRecord function.
432
433 """
435 """
436 Initializes the handler with a specific host address and port.
437 """
438 SocketHandler.__init__(self, host, port)
439 self.closeOnError = 0
440
442 """
443 The factory method of SocketHandler is here overridden to create
444 a UDP socket (SOCK_DGRAM).
445 """
446 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
447 return s
448
450 """
451 Send a pickled string to a socket.
452
453 This function no longer allows for partial sends which can happen
454 when the network is busy - UDP does not guarantee delivery and
455 can deliver packets out of sequence.
456 """
457 if self.sock is None:
458 self.createSocket()
459 self.sock.sendto(s, (self.host, self.port))
460
462 """
463 A handler class which sends formatted logging records to a syslog
464 server. Based on Sam Rushing's syslog module:
465 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
466 Contributed by Nicolas Untz (after which minor refactoring changes
467 have been made).
468 """
469
470
471
472
473
474
475
476
477
478
479
480 LOG_EMERG = 0
481 LOG_ALERT = 1
482 LOG_CRIT = 2
483 LOG_ERR = 3
484 LOG_WARNING = 4
485 LOG_NOTICE = 5
486 LOG_INFO = 6
487 LOG_DEBUG = 7
488
489
490 LOG_KERN = 0
491 LOG_USER = 1
492 LOG_MAIL = 2
493 LOG_DAEMON = 3
494 LOG_AUTH = 4
495 LOG_SYSLOG = 5
496 LOG_LPR = 6
497 LOG_NEWS = 7
498 LOG_UUCP = 8
499 LOG_CRON = 9
500 LOG_AUTHPRIV = 10
501
502
503 LOG_LOCAL0 = 16
504 LOG_LOCAL1 = 17
505 LOG_LOCAL2 = 18
506 LOG_LOCAL3 = 19
507 LOG_LOCAL4 = 20
508 LOG_LOCAL5 = 21
509 LOG_LOCAL6 = 22
510 LOG_LOCAL7 = 23
511
512 priority_names = {
513 "alert": LOG_ALERT,
514 "crit": LOG_CRIT,
515 "critical": LOG_CRIT,
516 "debug": LOG_DEBUG,
517 "emerg": LOG_EMERG,
518 "err": LOG_ERR,
519 "error": LOG_ERR,
520 "info": LOG_INFO,
521 "notice": LOG_NOTICE,
522 "panic": LOG_EMERG,
523 "warn": LOG_WARNING,
524 "warning": LOG_WARNING,
525 }
526
527 facility_names = {
528 "auth": LOG_AUTH,
529 "authpriv": LOG_AUTHPRIV,
530 "cron": LOG_CRON,
531 "daemon": LOG_DAEMON,
532 "kern": LOG_KERN,
533 "lpr": LOG_LPR,
534 "mail": LOG_MAIL,
535 "news": LOG_NEWS,
536 "security": LOG_AUTH,
537 "syslog": LOG_SYSLOG,
538 "user": LOG_USER,
539 "uucp": LOG_UUCP,
540 "local0": LOG_LOCAL0,
541 "local1": LOG_LOCAL1,
542 "local2": LOG_LOCAL2,
543 "local3": LOG_LOCAL3,
544 "local4": LOG_LOCAL4,
545 "local5": LOG_LOCAL5,
546 "local6": LOG_LOCAL6,
547 "local7": LOG_LOCAL7,
548 }
549
551 """
552 Initialize a handler.
553
554 If address is specified as a string, UNIX socket is used.
555 If facility is not specified, LOG_USER is used.
556 """
557 logging.Handler.__init__(self)
558
559 self.address = address
560 self.facility = facility
561 if type(address) == types.StringType:
562 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
563
564 try:
565 self.socket.connect(address)
566 except socket.error:
567 self.socket.close()
568 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
569 self.socket.connect(address)
570 self.unixsocket = 1
571 else:
572 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
573 self.unixsocket = 0
574
575 self.formatter = None
576
577
578
579
580
581 log_format_string = '<%d>%s\000'
582
584 """
585 Encode the facility and priority. You can pass in strings or
586 integers - if strings are passed, the facility_names and
587 priority_names mapping dictionaries are used to convert them to
588 integers.
589 """
590 if type(facility) == types.StringType:
591 facility = self.facility_names[facility]
592 if type(priority) == types.StringType:
593 priority = self.priority_names[priority]
594 return (facility << 3) | priority
595
603
604 - def emit(self, record):
605 """
606 Emit a record.
607
608 The record is formatted, and then sent to the syslog server. If
609 exception information is present, it is NOT sent to the server.
610 """
611 msg = self.format(record)
612 """
613 We need to convert record level to lowercase, maybe this will
614 change in the future.
615 """
616 msg = self.log_format_string % (
617 self.encodePriority(self.facility,
618 string.lower(record.levelname)),
619 msg)
620 try:
621 if self.unixsocket:
622 self.socket.send(msg)
623 else:
624 self.socket.sendto(msg, self.address)
625 except:
626 self.handleError(record)
627
629 """
630 A handler class which sends an SMTP email for each logging event.
631 """
632 - def __init__(self, mailhost, fromaddr, toaddrs, subject):
633 """
634 Initialize the handler.
635
636 Initialize the instance with the from and to addresses and subject
637 line of the email. To specify a non-standard SMTP port, use the
638 (host, port) tuple format for the mailhost argument.
639 """
640 logging.Handler.__init__(self)
641 if type(mailhost) == types.TupleType:
642 host, port = mailhost
643 self.mailhost = host
644 self.mailport = port
645 else:
646 self.mailhost = mailhost
647 self.mailport = None
648 self.fromaddr = fromaddr
649 if type(toaddrs) == types.StringType:
650 toaddrs = [toaddrs]
651 self.toaddrs = toaddrs
652 self.subject = subject
653
655 """
656 Determine the subject for the email.
657
658 If you want to specify a subject line which is record-dependent,
659 override this method.
660 """
661 return self.subject
662
663 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
664
665 monthname = [None,
666 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
667 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
668
670 """
671 Return the current date and time formatted for a MIME header.
672 Needed for Python 1.5.2 (no email package available)
673 """
674 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())
675 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
676 self.weekdayname[wd],
677 day, self.monthname[month], year,
678 hh, mm, ss)
679 return s
680
681 - def emit(self, record):
682 """
683 Emit a record.
684
685 Format the record and send it to the specified addressees.
686 """
687 try:
688 import smtplib
689 try:
690 from email.Utils import formatdate
691 except:
692 formatdate = self.date_time
693 port = self.mailport
694 if not port:
695 port = smtplib.SMTP_PORT
696 smtp = smtplib.SMTP(self.mailhost, port)
697 msg = self.format(record)
698 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
699 self.fromaddr,
700 string.join(self.toaddrs, ","),
701 self.getSubject(record),
702 formatdate(), msg)
703 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
704 smtp.quit()
705 except:
706 self.handleError(record)
707
709 """
710 A handler class which sends events to the NT Event Log. Adds a
711 registry entry for the specified application name. If no dllname is
712 provided, win32service.pyd (which contains some basic message
713 placeholders) is used. Note that use of these placeholders will make
714 your event logs big, as the entire message source is held in the log.
715 If you want slimmer logs, you have to pass in the name of your own DLL
716 which contains the message definitions you want to use in the event log.
717 """
718 - def __init__(self, appname, dllname=None, logtype="Application"):
719 logging.Handler.__init__(self)
720 try:
721 import win32evtlogutil, win32evtlog
722 self.appname = appname
723 self._welu = win32evtlogutil
724 if not dllname:
725 dllname = os.path.split(self._welu.__file__)
726 dllname = os.path.split(dllname[0])
727 dllname = os.path.join(dllname[0], r'win32service.pyd')
728 self.dllname = dllname
729 self.logtype = logtype
730 self._welu.AddSourceToRegistry(appname, dllname, logtype)
731 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
732 self.typemap = {
733 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
734 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
735 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
736 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
737 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
738 }
739 except ImportError:
740 print "The Python Win32 extensions for NT (service, event "\
741 "logging) appear not to be available."
742 self._welu = None
743
745 """
746 Return the message ID for the event record. If you are using your
747 own messages, you could do this by having the msg passed to the
748 logger being an ID rather than a formatting string. Then, in here,
749 you could use a dictionary lookup to get the message ID. This
750 version returns 1, which is the base message ID in win32service.pyd.
751 """
752 return 1
753
755 """
756 Return the event category for the record.
757
758 Override this if you want to specify your own categories. This version
759 returns 0.
760 """
761 return 0
762
764 """
765 Return the event type for the record.
766
767 Override this if you want to specify your own types. This version does
768 a mapping using the handler's typemap attribute, which is set up in
769 __init__() to a dictionary which contains mappings for DEBUG, INFO,
770 WARNING, ERROR and CRITICAL. If you are using your own levels you will
771 either need to override this method or place a suitable dictionary in
772 the handler's typemap attribute.
773 """
774 return self.typemap.get(record.levelno, self.deftype)
775
776 - def emit(self, record):
777 """
778 Emit a record.
779
780 Determine the message ID, event category and event type. Then
781 log the message in the NT event log.
782 """
783 if self._welu:
784 try:
785 id = self.getMessageID(record)
786 cat = self.getEventCategory(record)
787 type = self.getEventType(record)
788 msg = self.format(record)
789 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
790 except:
791 self.handleError(record)
792
794 """
795 Clean up this handler.
796
797 You can remove the application name from the registry as a
798 source of event log entries. However, if you do this, you will
799 not be able to see the events as you intended in the Event Log
800 Viewer - it needs to be able to access the registry to get the
801 DLL name.
802 """
803
804 logging.Handler.close(self)
805
807 """
808 A class which sends records to a Web server, using either GET or
809 POST semantics.
810 """
811 - def __init__(self, host, url, method="GET"):
812 """
813 Initialize the instance with the host, the request URL, and the method
814 ("GET" or "POST")
815 """
816 logging.Handler.__init__(self)
817 method = string.upper(method)
818 if method not in ["GET", "POST"]:
819 raise ValueError, "method must be GET or POST"
820 self.host = host
821 self.url = url
822 self.method = method
823
825 """
826 Default implementation of mapping the log record into a dict
827 that is sent as the CGI data. Overwrite in your class.
828 Contributed by Franz Glasner.
829 """
830 return record.__dict__
831
832 - def emit(self, record):
833 """
834 Emit a record.
835
836 Send the record to the Web server as an URL-encoded dictionary
837 """
838 try:
839 import httplib, urllib
840 h = httplib.HTTP(self.host)
841 url = self.url
842 data = urllib.urlencode(self.mapLogRecord(record))
843 if self.method == "GET":
844 if (string.find(url, '?') >= 0):
845 sep = '&'
846 else:
847 sep = '?'
848 url = url + "%c%s" % (sep, data)
849 h.putrequest(self.method, url)
850 if self.method == "POST":
851 h.putheader("Content-length", str(len(data)))
852 h.endheaders()
853 if self.method == "POST":
854 h.send(data)
855 h.getreply()
856 except:
857 self.handleError(record)
858
860 """
861 A handler class which buffers logging records in memory. Whenever each
862 record is added to the buffer, a check is made to see if the buffer should
863 be flushed. If it should, then flush() is expected to do what's needed.
864 """
866 """
867 Initialize the handler with the buffer size.
868 """
869 logging.Handler.__init__(self)
870 self.capacity = capacity
871 self.buffer = []
872
874 """
875 Should the handler flush its buffer?
876
877 Returns true if the buffer is up to capacity. This method can be
878 overridden to implement custom flushing strategies.
879 """
880 return (len(self.buffer) >= self.capacity)
881
882 - def emit(self, record):
883 """
884 Emit a record.
885
886 Append the record. If shouldFlush() tells us to, call flush() to process
887 the buffer.
888 """
889 self.buffer.append(record)
890 if self.shouldFlush(record):
891 self.flush()
892
894 """
895 Override to implement custom flushing behaviour.
896
897 This version just zaps the buffer to empty.
898 """
899 self.buffer = []
900
902 """
903 Close the handler.
904
905 This version just flushes and chains to the parent class' close().
906 """
907 self.flush()
908 logging.Handler.close(self)
909
911 """
912 A handler class which buffers logging records in memory, periodically
913 flushing them to a target handler. Flushing occurs whenever the buffer
914 is full, or when an event of a certain severity or greater is seen.
915 """
917 """
918 Initialize the handler with the buffer size, the level at which
919 flushing should occur and an optional target.
920
921 Note that without a target being set either here or via setTarget(),
922 a MemoryHandler is no use to anyone!
923 """
924 BufferingHandler.__init__(self, capacity)
925 self.flushLevel = flushLevel
926 self.target = target
927
929 """
930 Check for buffer full or a record at the flushLevel or higher.
931 """
932 return (len(self.buffer) >= self.capacity) or \
933 (record.levelno >= self.flushLevel)
934
936 """
937 Set the target handler for this handler.
938 """
939 self.target = target
940
942 """
943 For a MemoryHandler, flushing means just sending the buffered
944 records to the target, if there is one. Override if you want
945 different behaviour.
946 """
947 if self.target:
948 for record in self.buffer:
949 self.target.handle(record)
950 self.buffer = []
951
953 """
954 Flush, set the target to None and lose the buffer.
955 """
956 self.flush()
957 self.target = None
958 BufferingHandler.close(self)
959