Changeset 77:8f70e7f3ca6b in livinglogic.python.tipimaid

Show
Ignore:
Timestamp:
02/02/09 18:27:22 (10 years ago)
Author:
Nik Tautenhahn <nik@…>
Branch:
default
Message:

liaalh has an execute switch to execute arbitrary commands with the old logfile as parameter

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • liaalh.py

    r75 r77  
    33 
    44# Apache sends a SIGTERM to its piped logger when it exits - when apache is killed with -9, it doesn't send anything 
    5  
    6 import sys, os, datetime, errno, bisect, re, gzip, signal, time 
    7  
     5# TODO: Add an option to gzip a logfile when a new one is started 
     6 
     7import sys, os, datetime, errno, bisect, re, gzip, signal, time, threading 
     8 
     9subprocessworks = False 
     10try: 
     11    import subprocess # only python >= 2.6 
     12    subprocessworks = True 
     13except ImportError: 
     14    pass 
    815 
    916class LogLine(tuple): 
     
    1623 
    1724class Buffer(object): 
    18     def __init__(self, pattern='', gzip_logs=None, buffertime=0, stream=sys.stdin, utcrotate=False, symlinkpattern=None): 
     25    def __init__(self, pattern='', gzip_logs=None, buffertime=0, stream=sys.stdin, utcrotate=False, symlinkpattern=None, execute=None): 
    1926        self.pattern = pattern 
    2027        self.gzip_logs = gzip_logs 
     
    2734            self.pattern = "%s.gz" % self.pattern 
    2835        self.utcrotate = utcrotate 
     36        self.execute = execute 
     37        if execute is not None: 
     38            if subprocessworks: 
     39                self.do_something = self.do_something_subprocess 
    2940        self.updateutcoffset() 
    3041        self.handlevirtualhost = "%v" in pattern 
     
    92103                f.flush() 
    93104                f.close() 
     105                if self.execute is not None: 
     106                    thr = threading.Thread(target=self.do_something, args=[os.path.abspath(f.name)]) 
     107                    thr.start() 
    94108                self.updateutcoffset() 
    95109                self.servers[server] = f = self.openfile(filename, server) 
     
    107121                utclogdate = self.apachedate2utc(datestring) 
    108122                self.writeline(utclogdate, server, data) 
    109             except IndexError, exc: # index error bc we didn't find an apache date -> malformed logline 
     123            except IndexError, exc: # index error because we didn't find an apache date -> malformed logline 
    110124                continue # ignore it 
    111125 
     
    123137                    utclogdate = self.apachedate2utc(datestring) 
    124138                    self.add(LogLine((utclogdate, server, data))) 
    125                 except IndexError, exc: # index error bc we didn't find an apache date -> malformed logline 
     139                except IndexError, exc: # index error because we didn't find an apache date -> malformed logline 
    126140                    continue # ignore it 
    127141                except Exception, exc: 
     
    132146            raise 
    133147 
    134     def add(self, logline): 
     148    def do_something_subprocess(self, filename): 
     149        try: 
     150            retcode = subprocess.call([self.execute, filename]) 
     151        except OSError, exc: 
     152            sys.stderr.write("Subprocess \"%s %s\" caused exception %r" % (self.execute, filename, retcode)) 
     153            sys.stderr.flush() 
     154        if retcode != 0: 
     155            sys.stderr.write("Subprocess \"%s %s\" returned error code %s" % (self.execute, filename, retcode)) 
     156            sys.stderr.flush() 
     157 
     158    def do_something(self, filename): 
     159        try: 
     160            retcode = os.system("%s %s" % (self.execute, filename)) 
     161        except OSError, exc: 
     162            sys.stderr.write("Subprocess \"%s %s\" caused exception %r" % (self.execute, filename, retcode)) 
     163            sys.stderr.flush() 
     164        if retcode != 0: 
     165            sys.stderr.write("Subprocess \"%s %s\" returned error code %s" % (self.execute, filename, retcode)) 
     166            sys.stderr.flush() 
     167 
     168    def add(self, logline): # keep entries in the buffer sorted 
    135169        if not self.data or self.data[-1] <= logline: 
    136170            self.data.append(logline) 
     
    174208    import optparse 
    175209    p = optparse.OptionParser(usage="usage: %prog filename-pattern [options]\nIf you use virtual hosts please note that the virtual host column (%v) has to be the first column in every logfile!") 
    176     p.add_option("-z", "--gzip", dest="gzip", type="int", action="store", help="If set, logs are gzipped with this compression level (lowest: 1, highest: 9)", default=None) 
    177210    p.add_option("-b", "--buffertime", dest="buffertime", type="int", action="store", help="Time in seconds for which log entries are buffered, default=0. Set to 0 to disable buffering", default=0) 
     211    p.add_option("-z", "--continuous-gzip", dest="gzip", type="int", action="store", help="If set, logs are (continuously!) gzipped with this compression level (lowest: 1, highest: 9)", default=None) 
    178212    p.add_option("-u", "--utcrotate", dest="utcrotate", action="store_true", help="If set, UTC time determines the time for filenames and rotation. Otherwise local time is used.", default=False) 
    179213    p.add_option("-s", "--symlink", dest="symlinkpattern", action="store", help="""Create a symlink pointing to the most recent log file (of each virtual host if you use %v). Needs a filename pattern for the symlink but only "%v" is allowed here as symlinks which include time/date data are useless, e.g. %v/access.log or symlinks/access-%v.log""", default=None) 
     214    p.add_option("-x", "--execute", dest="execute", type="string", action="store", help="After writing to a logfile is finished and a new one is created (e.g. after rotating the logs), the given executable is started with the logfile as its first (and only) parameter. You could use gzip, bzip2, a self-written bash-script or even rm here...", default=None) 
     215    #TODO: What happens with execute's output? 
    180216    (options, args) = p.parse_args() 
    181217    if options.gzip is not None: 
     
    189225        sys.stderr.flush() 
    190226        return 1 
    191     if options.symlinkpattern is not None and "%v" in args[0] and ("%v" in options.symlinkpattern and options.symlinkpattern.count("%") > 1) or ("%v" not in options.symlinkpattern): 
     227    if options.symlinkpattern is not None and ("%v" in args[0] and ("%v" in options.symlinkpattern and options.symlinkpattern.count("%") > 1) or ("%v" not in options.symlinkpattern)): 
    192228        p.print_usage(sys.stderr) 
    193229        sys.stderr.write("%s: If you split logfiles by virtual hosts you should use virtual hosts (%v) in the symlink-pattern as well. But you shouldn't use any patterns for time/date data.\n" % p.get_prog_name()) 
     
    195231        return 1 
    196232 
    197     buf = Buffer(pattern=args[0], gzip_logs=options.gzip, buffertime=options.buffertime, utcrotate=options.utcrotate, symlinkpattern=options.symlinkpattern) 
     233    buf = Buffer(pattern=args[0], gzip_logs=options.gzip, buffertime=options.buffertime, utcrotate=options.utcrotate, symlinkpattern=options.symlinkpattern, execute=options.execute) 
    198234    buf.run() 
    199235