CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ztail.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import time
4 import os
5 import sys
6 import zlib
7 import struct
8 
9 DECOMPRESS_BUF_SIZE = 4*1024*1024
10 
11 BLOCK_MAGIC = "\x00\x00\xFF\xFF"
12 ENDLINE_MAGIC = "\n"
13 
14 # we treat everything as a deflate stream
15 # gzip header has no power here
17  assert body[0:2] == "\x1f\x8b"
18  method, flags, mtime = struct.unpack("<BBIxx", body[2:10])
19 
20  FHCRC = 0x02
21  FEXTRA = 0x04
22  FNAME = 0x08
23  FCOMMENT = 0x10
24 
25  i = 10
26 
27  if flags & FEXTRA:
28  size, = struct.unpack("<H", body[i:i+2])
29  i += size + 2
30 
31  def skip_until_zero(ix):
32  while body[ix] != '\x00': ix += 1
33  return ix + 1
34 
35  if flags & FNAME: i = skip_until_zero(i)
36  if flags & FCOMMENT: i = skip_until_zero(i)
37  if flags & FHCRC: i += 2
38 
39  body = body[i:]
40  return body
41 
42 class Decoder(object):
43  def __init__(self, fname, last_n_lines):
44  self.f = open(fname, "rb")
45  self.last_n_lines = last_n_lines
46  self.reset()
47 
48  def reset(self):
49  self.sync = False
50  if hasattr(self, 'zstream'):
51  self.zstream.flush()
52 
53  self.zstream = zlib.decompressobj(-zlib.MAX_WBITS)
54 
55  def decode(self, bytes, if_start=False):
56  if not bytes:
57  return ""
58 
59  if if_start:
60  self.sync = True
61  #self.zstream = zlib.decompressobj(zlib.MAX_WBITS | 32)
62  bytes = strip_gzip_header(bytes)
63  elif not self.sync:
64  x = bytes.find(BLOCK_MAGIC)
65  if x != -1:
66  bytes = bytes[x + len(BLOCK_MAGIC):]
67  self.sync = True
68 
69  if not self.sync:
70  # not in sync, can't decode
71  return ""
72 
73  text = self.zstream.decompress(bytes)
74  #print "decoded:", len(text), len(self.zstream.unused_data)
75  if len(self.zstream.unused_data) == 8:
76  # this usually means checksum and len is left
77  # but we don't care about any of those!
78  self.zstream.flush()
79  self.zstream = None
80 
81  return text
82 
83  def output_line(self, line):
84  sys.stdout.write(line)
85  sys.stdout.flush()
86 
88  f = self.f
89 
90  f.seek(0, 2)
91  end = f.tell()
92  start = max(0, end - DECOMPRESS_BUF_SIZE)
93  f.seek(start, 0)
94 
95  body = f.read(end - start)
96  text = self.decode(body, start == 0)
97 
98  self.known_size = end
99  return text
100 
101  def initial(self):
102  text = self.initial_synchronize()
103 
104  n_lines = self.last_n_lines
105  lines = text.rsplit(ENDLINE_MAGIC, n_lines + 1)
106  if len(lines) > n_lines:
107  lines = lines[1:]
108 
109  self.output_line(ENDLINE_MAGIC.join(lines))
110 
111  def follow(self):
112  if self.known_size is None:
113  raise Exception("Call initial() first.")
114 
115  while self.zstream:
116  size = os.fstat(self.f.fileno()).st_size
117 
118  if self.known_size > size:
119  sys.stderr.write("%s: file truncated\n" % sys.argv[0])
120  sys.stderr.write("%s: waiting for the next write\n" % sys.argv[0])
121  sys.stderr.flush()
122 
123  if self.sync:
124  self.sync = False
125  self.zstream.flush()
126  self.zstream = zlib.decompressobj(-zlib.MAX_WBITS)
127 
128  text = self.initial_synchronize()
129  continue
130  elif self.known_size == size:
131  time.sleep(1)
132  continue
133 
134  assert self.f.tell() == self.known_size
135  body = self.f.read(size - self.known_size)
136 
137  text = self.decode(body, self.known_size == 0)
138  self.output_line(text)
139 
140  self.known_size = size
141 
142 if __name__ == "__main__":
143  import argparse
144  parser = argparse.ArgumentParser(description='tail, but for gzip files (with Z_FULL_SYNC)')
145  parser.add_argument('-f', action="store_true", help='watch the file for changes')
146  parser.add_argument('-n', type=int, help='output the last K lines', metavar='K', default=10)
147  parser.add_argument('file', help="file name to watch")
148 
149  args = parser.parse_args()
150 
151  d = Decoder(args.file, args.n)
152  d.initial()
153 
154  if args.f:
155  d.follow()
def strip_gzip_header
Definition: ztail.py:16
def output_line
Definition: ztail.py:83
def __init__
Definition: ztail.py:43
def decode
Definition: ztail.py:55
def follow
Definition: ztail.py:111
def initial
Definition: ztail.py:101
def reset
Definition: ztail.py:48
def initial_synchronize
Definition: ztail.py:87