#!/usr/bin/env pythonimport optparseimport osimport sysimport timefrom thrift.transport import TTransportfrom thrift.transport import TSocketfrom thrift.transport.TTransport import TTransportExceptionfrom thrift.protocol import TBinaryProtocolfrom scribe import scribeclass Error(Exception): passclass FileError(Error): passclass Tail(object): def __init__(self, path, sleep=1.0, reopen_count=5): self.path = path self.sleep = sleep self.reopen_count = reopen_count def __iter__(self): while True: pos = self.file.tell() line = self.file.readline() if not line: self.wait(pos) else: yield line def open(self, tail=True): try: self.real_path = os.path.realpath(self.path) self.inode = os.stat(self.path).st_ino except OSError, error: raise FileError(error) try: self.file = open(self.real_path) except IOError, error: raise FileError(error) if tail: self.file.seek(0, 2) def close(self): try: self.file.close() except Exception: pass def reopen(self): self.close() reopen_count = self.reopen_count while reopen_count >= 0: reopen_count -= 1 try: self.open(tail=False) return True except FileError: time.sleep(self.sleep) return False def check(self, pos): try: if self.real_path != os.path.realpath(self.path): return True stat = os.stat(self.path) if self.inode != stat.st_ino: return True if pos > stat.st_size: return True except OSError: return True return False def wait(self, pos): if self.check(pos): if not self.reopen(): raise Error('Unable to reopen file: %s' % self.path) else: self.file.seek(pos) time.sleep(self.sleep)def scribe_fix_legacy(): global scribe old_log_entry = scribe.LogEntry def new_log_entry(**kwargs): return old_log_entry(kwargs) scribe.LogEntry = new_log_entrydef handle(path, category, host='127.0.0.1', port=1463, prefix='', postfix=''): result = 0 socket = TSocket.TSocket(host=host, port=port) transport = TTransport.TFramedTransport(socket) protocol = TBinaryProtocol.TBinaryProtocol( trans=transport, strictRead=False, strictWrite=False, ) client = scribe.Client(iprot=protocol, oprot=protocol) try: transport.open() tail = Tail(path) try: tail.open() for line in tail: try: log_entry = scribe.LogEntry( category=category, message=prefix+line+postfix, ) except TypeError: scribe_fix_legacy() log_entry = scribe.LogEntry( category=category, message=prefix+line+postfix, ) result = client.Log(messages=[log_entry]) finally: tail.close() finally: try: transport.close() except Exception: pass if result == scribe.ResultCode.OK: pass elif result == scribe.ResultCode.TRY_LATER: raise Error('Scribe Error: TRY LATER') else: raise Error('Scribe Error: Unknown error code (%s)' % result)if __name__ == '__main__': parser = optparse.OptionParser() parser.add_option( '--file', dest='file', help='file to tail into Scribe', metavar='FILE', ) parser.add_option( '--category', dest='category', help='Scribe category', metavar='CATEGORY', ) parser.add_option( '--host', default='127.0.0.1', dest='host', help='destination Scribe host server', metavar='HOST', ) parser.add_option( '--port', default=1463, dest='port', help='destination Scribe port', metavar='PORT', type='int', ) parser.add_option( '--prefix', default='', dest='prefix', help='add to the beginning of each log line', metavar='PREFIX', ) parser.add_option( '--postfix', default='', dest='postfix', help='add to the end of each log line', metavar='POSTFIX', ) options, args = parser.parse_args() if options.file and options.category: try: handle( path=options.file, category=options.category, host=options.host, port=options.port, prefix=options.prefix, postfix=options.postfix, ) except KeyboardInterrupt: sys.exit(0) except (Error, TTransportException), error: print >> sys.stderr, error sys.exit(1) else: parser.print_help()
usage: scribe_log [options]options: -h, --help show this help message and exit --file=FILE file to tail into Scribe --category=CATEGORY Scribe category --host=HOST destination Scribe host server --port=PORT destination Scribe port --prefix=PREFIX add to the beginning of each log line --postfix=POSTFIX add to the end of each log line