blob: 778c5074b035ca095b4ee6f8e8ccc547e4336ada [file] [log] [blame]
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +00002#
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
Vinay Sajip48cfe382004-02-20 13:17:27 +000022information is not available unless 'sys._getframe()' is.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
Vinay Sajip48cfe382004-02-20 13:17:27 +000024Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000025
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
Raymond Hettingere21f6062003-11-08 11:40:03 +000099 in doRollover().
Guido van Rossum57102f82002-11-13 16:15:58 +0000100 """
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
Vinay Sajip48cfe382004-02-20 13:17:27 +0000135 self.retryTime = None
136 #
137 # Exponential backoff parameters.
138 #
139 self.retryStart = 1.0
140 self.retryMax = 30.0
141 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000142
143 def makeSocket(self):
144 """
145 A factory method which allows subclasses to define the precise
146 type of socket they want.
147 """
148 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
149 s.connect((self.host, self.port))
150 return s
151
Vinay Sajip48cfe382004-02-20 13:17:27 +0000152 def createSocket(self):
153 """
154 Try to create a socket, using an exponential backoff with
155 a max retry time. Thanks to Robert Olson for the original patch
156 (SF #815911) which has been slightly refactored.
157 """
158 now = time.time()
159 # Either retryTime is None, in which case this
160 # is the first time back after a disconnect, or
161 # we've waited long enough.
162 if self.retryTime is None:
163 attempt = 1
164 else:
165 attempt = (now >= self.retryTime)
166 if attempt:
167 try:
168 self.sock = self.makeSocket()
169 self.retryTime = None # next time, no delay before trying
170 except:
171 #Creation failed, so set the retry time and return.
172 if self.retryTime is None:
173 self.retryPeriod = self.retryStart
174 else:
175 self.retryPeriod = self.retryPeriod * self.retryFactor
176 if self.retryPeriod > self.retryMax:
177 self.retryPeriod = self.retryMax
178 self.retryTime = now + self.retryPeriod
179
Guido van Rossum57102f82002-11-13 16:15:58 +0000180 def send(self, s):
181 """
182 Send a pickled string to the socket.
183
184 This function allows for partial sends which can happen when the
185 network is busy.
186 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000187 if self.sock is None:
188 self.createSocket()
189 #self.sock can be None either because we haven't reached the retry
190 #time yet, or because we have reached the retry time and retried,
191 #but are still unable to connect.
192 if self.sock:
193 try:
194 if hasattr(self.sock, "sendall"):
195 self.sock.sendall(s)
196 else:
197 sentsofar = 0
198 left = len(s)
199 while left > 0:
200 sent = self.sock.send(s[sentsofar:])
201 sentsofar = sentsofar + sent
202 left = left - sent
203 except socket.error:
204 self.sock.close()
205 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000206
207 def makePickle(self, record):
208 """
209 Pickles the record in binary format with a length prefix, and
210 returns it ready for transmission across the socket.
211 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000212 ei = record.exc_info
213 if ei:
214 dummy = self.format(record) # just to get traceback text into record.exc_text
215 record.exc_info = None # to avoid Unpickleable error
Guido van Rossum57102f82002-11-13 16:15:58 +0000216 s = cPickle.dumps(record.__dict__, 1)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000217 if ei:
218 record.exc_info = ei # for next handler
Guido van Rossum57102f82002-11-13 16:15:58 +0000219 slen = struct.pack(">L", len(s))
220 return slen + s
221
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000222 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000223 """
224 Handle an error during logging.
225
226 An error has occurred during logging. Most likely cause -
227 connection lost. Close the socket so that we can retry on the
228 next event.
229 """
230 if self.closeOnError and self.sock:
231 self.sock.close()
232 self.sock = None #try to reconnect next time
233 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000234 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000235
236 def emit(self, record):
237 """
238 Emit a record.
239
240 Pickles the record and writes it to the socket in binary format.
241 If there is an error with the socket, silently drop the packet.
242 If there was a problem with the socket, re-establishes the
243 socket.
244 """
245 try:
246 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000247 self.send(s)
248 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000249 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000250
251 def close(self):
252 """
253 Closes the socket.
254 """
255 if self.sock:
256 self.sock.close()
257 self.sock = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000258 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000259
260class DatagramHandler(SocketHandler):
261 """
262 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000263 a datagram socket. The pickle which is sent is that of the LogRecord's
264 attribute dictionary (__dict__), so that the receiver does not need to
265 have the logging module installed in order to process the logging event.
266
267 To unpickle the record at the receiving end into a LogRecord, use the
268 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000269
270 """
271 def __init__(self, host, port):
272 """
273 Initializes the handler with a specific host address and port.
274 """
275 SocketHandler.__init__(self, host, port)
276 self.closeOnError = 0
277
278 def makeSocket(self):
279 """
280 The factory method of SocketHandler is here overridden to create
281 a UDP socket (SOCK_DGRAM).
282 """
283 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
284 return s
285
286 def send(self, s):
287 """
288 Send a pickled string to a socket.
289
290 This function no longer allows for partial sends which can happen
291 when the network is busy - UDP does not guarantee delivery and
292 can deliver packets out of sequence.
293 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000294 self.sock.sendto(s, (self.host, self.port))
295
296class SysLogHandler(logging.Handler):
297 """
298 A handler class which sends formatted logging records to a syslog
299 server. Based on Sam Rushing's syslog module:
300 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
301 Contributed by Nicolas Untz (after which minor refactoring changes
302 have been made).
303 """
304
305 # from <linux/sys/syslog.h>:
306 # ======================================================================
307 # priorities/facilities are encoded into a single 32-bit quantity, where
308 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
309 # facility (0-big number). Both the priorities and the facilities map
310 # roughly one-to-one to strings in the syslogd(8) source code. This
311 # mapping is included in this file.
312 #
313 # priorities (these are ordered)
314
315 LOG_EMERG = 0 # system is unusable
316 LOG_ALERT = 1 # action must be taken immediately
317 LOG_CRIT = 2 # critical conditions
318 LOG_ERR = 3 # error conditions
319 LOG_WARNING = 4 # warning conditions
320 LOG_NOTICE = 5 # normal but significant condition
321 LOG_INFO = 6 # informational
322 LOG_DEBUG = 7 # debug-level messages
323
324 # facility codes
325 LOG_KERN = 0 # kernel messages
326 LOG_USER = 1 # random user-level messages
327 LOG_MAIL = 2 # mail system
328 LOG_DAEMON = 3 # system daemons
329 LOG_AUTH = 4 # security/authorization messages
330 LOG_SYSLOG = 5 # messages generated internally by syslogd
331 LOG_LPR = 6 # line printer subsystem
332 LOG_NEWS = 7 # network news subsystem
333 LOG_UUCP = 8 # UUCP subsystem
334 LOG_CRON = 9 # clock daemon
335 LOG_AUTHPRIV = 10 # security/authorization messages (private)
336
337 # other codes through 15 reserved for system use
338 LOG_LOCAL0 = 16 # reserved for local use
339 LOG_LOCAL1 = 17 # reserved for local use
340 LOG_LOCAL2 = 18 # reserved for local use
341 LOG_LOCAL3 = 19 # reserved for local use
342 LOG_LOCAL4 = 20 # reserved for local use
343 LOG_LOCAL5 = 21 # reserved for local use
344 LOG_LOCAL6 = 22 # reserved for local use
345 LOG_LOCAL7 = 23 # reserved for local use
346
347 priority_names = {
348 "alert": LOG_ALERT,
349 "crit": LOG_CRIT,
350 "critical": LOG_CRIT,
351 "debug": LOG_DEBUG,
352 "emerg": LOG_EMERG,
353 "err": LOG_ERR,
354 "error": LOG_ERR, # DEPRECATED
355 "info": LOG_INFO,
356 "notice": LOG_NOTICE,
357 "panic": LOG_EMERG, # DEPRECATED
358 "warn": LOG_WARNING, # DEPRECATED
359 "warning": LOG_WARNING,
360 }
361
362 facility_names = {
363 "auth": LOG_AUTH,
364 "authpriv": LOG_AUTHPRIV,
365 "cron": LOG_CRON,
366 "daemon": LOG_DAEMON,
367 "kern": LOG_KERN,
368 "lpr": LOG_LPR,
369 "mail": LOG_MAIL,
370 "news": LOG_NEWS,
371 "security": LOG_AUTH, # DEPRECATED
372 "syslog": LOG_SYSLOG,
373 "user": LOG_USER,
374 "uucp": LOG_UUCP,
375 "local0": LOG_LOCAL0,
376 "local1": LOG_LOCAL1,
377 "local2": LOG_LOCAL2,
378 "local3": LOG_LOCAL3,
379 "local4": LOG_LOCAL4,
380 "local5": LOG_LOCAL5,
381 "local6": LOG_LOCAL6,
382 "local7": LOG_LOCAL7,
383 }
384
385 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
386 """
387 Initialize a handler.
388
389 If address is specified as a string, UNIX socket is used.
390 If facility is not specified, LOG_USER is used.
391 """
392 logging.Handler.__init__(self)
393
394 self.address = address
395 self.facility = facility
396 if type(address) == types.StringType:
Neal Norwitzd89c4062003-01-26 02:14:23 +0000397 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
Neal Norwitzf4cdb472003-01-26 16:15:24 +0000398 # syslog may require either DGRAM or STREAM sockets
399 try:
400 self.socket.connect(address)
401 except socket.error:
402 self.socket.close()
403 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000404 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000405 self.unixsocket = 1
406 else:
407 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
408 self.unixsocket = 0
409
410 self.formatter = None
411
412 # curious: when talking to the unix-domain '/dev/log' socket, a
413 # zero-terminator seems to be required. this string is placed
414 # into a class variable so that it can be overridden if
415 # necessary.
416 log_format_string = '<%d>%s\000'
417
418 def encodePriority (self, facility, priority):
419 """
420 Encode the facility and priority. You can pass in strings or
421 integers - if strings are passed, the facility_names and
422 priority_names mapping dictionaries are used to convert them to
423 integers.
424 """
425 if type(facility) == types.StringType:
426 facility = self.facility_names[facility]
427 if type(priority) == types.StringType:
428 priority = self.priority_names[priority]
429 return (facility << 3) | priority
430
431 def close (self):
432 """
433 Closes the socket.
434 """
435 if self.unixsocket:
436 self.socket.close()
Vinay Sajip48cfe382004-02-20 13:17:27 +0000437 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000438
439 def emit(self, record):
440 """
441 Emit a record.
442
443 The record is formatted, and then sent to the syslog server. If
444 exception information is present, it is NOT sent to the server.
445 """
446 msg = self.format(record)
447 """
448 We need to convert record level to lowercase, maybe this will
449 change in the future.
450 """
451 msg = self.log_format_string % (
452 self.encodePriority(self.facility,
453 string.lower(record.levelname)),
454 msg)
455 try:
456 if self.unixsocket:
457 self.socket.send(msg)
458 else:
459 self.socket.sendto(msg, self.address)
460 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000461 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000462
463class SMTPHandler(logging.Handler):
464 """
465 A handler class which sends an SMTP email for each logging event.
466 """
467 def __init__(self, mailhost, fromaddr, toaddrs, subject):
468 """
469 Initialize the handler.
470
471 Initialize the instance with the from and to addresses and subject
472 line of the email. To specify a non-standard SMTP port, use the
473 (host, port) tuple format for the mailhost argument.
474 """
475 logging.Handler.__init__(self)
476 if type(mailhost) == types.TupleType:
477 host, port = mailhost
478 self.mailhost = host
479 self.mailport = port
480 else:
481 self.mailhost = mailhost
482 self.mailport = None
483 self.fromaddr = fromaddr
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000484 if type(toaddrs) == types.StringType:
485 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000486 self.toaddrs = toaddrs
487 self.subject = subject
488
489 def getSubject(self, record):
490 """
491 Determine the subject for the email.
492
493 If you want to specify a subject line which is record-dependent,
494 override this method.
495 """
496 return self.subject
497
Neal Norwitzf297bd12003-04-23 03:49:43 +0000498 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
499
500 monthname = [None,
501 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
502 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
503
504 def date_time(self):
505 """Return the current date and time formatted for a MIME header."""
506 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())
507 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
508 self.weekdayname[wd],
509 day, self.monthname[month], year,
510 hh, mm, ss)
511 return s
512
Guido van Rossum57102f82002-11-13 16:15:58 +0000513 def emit(self, record):
514 """
515 Emit a record.
516
517 Format the record and send it to the specified addressees.
518 """
519 try:
520 import smtplib
521 port = self.mailport
522 if not port:
523 port = smtplib.SMTP_PORT
524 smtp = smtplib.SMTP(self.mailhost, port)
525 msg = self.format(record)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000526 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 +0000527 self.fromaddr,
528 string.join(self.toaddrs, ","),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000529 self.getSubject(record),
530 self.date_time(), msg)
Guido van Rossum57102f82002-11-13 16:15:58 +0000531 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
532 smtp.quit()
533 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000534 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000535
536class NTEventLogHandler(logging.Handler):
537 """
538 A handler class which sends events to the NT Event Log. Adds a
539 registry entry for the specified application name. If no dllname is
540 provided, win32service.pyd (which contains some basic message
541 placeholders) is used. Note that use of these placeholders will make
542 your event logs big, as the entire message source is held in the log.
543 If you want slimmer logs, you have to pass in the name of your own DLL
544 which contains the message definitions you want to use in the event log.
545 """
546 def __init__(self, appname, dllname=None, logtype="Application"):
547 logging.Handler.__init__(self)
548 try:
549 import win32evtlogutil, win32evtlog
550 self.appname = appname
551 self._welu = win32evtlogutil
552 if not dllname:
553 dllname = os.path.split(self._welu.__file__)
554 dllname = os.path.split(dllname[0])
555 dllname = os.path.join(dllname[0], r'win32service.pyd')
556 self.dllname = dllname
557 self.logtype = logtype
558 self._welu.AddSourceToRegistry(appname, dllname, logtype)
559 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
560 self.typemap = {
561 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
562 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000563 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000564 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
565 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
566 }
567 except ImportError:
568 print "The Python Win32 extensions for NT (service, event "\
569 "logging) appear not to be available."
570 self._welu = None
571
572 def getMessageID(self, record):
573 """
574 Return the message ID for the event record. If you are using your
575 own messages, you could do this by having the msg passed to the
576 logger being an ID rather than a formatting string. Then, in here,
577 you could use a dictionary lookup to get the message ID. This
578 version returns 1, which is the base message ID in win32service.pyd.
579 """
580 return 1
581
582 def getEventCategory(self, record):
583 """
584 Return the event category for the record.
585
586 Override this if you want to specify your own categories. This version
587 returns 0.
588 """
589 return 0
590
591 def getEventType(self, record):
592 """
593 Return the event type for the record.
594
595 Override this if you want to specify your own types. This version does
596 a mapping using the handler's typemap attribute, which is set up in
597 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000598 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000599 either need to override this method or place a suitable dictionary in
600 the handler's typemap attribute.
601 """
602 return self.typemap.get(record.levelno, self.deftype)
603
604 def emit(self, record):
605 """
606 Emit a record.
607
608 Determine the message ID, event category and event type. Then
609 log the message in the NT event log.
610 """
611 if self._welu:
612 try:
613 id = self.getMessageID(record)
614 cat = self.getEventCategory(record)
615 type = self.getEventType(record)
616 msg = self.format(record)
617 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
618 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000619 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000620
621 def close(self):
622 """
623 Clean up this handler.
624
625 You can remove the application name from the registry as a
626 source of event log entries. However, if you do this, you will
627 not be able to see the events as you intended in the Event Log
628 Viewer - it needs to be able to access the registry to get the
629 DLL name.
630 """
631 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000632 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000633
634class HTTPHandler(logging.Handler):
635 """
636 A class which sends records to a Web server, using either GET or
637 POST semantics.
638 """
639 def __init__(self, host, url, method="GET"):
640 """
641 Initialize the instance with the host, the request URL, and the method
642 ("GET" or "POST")
643 """
644 logging.Handler.__init__(self)
645 method = string.upper(method)
646 if method not in ["GET", "POST"]:
647 raise ValueError, "method must be GET or POST"
648 self.host = host
649 self.url = url
650 self.method = method
651
Neal Norwitzf297bd12003-04-23 03:49:43 +0000652 def mapLogRecord(self, record):
653 """
654 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +0000655 that is sent as the CGI data. Overwrite in your class.
Neal Norwitzf297bd12003-04-23 03:49:43 +0000656 Contributed by Franz Glasner.
657 """
658 return record.__dict__
659
Guido van Rossum57102f82002-11-13 16:15:58 +0000660 def emit(self, record):
661 """
662 Emit a record.
663
664 Send the record to the Web server as an URL-encoded dictionary
665 """
666 try:
667 import httplib, urllib
668 h = httplib.HTTP(self.host)
669 url = self.url
Neal Norwitzf297bd12003-04-23 03:49:43 +0000670 data = urllib.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +0000671 if self.method == "GET":
672 if (string.find(url, '?') >= 0):
673 sep = '&'
674 else:
675 sep = '?'
676 url = url + "%c%s" % (sep, data)
677 h.putrequest(self.method, url)
678 if self.method == "POST":
679 h.putheader("Content-length", str(len(data)))
680 h.endheaders()
681 if self.method == "POST":
682 h.send(data)
683 h.getreply() #can't do anything with the result
684 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000685 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000686
687class BufferingHandler(logging.Handler):
688 """
689 A handler class which buffers logging records in memory. Whenever each
690 record is added to the buffer, a check is made to see if the buffer should
691 be flushed. If it should, then flush() is expected to do what's needed.
692 """
693 def __init__(self, capacity):
694 """
695 Initialize the handler with the buffer size.
696 """
697 logging.Handler.__init__(self)
698 self.capacity = capacity
699 self.buffer = []
700
701 def shouldFlush(self, record):
702 """
703 Should the handler flush its buffer?
704
705 Returns true if the buffer is up to capacity. This method can be
706 overridden to implement custom flushing strategies.
707 """
708 return (len(self.buffer) >= self.capacity)
709
710 def emit(self, record):
711 """
712 Emit a record.
713
714 Append the record. If shouldFlush() tells us to, call flush() to process
715 the buffer.
716 """
717 self.buffer.append(record)
718 if self.shouldFlush(record):
719 self.flush()
720
721 def flush(self):
722 """
723 Override to implement custom flushing behaviour.
724
725 This version just zaps the buffer to empty.
726 """
727 self.buffer = []
728
Vinay Sajipf42d95e2004-02-21 22:14:34 +0000729 def close(self):
730 """
731 Close the handler.
732
733 This version just flushes and chains to the parent class' close().
734 """
735 self.flush()
736 logging.Handler.close(self)
737
Guido van Rossum57102f82002-11-13 16:15:58 +0000738class MemoryHandler(BufferingHandler):
739 """
740 A handler class which buffers logging records in memory, periodically
741 flushing them to a target handler. Flushing occurs whenever the buffer
742 is full, or when an event of a certain severity or greater is seen.
743 """
744 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
745 """
746 Initialize the handler with the buffer size, the level at which
747 flushing should occur and an optional target.
748
749 Note that without a target being set either here or via setTarget(),
750 a MemoryHandler is no use to anyone!
751 """
752 BufferingHandler.__init__(self, capacity)
753 self.flushLevel = flushLevel
754 self.target = target
755
756 def shouldFlush(self, record):
757 """
758 Check for buffer full or a record at the flushLevel or higher.
759 """
760 return (len(self.buffer) >= self.capacity) or \
761 (record.levelno >= self.flushLevel)
762
763 def setTarget(self, target):
764 """
765 Set the target handler for this handler.
766 """
767 self.target = target
768
769 def flush(self):
770 """
771 For a MemoryHandler, flushing means just sending the buffered
772 records to the target, if there is one. Override if you want
773 different behaviour.
774 """
775 if self.target:
776 for record in self.buffer:
777 self.target.handle(record)
778 self.buffer = []
779
780 def close(self):
781 """
782 Flush, set the target to None and lose the buffer.
783 """
784 self.flush()
785 self.target = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000786 BufferingHandler.close(self)