Package Ganga :: Package Utility :: Package external :: Package logging :: Module handlers
[hide private]
[frames] | no frames]

Source Code for Module Ganga.Utility.external.logging.handlers

  1  # Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. 
  2  # 
  3  # Permission to use, copy, modify, and distribute this software and its 
  4  # documentation for any purpose and without fee is hereby granted, 
  5  # provided that the above copyright notice appear in all copies and that 
  6  # both that copyright notice and this permission notice appear in 
  7  # supporting documentation, and that the name of Vinay Sajip 
  8  # not be used in advertising or publicity pertaining to distribution 
  9  # of the software without specific, written prior permission. 
 10  # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
 11  # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 
 12  # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
 13  # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 
 14  # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 
 15  # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 16   
 17  """ 
 18  Additional handlers for the logging package for Python. The core package is 
 19  based on PEP 282 and comments thereto in comp.lang.python, and influenced by 
 20  Apache's log4j system. 
 21   
 22  Should work under Python versions >= 1.5.2, except that source line 
 23  information is not available unless 'sys._getframe()' is. 
 24   
 25  Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. 
 26   
 27  To use, simply 'import logging' and log away! 
 28  """ 
 29   
 30  import sys, socket, types, os, string, cPickle, struct, time, glob 
 31   
 32  # GANGA FIX 
 33  import Ganga.Utility.external.logging as logging 
 34  # GANGA FIX 
 35   
 36  # 
 37  # Some constants... 
 38  # 
 39   
 40  DEFAULT_TCP_LOGGING_PORT    = 9020 
 41  DEFAULT_UDP_LOGGING_PORT    = 9021 
 42  DEFAULT_HTTP_LOGGING_PORT   = 9022 
 43  DEFAULT_SOAP_LOGGING_PORT   = 9023 
 44  SYSLOG_UDP_PORT             = 514 
 45   
