diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..900aa70 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "console_bottombar"] + path = console_bottombar + url = https://git.mount-mockery.de/pylib/console_bottombar.git +[submodule "task"] + path = task + url = https://git.mount-mockery.de/pylib/task.git diff --git a/console_bottombar b/console_bottombar new file mode 160000 index 0000000..c9d7f78 --- /dev/null +++ b/console_bottombar @@ -0,0 +1 @@ +Subproject commit c9d7f78f6d0feeeb5e14e4ca6a8ca22ce1c81165 diff --git a/ftail.py b/ftail.py new file mode 100644 index 0000000..35a4e08 --- /dev/null +++ b/ftail.py @@ -0,0 +1,68 @@ +import argparse +from console_bottombar import BottomBar +import os +import re +import sys +import tail +import task + +VERSION = "0.1.0" + +HELPTEXT = """ +F1: Get this help message +F2: Set a filter (regular expression) for the topic of a message + Examples: + * "/gfw/.*/main_light/" to get everything with "/gfw/" before "/main_light/" + * "^zigbee.*(?>!logging)$" to get everything starting with "zigbee" and not ending with "logging" + * "^(?!shellies).*/dirk/.*temperature$" to get everything not starting with "shellies" followed by "/dirk/" and ends with "temperature" +F12: Quit ftail + +'c': Clear screen +'q': Quit +""" + + +def tail_parser(line): + global my_bb + + try: + match = len(re.findall(my_bb.get_entry('msg_re'), line)) > 0 + except re.error: + print('No valid regular expression (%s). No filter active.' % my_bb.get_entry('msg_re')) + match = True + + if match: + sys.stdout.write(line) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='This is a mqtt sniffer.') + parser.add_argument('logfile') # , nargs='+' for >= 1 + + args = parser.parse_args() + + + if not os.access(args.logfile, os.R_OK): + print("Error: File %s is not readable!" % logfile) + sys.exit(1) + else: + + # + # Background tail task + # + tp = tail.Tail(args.logfile) + tp.register_callback(tail_parser) + my_tail = task.delayed(0.1, tp.follow, 0.1) + + # + # Bottombar + # + my_bb = BottomBar(VERSION, label='ftail') + my_bb.add_entry('help', 1, my_bb.FUNC_INFO, '[F1] Help', infotext=HELPTEXT) + my_bb.add_entry('msg_re', 2, my_bb.FUNC_TEXT, label='[F2] Filter', default="") + my_bb.add_entry('quit', 12, my_bb.FUNC_QUIT, "Quit", label='[F12]', right=True) + + my_tail.run() + my_bb.run() + + my_tail.stop() \ No newline at end of file diff --git a/tail.py b/tail.py new file mode 100644 index 0000000..028cfc2 --- /dev/null +++ b/tail.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +''' +Python-Tail - Unix tail follow implementation in Python. + +python-tail can be used to monitor changes to a file. + +Example: + import tail + + # Create a tail instance + t = tail.Tail('file-to-be-followed') + + # Register a callback function to be called when a new line is found in the followed file. + # If no callback function is registered, new lines would be printed to standard out. + t.register_callback(callback_function) + + # Follow the file with 5 seconds as sleep time between iterations. + # If sleep time is not provided 1 second is used as the default time. + t.follow(s=5) ''' + +# Author - Kasun Herath +# Source - https://github.com/kasun/python-tail + +import os +import sys +import time + +class Tail(object): + ''' Represents a tail command. ''' + def __init__(self, tailed_file): + ''' Initiate a Tail instance. + Check for file validity, assigns callback function to standard out. + + Arguments: + tailed_file - File to be followed. ''' + + self.check_file_validity(tailed_file) + self.tailed_file = tailed_file + self.callback = sys.stdout.write + + def follow(self, s=1): + ''' Do a tail follow. If a callback function is registered it is called with every new line. + Else printed to standard out. + + Arguments: + s - Number of seconds to wait between each iteration; Defaults to 1. ''' + + with open(self.tailed_file) as file_: + # Go to the end of file + file_.seek(0,2) + while True: + curr_position = file_.tell() + line = file_.readline() + if not line: + file_.seek(curr_position) + time.sleep(s) + else: + self.callback(line) + + def register_callback(self, func): + ''' Overrides default callback function to provided function. ''' + self.callback = func + + def check_file_validity(self, file_): + ''' Check whether the a given file exists, readable and is a file ''' + if not os.access(file_, os.F_OK): + raise TailError("File '%s' does not exist" % (file_)) + if not os.access(file_, os.R_OK): + raise TailError("File '%s' not readable" % (file_)) + if os.path.isdir(file_): + raise TailError("File '%s' is a directory" % (file_)) + +class TailError(Exception): + def __init__(self, msg): + self.message = msg + def __str__(self): + return self.message diff --git a/task b/task new file mode 160000 index 0000000..af35e83 --- /dev/null +++ b/task @@ -0,0 +1 @@ +Subproject commit af35e83d1f07fd4cb9070bdb77cf1f3bdda3a463