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