blob: 38711f8d4a3bbd45461870ff889e980cf4f0344e [file] [log] [blame]
Guido van Rossum57102f82002-11-13 16:15:58 +00001# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
2#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose and without fee is hereby granted,
5# provided that the above copyright notice appear in all copies and that
6# both that copyright notice and this permission notice appear in
7# supporting documentation, and that the name of Vinay Sajip
8# not be used in advertising or publicity pertaining to distribution
9# of the software without specific, written prior permission.
10# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
11# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
13# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Guido van Rossum57102f82002-11-13 16:15:58 +000016
17"""
18Logging package for Python. Based on PEP 282 and comments thereto in
19comp.lang.python, and influenced by Apache's log4j system.
20
21Should work under Python versions >= 1.5.2, except that source line
22information is not available unless 'inspect' is.
23
24Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
25
26To use, simply 'import logging' and log away!
27"""
28
29import sys, logging, socket, types, os, string, cPickle, struct
30
31from SocketServer import ThreadingTCPServer, StreamRequestHandler
32
33#
34# Some constants...
35#
36
37DEFAULT_TCP_LOGGING_PORT = 9020
38DEFAULT_UDP_LOGGING_PORT = 9021
39DEFAULT_HTTP_LOGGING_PORT = 9022
40DEFAULT_SOAP_LOGGING_PORT = 9023
41SYSLOG_UDP_PORT = 514
42
43
44class RotatingFileHandler(logging.FileHandler):
45 def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
46 """
47 Open the specified file and use it as the stream for logging.
48
49 By default, the file grows indefinitely. You can specify particular
50 values of maxBytes and backupCount to allow the file to rollover at
51 a predetermined size.
52
53 Rollover occurs whenever the current log file is nearly maxBytes in
54 length. If backupCount is >= 1, the system will successively create
55 new files with the same pathname as the base file, but with extensions
56 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
57 and a base file name of "app.log", you would get "app.log",
58 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
59 written to is always "app.log" - when it gets filled up, it is closed
60 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
61 exist, then they are renamed to "app.log.2", "app.log.3" etc.
62 respectively.
63
64 If maxBytes is zero, rollover never occurs.
65 """
66 logging.FileHandler.__init__(self, filename, mode)
67 self.maxBytes = maxBytes
68 self.backupCount = backupCount
69 if maxBytes > 0:
70 self.mode = "a"
71
72 def doRollover(self):
73 """
74 Do a rollover, as described in __init__().
75 """
76
77 self.stream.close()
78 if self.backupCount > 0:
79 for i in range(self.backupCount - 1, 0, -1):
80 sfn = "%s.%d" % (self.baseFilename, i)
81 dfn = "%s.%d" % (self.baseFilename, i + 1)
82 if os.path.exists(sfn):
83 #print "%s -> %s" % (sfn, dfn)
84 if os.path.exists(dfn):
85 os.remove(dfn)
86 os.rename(sfn, dfn)
87 dfn = self.baseFilename + ".1"
88 if os.path.exists(dfn):
89 os.remove(dfn)
90 os.rename(self.baseFilename, dfn)
91 #print "%s -> %s" % (self.baseFilename, dfn)
92 self.stream = open(self.baseFilename, "w")
93
94 def emit(self, record):
95 """
96 Emit a record.
97
98 Output the record to the file, catering for rollover as described
99 in setRollover().
100 """
101 if self.maxBytes > 0: # are we rolling over?
102 msg = "%s\n" % self.format(record)
103 #print msg
104 if self.stream.tell() + len(msg) >= self.maxBytes:
105 self.doRollover()
106 logging.FileHandler.emit(self, record)
107
108
109class SocketHandler(logging.Handler):
110 """
111 A handler class which writes logging records, in pickle format, to
112 a streaming socket. The socket is kept open across logging calls.
113 If the peer resets it, an attempt is made to reconnect on the next call.
114 Note that the very simple wire protocol used means that packet sizes
115 are expected to be encodable within 16 bits (i.e. < 32767 bytes).
116 """
117
118 def __init__(self, host, port):
119 """
120 Initializes the handler with a specific host address and port.
121
122 The attribute 'closeOnError' is set to 1 - which means that if
123 a socket error occurs, the socket is silently closed and then
124 reopened on the next logging call.
125 """
126 logging.Handler.__init__(self)
127 self.host = host
128 self.port = port
129 self.sock = None
130 self.closeOnError = 0
131
132 def makeSocket(self):
133 """
134 A factory method which allows subclasses to define the precise
135 type of socket they want.
136 """
137 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
138 s.connect((self.host, self.port))
139 return s
140
141 def send(self, s):
142 """
143 Send a pickled string to the socket.
144
145 This function allows for partial sends which can happen when the
146 network is busy.
147 """
148 v = sys.version_info
149 if v[0] >= 2 and v[1] >= 2:
150 self.sock.sendall(s)
151 else:
152 sentsofar = 0
153 left = len(s)
154 while left > 0:
155 sent = self.sock.send(s[sentsofar:])
156 sentsofar = sentsofar + sent
157 left = left - sent
158
159 def makePickle(self, record):
160 """
161 Pickles the record in binary format with a length prefix, and
162 returns it ready for transmission across the socket.
163 """
164 s = cPickle.dumps(record.__dict__, 1)
165 #n = len(s)
166 #slen = "%c%c" % ((n >> 8) & 0xFF, n & 0xFF)
167 slen = struct.pack(">L", len(s))
168 return slen + s
169
170 def handleError(self):
171 """
172 Handle an error during logging.
173
174 An error has occurred during logging. Most likely cause -
175 connection lost. Close the socket so that we can retry on the
176 next event.
177 """
178 if self.closeOnError and self.sock:
179 self.sock.close()
180 self.sock = None #try to reconnect next time
181 else:
182 logging.Handler.handleError(self)
183
184 def emit(self, record):
185 """
186 Emit a record.
187
188 Pickles the record and writes it to the socket in binary format.
189 If there is an error with the socket, silently drop the packet.
190 If there was a problem with the socket, re-establishes the
191 socket.
192 """
193 try:
194 s = self.makePickle(record)
195 if not self.sock:
196 self.sock = self.makeSocket()
197 self.send(s)
198 except:
199 self.handleError()
200
201 def close(self):
202 """
203 Closes the socket.
204 """
205 if self.sock:
206 self.sock.close()
207 self.sock = None
208
209class DatagramHandler(SocketHandler):
210 """
211 A handler class which writes logging records, in pickle format, to
212 a datagram socket. Note that the very simple wire protocol used means
213 that packet sizes are expected to be encodable within 16 bits
214 (i.e. < 32767 bytes).
215
216 """
217 def __init__(self, host, port):
218 """
219 Initializes the handler with a specific host address and port.
220 """
221 SocketHandler.__init__(self, host, port)
222 self.closeOnError = 0
223
224 def makeSocket(self):
225 """
226 The factory method of SocketHandler is here overridden to create
227 a UDP socket (SOCK_DGRAM).
228 """
229 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
230 return s
231
232 def send(self, s):
233 """
234 Send a pickled string to a socket.
235
236 This function no longer allows for partial sends which can happen
237 when the network is busy - UDP does not guarantee delivery and
238 can deliver packets out of sequence.
239 """
240 #old code
241 #sentsofar = 0
242 #left = len(s)
243 #addr = (self.host, self.port)
244 #while left > 0:
245 # sent = self.sock.sendto(s[sentsofar:], addr)
246 # sentsofar = sentsofar + sent
247 # left = left - sent
248 self.sock.sendto(s, (self.host, self.port))
249
250class SysLogHandler(logging.Handler):
251 """
252 A handler class which sends formatted logging records to a syslog
253 server. Based on Sam Rushing's syslog module:
254 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
255 Contributed by Nicolas Untz (after which minor refactoring changes
256 have been made).
257 """
258
259 # from <linux/sys/syslog.h>:
260 # ======================================================================
261 # priorities/facilities are encoded into a single 32-bit quantity, where
262 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
263 # facility (0-big number). Both the priorities and the facilities map
264 # roughly one-to-one to strings in the syslogd(8) source code. This
265 # mapping is included in this file.
266 #
267 # priorities (these are ordered)
268
269 LOG_EMERG = 0 # system is unusable
270 LOG_ALERT = 1 # action must be taken immediately
271 LOG_CRIT = 2 # critical conditions
272 LOG_ERR = 3 # error conditions
273 LOG_WARNING = 4 # warning conditions
274 LOG_NOTICE = 5 # normal but significant condition
275 LOG_INFO = 6 # informational
276 LOG_DEBUG = 7 # debug-level messages
277
278 # facility codes
279 LOG_KERN = 0 # kernel messages
280 LOG_USER = 1 # random user-level messages
281 LOG_MAIL = 2 # mail system
282 LOG_DAEMON = 3 # system daemons
283 LOG_AUTH = 4 # security/authorization messages
284 LOG_SYSLOG = 5 # messages generated internally by syslogd
285 LOG_LPR = 6 # line printer subsystem
286 LOG_NEWS = 7 # network news subsystem
287 LOG_UUCP = 8 # UUCP subsystem
288 LOG_CRON = 9 # clock daemon
289 LOG_AUTHPRIV = 10 # security/authorization messages (private)
290
291 # other codes through 15 reserved for system use
292 LOG_LOCAL0 = 16 # reserved for local use
293 LOG_LOCAL1 = 17 # reserved for local use
294 LOG_LOCAL2 = 18 # reserved for local use
295 LOG_LOCAL3 = 19 # reserved for local use
296 LOG_LOCAL4 = 20 # reserved for local use
297 LOG_LOCAL5 = 21 # reserved for local use
298 LOG_LOCAL6 = 22 # reserved for local use
299 LOG_LOCAL7 = 23 # reserved for local use
300
301 priority_names = {
302 "alert": LOG_ALERT,
303 "crit": LOG_CRIT,
304 "critical": LOG_CRIT,
305 "debug": LOG_DEBUG,
306 "emerg": LOG_EMERG,
307 "err": LOG_ERR,
308 "error": LOG_ERR, # DEPRECATED
309 "info": LOG_INFO,
310 "notice": LOG_NOTICE,
311 "panic": LOG_EMERG, # DEPRECATED
312 "warn": LOG_WARNING, # DEPRECATED
313 "warning": LOG_WARNING,
314 }
315
316 facility_names = {
317 "auth": LOG_AUTH,
318 "authpriv": LOG_AUTHPRIV,
319 "cron": LOG_CRON,
320 "daemon": LOG_DAEMON,
321 "kern": LOG_KERN,
322 "lpr": LOG_LPR,
323 "mail": LOG_MAIL,
324 "news": LOG_NEWS,
325 "security": LOG_AUTH, # DEPRECATED
326 "syslog": LOG_SYSLOG,
327 "user": LOG_USER,
328 "uucp": LOG_UUCP,
329 "local0": LOG_LOCAL0,
330 "local1": LOG_LOCAL1,
331 "local2": LOG_LOCAL2,
332 "local3": LOG_LOCAL3,
333 "local4": LOG_LOCAL4,
334 "local5": LOG_LOCAL5,
335 "local6": LOG_LOCAL6,
336 "local7": LOG_LOCAL7,
337 }
338
339 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
340 """
341 Initialize a handler.
342
343 If address is specified as a string, UNIX socket is used.
344 If facility is not specified, LOG_USER is used.
345 """
346 logging.Handler.__init__(self)
347
348 self.address = address
349 self.facility = facility
350 if type(address) == types.StringType:
Neal Norwitzd89c4062003-01-26 02:14:23 +0000351 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
Guido van Rossum57102f82002-11-13 16:15:58 +0000352 self.socket.connect(address)
353 self.unixsocket = 1
354 else:
355 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
356 self.unixsocket = 0
357
358 self.formatter = None
359
360 # curious: when talking to the unix-domain '/dev/log' socket, a
361 # zero-terminator seems to be required. this string is placed
362 # into a class variable so that it can be overridden if
363 # necessary.
364 log_format_string = '<%d>%s\000'
365
366 def encodePriority (self, facility, priority):
367 """
368 Encode the facility and priority. You can pass in strings or
369 integers - if strings are passed, the facility_names and
370 priority_names mapping dictionaries are used to convert them to
371 integers.
372 """
373 if type(facility) == types.StringType:
374 facility = self.facility_names[facility]
375 if type(priority) == types.StringType:
376 priority = self.priority_names[priority]
377 return (facility << 3) | priority
378
379 def close (self):
380 """
381 Closes the socket.
382 """
383 if self.unixsocket:
384 self.socket.close()
385
386 def emit(self, record):
387 """
388 Emit a record.
389
390 The record is formatted, and then sent to the syslog server. If
391 exception information is present, it is NOT sent to the server.
392 """
393 msg = self.format(record)
394 """
395 We need to convert record level to lowercase, maybe this will
396 change in the future.
397 """
398 msg = self.log_format_string % (
399 self.encodePriority(self.facility,
400 string.lower(record.levelname)),
401 msg)
402 try:
403 if self.unixsocket:
404 self.socket.send(msg)
405 else:
406 self.socket.sendto(msg, self.address)
407 except:
408 self.handleError()
409
410class SMTPHandler(logging.Handler):
411 """
412 A handler class which sends an SMTP email for each logging event.
413 """
414 def __init__(self, mailhost, fromaddr, toaddrs, subject):
415 """
416 Initialize the handler.
417
418 Initialize the instance with the from and to addresses and subject
419 line of the email. To specify a non-standard SMTP port, use the
420 (host, port) tuple format for the mailhost argument.
421 """
422 logging.Handler.__init__(self)
423 if type(mailhost) == types.TupleType:
424 host, port = mailhost
425 self.mailhost = host
426 self.mailport = port
427 else:
428 self.mailhost = mailhost
429 self.mailport = None
430 self.fromaddr = fromaddr
431 self.toaddrs = toaddrs
432 self.subject = subject
433
434 def getSubject(self, record):
435 """
436 Determine the subject for the email.
437
438 If you want to specify a subject line which is record-dependent,
439 override this method.
440 """
441 return self.subject
442
443 def emit(self, record):
444 """
445 Emit a record.
446
447 Format the record and send it to the specified addressees.
448 """
449 try:
450 import smtplib
451 port = self.mailport
452 if not port:
453 port = smtplib.SMTP_PORT
454 smtp = smtplib.SMTP(self.mailhost, port)
455 msg = self.format(record)
456 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (
457 self.fromaddr,
458 string.join(self.toaddrs, ","),
459 self.getSubject(record), msg
460 )
461 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
462 smtp.quit()
463 except:
464 self.handleError()
465
466class NTEventLogHandler(logging.Handler):
467 """
468 A handler class which sends events to the NT Event Log. Adds a
469 registry entry for the specified application name. If no dllname is
470 provided, win32service.pyd (which contains some basic message
471 placeholders) is used. Note that use of these placeholders will make
472 your event logs big, as the entire message source is held in the log.
473 If you want slimmer logs, you have to pass in the name of your own DLL
474 which contains the message definitions you want to use in the event log.
475 """
476 def __init__(self, appname, dllname=None, logtype="Application"):
477 logging.Handler.__init__(self)
478 try:
479 import win32evtlogutil, win32evtlog
480 self.appname = appname
481 self._welu = win32evtlogutil
482 if not dllname:
483 dllname = os.path.split(self._welu.__file__)
484 dllname = os.path.split(dllname[0])
485 dllname = os.path.join(dllname[0], r'win32service.pyd')
486 self.dllname = dllname
487 self.logtype = logtype
488 self._welu.AddSourceToRegistry(appname, dllname, logtype)
489 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
490 self.typemap = {
491 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
492 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
493 logging.WARN : win32evtlog.EVENTLOG_WARNING_TYPE,
494 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
495 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
496 }
497 except ImportError:
498 print "The Python Win32 extensions for NT (service, event "\
499 "logging) appear not to be available."
500 self._welu = None
501
502 def getMessageID(self, record):
503 """
504 Return the message ID for the event record. If you are using your
505 own messages, you could do this by having the msg passed to the
506 logger being an ID rather than a formatting string. Then, in here,
507 you could use a dictionary lookup to get the message ID. This
508 version returns 1, which is the base message ID in win32service.pyd.
509 """
510 return 1
511
512 def getEventCategory(self, record):
513 """
514 Return the event category for the record.
515
516 Override this if you want to specify your own categories. This version
517 returns 0.
518 """
519 return 0
520
521 def getEventType(self, record):
522 """
523 Return the event type for the record.
524
525 Override this if you want to specify your own types. This version does
526 a mapping using the handler's typemap attribute, which is set up in
527 __init__() to a dictionary which contains mappings for DEBUG, INFO,
528 WARN, ERROR and CRITICAL. If you are using your own levels you will
529 either need to override this method or place a suitable dictionary in
530 the handler's typemap attribute.
531 """
532 return self.typemap.get(record.levelno, self.deftype)
533
534 def emit(self, record):
535 """
536 Emit a record.
537
538 Determine the message ID, event category and event type. Then
539 log the message in the NT event log.
540 """
541 if self._welu:
542 try:
543 id = self.getMessageID(record)
544 cat = self.getEventCategory(record)
545 type = self.getEventType(record)
546 msg = self.format(record)
547 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
548 except:
549 self.handleError()
550
551 def close(self):
552 """
553 Clean up this handler.
554
555 You can remove the application name from the registry as a
556 source of event log entries. However, if you do this, you will
557 not be able to see the events as you intended in the Event Log
558 Viewer - it needs to be able to access the registry to get the
559 DLL name.
560 """
561 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
562 pass
563
564class HTTPHandler(logging.Handler):
565 """
566 A class which sends records to a Web server, using either GET or
567 POST semantics.
568 """
569 def __init__(self, host, url, method="GET"):
570 """
571 Initialize the instance with the host, the request URL, and the method
572 ("GET" or "POST")
573 """
574 logging.Handler.__init__(self)
575 method = string.upper(method)
576 if method not in ["GET", "POST"]:
577 raise ValueError, "method must be GET or POST"
578 self.host = host
579 self.url = url
580 self.method = method
581
582 def emit(self, record):
583 """
584 Emit a record.
585
586 Send the record to the Web server as an URL-encoded dictionary
587 """
588 try:
589 import httplib, urllib
590 h = httplib.HTTP(self.host)
591 url = self.url
592 data = urllib.urlencode(record.__dict__)
593 if self.method == "GET":
594 if (string.find(url, '?') >= 0):
595 sep = '&'
596 else:
597 sep = '?'
598 url = url + "%c%s" % (sep, data)
599 h.putrequest(self.method, url)
600 if self.method == "POST":
601 h.putheader("Content-length", str(len(data)))
602 h.endheaders()
603 if self.method == "POST":
604 h.send(data)
605 h.getreply() #can't do anything with the result
606 except:
607 self.handleError()
608
609class BufferingHandler(logging.Handler):
610 """
611 A handler class which buffers logging records in memory. Whenever each
612 record is added to the buffer, a check is made to see if the buffer should
613 be flushed. If it should, then flush() is expected to do what's needed.
614 """
615 def __init__(self, capacity):
616 """
617 Initialize the handler with the buffer size.
618 """
619 logging.Handler.__init__(self)
620 self.capacity = capacity
621 self.buffer = []
622
623 def shouldFlush(self, record):
624 """
625 Should the handler flush its buffer?
626
627 Returns true if the buffer is up to capacity. This method can be
628 overridden to implement custom flushing strategies.
629 """
630 return (len(self.buffer) >= self.capacity)
631
632 def emit(self, record):
633 """
634 Emit a record.
635
636 Append the record. If shouldFlush() tells us to, call flush() to process
637 the buffer.
638 """
639 self.buffer.append(record)
640 if self.shouldFlush(record):
641 self.flush()
642
643 def flush(self):
644 """
645 Override to implement custom flushing behaviour.
646
647 This version just zaps the buffer to empty.
648 """
649 self.buffer = []
650
651class MemoryHandler(BufferingHandler):
652 """
653 A handler class which buffers logging records in memory, periodically
654 flushing them to a target handler. Flushing occurs whenever the buffer
655 is full, or when an event of a certain severity or greater is seen.
656 """
657 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
658 """
659 Initialize the handler with the buffer size, the level at which
660 flushing should occur and an optional target.
661
662 Note that without a target being set either here or via setTarget(),
663 a MemoryHandler is no use to anyone!
664 """
665 BufferingHandler.__init__(self, capacity)
666 self.flushLevel = flushLevel
667 self.target = target
668
669 def shouldFlush(self, record):
670 """
671 Check for buffer full or a record at the flushLevel or higher.
672 """
673 return (len(self.buffer) >= self.capacity) or \
674 (record.levelno >= self.flushLevel)
675
676 def setTarget(self, target):
677 """
678 Set the target handler for this handler.
679 """
680 self.target = target
681
682 def flush(self):
683 """
684 For a MemoryHandler, flushing means just sending the buffered
685 records to the target, if there is one. Override if you want
686 different behaviour.
687 """
688 if self.target:
689 for record in self.buffer:
690 self.target.handle(record)
691 self.buffer = []
692
693 def close(self):
694 """
695 Flush, set the target to None and lose the buffer.
696 """
697 self.flush()
698 self.target = None
699 self.buffer = []