STREAM_ALL = '*'
 STREAM_SAME = '='
 STREAM_PREFIX_META = ':'
+STREAM_PREFIXES = ''.join([STREAM_ALL, STREAM_SAME, STREAM_PREFIX_META])
 STREAM_SERVER = f'{STREAM_PREFIX_META}server'
 STREAM_RAW = f'{STREAM_PREFIX_META}raw'
 
-_LOG_PREFIX_PRIVMSG = ''
-_LOG_PREFIX_SEND_RAW = '=>|'
-_LOG_PREFIX_RECV_RAW = '<-|'
-
 _NAMES_DESIRED_SERVER_CAPS = ('server-time', 'account-tag', 'sasl')
 
 
             return
         self.conn.send(msg)
         if to_log:
-            self._log(to_log, prefix='', stream=stream_for_log)
-        self._log(msg.raw, prefix=_LOG_PREFIX_SEND_RAW, stream=STREAM_RAW)
+            self._log(to_log, stream=stream_for_log)
+        self._log(msg.raw, stream=STREAM_RAW, out=True)
 
     def update_login(self, nick_confirmed: bool, nickname: str = '') -> None:
         '''Manage conn_setup.nickname, .nick_confirmed.
 
     def handle_msg(self, msg: IrcMessage) -> None:
         'Log msg.raw, then process incoming msg into appropriate client steps.'
-        self._log(msg.raw, prefix=_LOG_PREFIX_RECV_RAW, stream=STREAM_RAW)
+        self._log(msg.raw, stream=STREAM_RAW, out=False)
         match msg.verb:
             case 'PING':
                 self.send(IrcMessage(verb='PONG', params=(msg.params[0],)))
                 self.update_login(nickname=msg.params[0], nick_confirmed=True)
             case 'PRIVMSG':
                 nickname = msg.source.split('!')[0]
-                self._log(f'< [{nickname}] {msg.params[-1]}', stream=nickname,
-                          prefix=_LOG_PREFIX_PRIVMSG)
+                self._log(msg.params[-1], stream=nickname, out=False)
             case 'CAP':
                 if (result := self._caps.process_msg(msg.params[1:])):
                     if isinstance(result, str):
 
                               CMD_SHORTCUTS)
 from ircplom.irc_conn import IrcMessage
 from ircplom.client import (
-        STREAM_ALL, STREAM_PREFIX_META, STREAM_RAW, STREAM_SAME, STREAM_SERVER,
-        IrcConnSetup, Client, ClientQueueMixin, NewClientEvent)
+    STREAM_ALL, STREAM_PREFIX_META, STREAM_PREFIXES, STREAM_RAW, STREAM_SAME,
+    STREAM_SERVER, IrcConnSetup, Client, ClientQueueMixin, NewClientEvent)
 
 CMD_SHORTCUTS['disconnect'] = 'window.disconnect'
 CMD_SHORTCUTS['nick'] = 'window.nick'
 CMD_SHORTCUTS['privmsg'] = 'window.privmsg'
 CMD_SHORTCUTS['reconnect'] = 'window.reconnect'
 
+_LOG_PREFIX_SERVER = '$'
+_LOG_PREFIX_OUT = '>'
+_LOG_PREFIX_IN = '<'
+
 
 class _ClientWindow(Window, ClientQueueMixin):
 
     def cmd__privmsg(self, target: str, msg: str) -> None:
         'Send chat message msg to target.'
         self._send_msg('PRIVMSG', (target, msg), stream_for_log=target,
-                       to_log=f'>{self.prompt.prefix}{msg}')
+                       to_log=msg)
 
 
 class _PrivmsgPromptWidget(PromptWidget):
 
     def __init__(self, client_id: str, new_window: Callable, tui_log: Callable
                  ) -> None:
-        self.log = lambda msg, **kw: tui_log(msg=msg, client_id=client_id, **kw
-                                             ) or True
+        self._tui_log = lambda msg, **kw: tui_log(msg=msg,
+                                                  client_id=client_id, **kw)
         self.windows: list[_ClientWindow] = []
         self._tui_new_window = new_window
         for stream in (STREAM_SERVER, STREAM_RAW):
             return win
         return self._new_window(stream)
 
+    def log(self, msg: str, stream: str, **kwargs) -> None:
+        'From parsing stream, kwargs build prefix before sending to logger.'
+        prefix = ''
+        is_chat = stream[0] not in STREAM_PREFIXES
+        if 'out' in kwargs:
+            prefix += _LOG_PREFIX_OUT if kwargs['out'] else _LOG_PREFIX_IN
+        elif is_chat:
+            prefix += _LOG_PREFIX_OUT
+        else:
+            prefix += _LOG_PREFIX_SERVER
+        if is_chat:
+            prefix += f' [{stream if "out" in kwargs else self.nickname}]'
+        self._tui_log(msg, stream=stream, prefix=prefix, **kwargs)
+
     def update(self, **kwargs) -> bool:
         'Apply settings in kwargs, follow representation update triggers.'
         to_change = {}
                 client_id=client_id, tui_log=self._log,
                 new_window=lambda cls, **kw: self._new_window(
                     cls, _q_out=self._q_out, client_id=client_id, **kw))
-        if getattr(self._client_mngrs[client_id], todo)(**kwargs):
+        if getattr(self._client_mngrs[client_id], todo)(**kwargs) is not False:
             self.redraw_affected()
 
     def cmd__connect(self,
             for k, v in dc_asdict(kwargs['conn_setup']).items():
                 to_log += [f'  {k}: [{v}]']
         for item in to_log:
-            self._client_tui_trigger('log', stream=stream, msg=item)
+            self._client_tui_trigger('log', stream=stream, msg=item, **kwargs)
 
     def update_login(self, nick_confirmed: bool, nickname: str = '') -> None:
         super().update_login(nick_confirmed, nickname)