Improve logging and error handling across modules
- Added logging functionality to app.py and rns.py for better error tracking. - Improved exception handling in RNSManager methods to log specific failures. - Refactored code in various modules to ensure consistent logging practices. - Updated UI components to handle exceptions with user feedback. - Cleaned up formatting in several files for better readability.
This commit is contained in:
@@ -5,6 +5,7 @@ Ren Browser, a browser for the Reticulum Network built with Flet.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ from ren_browser.ui.ui import build_ui
|
|||||||
RENDERER = "plaintext"
|
RENDERER = "plaintext"
|
||||||
RNS_CONFIG_DIR = None
|
RNS_CONFIG_DIR = None
|
||||||
RNS_INSTANCE = None
|
RNS_INSTANCE = None
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def main(page: Page):
|
async def main(page: Page):
|
||||||
@@ -63,7 +65,7 @@ async def main(page: Page):
|
|||||||
|
|
||||||
ren_browser.logs.setup_rns_logging()
|
ren_browser.logs.setup_rns_logging()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.exception("Unable to configure RNS logging")
|
||||||
|
|
||||||
success = rns.initialize_reticulum(config_override)
|
success = rns.initialize_reticulum(config_override)
|
||||||
if not success:
|
if not success:
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def parse_micron_line(line: str) -> list:
|
|||||||
"underline": underline,
|
"underline": underline,
|
||||||
"color": color,
|
"color": color,
|
||||||
"bgcolor": bgcolor,
|
"bgcolor": bgcolor,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
current_text = ""
|
current_text = ""
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ def parse_micron_line(line: str) -> list:
|
|||||||
"underline": underline,
|
"underline": underline,
|
||||||
"color": color,
|
"color": color,
|
||||||
"bgcolor": bgcolor,
|
"bgcolor": bgcolor,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return spans
|
return spans
|
||||||
@@ -196,8 +196,9 @@ def _render_micron_internal(content: str, on_link_click=None) -> ft.Control:
|
|||||||
before = line[last_end : link_match.start()]
|
before = line[last_end : link_match.start()]
|
||||||
if before:
|
if before:
|
||||||
before_spans = parse_micron_line(before)
|
before_spans = parse_micron_line(before)
|
||||||
for span in before_spans:
|
row_controls.extend(
|
||||||
row_controls.append(create_text_span(span))
|
create_text_span(span) for span in before_spans
|
||||||
|
)
|
||||||
|
|
||||||
label = link_match.group(1)
|
label = link_match.group(1)
|
||||||
url = link_match.group(2)
|
url = link_match.group(2)
|
||||||
@@ -225,8 +226,9 @@ def _render_micron_internal(content: str, on_link_click=None) -> ft.Control:
|
|||||||
after = line[last_end:]
|
after = line[last_end:]
|
||||||
if after:
|
if after:
|
||||||
after_spans = parse_micron_line(after)
|
after_spans = parse_micron_line(after)
|
||||||
for span in after_spans:
|
row_controls.extend(
|
||||||
row_controls.append(create_text_span(span))
|
create_text_span(span) for span in after_spans
|
||||||
|
)
|
||||||
|
|
||||||
if row_controls:
|
if row_controls:
|
||||||
controls.append(
|
controls.append(
|
||||||
|
|||||||
@@ -2,21 +2,23 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import RNS
|
import RNS
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RNSManager:
|
class RNSManager:
|
||||||
"""Manage Reticulum lifecycle and configuration."""
|
"""Manage Reticulum lifecycle and configuration."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.reticulum = None
|
self.reticulum = None
|
||||||
self.config_path: Optional[str] = None
|
self.config_path: str | None = None
|
||||||
self.last_error: Optional[str] = None
|
self.last_error: str | None = None
|
||||||
|
|
||||||
def _is_android(self) -> bool:
|
def _is_android(self) -> bool:
|
||||||
vendor = getattr(RNS, "vendor", None)
|
vendor = getattr(RNS, "vendor", None)
|
||||||
@@ -47,9 +49,8 @@ class RNSManager:
|
|||||||
return Path(tempfile.gettempdir())
|
return Path(tempfile.gettempdir())
|
||||||
|
|
||||||
def _default_config_root(self) -> Path:
|
def _default_config_root(self) -> Path:
|
||||||
override = (
|
override = os.environ.get("REN_BROWSER_RNS_DIR") or os.environ.get(
|
||||||
os.environ.get("REN_BROWSER_RNS_DIR")
|
"REN_RETICULUM_CONFIG_DIR",
|
||||||
or os.environ.get("REN_RETICULUM_CONFIG_DIR")
|
|
||||||
)
|
)
|
||||||
if override:
|
if override:
|
||||||
return Path(override).expanduser()
|
return Path(override).expanduser()
|
||||||
@@ -57,8 +58,10 @@ class RNSManager:
|
|||||||
return self._android_storage_root() / "ren_browser" / "reticulum"
|
return self._android_storage_root() / "ren_browser" / "reticulum"
|
||||||
return Path.home() / ".reticulum"
|
return Path.home() / ".reticulum"
|
||||||
|
|
||||||
def _resolve_config_dir(self, preferred: Optional[str | Path]) -> Path:
|
def _resolve_config_dir(self, preferred: str | Path | None) -> Path:
|
||||||
target = Path(preferred).expanduser() if preferred else self._default_config_root()
|
target = (
|
||||||
|
Path(preferred).expanduser() if preferred else self._default_config_root()
|
||||||
|
)
|
||||||
allow_fallback = preferred is None
|
allow_fallback = preferred is None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -128,7 +131,7 @@ class RNSManager:
|
|||||||
config_file.write_text(base_content, encoding="utf-8")
|
config_file.write_text(base_content, encoding="utf-8")
|
||||||
os.chmod(config_file, 0o600)
|
os.chmod(config_file, 0o600)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.exception("Failed to seed default config at %s", config_file)
|
||||||
|
|
||||||
def _ensure_default_tcp_interfaces(self) -> None:
|
def _ensure_default_tcp_interfaces(self) -> None:
|
||||||
if not self.config_path:
|
if not self.config_path:
|
||||||
@@ -152,7 +155,10 @@ class RNSManager:
|
|||||||
cfg.write("\n")
|
cfg.write("\n")
|
||||||
cfg.write("\n" + snippet + "\n")
|
cfg.write("\n" + snippet + "\n")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.exception(
|
||||||
|
"Failed to append default TCP interfaces to %s",
|
||||||
|
config_file,
|
||||||
|
)
|
||||||
|
|
||||||
def _get_or_create_config_dir(self) -> Path:
|
def _get_or_create_config_dir(self) -> Path:
|
||||||
if self.config_path:
|
if self.config_path:
|
||||||
@@ -162,7 +168,7 @@ class RNSManager:
|
|||||||
self.config_path = str(resolved)
|
self.config_path = str(resolved)
|
||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
def initialize(self, config_dir: Optional[str] = None) -> bool:
|
def initialize(self, config_dir: str | None = None) -> bool:
|
||||||
"""Initialize the Reticulum instance."""
|
"""Initialize the Reticulum instance."""
|
||||||
self.last_error = None
|
self.last_error = None
|
||||||
try:
|
try:
|
||||||
@@ -225,7 +231,7 @@ class RNSManager:
|
|||||||
self.last_error = str(exc)
|
self.last_error = str(exc)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_config_path(self) -> Optional[str]:
|
def get_config_path(self) -> str | None:
|
||||||
"""Return the directory holding the active Reticulum config."""
|
"""Return the directory holding the active Reticulum config."""
|
||||||
if self.config_path:
|
if self.config_path:
|
||||||
return self.config_path
|
return self.config_path
|
||||||
@@ -240,7 +246,7 @@ class RNSManager:
|
|||||||
"""Return the current Reticulum instance, if any."""
|
"""Return the current Reticulum instance, if any."""
|
||||||
return self.reticulum
|
return self.reticulum
|
||||||
|
|
||||||
def get_last_error(self) -> Optional[str]:
|
def get_last_error(self) -> str | None:
|
||||||
"""Return the last recorded error string."""
|
"""Return the last recorded error string."""
|
||||||
return self.last_error
|
return self.last_error
|
||||||
|
|
||||||
@@ -248,7 +254,7 @@ class RNSManager:
|
|||||||
rns_manager = RNSManager()
|
rns_manager = RNSManager()
|
||||||
|
|
||||||
|
|
||||||
def initialize_reticulum(config_dir: Optional[str] = None) -> bool:
|
def initialize_reticulum(config_dir: str | None = None) -> bool:
|
||||||
"""Initialize Reticulum using the shared manager."""
|
"""Initialize Reticulum using the shared manager."""
|
||||||
return rns_manager.initialize(config_dir)
|
return rns_manager.initialize(config_dir)
|
||||||
|
|
||||||
@@ -263,7 +269,7 @@ def get_reticulum_instance():
|
|||||||
return rns_manager.get_reticulum_instance()
|
return rns_manager.get_reticulum_instance()
|
||||||
|
|
||||||
|
|
||||||
def get_config_path() -> Optional[str]:
|
def get_config_path() -> str | None:
|
||||||
"""Expose the active configuration directory."""
|
"""Expose the active configuration directory."""
|
||||||
return rns_manager.get_config_path()
|
return rns_manager.get_config_path()
|
||||||
|
|
||||||
@@ -278,7 +284,6 @@ def write_config_file(content: str) -> bool:
|
|||||||
return rns_manager.write_config_file(content)
|
return rns_manager.write_config_file(content)
|
||||||
|
|
||||||
|
|
||||||
def get_last_error() -> Optional[str]:
|
def get_last_error() -> str | None:
|
||||||
"""Return the last recorded Reticulum error."""
|
"""Return the last recorded Reticulum error."""
|
||||||
return rns_manager.get_last_error()
|
return rns_manager.get_last_error()
|
||||||
|
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ class StorageManager:
|
|||||||
|
|
||||||
if self.page and hasattr(self.page, "client_storage"):
|
if self.page and hasattr(self.page, "client_storage"):
|
||||||
self.page.client_storage.set(
|
self.page.client_storage.set(
|
||||||
"ren_browser_settings", json.dumps(settings)
|
"ren_browser_settings",
|
||||||
|
json.dumps(settings),
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ class TabsManager:
|
|||||||
|
|
||||||
default_content = (
|
default_content = (
|
||||||
render_micron(
|
render_micron(
|
||||||
"Welcome to Ren Browser", on_link_click=handle_link_click_home
|
"Welcome to Ren Browser",
|
||||||
|
on_link_click=handle_link_click_home,
|
||||||
)
|
)
|
||||||
if app_module.RENDERER == "micron"
|
if app_module.RENDERER == "micron"
|
||||||
else render_plaintext("Welcome to Ren Browser")
|
else render_plaintext("Welcome to Ren Browser")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import logging
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
import RNS
|
import RNS
|
||||||
@@ -13,6 +14,7 @@ from ren_browser.storage.storage import get_storage_manager
|
|||||||
|
|
||||||
BUTTON_BG = "#0B3D91"
|
BUTTON_BG = "#0B3D91"
|
||||||
BUTTON_BG_HOVER = "#082C6C"
|
BUTTON_BG_HOVER = "#082C6C"
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _blue_button_style() -> ft.ButtonStyle:
|
def _blue_button_style() -> ft.ButtonStyle:
|
||||||
@@ -52,14 +54,14 @@ def _get_interface_statuses():
|
|||||||
for interface in interfaces:
|
for interface in interfaces:
|
||||||
if interface is None:
|
if interface is None:
|
||||||
continue
|
continue
|
||||||
if (
|
if interface.__class__.__name__ == "LocalClientInterface" and getattr(
|
||||||
interface.__class__.__name__ == "LocalClientInterface"
|
interface, "is_connected_to_shared_instance", False,
|
||||||
and getattr(interface, "is_connected_to_shared_instance", False)
|
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
statuses.append(
|
statuses.append(
|
||||||
{
|
{
|
||||||
"name": getattr(interface, "name", None) or interface.__class__.__name__,
|
"name": getattr(interface, "name", None)
|
||||||
|
or interface.__class__.__name__,
|
||||||
"online": bool(getattr(interface, "online", False)),
|
"online": bool(getattr(interface, "online", False)),
|
||||||
"type": interface.__class__.__name__,
|
"type": interface.__class__.__name__,
|
||||||
"bitrate": getattr(interface, "bitrate", None),
|
"bitrate": getattr(interface, "bitrate", None),
|
||||||
@@ -194,7 +196,9 @@ def _build_storage_field(storage):
|
|||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
info = storage.get_storage_info()
|
info = storage.get_storage_info()
|
||||||
storage_field.value = "\n".join(f"{key}: {value}" for key, value in info.items())
|
storage_field.value = "\n".join(
|
||||||
|
f"{key}: {value}" for key, value in info.items()
|
||||||
|
)
|
||||||
|
|
||||||
refresh()
|
refresh()
|
||||||
return storage_field, refresh
|
return storage_field, refresh
|
||||||
@@ -245,8 +249,12 @@ def open_settings_tab(page: ft.Page, tab_manager):
|
|||||||
try:
|
try:
|
||||||
color_preview.bgcolor = page_bgcolor_field.value
|
color_preview.bgcolor = page_bgcolor_field.value
|
||||||
page.update()
|
page.update()
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
pass
|
logger.warning(
|
||||||
|
"Ignoring invalid background color '%s': %s",
|
||||||
|
page_bgcolor_field.value,
|
||||||
|
exc,
|
||||||
|
)
|
||||||
|
|
||||||
page_bgcolor_field.on_change = on_bgcolor_change
|
page_bgcolor_field.on_change = on_bgcolor_change
|
||||||
|
|
||||||
@@ -484,4 +492,3 @@ def open_settings_tab(page: ft.Page, tab_manager):
|
|||||||
idx = len(tab_manager.manager.tabs) - 1
|
idx = len(tab_manager.manager.tabs) - 1
|
||||||
tab_manager.select_tab(idx)
|
tab_manager.select_tab(idx)
|
||||||
page.update()
|
page.update()
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ def build_ui(page: Page):
|
|||||||
|
|
||||||
if req.page_path.endswith(".mu"):
|
if req.page_path.endswith(".mu"):
|
||||||
new_control = render_micron(
|
new_control = render_micron(
|
||||||
result, on_link_click=handle_link_click
|
result,
|
||||||
|
on_link_click=handle_link_click,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
new_control = render_plaintext(result)
|
new_control = render_plaintext(result)
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ class TestStorageManager:
|
|||||||
clear=True,
|
clear=True,
|
||||||
),
|
),
|
||||||
patch("pathlib.Path.mkdir"),
|
patch("pathlib.Path.mkdir"),
|
||||||
):
|
patch(
|
||||||
with patch(
|
|
||||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
||||||
|
),
|
||||||
):
|
):
|
||||||
storage = StorageManager()
|
storage = StorageManager()
|
||||||
storage._storage_dir = storage._get_storage_directory()
|
storage._storage_dir = storage._get_storage_directory()
|
||||||
@@ -99,9 +99,9 @@ class TestStorageManager:
|
|||||||
clear=True,
|
clear=True,
|
||||||
),
|
),
|
||||||
patch("pathlib.Path.mkdir"),
|
patch("pathlib.Path.mkdir"),
|
||||||
):
|
patch(
|
||||||
with patch(
|
|
||||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
||||||
|
),
|
||||||
):
|
):
|
||||||
storage = StorageManager()
|
storage = StorageManager()
|
||||||
storage._storage_dir = storage._get_storage_directory()
|
storage._storage_dir = storage._get_storage_directory()
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ class TestOpenSettingsTab:
|
|||||||
"ren_browser.ui.settings.get_storage_manager",
|
"ren_browser.ui.settings.get_storage_manager",
|
||||||
return_value=mock_storage_manager,
|
return_value=mock_storage_manager,
|
||||||
),
|
),
|
||||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
patch(
|
||||||
|
"ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns",
|
||||||
|
),
|
||||||
patch("pathlib.Path.read_text", return_value="config content"),
|
patch("pathlib.Path.read_text", return_value="config content"),
|
||||||
):
|
):
|
||||||
open_settings_tab(mock_page, mock_tab_manager)
|
open_settings_tab(mock_page, mock_tab_manager)
|
||||||
@@ -130,7 +132,9 @@ class TestOpenSettingsTab:
|
|||||||
"ren_browser.ui.settings.get_storage_manager",
|
"ren_browser.ui.settings.get_storage_manager",
|
||||||
return_value=mock_storage_manager,
|
return_value=mock_storage_manager,
|
||||||
),
|
),
|
||||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
patch(
|
||||||
|
"ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns",
|
||||||
|
),
|
||||||
patch("pathlib.Path.read_text", side_effect=Exception("File not found")),
|
patch("pathlib.Path.read_text", side_effect=Exception("File not found")),
|
||||||
):
|
):
|
||||||
open_settings_tab(mock_page, mock_tab_manager)
|
open_settings_tab(mock_page, mock_tab_manager)
|
||||||
@@ -155,7 +159,9 @@ class TestOpenSettingsTab:
|
|||||||
"ren_browser.ui.settings.get_storage_manager",
|
"ren_browser.ui.settings.get_storage_manager",
|
||||||
return_value=mock_storage_manager,
|
return_value=mock_storage_manager,
|
||||||
),
|
),
|
||||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
patch(
|
||||||
|
"ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns",
|
||||||
|
),
|
||||||
patch("pathlib.Path.read_text", return_value="config"),
|
patch("pathlib.Path.read_text", return_value="config"),
|
||||||
patch("pathlib.Path.write_text") as mock_write,
|
patch("pathlib.Path.write_text") as mock_write,
|
||||||
):
|
):
|
||||||
@@ -194,7 +200,9 @@ class TestOpenSettingsTab:
|
|||||||
"ren_browser.ui.settings.get_storage_manager",
|
"ren_browser.ui.settings.get_storage_manager",
|
||||||
return_value=mock_storage_manager,
|
return_value=mock_storage_manager,
|
||||||
),
|
),
|
||||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
patch(
|
||||||
|
"ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns",
|
||||||
|
),
|
||||||
patch("pathlib.Path.read_text", return_value="config"),
|
patch("pathlib.Path.read_text", return_value="config"),
|
||||||
patch("pathlib.Path.write_text", side_effect=Exception("disk full")),
|
patch("pathlib.Path.write_text", side_effect=Exception("disk full")),
|
||||||
):
|
):
|
||||||
@@ -229,7 +237,9 @@ class TestOpenSettingsTab:
|
|||||||
"ren_browser.ui.settings.get_storage_manager",
|
"ren_browser.ui.settings.get_storage_manager",
|
||||||
return_value=mock_storage_manager,
|
return_value=mock_storage_manager,
|
||||||
),
|
),
|
||||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
patch(
|
||||||
|
"ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns",
|
||||||
|
),
|
||||||
patch("pathlib.Path.read_text", return_value="config"),
|
patch("pathlib.Path.read_text", return_value="config"),
|
||||||
):
|
):
|
||||||
open_settings_tab(mock_page, mock_tab_manager)
|
open_settings_tab(mock_page, mock_tab_manager)
|
||||||
|
|||||||
Reference in New Issue
Block a user