Changeset 391 for python/trunk/Lib/logging/handlers.py
- Timestamp:
- Mar 19, 2014, 11:31:01 PM (11 years ago)
- Location:
- python/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
python/trunk
-
Property svn:mergeinfo
set to
/python/vendor/Python-2.7.6 merged eligible /python/vendor/current merged eligible
-
Property svn:mergeinfo
set to
-
python/trunk/Lib/logging/handlers.py
r2 r391 1 # Copyright 2001-20 07by Vinay Sajip. All Rights Reserved.1 # Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. 2 2 # 3 3 # Permission to use, copy, modify, and distribute this software and its … … 17 17 """ 18 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 Copyright (C) 2001-2009 Vinay Sajip. All Rights Reserved. 19 based on PEP 282 and comments thereto in comp.lang.python. 20 21 Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. 23 22 24 23 To use, simply 'import logging.handlers' and log away! 25 24 """ 26 25 27 import logging, socket, types, os, string, cPickle, struct, time, re28 from stat import ST_DEV, ST_INO 26 import errno, logging, socket, os, cPickle, struct, time, re 27 from stat import ST_DEV, ST_INO, ST_MTIME 29 28 30 29 try: … … 32 31 except ImportError: 33 32 codecs = None 33 try: 34 unicode 35 _unicode = True 36 except NameError: 37 _unicode = False 34 38 35 39 # … … 42 46 DEFAULT_SOAP_LOGGING_PORT = 9023 43 47 SYSLOG_UDP_PORT = 514 48 SYSLOG_TCP_PORT = 514 44 49 45 50 _MIDNIGHT = 24 * 60 * 60 # number of seconds in a day … … 103 108 If maxBytes is zero, rollover never occurs. 104 109 """ 110 # If rotation/rollover is wanted, it doesn't make sense to use another 111 # mode. If for example 'w' were specified, then if there were multiple 112 # runs of the calling application, the logs from previous runs would be 113 # lost if the 'w' is respected, because the log file would be truncated 114 # on each run. 105 115 if maxBytes > 0: 106 mode = 'a' # doesn't make sense otherwise!116 mode = 'a' 107 117 BaseRotatingHandler.__init__(self, filename, mode, encoding, delay) 108 118 self.maxBytes = maxBytes … … 113 123 Do a rollover, as described in __init__(). 114 124 """ 115 116 self.stream.close() 125 if self.stream: 126 self.stream.close() 127 self.stream = None 117 128 if self.backupCount > 0: 118 129 for i in range(self.backupCount - 1, 0, -1): … … 127 138 if os.path.exists(dfn): 128 139 os.remove(dfn) 129 os.rename(self.baseFilename, dfn) 130 #print "%s -> %s" % (self.baseFilename, dfn) 131 self.mode = 'w' 132 self.stream = self._open() 140 # Issue 18940: A file may not have been created if delay is True. 141 if os.path.exists(self.baseFilename): 142 os.rename(self.baseFilename, dfn) 143 if not self.delay: 144 self.stream = self._open() 133 145 134 146 def shouldRollover(self, record): … … 156 168 files are kept - the oldest ones are deleted. 157 169 """ 158 def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay= 0, utc=0):170 def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False): 159 171 BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay) 160 self.when = string.upper(when)172 self.when = when.upper() 161 173 self.backupCount = backupCount 162 174 self.utc = utc … … 173 185 # Case of the 'when' specifier is not important; lower or upper case 174 186 # will work. 175 currentTime = int(time.time())176 187 if self.when == 'S': 177 188 self.interval = 1 # one second … … 204 215 self.extMatch = re.compile(self.extMatch) 205 216 self.interval = self.interval * interval # multiply by units requested 206 self.rolloverAt = self.computeRollover(int(time.time())) 207 208 #print "Will rollover at %d, %d seconds from now" % (self.rolloverAt, self.rolloverAt - currentTime) 217 if os.path.exists(filename): 218 t = os.stat(filename)[ST_MTIME] 219 else: 220 t = int(time.time()) 221 self.rolloverAt = self.computeRollover(t) 209 222 210 223 def computeRollover(self, currentTime): … … 261 274 if dstNow != dstAtRollover: 262 275 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour 263 newRolloverAt = newRolloverAt -3600276 addend = -3600 264 277 else: # DST bows out before next rollover, so we need to add an hour 265 newRolloverAt = newRolloverAt + 3600 278 addend = 3600 279 newRolloverAt += addend 266 280 result = newRolloverAt 267 281 return result … … 313 327 if self.stream: 314 328 self.stream.close() 329 self.stream = None 315 330 # get the time that this sequence started at and make it a TimeTuple 331 currentTime = int(time.time()) 332 dstNow = time.localtime(currentTime)[-1] 316 333 t = self.rolloverAt - self.interval 317 334 if self.utc: … … 319 336 else: 320 337 timeTuple = time.localtime(t) 338 dstThen = timeTuple[-1] 339 if dstNow != dstThen: 340 if dstNow: 341 addend = 3600 342 else: 343 addend = -3600 344 timeTuple = time.localtime(t + addend) 321 345 dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple) 322 346 if os.path.exists(dfn): 323 347 os.remove(dfn) 324 os.rename(self.baseFilename, dfn) 348 # Issue 18940: A file may not have been created if delay is True. 349 if os.path.exists(self.baseFilename): 350 os.rename(self.baseFilename, dfn) 325 351 if self.backupCount > 0: 326 # find the oldest log file and delete it327 #s = glob.glob(self.baseFilename + ".20*")328 #if len(s) > self.backupCount:329 # s.sort()330 # os.remove(s[0])331 352 for s in self.getFilesToDelete(): 332 353 os.remove(s) 333 #print "%s -> %s" % (self.baseFilename, dfn) 334 self.mode = 'w' 335 self.stream = self._open() 336 currentTime = int(time.time()) 354 if not self.delay: 355 self.stream = self._open() 337 356 newRolloverAt = self.computeRollover(currentTime) 338 357 while newRolloverAt <= currentTime: … … 340 359 #If DST changes and midnight or weekly rollover, adjust for this. 341 360 if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc: 342 dstNow = time.localtime(currentTime)[-1]343 361 dstAtRollover = time.localtime(newRolloverAt)[-1] 344 362 if dstNow != dstAtRollover: 345 363 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour 346 newRolloverAt = newRolloverAt -3600364 addend = -3600 347 365 else: # DST bows out before next rollover, so we need to add an hour 348 newRolloverAt = newRolloverAt + 3600 366 addend = 3600 367 newRolloverAt += addend 349 368 self.rolloverAt = newRolloverAt 350 369 … … 371 390 def __init__(self, filename, mode='a', encoding=None, delay=0): 372 391 logging.FileHandler.__init__(self, filename, mode, encoding, delay) 373 if not os.path.exists(self.baseFilename): 374 self.dev, self.ino = -1, -1 375 else: 376 stat = os.stat(self.baseFilename) 377 self.dev, self.ino = stat[ST_DEV], stat[ST_INO] 392 self.dev, self.ino = -1, -1 393 self._statstream() 394 395 def _statstream(self): 396 if self.stream: 397 sres = os.fstat(self.stream.fileno()) 398 self.dev, self.ino = sres[ST_DEV], sres[ST_INO] 378 399 379 400 def emit(self, record): … … 385 406 current stream. 386 407 """ 387 if not os.path.exists(self.baseFilename): 388 stat = None 389 changed = 1 390 else: 391 stat = os.stat(self.baseFilename) 392 changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino) 393 if changed and self.stream is not None: 394 self.stream.flush() 395 self.stream.close() 396 self.stream = self._open() 397 if stat is None: 398 stat = os.stat(self.baseFilename) 399 self.dev, self.ino = stat[ST_DEV], stat[ST_INO] 408 # Reduce the chance of race conditions by stat'ing by path only 409 # once and then fstat'ing our new fd if we opened a new log stream. 410 # See issue #14632: Thanks to John Mulligan for the problem report 411 # and patch. 412 try: 413 # stat the file by path, checking for existence 414 sres = os.stat(self.baseFilename) 415 except OSError as err: 416 if err.errno == errno.ENOENT: 417 sres = None 418 else: 419 raise 420 # compare file system stat with that of our stream file handle 421 if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino: 422 if self.stream is not None: 423 # we have an open file handle, clean it up 424 self.stream.flush() 425 self.stream.close() 426 # open a new file handle and get new stat info from that fd 427 self.stream = self._open() 428 self._statstream() 400 429 logging.FileHandler.emit(self, record) 401 430 … … 507 536 ei = record.exc_info 508 537 if ei: 509 dummy = self.format(record) # just to get traceback text into record.exc_text 538 # just to get traceback text into record.exc_text ... 539 dummy = self.format(record) 510 540 record.exc_info = None # to avoid Unpickleable error 511 s = cPickle.dumps(record.__dict__, 1) 541 # See issue #14436: If msg or args are objects, they may not be 542 # available on the receiving end. So we convert the msg % args 543 # to a string, save it as msg and zap the args. 544 d = dict(record.__dict__) 545 d['msg'] = record.getMessage() 546 d['args'] = None 547 s = cPickle.dumps(d, 1) 512 548 if ei: 513 549 record.exc_info = ei # for next handler … … 550 586 Closes the socket. 551 587 """ 552 if self.sock: 553 self.sock.close() 554 self.sock = None 588 self.acquire() 589 try: 590 if self.sock: 591 self.sock.close() 592 self.sock = None 593 finally: 594 self.release() 555 595 logging.Handler.close(self) 556 596 … … 632 672 LOG_UUCP = 8 # UUCP subsystem 633 673 LOG_CRON = 9 # clock daemon 634 LOG_AUTHPRIV = 10 # security/authorization messages (private) 674 LOG_AUTHPRIV = 10 # security/authorization messages (private) 675 LOG_FTP = 11 # FTP daemon 635 676 636 677 # other codes through 15 reserved for system use … … 664 705 "cron": LOG_CRON, 665 706 "daemon": LOG_DAEMON, 707 "ftp": LOG_FTP, 666 708 "kern": LOG_KERN, 667 709 "lpr": LOG_LPR, … … 694 736 } 695 737 696 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER): 738 def __init__(self, address=('localhost', SYSLOG_UDP_PORT), 739 facility=LOG_USER, socktype=None): 697 740 """ 698 741 Initialize a handler. … … 700 743 If address is specified as a string, a UNIX socket is used. To log to a 701 744 local syslogd, "SysLogHandler(address="/dev/log")" can be used. 702 If facility is not specified, LOG_USER is used. 745 If facility is not specified, LOG_USER is used. If socktype is 746 specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific 747 socket type will be used. For Unix sockets, you can also specify a 748 socktype of None, in which case socket.SOCK_DGRAM will be used, falling 749 back to socket.SOCK_STREAM. 703 750 """ 704 751 logging.Handler.__init__(self) … … 706 753 self.address = address 707 754 self.facility = facility 708 if type(address) == types.StringType: 755 self.socktype = socktype 756 757 if isinstance(address, basestring): 709 758 self.unixsocket = 1 710 759 self._connect_unixsocket(address) 711 760 else: 712 761 self.unixsocket = 0 713 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 714 762 if socktype is None: 763 socktype = socket.SOCK_DGRAM 764 self.socket = socket.socket(socket.AF_INET, socktype) 765 if socktype == socket.SOCK_STREAM: 766 self.socket.connect(address) 767 self.socktype = socktype 715 768 self.formatter = None 716 769 717 770 def _connect_unixsocket(self, address): 718 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) 719 # syslog may require either DGRAM or STREAM sockets 771 use_socktype = self.socktype 772 if use_socktype is None: 773 use_socktype = socket.SOCK_DGRAM 774 self.socket = socket.socket(socket.AF_UNIX, use_socktype) 720 775 try: 721 776 self.socket.connect(address) 777 # it worked, so set self.socktype to the used type 778 self.socktype = use_socktype 722 779 except socket.error: 723 780 self.socket.close() 724 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 725 self.socket.connect(address) 781 if self.socktype is not None: 782 # user didn't specify falling back, so fail 783 raise 784 use_socktype = socket.SOCK_STREAM 785 self.socket = socket.socket(socket.AF_UNIX, use_socktype) 786 try: 787 self.socket.connect(address) 788 # it worked, so set self.socktype to the used type 789 self.socktype = use_socktype 790 except socket.error: 791 self.socket.close() 792 raise 726 793 727 794 # curious: when talking to the unix-domain '/dev/log' socket, a … … 738 805 integers. 739 806 """ 740 if type(facility) == types.StringType:807 if isinstance(facility, basestring): 741 808 facility = self.facility_names[facility] 742 if type(priority) == types.StringType:809 if isinstance(priority, basestring): 743 810 priority = self.priority_names[priority] 744 811 return (facility << 3) | priority … … 748 815 Closes the socket. 749 816 """ 750 if self.unixsocket: 751 self.socket.close() 817 self.acquire() 818 try: 819 if self.unixsocket: 820 self.socket.close() 821 finally: 822 self.release() 752 823 logging.Handler.close(self) 753 824 … … 769 840 exception information is present, it is NOT sent to the server. 770 841 """ 771 msg = self.format(record) 842 msg = self.format(record) + '\000' 772 843 """ 773 844 We need to convert record level to lowercase, maybe this will 774 845 change in the future. 775 846 """ 776 msg = self.log_format_string % ( 777 self.encodePriority(self.facility, 778 self.mapPriority(record.levelname)), 779 msg) 847 prio = '<%d>' % self.encodePriority(self.facility, 848 self.mapPriority(record.levelname)) 849 # Message is a string. Convert to bytes as required by RFC 5424 850 if type(msg) is unicode: 851 msg = msg.encode('utf-8') 852 msg = prio + msg 780 853 try: 781 854 if self.unixsocket: … … 783 856 self.socket.send(msg) 784 857 except socket.error: 858 self.socket.close() # See issue 17981 785 859 self._connect_unixsocket(self.address) 786 860 self.socket.send(msg) 861 elif self.socktype == socket.SOCK_DGRAM: 862 self.socket.sendto(msg, self.address) 787 863 else: 788 self.socket.send to(msg, self.address)864 self.socket.sendall(msg) 789 865 except (KeyboardInterrupt, SystemExit): 790 866 raise … … 796 872 A handler class which sends an SMTP email for each logging event. 797 873 """ 798 def __init__(self, mailhost, fromaddr, toaddrs, subject, credentials=None): 874 def __init__(self, mailhost, fromaddr, toaddrs, subject, 875 credentials=None, secure=None): 799 876 """ 800 877 Initialize the handler. … … 804 881 (host, port) tuple format for the mailhost argument. To specify 805 882 authentication credentials, supply a (username, password) tuple 806 for the credentials argument. 883 for the credentials argument. To specify the use of a secure 884 protocol (TLS), pass in a tuple for the secure argument. This will 885 only be used when authentication credentials are supplied. The tuple 886 will be either an empty tuple, or a single-value tuple with the name 887 of a keyfile, or a 2-value tuple with the names of the keyfile and 888 certificate file. (This tuple is passed to the `starttls` method). 807 889 """ 808 890 logging.Handler.__init__(self) 809 if type(mailhost) == types.TupleType:891 if isinstance(mailhost, tuple): 810 892 self.mailhost, self.mailport = mailhost 811 893 else: 812 894 self.mailhost, self.mailport = mailhost, None 813 if type(credentials) == types.TupleType:895 if isinstance(credentials, tuple): 814 896 self.username, self.password = credentials 815 897 else: 816 898 self.username = None 817 899 self.fromaddr = fromaddr 818 if type(toaddrs) == types.StringType:900 if isinstance(toaddrs, basestring): 819 901 toaddrs = [toaddrs] 820 902 self.toaddrs = toaddrs 821 903 self.subject = subject 904 self.secure = secure 905 self._timeout = 5.0 822 906 823 907 def getSubject(self, record): … … 830 914 return self.subject 831 915 832 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']833 834 monthname = [None,835 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',836 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']837 838 def date_time(self):839 """840 Return the current date and time formatted for a MIME header.841 Needed for Python 1.5.2 (no email package available)842 """843 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())844 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (845 self.weekdayname[wd],846 day, self.monthname[month], year,847 hh, mm, ss)848 return s849 850 916 def emit(self, record): 851 917 """ … … 856 922 try: 857 923 import smtplib 858 try: 859 from email.utils import formatdate 860 except ImportError: 861 formatdate = self.date_time 924 from email.utils import formatdate 862 925 port = self.mailport 863 926 if not port: 864 927 port = smtplib.SMTP_PORT 865 smtp = smtplib.SMTP(self.mailhost, port )928 smtp = smtplib.SMTP(self.mailhost, port, timeout=self._timeout) 866 929 msg = self.format(record) 867 930 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % ( 868 931 self.fromaddr, 869 string.join(self.toaddrs, ","),932 ",".join(self.toaddrs), 870 933 self.getSubject(record), 871 934 formatdate(), msg) 872 935 if self.username: 936 if self.secure is not None: 937 smtp.ehlo() 938 smtp.starttls(*self.secure) 939 smtp.ehlo() 873 940 smtp.login(self.username, self.password) 874 941 smtp.sendmail(self.fromaddr, self.toaddrs, msg) … … 911 978 } 912 979 except ImportError: 913 print 914 "logging) appear not to be available." 980 print("The Python Win32 extensions for NT (service, event "\ 981 "logging) appear not to be available.") 915 982 self._welu = None 916 983 … … 990 1057 """ 991 1058 logging.Handler.__init__(self) 992 method = string.upper(method)1059 method = method.upper() 993 1060 if method not in ["GET", "POST"]: 994 raise ValueError , "method must be GET or POST"1061 raise ValueError("method must be GET or POST") 995 1062 self.host = host 996 1063 self.url = url … … 1009 1076 Emit a record. 1010 1077 1011 Send the record to the Web server as a n URL-encoded dictionary1078 Send the record to the Web server as a percent-encoded dictionary 1012 1079 """ 1013 1080 try: … … 1018 1085 data = urllib.urlencode(self.mapLogRecord(record)) 1019 1086 if self.method == "GET": 1020 if ( string.find(url,'?') >= 0):1087 if (url.find('?') >= 0): 1021 1088 sep = '&' 1022 1089 else: … … 1026 1093 # support multiple hosts on one IP address... 1027 1094 # need to strip optional :port from host, if present 1028 i = string.find(host,":")1095 i = host.find(":") 1029 1096 if i >= 0: 1030 1097 host = host[:i] … … 1034 1101 "application/x-www-form-urlencoded") 1035 1102 h.putheader("Content-length", str(len(data))) 1036 h.endheaders() 1037 if self.method == "POST": 1038 h.send(data) 1103 h.endheaders(data if self.method == "POST" else None) 1039 1104 h.getreply() #can't do anything with the result 1040 1105 except (KeyboardInterrupt, SystemExit): … … 1083 1148 This version just zaps the buffer to empty. 1084 1149 """ 1085 self.buffer = [] 1150 self.acquire() 1151 try: 1152 self.buffer = [] 1153 finally: 1154 self.release() 1086 1155 1087 1156 def close(self): … … 1131 1200 different behaviour. 1132 1201 """ 1133 if self.target: 1134 for record in self.buffer: 1135 self.target.handle(record) 1136 self.buffer = [] 1202 self.acquire() 1203 try: 1204 if self.target: 1205 for record in self.buffer: 1206 self.target.handle(record) 1207 self.buffer = [] 1208 finally: 1209 self.release() 1137 1210 1138 1211 def close(self): … … 1141 1214 """ 1142 1215 self.flush() 1143 self.target = None 1144 BufferingHandler.close(self) 1216 self.acquire() 1217 try: 1218 self.target = None 1219 BufferingHandler.close(self) 1220 finally: 1221 self.release()
Note:
See TracChangeset
for help on using the changeset viewer.