46 -class BaseRotatingHandler(logging.FileHandler):
47 """ 48 Base class for handlers that rotate log files at a certain point. 49 Not meant to be instantiated directly. Instead, use RotatingFileHandler 50 or TimedRotatingFileHandler. 51 """
52 - def __init__(self, filename, mode):
53 """ 54 Use the specified filename for streamed logging 55 """ 56 logging.FileHandler.__init__(self, filename, mode)
57
58 - def emit(self, record):
59 """ 60 Emit a record. 61 62 Output the record to the file, catering for rollover as described 63 in doRollover(). 64 """ 65 try: 66 if self.shouldRollover(record): 67 self.doRollover() 68 logging.FileHandler.emit(self, record) 69 except: 70 self.handleError(record)
71
72 -class RotatingFileHandler(BaseRotatingHandler):
73 """ 74 Handler for logging to a set of files, which switches from one file 75 to the next when the current file reaches a certain size. 76 """
77 - def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
78 """ 79 Open the specified file and use it as the stream for logging. 80 81 By default, the file grows indefinitely. You can specify particular 82 values of maxBytes and backupCount to allow the file to rollover at 83 a predetermined size. 84 85 Rollover occurs whenever the current log file is nearly maxBytes in 86 length. If backupCount is >= 1, the system will successively create 87 new files with the same pathname as the base file, but with extensions 88 ".1", ".2" etc. appended to it. For example, with a backupCount of 5 89 and a base file name of "app.log", you would get "app.log", 90 "app.log.1", "app.log.2", ... through to "app.log.5". The file being 91 written to is always "app.log" - when it gets filled up, it is closed 92 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc. 93 exist, then they are renamed to "app.log.2", "app.log.3" etc. 94 respectively. 95 96 If maxBytes is zero, rollover never occurs. 97 """ 98 self.mode = mode 99 if maxBytes > 0: 100 self.mode = "a" # doesn't make sense otherwise! 101 BaseRotatingHandler.__init__(self, filename, self.mode) 102 self.maxBytes = maxBytes 103 self.backupCount = backupCount
104
105 - def doRollover(self):
106 """ 107 Do a rollover, as described in __init__(). 108 """ 109 110 self.stream.close() 111 if self.backupCount > 0: 112 for i in range(self.backupCount - 1, 0, -1): 113 sfn = "%s.%d" % (self.baseFilename, i) 114 dfn = "%s.%d" % (self.baseFilename, i + 1) 115 if os.path.exists(sfn): 116 #print "%s -> %s" % (sfn, dfn) 117 if os.path.exists(dfn): 118 os.remove(dfn) 119 os.rename(sfn, dfn) 120 dfn = self.baseFilename + ".1" 121 if os.path.exists(dfn): 122 os.remove(dfn) 123 os.rename(self.baseFilename, dfn) 124 #print "%s -> %s" % (self.baseFilename, dfn) 125 self.stream = open(self.baseFilename, "w")
126
127 - def shouldRollover(self, record):
128 """ 129 Determine if rollover should occur. 130 131 Basically, see if the supplied record would cause the file to exceed 132 the size limit we have. 133 """ 134 if self.maxBytes > 0: # are we rolling over? 135 msg = "%s\n" % self.format(record) 136 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature 137 if self.stream.tell() + len(msg) >= self.maxBytes: 138 return 1 139 return 0
140
141 -class TimedRotatingFileHandler(BaseRotatingHandler):
142 """ 143 Handler for logging to a file, rotating the log file at certain timed 144 intervals. 145 146 If backupCount is > 0, when rollover is done, no more than backupCount 147 files are kept - the oldest ones are deleted. 148 """
149 - def __init__(self, filename, when='h', interval=1, backupCount=0):
150 BaseRotatingHandler.__init__(self, filename, 'a') 151 self.when = string.upper(when) 152 self.backupCount = backupCount 153 # Calculate the real rollover interval, which is just the number of 154 # seconds between rollovers. Also set the filename suffix used when 155 # a rollover occurs. Current 'when' events supported: 156 # S - Seconds 157 # M - Minutes 158 # H - Hours 159 # D - Days 160 # midnight - roll over at midnight 161 # W{0-6} - roll over on a certain day; 0 - Monday 162 # 163 # Case of the 'when' specifier is not important; lower or upper case 164 # will work. 165 currentTime = int(time.time()) 166 if self.when == 'S': 167 self.interval = 1 # one second 168 self.suffix = "%Y-%m-%d_%H-%M-%S" 169 elif self.when == 'M': 170 self.interval = 60 # one minute 171 self.suffix = "%Y-%m-%d_%H-%M" 172 elif self.when == 'H': 173 self.interval = 60 * 60 # one hour 174 self.suffix = "%Y-%m-%d_%H" 175 elif self.when == 'D' or self.when == 'MIDNIGHT': 176 self.interval = 60 * 60 * 24 # one day 177 self.suffix = "%Y-%m-%d" 178 elif self.when.startswith('W'): 179 self.interval = 60 * 60 * 24 * 7 # one week 180 if len(self.when) != 2: 181 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when) 182 if self.when[1] < '0' or self.when[1] > '6': 183 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when) 184 self.dayOfWeek = int(self.when[1]) 185 self.suffix = "%Y-%m-%d" 186 else: 187 raise ValueError("Invalid rollover interval specified: %s" % self.when) 188 189 self.interval = self.interval * interval # multiply by units requested 190 self.rolloverAt = currentTime + self.interval 191 192 # If we are rolling over at midnight or weekly, then the interval is already known. 193 # What we need to figure out is WHEN the next interval is. In other words, 194 # if you are rolling over at midnight, then your base interval is 1 day, 195 # but you want to start that one day clock at midnight, not now. So, we 196 # have to fudge the rolloverAt value in order to trigger the first rollover 197 # at the right time. After that, the regular interval will take care of 198 # the rest. Note that this code doesn't care about leap seconds. :) 199 if self.when == 'MIDNIGHT' or self.when.startswith('W'): 200 # This could be done with less code, but I wanted it to be clear 201 t = time.localtime(currentTime) 202 currentHour = t[3] 203 currentMinute = t[4] 204 currentSecond = t[5] 205 # r is the number of seconds left between now and midnight 206 r = (24 - currentHour) * 60 * 60 # number of hours in seconds 207 r = r + (59 - currentMinute) * 60 # plus the number of minutes (in secs) 208 r = r + (59 - currentSecond) # plus the number of seconds 209 self.rolloverAt = currentTime + r 210 # If we are rolling over on a certain day, add in the number of days until 211 # the next rollover, but offset by 1 since we just calculated the time 212 # until the next day starts. There are three cases: 213 # Case 1) The day to rollover is today; in this case, do nothing 214 # Case 2) The day to rollover is further in the interval (i.e., today is 215 # day 2 (Wednesday) and rollover is on day 6 (Sunday). Days to 216 # next rollover is simply 6 - 2 - 1, or 3. 217 # Case 3) The day to rollover is behind us in the interval (i.e., today 218 # is day 5 (Saturday) and rollover is on day 3 (Thursday). 219 # Days to rollover is 6 - 5 + 3, or 4. In this case, it's the 220 # number of days left in the current week (1) plus the number 221 # of days in the next week until the rollover day (3). 222 if when.startswith('W'): 223 day = t[6] # 0 is Monday 224 if day > self.dayOfWeek: 225 daysToWait = (day - self.dayOfWeek) - 1 226 self.rolloverAt = self.rolloverAt + (daysToWait * (60 * 60 * 24)) 227 if day < self.dayOfWeek: 228 daysToWait = (6 - self.dayOfWeek) + day 229 self.rolloverAt = self.rolloverAt + (daysToWait * (60 * 60 * 24))
230 231 #print "Will rollover at %d, %d seconds from now" % (self.rolloverAt, self.rolloverAt - currentTime) 232
233 - def shouldRollover(self, record):
234 """ 235 Determine if rollover should occur 236 237 record is not used, as we are just comparing times, but it is needed so 238 the method siguratures are the same 239 """ 240 t = int(time.time()) 241 if t >= self.rolloverAt: 242 return 1 243 #print "No need to rollover: %d, %d" % (t, self.rolloverAt) 244 return 0
245
246 - def doRollover(self):
247 """ 248 do a rollover; in this case, a date/time stamp is appended to the filename 249 when the rollover happens. However, you want the file to be named for the 250 start of the interval, not the current time. If there is a backup count, 251 then we have to get a list of matching filenames, sort them and remove 252 the one with the oldest suffix. 253 """ 254 self.stream.close() 255 # get the time that this sequence started at and make it a TimeTuple 256 t = self.rolloverAt - self.interval 257 timeTuple = time.localtime(t) 258 dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple) 259 if os.path.exists(dfn): 260 os.remove(dfn) 261 os.rename(self.baseFilename, dfn) 262 if self.backupCount > 0: 263 # find the oldest log file and delete it 264 s = glob.glob(self.baseFilename + ".20*") 265 if len(s) > self.backupCount: 266 s.sort() 267 os.remove(s[0]) 268 #print "%s -> %s" % (self.baseFilename, dfn) 269 self.stream = open(self.baseFilename, "w") 270 self.rolloverAt = int(time.time()) + self.interval
271
272 -class SocketHandler(logging.Handler):
273 """ 274 A handler class which writes logging records, in pickle format, to 275 a streaming socket. The socket is kept open across logging calls. 276 If the peer resets it, an attempt is made to reconnect on the next call. 277 The pickle which is sent is that of the LogRecord's attribute dictionary 278 (__dict__), so that the receiver does not need to have the logging module 279 installed in order to process the logging event. 280 281 To unpickle the record at the receiving end into a LogRecord, use the 282 makeLogRecord function. 283 """ 284
285 - def __init__(self, host, port):
286 """ 287 Initializes the handler with a specific host address and port. 288 289 The attribute 'closeOnError' is set to 1 - which means that if 290 a socket error occurs, the socket is silently closed and then 291 reopened on the next logging call. 292 """ 293 logging.Handler.__init__(self) 294 self.host = host 295 self.port = port 296 self.sock = None 297 self.closeOnError = 0 298 self.retryTime = None 299 # 300 # Exponential backoff parameters. 301 # 302 self.retryStart = 1.0 303 self.retryMax = 30.0 304 self.retryFactor = 2.0
305
306 - def makeSocket(self):
307 """ 308 A factory method which allows subclasses to define the precise 309 type of socket they want. 310 """ 311 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 312 s.connect((self.host, self.port)) 313 return s
314
315 - def createSocket(self):
316 """ 317 Try to create a socket, using an exponential backoff with 318 a max retry time. Thanks to Robert Olson for the original patch 319 (SF #815911) which has been slightly refactored. 320 """ 321 now = time.time() 322 # Either retryTime is None, in which case this 323 # is the first time back after a disconnect, or 324 # we've waited long enough. 325 if self.retryTime is None: 326 attempt = 1 327 else: 328 attempt = (now >= self.retryTime) 329 if attempt: 330 try: 331 self.sock = self.makeSocket() 332 self.retryTime = None # next time, no delay before trying 333 except: 334 #Creation failed, so set the retry time and return. 335 if self.retryTime is None: 336 self.retryPeriod = self.retryStart 337 else: 338 self.retryPeriod = self.retryPeriod * self.retryFactor 339 if self.retryPeriod > self.retryMax: 340 self.retryPeriod = self.retryMax 341 self.retryTime = now + self.retryPeriod
342
343 - def send(self, s):
344 """ 345 Send a pickled string to the socket. 346 347 This function allows for partial sends which can happen when the 348 network is busy. 349 """ 350 if self.sock is None: 351 self.createSocket() 352 #self.sock can be None either because we haven't reached the retry 353 #time yet, or because we have reached the retry time and retried, 354 #but are still unable to connect. 355 if self.sock: 356 try: 357 if hasattr(self.sock, "sendall"): 358 self.sock.sendall(s) 359 else: 360 sentsofar = 0 361 left = len(s) 362 while left > 0: 363 sent = self.sock.send(s[sentsofar:]) 364 sentsofar = sentsofar + sent 365 left = left - sent 366 except socket.error: 367 self.sock.close() 368 self.sock = None # so we can call createSocket next time
369
370 - def makePickle(self, record):
371 """ 372 Pickles the record in binary format with a length prefix, and 373 returns it ready for transmission across the socket. 374 """ 375 ei = record.exc_info 376 if ei: 377 dummy = self.format(record) # just to get traceback text into record.exc_text 378 record.exc_info = None # to avoid Unpickleable error 379 s = cPickle.dumps(record.__dict__, 1) 380 if ei: 381 record.exc_info = ei # for next handler 382 slen = struct.pack(">L", len(s)) 383 return slen + s
384
385 - def handleError(self, record):
386 """ 387 Handle an error during logging. 388 389 An error has occurred during logging. Most likely cause - 390 connection lost. Close the socket so that we can retry on the 391 next event. 392 """ 393 if self.closeOnError and self.sock: 394 self.sock.close() 395 self.sock = None #try to reconnect next time 396 else: 397 logging.Handler.handleError(self, record)
398
399 - def emit(self, record):
400 """ 401 Emit a record. 402 403 Pickles the record and writes it to the socket in binary format. 404 If there is an error with the socket, silently drop the packet. 405 If there was a problem with the socket, re-establishes the 406 socket. 407 """ 408 try: 409 s = self.makePickle(record) 410 self.send(s) 411 except: 412 self.handleError(record)
413
414 - def close(self):
415 """ 416 Closes the socket. 417 """ 418 if self.sock: 419 self.sock.close() 420 self.sock = None 421 logging.Handler.close(self)
422
423 -class DatagramHandler(SocketHandler):
424 """ 425 A handler class which writes logging records, in pickle format, to 426 a datagram socket. The pickle which is sent is that of the LogRecord's 427 attribute dictionary (__dict__), so that the receiver does not need to 428 have the logging module installed in order to process the logging event. 429 430 To unpickle the record at the receiving end into a LogRecord, use the 431 makeLogRecord function. 432 433 """
434 - def __init__(self, host, port):
435 """ 436 Initializes the handler with a specific host address and port. 437 """ 438 SocketHandler.__init__(self, host, port) 439 self.closeOnError = 0
440
441 - def makeSocket(self):
442 """ 443 The factory method of SocketHandler is here overridden to create 444 a UDP socket (SOCK_DGRAM). 445 """ 446 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 447 return s
448
449 - def send(self, s):
450 """ 451 Send a pickled string to a socket. 452 453 This function no longer allows for partial sends which can happen 454 when the network is busy - UDP does not guarantee delivery and 455 can deliver packets out of sequence. 456 """ 457 if self.sock is None: 458 self.createSocket() 459 self.sock.sendto(s, (self.host, self.port))
460
461 -class SysLogHandler(logging.Handler):
462 """ 463 A handler class which sends formatted logging records to a syslog 464 server. Based on Sam Rushing's syslog module: 465 http://www.nightmare.com/squirl/python-ext/misc/syslog.py 466 Contributed by Nicolas Untz (after which minor refactoring changes 467 have been made). 468 """ 469 470 # from <linux/sys/syslog.h>: 471 # ====================================================================== 472 # priorities/facilities are encoded into a single 32-bit quantity, where 473 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the 474 # facility (0-big number). Both the priorities and the facilities map 475 # roughly one-to-one to strings in the syslogd(8) source code. This 476 # mapping is included in this file. 477 # 478 # priorities (these are ordered) 479 480 LOG_EMERG = 0 # system is unusable 481 LOG_ALERT = 1 # action must be taken immediately 482 LOG_CRIT = 2 # critical conditions 483 LOG_ERR = 3 # error conditions 484 LOG_WARNING = 4 # warning conditions 485 LOG_NOTICE = 5 # normal but significant condition 486 LOG_INFO = 6 # informational 487 LOG_DEBUG = 7 # debug-level messages 488 489 # facility codes 490 LOG_KERN = 0 # kernel messages 491 LOG_USER = 1 # random user-level messages 492 LOG_MAIL = 2 # mail system 493 LOG_DAEMON = 3 # system daemons 494 LOG_AUTH = 4 # security/authorization messages 495 LOG_SYSLOG = 5 # messages generated internally by syslogd 496 LOG_LPR = 6 # line printer subsystem 497 LOG_NEWS = 7 # network news subsystem 498 LOG_UUCP = 8 # UUCP subsystem 499 LOG_CRON = 9 # clock daemon 500 LOG_AUTHPRIV = 10 # security/authorization messages (private) 501 502 # other codes through 15 reserved for system use 503 LOG_LOCAL0 = 16 # reserved for local use 504 LOG_LOCAL1 = 17 # reserved for local use 505 LOG_LOCAL2 = 18 # reserved for local use 506 LOG_LOCAL3 = 19 # reserved for local use 507 LOG_LOCAL4 = 20 # reserved for local use 508 LOG_LOCAL5 = 21 # reserved for local use 509 LOG_LOCAL6 = 22 # reserved for local use 510 LOG_LOCAL7 = 23 # reserved for local use 511 512 priority_names = { 513 "alert": LOG_ALERT, 514 "crit": LOG_CRIT, 515 "critical": LOG_CRIT, 516 "debug": LOG_DEBUG, 517 "emerg": LOG_EMERG, 518 "err": LOG_ERR, 519 "error": LOG_ERR, # DEPRECATED 520 "info": LOG_INFO, 521 "notice": LOG_NOTICE, 522 "panic": LOG_EMERG, # DEPRECATED 523 "warn": LOG_WARNING, # DEPRECATED 524 "warning": LOG_WARNING, 525 } 526 527 facility_names = { 528 "auth": LOG_AUTH, 529 "authpriv": LOG_AUTHPRIV, 530 "cron": LOG_CRON, 531 "daemon": LOG_DAEMON, 532 "kern": LOG_KERN, 533 "lpr": LOG_LPR, 534 "mail": LOG_MAIL, 535 "news": LOG_NEWS, 536 "security": LOG_AUTH, # DEPRECATED 537 "syslog": LOG_SYSLOG, 538 "user": LOG_USER, 539 "uucp": LOG_UUCP, 540 "local0": LOG_LOCAL0, 541 "local1": LOG_LOCAL1, 542 "local2": LOG_LOCAL2, 543 "local3": LOG_LOCAL3, 544 "local4": LOG_LOCAL4, 545 "local5": LOG_LOCAL5, 546 "local6": LOG_LOCAL6, 547 "local7": LOG_LOCAL7, 548 } 549
550 - def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
551 """ 552 Initialize a handler. 553 554 If address is specified as a string, UNIX socket is used. 555 If facility is not specified, LOG_USER is used. 556 """ 557 logging.Handler.__init__(self) 558 559 self.address = address 560 self.facility = facility 561 if type(address) == types.StringType: 562 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) 563 # syslog may require either DGRAM or STREAM sockets 564 try: 565 self.socket.connect(address) 566 except socket.error: 567 self.socket.close() 568 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 569 self.socket.connect(address) 570 self.unixsocket = 1 571 else: 572 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 573 self.unixsocket = 0 574 575 self.formatter = None
576 577 # curious: when talking to the unix-domain '/dev/log' socket, a 578 # zero-terminator seems to be required. this string is placed 579 # into a class variable so that it can be overridden if 580 # necessary. 581 log_format_string = '<%d>%s\000' 582
583 - def encodePriority (self, facility, priority):
584 """ 585 Encode the facility and priority. You can pass in strings or 586 integers - if strings are passed, the facility_names and 587 priority_names mapping dictionaries are used to convert them to 588 integers. 589 """ 590 if type(facility) == types.StringType: 591 facility = self.facility_names[facility] 592 if type(priority) == types.StringType: 593 priority = self.priority_names[priority] 594 return (facility << 3) | priority
595
596 - def close (self):
597 """ 598 Closes the socket. 599 """ 600 if self.unixsocket: 601 self.socket.close() 602 logging.Handler.close(self)
603
604 - def emit(self, record):
605 """ 606 Emit a record. 607 608 The record is formatted, and then sent to the syslog server. If 609 exception information is present, it is NOT sent to the server. 610 """ 611 msg = self.format(record) 612 """ 613 We need to convert record level to lowercase, maybe this will 614 change in the future. 615 """ 616 msg = self.log_format_string % ( 617 self.encodePriority(self.facility, 618 string.lower(record.levelname)), 619 msg) 620 try: 621 if self.unixsocket: 622 self.socket.send(msg) 623 else: 624 self.socket.sendto(msg, self.address) 625 except: 626 self.handleError(record)
627
628 -class SMTPHandler(logging.Handler):
629 """ 630 A handler class which sends an SMTP email for each logging event. 631 """
632 - def __init__(self, mailhost, fromaddr, toaddrs, subject):
633 """ 634 Initialize the handler. 635 636 Initialize the instance with the from and to addresses and subject 637 line of the email. To specify a non-standard SMTP port, use the 638 (host, port) tuple format for the mailhost argument. 639 """ 640 logging.Handler.__init__(self) 641 if type(mailhost) == types.TupleType: 642 host, port = mailhost 643 self.mailhost = host 644 self.mailport = port 645 else: 646 self.mailhost = mailhost 647 self.mailport = None 648 self.fromaddr = fromaddr 649 if type(toaddrs) == types.StringType: 650 toaddrs = [toaddrs] 651 self.toaddrs = toaddrs 652 self.subject = subject
653
654 - def getSubject(self, record):
655 """ 656 Determine the subject for the email. 657 658 If you want to specify a subject line which is record-dependent, 659 override this method. 660 """ 661 return self.subject
662 663 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 664 665 monthname = [None, 666 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 667 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 668
669 - def date_time(self):
670 """ 671 Return the current date and time formatted for a MIME header. 672 Needed for Python 1.5.2 (no email package available) 673 """ 674 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time()) 675 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 676 self.weekdayname[wd], 677 day, self.monthname[month], year, 678 hh, mm, ss) 679 return s
680
681 - def emit(self, record):
682 """ 683 Emit a record. 684 685 Format the record and send it to the specified addressees. 686 """ 687 try: 688 import smtplib 689 try: 690 from email.Utils import formatdate 691 except: 692 formatdate = self.date_time 693 port = self.mailport 694 if not port: 695 port = smtplib.SMTP_PORT 696 smtp = smtplib.SMTP(self.mailhost, port) 697 msg = self.format(record) 698 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % ( 699 self.fromaddr, 700 string.join(self.toaddrs, ","), 701 self.getSubject(record), 702 formatdate(), msg) 703 smtp.sendmail(self.fromaddr, self.toaddrs, msg) 704 smtp.quit() 705 except: 706 self.handleError(record)
707
708 -class NTEventLogHandler(logging.Handler):
709 """ 710 A handler class which sends events to the NT Event Log. Adds a 711 registry entry for the specified application name. If no dllname is 712 provided, win32service.pyd (which contains some basic message 713 placeholders) is used. Note that use of these placeholders will make 714 your event logs big, as the entire message source is held in the log. 715 If you want slimmer logs, you have to pass in the name of your own DLL 716 which contains the message definitions you want to use in the event log. 717 """
718 - def __init__(self, appname, dllname=None, logtype="Application"):
719 logging.Handler.__init__(self) 720 try: 721 import win32evtlogutil, win32evtlog 722 self.appname = appname 723 self._welu = win32evtlogutil 724 if not dllname: 725 dllname = os.path.split(self._welu.__file__) 726 dllname = os.path.split(dllname[0]) 727 dllname = os.path.join(dllname[0], r'win32service.pyd') 728 self.dllname = dllname 729 self.logtype = logtype 730 self._welu.AddSourceToRegistry(appname, dllname, logtype) 731 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE 732 self.typemap = { 733 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE, 734 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE, 735 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE, 736 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE, 737 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE, 738 } 739 except ImportError: 740 print "The Python Win32 extensions for NT (service, event "\ 741 "logging) appear not to be available." 742 self._welu = None
743
744 - def getMessageID(self, record):
745 """ 746 Return the message ID for the event record. If you are using your 747 own messages, you could do this by having the msg passed to the 748 logger being an ID rather than a formatting string. Then, in here, 749 you could use a dictionary lookup to get the message ID. This 750 version returns 1, which is the base message ID in win32service.pyd. 751 """ 752 return 1
753
754 - def getEventCategory(self, record):
755 """ 756 Return the event category for the record. 757 758 Override this if you want to specify your own categories. This version 759 returns 0. 760 """ 761 return 0
762
763 - def getEventType(self, record):
764 """ 765 Return the event type for the record. 766 767 Override this if you want to specify your own types. This version does 768 a mapping using the handler's typemap attribute, which is set up in 769 __init__() to a dictionary which contains mappings for DEBUG, INFO, 770 WARNING, ERROR and CRITICAL. If you are using your own levels you will 771 either need to override this method or place a suitable dictionary in 772 the handler's typemap attribute. 773 """ 774 return self.typemap.get(record.levelno, self.deftype)
775
776 - def emit(self, record):
777 """ 778 Emit a record. 779 780 Determine the message ID, event category and event type. Then 781 log the message in the NT event log. 782 """ 783 if self._welu: 784 try: 785 id = self.getMessageID(record) 786 cat = self.getEventCategory(record) 787 type = self.getEventType(record) 788 msg = self.format(record) 789 self._welu.ReportEvent(self.appname, id, cat, type, [msg]) 790 except: 791 self.handleError(record)
792
793 - def close(self):
794 """ 795 Clean up this handler. 796 797 You can remove the application name from the registry as a 798 source of event log entries. However, if you do this, you will 799 not be able to see the events as you intended in the Event Log 800 Viewer - it needs to be able to access the registry to get the 801 DLL name. 802 """ 803 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype) 804 logging.Handler.close(self)
805
806 -class HTTPHandler(logging.Handler):
807 """ 808 A class which sends records to a Web server, using either GET or 809 POST semantics. 810 """
811 - def __init__(self, host, url, method="GET"):
812 """ 813 Initialize the instance with the host, the request URL, and the method 814 ("GET" or "POST") 815 """ 816 logging.Handler.__init__(self) 817 method = string.upper(method) 818 if method not in ["GET", "POST"]: 819 raise ValueError, "method must be GET or POST" 820 self.host = host 821 self.url = url 822 self.method = method
823
824 - def mapLogRecord(self, record):
825 """ 826 Default implementation of mapping the log record into a dict 827 that is sent as the CGI data. Overwrite in your class. 828 Contributed by Franz Glasner. 829 """ 830 return record.__dict__
831
832 - def emit(self, record):
833 """ 834 Emit a record. 835 836 Send the record to the Web server as an URL-encoded dictionary 837 """ 838 try: 839 import httplib, urllib 840 h = httplib.HTTP(self.host) 841 url = self.url 842 data = urllib.urlencode(self.mapLogRecord(record)) 843 if self.method == "GET": 844 if (string.find(url, '?') >= 0): 845 sep = '&' 846 else: 847 sep = '?' 848 url = url + "%c%s" % (sep, data) 849 h.putrequest(self.method, url) 850 if self.method == "POST": 851 h.putheader("Content-length", str(len(data))) 852 h.endheaders() 853 if self.method == "POST": 854 h.send(data) 855 h.getreply() #can't do anything with the result 856 except: 857 self.handleError(record)
858
859 -class BufferingHandler(logging.Handler):
860 """ 861 A handler class which buffers logging records in memory. Whenever each 862 record is added to the buffer, a check is made to see if the buffer should 863 be flushed. If it should, then flush() is expected to do what's needed. 864 """
865 - def __init__(self, capacity):
866 """ 867 Initialize the handler with the buffer size. 868 """ 869 logging.Handler.__init__(self) 870 self.capacity = capacity 871 self.buffer = []
872
873 - def shouldFlush(self, record):
874 """ 875 Should the handler flush its buffer? 876 877 Returns true if the buffer is up to capacity. This method can be 878 overridden to implement custom flushing strategies. 879 """ 880 return (len(self.buffer) >= self.capacity)
881
882 - def emit(self, record):
883 """ 884 Emit a record. 885 886 Append the record. If shouldFlush() tells us to, call flush() to process 887 the buffer. 888 """ 889 self.buffer.append(record) 890 if self.shouldFlush(record): 891 self.flush()
892
893 - def flush(self):
894 """ 895 Override to implement custom flushing behaviour. 896 897 This version just zaps the buffer to empty. 898 """ 899 self.buffer = []
900
901 - def close(self):
902 """ 903 Close the handler. 904 905 This version just flushes and chains to the parent class' close(). 906 """ 907 self.flush() 908 logging.Handler.close(self)
909
910 -class MemoryHandler(BufferingHandler):
911 """ 912 A handler class which buffers logging records in memory, periodically 913 flushing them to a target handler. Flushing occurs whenever the buffer 914 is full, or when an event of a certain severity or greater is seen. 915 """
916 - def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
917 """ 918 Initialize the handler with the buffer size, the level at which 919 flushing should occur and an optional target. 920 921 Note that without a target being set either here or via setTarget(), 922 a MemoryHandler is no use to anyone! 923 """ 924 BufferingHandler.__init__(self, capacity) 925 self.flushLevel = flushLevel 926 self.target = target
927
928 - def shouldFlush(self, record):
929 """ 930 Check for buffer full or a record at the flushLevel or higher. 931 """ 932 return (len(self.buffer) >= self.capacity) or \ 933 (record.levelno >= self.flushLevel)
934
935 - def setTarget(self, target):
936 """ 937 Set the target handler for this handler. 938 """ 939 self.target = target
940
941 - def flush(self):
942 """ 943 For a MemoryHandler, flushing means just sending the buffered 944 records to the target, if there is one. Override if you want 945 different behaviour. 946 """ 947 if self.target: 948 for record in self.buffer: 949 self.target.handle(record) 950 self.buffer = []
951
952 - def close(self):
953 """ 954 Flush, set the target to None and lose the buffer. 955 """ 956 self.flush() 957 self.target = None 958 BufferingHandler.close(self)
959