CMS 3D CMS Logo

ztee.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 import argparse
4 import subprocess
5 import select, signal, time, errno
6 import sys, os
7 import zlib
8 
9 def log(s):
10  sys.stderr.write("m: " + s + "\n");
11  sys.stderr.flush()
12 
13 class GZipLog(object):
14  def __init__(self, log_file):
15  self.file = None
16 
17  self.last_flush = time.time()
18  self.zstream = zlib.compressobj(6, zlib.DEFLATED, zlib.MAX_WBITS | 16)
19 
20  # 'gzip' block will be flushed if we reach max bytes or the timeout
21  self.block = []
22  self.block_size = 0
23  self.block_size_max = 2*1024*1024 # bytes
24  self.block_timeout = 15 # seconds
25 
26  self.file = open(log_file, "wb+")
27  self.file_min_size = 2*1024*1024 # this amount of space we keep after truncating
28  self.file_max_size = 16*1024*1024 # we truncate if file gets bigger
29 
30  self.file_truncate_pos = None
31  self.file_truncate_state = None
32 
33  def write_block(self, data):
34  self.file.write(self.zstream.compress( data.encode("utf-8") ))
35  self.file.write(self.zstream.flush(zlib.Z_FULL_FLUSH))
36 
37  def flush_block(self):
38  self.last_flush = time.time()
39 
40  data, size, = "".join(self.block), self.block_size
41  self.block, self.block_size = [], 0
42 
43  if size == 0:
44  # nothing to do
45  return
46 
47  # check if we need to truncate the file
48  # 'size' refers to uncompressed data, so the file be truncated at max_size -+ max_block_size
49  # there is no way know compressed size without collapsing the zlib state
50  if (self.file.tell() + size) > self.file_max_size:
51  if self.file_truncate_pos is not None:
52  # tell it to cleanup
53  self.zstream.flush(zlib.Z_FINISH)
54 
55  self.file.seek(self.file_truncate_pos, 0)
56  self.file.truncate()
57  self.zstream = self.file_truncate_state.copy()
58 
59  self.write_block("\n\n--- file was cut at this point ---\n\n")
60 
61  # write it
62  self.write_block(data)
63 
64  # check if we can save the ptr into truncate_position
65  if self.file_truncate_pos is None:
66  if self.file.tell() >= self.file_min_size:
67  self.file_truncate_pos = self.file.tell()
68  self.file_truncate_state = self.zstream.copy()
69 
70  def finish(self):
71  self.flush_block()
72  self.file.write(self.zstream.flush(zlib.Z_FINISH))
73  self.file.close()
74  self.file = None
75 
76  def try_flush(self):
77  timeout = (time.time() - self.last_flush) > self.block_timeout
78  large_block = self.block_size > self.block_size_max
79 
80  if timeout or large_block:
81  self.flush_block()
82 
83  def write(self, bytes):
84  if bytes:
85  self.block.append(bytes)
86  self.block_size += len(bytes)
87 
88  self.try_flush()
89 
90  def handle_timeout(self):
91  self.try_flush()
92 
93 
94 def capture(fd, args):
95  log_handler = GZipLog(log_file=args.log)
96 
97  def sigusr1_handle(*kargs, **kwargs):
98  log_handler._sigusr_interrupt = True
99  if log_handler.file:
100  log_handler.flush_block()
101 
102  signal.signal(signal.SIGUSR1, sigusr1_handle)
103 
104  try:
105  while True:
106  try:
107  log_handler._sigusr_interrupt = False
108  rlist, wlist, xlist = select.select([fd], [], [], 5)
109  except select.error as e:
110  if e[0] != errno.EINTR: raise
111  if not log_handler._sigusr_interrupt: raise
112 
113  continue
114 
115  if not rlist:
116  log_handler.handle_timeout()
117  continue
118 
119  bytes = os.read(fd, 4096)
120  if (bytes == ''):
121  break
122 
123  log_handler.write(bytes)
124  if not args.q:
125  sys.stdout.write(bytes)
126  finally:
127  log_handler.finish()
128 
129 if __name__ == "__main__":
130  parser = argparse.ArgumentParser(description="Take all input and write a compressed log file.")
131  parser.add_argument('-q', action='store_true', help="Don't write to stdout, just the log file.")
132  parser.add_argument("log", type=str, help="Filename to write.", metavar="<logfile.gz>")
133  args = parser.parse_args()
134 
135  if args.q:
136  fd = sys.stdout.fileno()
137  sys.stdout.close()
138  os.close(fd)
139 
140  capture(sys.stdin.fileno(), args)
def finish(self)
Definition: ztee.py:70
def handle_timeout(self)
Definition: ztee.py:90
file_min_size
Definition: ztee.py:27
def capture(fd, args)
Definition: ztee.py:94
last_flush
Definition: ztee.py:17
block_size
Definition: ztee.py:22
file_truncate_state
Definition: ztee.py:31
def write(self, bytes)
Definition: ztee.py:83
static std::string join(char **cmd)
Definition: RemoteFile.cc:21
def write_block(self, data)
Definition: ztee.py:33
def log(s)
Definition: ztee.py:9
block_timeout
Definition: ztee.py:24
def flush_block(self)
Definition: ztee.py:37
file_max_size
Definition: ztee.py:28
def try_flush(self)
Definition: ztee.py:76
file_truncate_pos
Definition: ztee.py:30
block_size_max
Definition: ztee.py:23
def __init__(self, log_file)
Definition: ztee.py:14