From 1c3e1e87e5c075dfad8ed929a2074b95981c1a16 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Mon, 4 Aug 2025 15:09:11 +0200 Subject: [PATCH] Refactor logging. --- ircplom/client.py | 44 +++++++++++++++++++++++++------------------ ircplom/client_tui.py | 2 +- ircplom/events.py | 21 ++++++++++++++++++++- ircplom/tui_base.py | 13 ++++++------- 4 files changed, 53 insertions(+), 27 deletions(-) diff --git a/ircplom/client.py b/ircplom/client.py index d4e91b6..c95c99d 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -7,14 +7,19 @@ from threading import Thread from typing import Optional from uuid import UUID, uuid4 # ourselves -from ircplom.events import (AffectiveEvent, ExceptionEvent, PayloadMixin, - QueueMixin) +from ircplom.events import (AffectiveEvent, ExceptionEvent, Logger, + PayloadMixin, QueueMixin) from ircplom.irc_conn import (BaseIrcConnection, IrcConnAbortException, IrcMessage) ClientsDb = dict[UUID, 'Client'] CHAT_GLOB = '*' +_LOG_PREFIX_PRIVMSG = '' +_LOG_PREFIX_SEND_FMT = '> ' +_LOG_PREFIX_SEND_RAW = '=>|' +_LOG_PREFIX_RECV_RAW = '<-|' + @dataclass class ClientIdMixin: @@ -92,6 +97,7 @@ class Client(ABC, ClientQueueMixin): self._cap_neg_states: dict[str, bool] = {} self.caps: dict[str, _ServerCapability] = {} self.id_ = uuid4() + self.log = Logger(self._log) self.update_login(nick_confirmed=False, nickname=self.conn_setup.nickname) self.start_connecting() @@ -106,7 +112,7 @@ class Client(ABC, ClientQueueMixin): client_id=self.id_) self._cput(ClientEvent.make_subtype('on_connect')) except IrcConnAbortException as e: - self.log(f'# ALERT: {e}') + self.log.alert(str(e)) except Exception as e: # pylint: disable=broad-exception-caught self._put(ExceptionEvent(e)) @@ -114,7 +120,7 @@ class Client(ABC, ClientQueueMixin): def on_connect(self) -> None: 'Steps to perform right after connection.' - self.log(msg='# connected to server', chat=CHAT_GLOB) + self.log.add(msg='connected to server', chat=CHAT_GLOB) self.try_send_cap('LS', ('302',)) self.send(IrcMessage(verb='USER', params=(getuser(), '0', '*', @@ -164,7 +170,7 @@ class Client(ABC, ClientQueueMixin): self.cap_neg_set(verb, done=True) @abstractmethod - def log(self, msg: str, chat: str = '') -> None: + def _log(self, msg: str, chat: str = '') -> None: '''Write msg into log of chat, whatever shape that may have. Messages to chat=CHAT_GLOB are meant to appear in all widgets mapped to @@ -174,11 +180,11 @@ class Client(ABC, ClientQueueMixin): def send(self, msg: IrcMessage, chat: str = '') -> None: 'Send line-separator-delimited message over socket.' if not self.conn: - self.log('# ALERT: cannot send, connection seems closed') + self.log.alert('cannot send, connection seems closed') return self.conn.send(msg) - self.log(msg=f'> {msg.raw}', chat=chat) - self.log(msg=f'=>| {msg.raw}', chat=':raw') + self.log.add(msg.raw, prefix=_LOG_PREFIX_SEND_FMT, chat=chat) + self.log.add(msg.raw, prefix=_LOG_PREFIX_SEND_RAW, chat=':raw') def update_login(self, nick_confirmed: bool, nickname: str = '') -> None: '''Manage conn_setup..nickname, .nick_confirmed. @@ -186,20 +192,21 @@ class Client(ABC, ClientQueueMixin): (Useful for subclass extension.) ''' first_run = not hasattr(self.conn_setup, 'nickname') - prefix = '# nickname' + prefix = 'nickname' if first_run or (nickname and nickname != self.conn_setup.nickname): verb = ('set' if first_run else f'changed from "{self.conn_setup.nickname}"') self.conn_setup.nickname = nickname - self.log(msg=f'{prefix} {verb} to "{nickname}"', chat=CHAT_GLOB) + self.log.add(f'{prefix} {verb} to "{nickname}"', chat=CHAT_GLOB) if first_run or nick_confirmed != self.nick_confirmed: self.nick_confirmed = nick_confirmed if not first_run: - self.log(f'{prefix} {"" if nick_confirmed else "un"}confirmed') + self.log.add(f'{prefix} {"" if nick_confirmed else "un"}' + 'confirmed') def close(self) -> None: 'Close both recv Loop and socket.' - self.log(msg='# disconnecting from server', chat=CHAT_GLOB) + self.log.add(msg='disconnecting from server', chat=CHAT_GLOB) if self.conn: self.conn.close() self.conn = None @@ -207,7 +214,7 @@ class Client(ABC, ClientQueueMixin): def handle_msg(self, msg: IrcMessage) -> None: 'Process incoming msg towards appropriate client steps.' - self.log(f'<-| {msg.raw}', ':raw') + self.log.add(msg.raw, prefix=_LOG_PREFIX_RECV_RAW, chat=':raw') match msg.verb: case 'PING': self.send(IrcMessage(verb='PONG', params=(msg.params[0],))) @@ -216,7 +223,8 @@ class Client(ABC, ClientQueueMixin): case '001' | 'NICK': self.update_login(nickname=msg.params[0], nick_confirmed=True) case 'PRIVMSG': - self.log(msg=str(msg.params), chat=msg.source) + self.log.add(msg=str(msg.params), prefix=_LOG_PREFIX_PRIVMSG, + chat=msg.source) case 'CAP': match msg.params[1]: case 'LS' | 'LIST': @@ -230,9 +238,9 @@ class Client(ABC, ClientQueueMixin): if self.cap_neg_done('LIST'): self.try_send_cap('END') if not self.cap_neg('printing'): - self.log('# server capabilities (enabled: "+"):') + self.log.add('server capabilities (enabled: "+"):') for cap_name, cap in self.caps.items(): - self.log('# ' + cap.str_for_log(cap_name)) + self.log.add(cap.str_for_log(cap_name)) self.cap_neg_set('printing', done=True) elif self.cap_neg_done('LS'): for cap_name in ('server-time', 'account-tag', 'sasl'): @@ -257,8 +265,8 @@ class InitReconnectEvent(ClientEvent): def affect(self, target: 'Client') -> None: if target.conn: - target.log('# ALERT: reconnection called, but still seem ' - 'connected, so nothing to do.') + target.log.alert('reconnection called, but still seem connected, ' + 'so nothing to do.') else: target.start_connecting() diff --git a/ircplom/client_tui.py b/ircplom/client_tui.py index 7c482ab..32deae0 100644 --- a/ircplom/client_tui.py +++ b/ircplom/client_tui.py @@ -126,7 +126,7 @@ class _ClientPromptEvent(_ClientWindowEvent, PayloadMixin): class _ClientKnowingTui(Client): - def log(self, msg: str, chat: str = '') -> None: + def _log(self, msg: str, chat: str = '') -> None: self._cput(_ClientLogEvent, chat=chat, payload=msg) def update_login(self, nick_confirmed: bool, nickname: str = '') -> None: diff --git a/ircplom/events.py b/ircplom/events.py index e06ce34..07c82bc 100644 --- a/ircplom/events.py +++ b/ircplom/events.py @@ -3,7 +3,10 @@ from abc import abstractmethod, ABC from dataclasses import dataclass from queue import SimpleQueue, Empty as QueueEmpty from threading import Thread -from typing import Any, Iterator, Literal, Self +from typing import Any, Callable, Iterator, Literal, Self + +_LOG_PREFIX_DEFAULT = '# ' +_LOG_PREFIX_ALERT = f'{_LOG_PREFIX_DEFAULT}ALERT: ' @dataclass @@ -85,3 +88,19 @@ class Loop(QueueMixin): self._put(it_yield) except Exception as e: # pylint: disable=broad-exception-caught self._put(ExceptionEvent(e)) + + +class Logger: + 'Wrapper for logging tasks.' + + def __init__(self, to_call: Callable[[str], None]) -> None: + self._to_call = to_call + + def add(self, msg: str, prefix: str = _LOG_PREFIX_DEFAULT, **kwargs + ) -> None: + 'Add msg to log.' + self._to_call(f'{prefix}{msg}', **kwargs) + + def alert(self, msg: str, **kwargs) -> None: + 'Add msg prefixed with _LOG_PREFIX_ALERT to log.' + self.add(msg, prefix=_LOG_PREFIX_ALERT, **kwargs) diff --git a/ircplom/tui_base.py b/ircplom/tui_base.py index e19e90f..ef7da24 100644 --- a/ircplom/tui_base.py +++ b/ircplom/tui_base.py @@ -11,7 +11,7 @@ from typing import Callable, Generator, Iterator, NamedTuple, Optional from blessed import Terminal as BlessedTerminal # ourselves from ircplom.events import ( - AffectiveEvent, Loop, PayloadMixin, QueueMixin, QuitEvent) + AffectiveEvent, Logger, Loop, PayloadMixin, QueueMixin, QuitEvent) # from ircplom.irc_conn import IrcMessage _MIN_HEIGHT = 4 @@ -392,7 +392,7 @@ class _KeyboardEvent(TuiEvent, PayloadMixin): elif len(self.payload) == 1: target.window.prompt.insert(self.payload) else: - target.log(f'# ALERT: unknown keyboard input: {self.payload}') + target.log.alert(f'unknown keyboard input: {self.payload}') super().affect(target) @@ -404,6 +404,9 @@ class BaseTui(QueueMixin): self.term = term self._window_idx = 0 self.windows = [Window(idx=self._window_idx, term=self.term)] + self.log = Logger( + lambda msg: # pylint: disable=unnecessary-lambda # to keep … + self.window.log.append(msg)) # … up-to-date _what_ window's .log self._put(_SetScreenEvent()) def cmd_name_to_cmd(self, cmd_name: str) -> Optional[Callable]: @@ -428,10 +431,6 @@ class BaseTui(QueueMixin): 'Currently selected Window.' return self.windows[self._window_idx] - def log(self, msg: str) -> None: - 'Post msg to active window\'s log.' - self.window.log.append(msg) - def _switch_window(self, idx: int) -> None: self._window_idx = idx self.window.draw() @@ -466,7 +465,7 @@ class BaseTui(QueueMixin): else: alert = 'not prefixed by /' if alert: - self.log(f'# ALERT: invalid prompt command: {alert}') + self.log.alert(f'invalid prompt command: {alert}') def cmd__quit(self) -> None: 'Trigger program exit.' -- 2.30.2