Initial ftail implementation

This commit is contained in:
Dirk Alders 2023-08-27 21:07:42 +02:00
parent 13b6ea2d9a
commit 00d7c6dd42
5 changed files with 154 additions and 0 deletions

6
.gitmodules vendored Normal file
View File

@ -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

1
console_bottombar Submodule

@ -0,0 +1 @@
Subproject commit c9d7f78f6d0feeeb5e14e4ca6a8ca22ce1c81165

68
ftail.py Normal file
View File

@ -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()

78
tail.py Normal file
View File

@ -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 <kasunh01 at gmail.com>
# 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

1
task Submodule

@ -0,0 +1 @@
Subproject commit af35e83d1f07fd4cb9070bdb77cf1f3bdda3a463