blob: 7ed11353399da3f5d79ea865c1df452789fb8bec [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.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000114 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.
Guido van Rossum57102f82002-11-13 16:15:58 +0000120 """
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 """
Neal Norwitzf297bd12003-04-23 03:49:43 +0000152 if hasattr(self.sock, "sendall"):
Guido van Rossum57102f82002-11-13 16:15:58 +0000153 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
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000173 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000174 """
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:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000185 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000186
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:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000202 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000203
204 def close(self):
205 """
206 Closes the socket.
207 """
208 if self.sock:
209 self.sock.close()
210 self.sock = None
211
212class DatagramHandler(SocketHandler):
213 """
214 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000215 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.
Guido van Rossum57102f82002-11-13 16:15:58 +0000221
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 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000246 self.sock.sendto(s, (self.host, self.port))
247
248class 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:
Neal Norwitzd89c4062003-01-26 02:14:23 +0000349 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
Neal Norwitzf4cdb472003-01-26 16:15:24 +0000350 # 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)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000356 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000357 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:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000412 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000413
414class 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
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000435 if type(toaddrs) == types.StringType:
436 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000437 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
Neal Norwitzf297bd12003-04-23 03:49:43 +0000449 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
Guido van Rossum57102f82002-11-13 16:15:58 +0000464 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)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000477 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 +0000478 self.fromaddr,
479 string.join(self.toaddrs, ","),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000480 self.getSubject(record),
481 self.date_time(), msg)
Guido van Rossum57102f82002-11-13 16:15:58 +0000482 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
483 smtp.quit()
484 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000485 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000486
487class 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,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000514 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000515 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,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000549 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000550 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:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000570 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000571
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
585class 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
Neal Norwitzf297bd12003-04-23 03:49:43 +0000603 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
Guido van Rossum57102f82002-11-13 16:15:58 +0000611 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
Neal Norwitzf297bd12003-04-23 03:49:43 +0000621 data = urllib.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +0000622 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:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000636 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000637
638class 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
680class 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 = []