Source code for honeycomb.utils.tailer
# -*- coding: utf-8 -*-
"""Honeycomb service log tailer."""
import os
import sys
import time
import random
import logging
import click
from attr import attrs, attrib
logger = logging.getLogger(__name__)
[docs]@attrs
class Tailer(object):
"""Colorized file tailer.
Print lines from a file prefixed with a colored name. Optionally continue to follow file.
"""
name = attrib(type=str)
filepath = attrib(type=str)
color = attrib("", type=str)
nlines = attrib(10, type=int)
follow = attrib(False, type=bool)
outfile = attrib(sys.stdout)
sleeptime = attrib(0.5, type=int)
show_name = attrib(True, type=bool)
used_colors = attrib([], type=list)
colors = attrib(["red", "green", "yellow", "magenta", "blue", "cyan"], init=False)
[docs] def print_log(self, line):
"""Print a line from a logfile."""
click.echo(line.replace("\n", ""), file=self.outfile)
[docs] def print_named_log(self, line):
"""Print a line from a logfile prefixed with service name."""
click.echo("{}: {}".format(click.style(self.name, fg=self.color), line.replace("\n", "")), file=self.outfile)
def _print(self, line):
if self.show_name:
self.print_named_log(line)
else:
self.print_log(line)
[docs] def follow_file(self):
"""Follow a file and send every new line to a callback."""
logger.debug("following %s", self.filepath)
with open(self.filepath) as fh:
# Go to the end of file
fh.seek(0, os.SEEK_END)
while True:
curr_position = fh.tell()
line = fh.readline()
if not line:
fh.seek(curr_position)
time.sleep(self.sleeptime)
else:
self._print(line)
def __attrs_post_init__(self):
"""Seek file from end for nlines and call printlog on them, then follow if needed."""
logger.debug("reading %d lines from %s", self.nlines, self.filepath)
if not self.color:
self.color = self.colors[random.randint(0, len(self.colors) - 1)]
with open(self.filepath) as fh:
fh.seek(0, os.SEEK_END)
end_position = curr_position = fh.tell()
line_count = 0
while curr_position >= 0:
fh.seek(curr_position)
next_char = fh.read(1)
if next_char == "\n" and curr_position != end_position - 1:
line_count += 1
if line_count == self.nlines:
break
curr_position -= 1
if curr_position < 0:
fh.seek(0)
for line in fh.readlines():
self._print(line)
if self.follow:
self.follow_file()
[docs] def stop(self):
"""Stop follow."""
self.running = False