File indexing completed on 2024-04-06 12:10:15
0001
0002
0003 import argparse
0004 import subprocess
0005 import select, signal, time, errno
0006 import sys, os
0007 import zlib
0008
0009 def log(s):
0010 sys.stderr.write("m: " + s + "\n");
0011 sys.stderr.flush()
0012
0013 class GZipLog(object):
0014 def __init__(self, log_file):
0015 self.file = None
0016
0017 self.last_flush = time.time()
0018 self.zstream = zlib.compressobj(6, zlib.DEFLATED, zlib.MAX_WBITS | 16)
0019
0020
0021 self.block = []
0022 self.block_size = 0
0023 self.block_size_max = 2*1024*1024
0024 self.block_timeout = 15
0025
0026 self.file = open(log_file, "wb+")
0027 self.file_min_size = 2*1024*1024
0028 self.file_max_size = 16*1024*1024
0029
0030 self.file_truncate_pos = None
0031 self.file_truncate_state = None
0032
0033 def write_block(self, data):
0034 self.file.write(self.zstream.compress( data.encode("utf-8") ))
0035 self.file.write(self.zstream.flush(zlib.Z_FULL_FLUSH))
0036
0037 def flush_block(self):
0038 self.last_flush = time.time()
0039
0040 data, size, = "".join(self.block), self.block_size
0041 self.block, self.block_size = [], 0
0042
0043 if size == 0:
0044
0045 return
0046
0047
0048
0049
0050 if (self.file.tell() + size) > self.file_max_size:
0051 if self.file_truncate_pos is not None:
0052
0053 self.zstream.flush(zlib.Z_FINISH)
0054
0055 self.file.seek(self.file_truncate_pos, 0)
0056 self.file.truncate()
0057 self.zstream = self.file_truncate_state.copy()
0058
0059 self.write_block("\n\n--- file was cut at this point ---\n\n")
0060
0061
0062 self.write_block(data)
0063
0064
0065 if self.file_truncate_pos is None:
0066 if self.file.tell() >= self.file_min_size:
0067 self.file_truncate_pos = self.file.tell()
0068 self.file_truncate_state = self.zstream.copy()
0069
0070 def finish(self):
0071 self.flush_block()
0072 self.file.write(self.zstream.flush(zlib.Z_FINISH))
0073 self.file.close()
0074 self.file = None
0075
0076 def try_flush(self):
0077 timeout = (time.time() - self.last_flush) > self.block_timeout
0078 large_block = self.block_size > self.block_size_max
0079
0080 if timeout or large_block:
0081 self.flush_block()
0082
0083 def write(self, bytes):
0084 if bytes:
0085 self.block.append(bytes)
0086 self.block_size += len(bytes)
0087
0088 self.try_flush()
0089
0090 def handle_timeout(self):
0091 self.try_flush()
0092
0093
0094 def capture(fd, args):
0095 log_handler = GZipLog(log_file=args.log)
0096
0097 def sigusr1_handle(*kargs, **kwargs):
0098 log_handler._sigusr_interrupt = True
0099 if log_handler.file:
0100 log_handler.flush_block()
0101
0102 signal.signal(signal.SIGUSR1, sigusr1_handle)
0103
0104 try:
0105 while True:
0106 try:
0107 log_handler._sigusr_interrupt = False
0108 rlist, wlist, xlist = select.select([fd], [], [], 5)
0109 except select.error as e:
0110 if e[0] != errno.EINTR: raise
0111 if not log_handler._sigusr_interrupt: raise
0112
0113 continue
0114
0115 if not rlist:
0116 log_handler.handle_timeout()
0117 continue
0118
0119 bytes = os.read(fd, 4096)
0120 if (bytes == ''):
0121 break
0122
0123 log_handler.write(bytes)
0124 if not args.q:
0125 sys.stdout.write(bytes)
0126 finally:
0127 log_handler.finish()
0128
0129 if __name__ == "__main__":
0130 parser = argparse.ArgumentParser(description="Take all input and write a compressed log file.")
0131 parser.add_argument('-q', action='store_true', help="Don't write to stdout, just the log file.")
0132 parser.add_argument("log", type=str, help="Filename to write.", metavar="<logfile.gz>")
0133 args = parser.parse_args()
0134
0135 if args.q:
0136 fd = sys.stdout.fileno()
0137 sys.stdout.close()
0138 os.close(fd)
0139
0140 capture(sys.stdin.fileno(), args)