Added log viewer

This commit is contained in:
Mark Qvist 2024-12-07 22:27:39 +01:00
parent 922107df50
commit 9e992c83fd
3 changed files with 104 additions and 14 deletions

View File

@ -16,6 +16,7 @@ import multiprocessing.connection
from copy import deepcopy from copy import deepcopy
from threading import Lock from threading import Lock
from collections import deque
from .res import sideband_fb_data from .res import sideband_fb_data
from .sense import Telemeter, Commands from .sense import Telemeter, Commands
from .plugins import SidebandCommandPlugin, SidebandServicePlugin, SidebandTelemetryPlugin from .plugins import SidebandCommandPlugin, SidebandServicePlugin, SidebandTelemetryPlugin
@ -110,6 +111,8 @@ class SidebandCore():
DEFAULT_APPEARANCE = ["account", [0,0,0,1], [1,1,1,1]] DEFAULT_APPEARANCE = ["account", [0,0,0,1], [1,1,1,1]]
LOG_DEQUE_MAXLEN = 256
aspect_filter = "lxmf.delivery" aspect_filter = "lxmf.delivery"
def received_announce(self, destination_hash, announced_identity, app_data): def received_announce(self, destination_hash, announced_identity, app_data):
# Add the announce to the directory announce # Add the announce to the directory announce
@ -142,6 +145,7 @@ class SidebandCore():
self.is_standalone = False self.is_standalone = False
self.log_verbose = verbose self.log_verbose = verbose
self.log_deque = deque(maxlen=self.LOG_DEQUE_MAXLEN)
self.owner_app = owner_app self.owner_app = owner_app
self.reticulum = None self.reticulum = None
self.webshare_server = None self.webshare_server = None
@ -3630,6 +3634,14 @@ class SidebandCore():
if self.is_client: if self.is_client:
self.service_rpc_set_debug(debug) self.service_rpc_set_debug(debug)
def _log_handler(self, message):
self.log_deque.append(message)
print(message)
# TODO: Get service log on Android
def get_log(self):
return "\n".join(self.log_deque)
def __start_jobs_immediate(self): def __start_jobs_immediate(self):
if self.log_verbose: if self.log_verbose:
selected_level = 6 selected_level = 6
@ -3637,7 +3649,7 @@ class SidebandCore():
selected_level = 2 selected_level = 2
self.setstate("init.loadingstate", "Substantiating Reticulum") self.setstate("init.loadingstate", "Substantiating Reticulum")
self.reticulum = RNS.Reticulum(configdir=self.rns_configdir, loglevel=selected_level) self.reticulum = RNS.Reticulum(configdir=self.rns_configdir, loglevel=selected_level, logdest=self._log_handler)
if self.is_service: if self.is_service:
self.__start_rpc_listener() self.__start_rpc_listener()

View File

