import asyncio from tabulate import tabulate from copy import deepcopy import re import os import sys import time lock = asyncio.Lock() logs_path = "/tmp/darkfi/" node_info = { } ping_times = { } def debug(line): #print(line) pass def process(info, line): regex_listen = re.compile( ".* Listening on (\d+[.]\d+[.]\d+[.]\d+:\d+)") regex_inbound_connect = re.compile( ".* Connected inbound \[(\d+[.]\d+[.]\d+[.]\d+:\d+)\]") regex_outbound_slots = re.compile( ".* Starting (\d+) outbound connection slots.") regex_outbound_connect = re.compile( ".* #(\d+) connected to outbound \[(\d+[.]\d+[.]\d+[.]\d+:\d+)\]") regex_channel_disconnected = re.compile( ".* Channel (\d+[.]\d+[.]\d+[.]\d+:\d+) disconnected") regex_pong_recv = re.compile( ".* Received Pong message (\d+)ms from \[(\d+[.]\d+[.]\d+[.]\d+:\d+)\]") if "net: P2p::start() [BEGIN]" in line: info["status"] = "p2p-start" elif "net: SeedSession::start() [START]" in line: info["status"] = "seed-start" elif "net: SeedSession::start() [END]" in line: info["status"] = "seed-done" elif "net: P2p::start() [END]" in line: info["status"] = "p2p-done" elif "net: P2p::run() [BEGIN]" in line: info["status"] = "p2p-run" elif "Not configured for accepting incoming connections." in line: info["inbounds"] = ["Disabled"] elif (match := regex_listen.match(line)) is not None: address = match.group(1) info["listen"] = address elif (match := regex_inbound_connect.match(line)) is not None: address = match.group(1) info["inbounds"].append(address) elif (match := regex_outbound_slots.match(line)) is not None: slots = match.group(1) info["outbounds"] = ["None" for _ in range(int(slots))] elif (match := regex_outbound_connect.match(line)) is not None: slot = match.group(1) address = match.group(2) info["outbounds"][int(slot)] = address elif (match := regex_channel_disconnected.match(line)) is not None: address = match.group(1) try: info["inbounds"].remove(address) except ValueError: pass try: idx = info["outbounds"].index(address) info["outbounds"][idx] = "None" except ValueError: pass elif (match := regex_pong_recv.match(line)) is not None: ping_time = match.group(1) address = match.group(2) ping_times[address] = ping_time async def scanner(filename): global table_data async with lock: node_info[filename] = { "status": "none", "inbounds": [], "outbounds": [], } info = node_info[filename] with open(logs_path + filename) as fileh: while True: line = fileh.readline() if line: debug("R: " + filename + ": " + line[:-1]) async with lock: process(info, line) else: await asyncio.sleep(0.5) def clear_lines(n): for i in range(n): sys.stdout.write('\033[F') def get_ping(addr): ping_time = "none" if addr in ping_times: ping_time = str(ping_times[addr]) + " ms" return ping_time def table_format(ninfo): table_data = [] for filename, info in ninfo.items(): table_data.append([filename, "", ""]) table_data.append(["", "status", info["status"]]) if "listen" in info: table_data.append(["", "listen", info["listen"]]) inbounds = info["inbounds"] if inbounds: table_data.append(["", "inbounds", inbounds[0], get_ping(inbounds[0])]) for inbound in inbounds[1:]: table_data.append(["", "", inbound, get_ping(inbound)]) outbounds = info["outbounds"] if outbounds: table_data.append(["", "outbounds", outbounds[0], get_ping(outbounds[0])]) for outbound in outbounds[1:]: table_data.append(["", "", outbound, get_ping(outbound)]) headers = ["Name", "Attribute", "Value", "Ping Times"] return headers, table_data async def refresh_table(tick=1): for filename in os.listdir(logs_path): asyncio.create_task(scanner(filename)) previous_lines = 0 while True: clear_lines(previous_lines) async with lock: ninfo = deepcopy(node_info) headers, table_data = table_format(ninfo) lines = tabulate(table_data, headers=headers).split("\n") debug("-------------------") for line in lines: print('\x1b[2K\r', end="") print(line) previous_lines = len(lines) await asyncio.sleep(1) asyncio.run(refresh_table())