blob: bb3fe70de477657662ebf83cae3fb718f24e3eb6 [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)
Neal Norwitzf4cdb472003-01-26 16:15:24 +0000352 # syslog may require either DGRAM or STREAM sockets
353 try:
354 self.socket.connect(address)
355 except socket.error:
356 self.socket.close()
357 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
358 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000359 self.unixsocket = 1
360 else:
361 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
362 self.unixsocket = 0
363
364 self.formatter = None
365
366 # curious: when talking to the unix-domain '/dev/log' socket, a
367 # zero-terminator seems to be required. this string is placed
368 # into a class variable so that it can be overridden if
369 # necessary.
370 log_format_string = '<%d>%s\000'
371
372 def encodePriority (self, facility, priority):
373 """
374 Encode the facility and priority. You can pass in strings or
375 integers - if strings are passed, the facility_names and
376 priority_names mapping dictionaries are used to convert them to
377 integers.
378 """
379 if type(facility) == types.StringType:
380 facility = self.facility_names[facility]
381 if type(priority) == types.StringType:
382 priority = self.priority_names[priority]
383 return (facility << 3) | priority
384
385 def close (self):
386 """
387 Closes the socket.
388 """
389 if self.unixsocket:
390 self.socket.close()
391
392 def emit(self, record):
393 """
394 Emit a record.
395
396 The record is formatted, and then sent to the syslog server. If
397 exception information is present, it is NOT sent to the server.
398 """
399 msg = self.format(record)
400 """
401 We need to convert record level to lowercase, maybe this will
402 change in the future.
403 """
404 msg = self.log_format_string % (
405 self.encodePriority(self.facility,
406 string.lower(record.levelname)),
407 msg)
408 try:
409 if self.unixsocket:
410 self.socket.send(msg)
411 else:
412 self.socket.sendto(msg, self.address)
413 except:
414 self.handleError()
415
416class SMTPHandler(logging.Handler):
417 """
418 A handler class which sends an SMTP email for each logging event.
419 """
420 def __init__(self, mailhost, fromaddr, toaddrs, subject):
421 """
422 Initialize the handler.
423
424 Initialize the instance with the from and to addresses and subject
425 line of the email. To specify a non-standard SMTP port, use the
426 (host, port) tuple format for the mailhost argument.
427 """
428 logging.Handler.__init__(self)
429 if type(mailhost) == types.TupleType:
430 host, port = mailhost
431 self.mailhost = host
432 self.mailport = port
433 else:
434 self.mailhost = mailhost
435 self.mailport = None
436 self.fromaddr = fromaddr
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 def emit(self, record):
450 """
451 Emit a record.
452
453 Format the record and send it to the specified addressees.
454 """
455 try:
456 import smtplib
457 port = self.mailport
458 if not port:
459 port = smtplib.SMTP_PORT
460 smtp = smtplib.SMTP(self.mailhost, port)
461 msg = self.format(record)
462 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (
463 self.fromaddr,
464 string.join(self.toaddrs, ","),
465 self.getSubject(record), msg
466 )
467 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
468 smtp.quit()
469 except:
470 self.handleError()
471
472class NTEventLogHandler(logging.Handler):
473 """
474 A handler class which sends events to the NT Event Log. Adds a
475 registry entry for the specified application name. If no dllname is
476 provided, win32service.pyd (which contains some basic message
477 placeholders) is used. Note that use of these placeholders will make
478 your event logs big, as the entire message source is held in the log.
479 If you want slimmer logs, you have to pass in the name of your own DLL
480 which contains the message definitions you want to use in the event log.
481 """
482 def __init__(self, appname, dllname=None, logtype="Application"):
483 logging.Handler.__init__(self)
484 try:
485 import win32evtlogutil, win32evtlog
486 self.appname = appname
487 self._welu = win32evtlogutil
488 if not dllname:
489 dllname = os.path.split(self._welu.__file__)
490 dllname = os.path.split(dllname[0])
491 dllname = os.path.join(dllname[0], r'win32service.pyd')
492 self.dllname = dllname
493 self.logtype = logtype
494 self._welu.AddSourceToRegistry(appname, dllname, logtype)
495 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
496 self.typemap = {
497 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
498 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
499 logging.WARN : win32evtlog.EVENTLOG_WARNING_TYPE,
500 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
501 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
502 }
503 except ImportError:
504 print "The Python Win32 extensions for NT (service, event "\
505 "logging) appear not to be available."
506 self._welu = None
507
508 def getMessageID(self, record):
509 """
510 Return the message ID for the event record. If you are using your
511 own messages, you could do this by having the msg passed to the
512 logger being an ID rather than a formatting string. Then, in here,
513 you could use a dictionary lookup to get the message ID. This
514 version returns 1, which is the base message ID in win32service.pyd.
515 """
516 return 1
517
518 def getEventCategory(self, record):
519 """
520 Return the event category for the record.
521
522 Override this if you want to specify your own categories. This version
523 returns 0.
524 """
525 return 0
526
527 def getEventType(self, record):
528 """
529 Return the event type for the record.
530
531 Override this if you want to specify your own types. This version does
532 a mapping using the handler's typemap attribute, which is set up in
533 __init__() to a dictionary which contains mappings for DEBUG, INFO,
534 WARN, ERROR and CRITICAL. If you are using your own levels you will
535 either need to override this method or place a suitable dictionary in
536 the handler's typemap attribute.
537 """
538 return self.typemap.get(record.levelno, self.deftype)
539
540 def emit(self, record):
541 """
542 Emit a record.
543
544 Determine the message ID, event category and event type. Then
545 log the message in the NT event log.
546 """
547 if self._welu:
548 try:
549 id = self.getMessageID(record)
550 cat = self.getEventCategory(record)
551 type = self.getEventType(record)
552 msg = self.format(record)
553 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
554 except:
555 self.handleError()
556
557 def close(self):
558 """
559 Clean up this handler.
560
561 You can remove the application name from the registry as a
562 source of event log entries. However, if you do this, you will
563 not be able to see the events as you intended in the Event Log
564 Viewer - it needs to be able to access the registry to get the
565 DLL name.
566 """
567 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
568 pass
569
570class HTTPHandler(logging.Handler):
571 """
572 A class which sends records to a Web server, using either GET or
573 POST semantics.
574 """
575 def __init__(self, host, url, method="GET"):
576 """
577 Initialize the instance with the host, the request URL, and the method
578 ("GET" or "POST")
579 """
580 logging.Handler.__init__(self)
581 method = string.upper(method)
582 if method not in ["GET", "POST"]:
583 raise ValueError, "method must be GET or POST"
584 self.host = host
585 self.url = url
586 self.method = method
587
588 def emit(self, record):
589 """
590 Emit a record.
591
592 Send the record to the Web server as an URL-encoded dictionary
593 """
594 try:
595 import httplib, urllib
596 h = httplib.HTTP(self.host)
597 url = self.url
598 data = urllib.urlencode(record.__dict__)
599 if self.method == "GET":
600 if (string.find(url, '?') >= 0):
601 sep = '&'
602 else:
603 sep = '?'
604 url = url + "%c%s" % (sep, data)
605 h.putrequest(self.method, url)
606 if self.method == "POST":
607 h.putheader("Content-length", str(len(data)))
608 h.endheaders()
609 if self.method == "POST":
610 h.send(data)
611 h.getreply() #can't do anything with the result
612 except:
613 self.handleError()
614
615class BufferingHandler(logging.Handler):
616 """
617 A handler class which buffers logging records in memory. Whenever each
618 record is added to the buffer, a check is made to see if the buffer should
619 be flushed. If it should, then flush() is expected to do what's needed.
620 """
621 def __init__(self, capacity):
622 """
623 Initialize the handler with the buffer size.
624 """
625 logging.Handler.__init__(self)
626 self.capacity = capacity
627 self.buffer = []
628
629 def shouldFlush(self, record):
630 """
631 Should the handler flush its buffer?
632
633 Returns true if the buffer is up to capacity. This method can be
634 overridden to implement custom flushing strategies.
635 """
636 return (len(self.buffer) >= self.capacity)
637
638 def emit(self, record):
639 """
640 Emit a record.
641
642 Append the record. If shouldFlush() tells us to, call flush() to process
643 the buffer.
644 """
645 self.buffer.append(record)
646 if self.shouldFlush(record):
647 self.flush()
648
649 def flush(self):
650 """
651 Override to implement custom flushing behaviour.
652
653 This version just zaps the buffer to empty.
654 """
655 self.buffer = []
656
657class MemoryHandler(BufferingHandler):
658 """
659 A handler class which buffers logging records in memory, periodically
660 flushing them to a target handler. Flushing occurs whenever the buffer
661 is full, or when an event of a certain severity or greater is seen.
662 """
663 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
664 """
665 Initialize the handler with the buffer size, the level at which
666 flushing should occur and an optional target.
667
668 Note that without a target being set either here or via setTarget(),
669 a MemoryHandler is no use to anyone!
670 """
671 BufferingHandler.__init__(self, capacity)
672 self.flushLevel = flushLevel
673 self.target = target
674
675 def shouldFlush(self, record):
676 """
677 Check for buffer full or a record at the flushLevel or higher.
678 """
679 return (len(self.buffer) >= self.capacity) or \
680 (record.levelno >= self.flushLevel)
681
682 def setTarget(self, target):
683 """
684 Set the target handler for this handler.
685 """
686 self.target = target
687
688 def flush(self):
689 """
690 For a MemoryHandler, flushing means just sending the buffered
691 records to the target, if there is one. Override if you want
692 different behaviour.
693 """
694 if self.target:
695 for record in self.buffer:
696 self.target.handle(record)
697 self.buffer = []
698
699 def close(self):
700 """
701 Flush, set the target to None and lose the buffer.
702 """
703 self.flush()
704 self.target = None
705 self.buffer = []