@ -1287,7 +1287,7 @@ layout_settings_screen = """
MDLabel: MDLabel:
id: scaling_info id: scaling_info
markup: True markup: True
text: "You can scale the entire Sideband UI by specifying a scaling factor in the field below. After setting it, restart sideband for the scaling to take effect.\\n\\nSet to 0.0 to disable scaling adjustments." text: "You can scale the entire Sideband UI by specifying a scaling factor in the field below. After setting it, restart sideband for the scaling to take effect.\\n\\nSet to 0.0 to disable scaling adjustments.\\n\\n[b]Please note![/b] On some devices, the default scaling factor will be higher than 1.0, and setting a smaller value will result in miniscule UI elements."
size_hint_y: None size_hint_y: None
text_size: self.width, None text_size: self.width, None
height: self.texture_size[1] height: self.texture_size[1]

View File

@ -10,6 +10,7 @@ from kivymd.uix.recycleview import MDRecycleView
from kivymd.uix.list import OneLineIconListItem from kivymd.uix.list import OneLineIconListItem
from kivymd.uix.pickers import MDColorPicker from kivymd.uix.pickers import MDColorPicker
from kivymd.icon_definitions import md_icons from kivymd.icon_definitions import md_icons
from kivymd.toast import toast
from kivy.properties import StringProperty, BooleanProperty from kivy.properties import StringProperty, BooleanProperty
from kivy.effects.scroll import ScrollEffect from kivy.effects.scroll import ScrollEffect
from kivy.clock import Clock from kivy.clock import Clock
@ -29,6 +30,7 @@ class Utilities():
self.screen = None self.screen = None
self.rnstatus_screen = None self.rnstatus_screen = None
self.rnstatus_instance = None self.rnstatus_instance = None
self.logviewer_screen = None
if not self.app.root.ids.screen_manager.has_screen("utilities_screen"): if not self.app.root.ids.screen_manager.has_screen("utilities_screen"):
self.screen = Builder.load_string(layout_utilities_screen) self.screen = Builder.load_string(layout_utilities_screen)
@ -72,17 +74,12 @@ class Utilities():
import io import io
from contextlib import redirect_stdout from contextlib import redirect_stdout
output_marker = "===begin rnstatus output==="
output = "None" output = "None"
with io.StringIO() as buffer, redirect_stdout(buffer): with io.StringIO() as buffer, redirect_stdout(buffer):
print(output_marker, end="") with RNS.logging_lock:
self.rnstatus_instance.main(rns_instance=RNS.Reticulum.get_instance()) self.rnstatus_instance.main(rns_instance=RNS.Reticulum.get_instance())
output = buffer.getvalue() output = buffer.getvalue()
remainder = output[:output.find(output_marker)]
output = output[output.find(output_marker)+len(output_marker):]
print(remainder, end="")
def cb(dt): def cb(dt):
self.rnstatus_screen.ids.rnstatus_output.text = f"[font=RobotoMono-Regular]{output}[/font]" self.rnstatus_screen.ids.rnstatus_output.text = f"[font=RobotoMono-Regular]{output}[/font]"
Clock.schedule_once(cb, 0.2) Clock.schedule_once(cb, 0.2)
@ -90,6 +87,44 @@ class Utilities():
if self.app.root.ids.screen_manager.current == "rnstatus_screen": if self.app.root.ids.screen_manager.current == "rnstatus_screen":
Clock.schedule_once(self.update_rnstatus, 1) Clock.schedule_once(self.update_rnstatus, 1)
### Log viewer screen
######################################
def logviewer_action(self, sender=None):
if not self.app.root.ids.screen_manager.has_screen("logviewer_screen"):
self.logviewer_screen = Builder.load_string(layout_logviewer_screen)
self.logviewer_screen.app = self.app
self.logviewer_screen.delegate = self
self.app.root.ids.screen_manager.add_widget(self.logviewer_screen)
self.app.root.ids.screen_manager.transition.direction = "left"
self.app.root.ids.screen_manager.current = "logviewer_screen"
self.app.sideband.setstate("app.displaying", self.app.root.ids.screen_manager.current)
self.update_logviewer()
def update_logviewer(self, sender=None):
threading.Thread(target=self.update_logviewer_job, daemon=True).start()
def update_logviewer_job(self, sender=None):
try:
output = self.app.sideband.get_log()
except Exception as e:
output = f"An error occurred while retrieving log entries:\n{e}"
self.logviewer_screen.log_contents = output
def cb(dt):
self.logviewer_screen.ids.logviewer_output.text = f"[font=RobotoMono-Regular]{output}[/font]"
Clock.schedule_once(cb, 0.2)
if self.app.root.ids.screen_manager.current == "logviewer_screen":
Clock.schedule_once(self.update_logviewer, 1)
def logviewer_copy(self, sender=None):
Clipboard.copy(self.logviewer_screen.log_contents)
if True or RNS.vendor.platformutils.is_android():
toast("Log copied to clipboard")
layout_utilities_screen = """ layout_utilities_screen = """
MDScreen: MDScreen:
@ -156,8 +191,8 @@ MDScreen:
icon_size: dp(24) icon_size: dp(24)
font_size: dp(16) font_size: dp(16)
size_hint: [1.0, None] size_hint: [1.0, None]
on_release: root.delegate.rnstatus_action(self) on_release: root.delegate.logviewer_action(self)
disabled: True disabled: False
""" """
@ -177,12 +212,12 @@ MDScreen:
[['menu', lambda x: root.app.nav_drawer.set_state("open")]] [['menu', lambda x: root.app.nav_drawer.set_state("open")]]
right_action_items: right_action_items:
[ [
['refresh', lambda x: root.delegate.update_rnstatus()], # ['refresh', lambda x: root.delegate.update_rnstatus()],
['close', lambda x: root.app.close_sub_utilities_action(self)], ['close', lambda x: root.app.close_sub_utilities_action(self)],
] ]
MDScrollView: MDScrollView:
id: sensors_scrollview id: rnstatus_scrollview
size_hint_x: 1 size_hint_x: 1
size_hint_y: None size_hint_y: None
size: [root.width, root.height-root.ids.top_bar.height] size: [root.width, root.height-root.ids.top_bar.height]
@ -203,3 +238,46 @@ MDScreen:
text_size: self.width, None text_size: self.width, None
height: self.texture_size[1] height: self.texture_size[1]
""" """
layout_logviewer_screen = """
MDScreen:
name: "logviewer_screen"
BoxLayout:
orientation: "vertical"
MDTopAppBar:
id: top_bar
title: "Log Viewer"
anchor_title: "left"
elevation: 0
left_action_items:
[['menu', lambda x: root.app.nav_drawer.set_state("open")]]
right_action_items:
[
['content-copy', lambda x: root.delegate.logviewer_copy()],
['close', lambda x: root.app.close_sub_utilities_action(self)],
]
MDScrollView:
id: logviewer_scrollview
size_hint_x: 1
size_hint_y: None
size: [root.width, root.height-root.ids.top_bar.height]
do_scroll_x: False
do_scroll_y: True
MDGridLayout:
cols: 1
padding: [dp(28), dp(14), dp(28), dp(28)]
size_hint_y: None
height: self.minimum_height
MDLabel:
id: logviewer_output
markup: True
text: ""
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
"""