From 8c6d8a3c22ac33b8b86be8dc15c7bca2cfe41f75 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 19 Mar 2026 00:57:09 +0000 Subject: [PATCH] logfile colors --- README.md | 28 ++++++++++++++++++ copyparty/svchub.py | 72 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e45801c..ab930670 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com * [searching](#searching) - search by size, date, path/name, mp3-tags, ... * [server config](#server-config) - using arguments or config files, or a mix of both * [version-checker](#version-checker) - sleep better at night + * [logging](#logging) - serverlog is sent to stdout by default * [zeroconf](#zeroconf) - announce enabled services on the LAN ([pic](https://user-images.githubusercontent.com/241032/215344737-0eae8d98-9496-4256-9aa8-cd2f6971810d.png)) * [mdns](#mdns) - LAN domain-name and feature announcer * [ssdp](#ssdp) - windows-explorer announcer @@ -1339,6 +1340,33 @@ config file example: ``` +## logging + +serverlog is sent to stdout by default (but logging to a file is also possible) + +"stdout" usually means either the terminal, or journalctl, or whatever is collecting logs from your docker containers, so that depends on your setup + +* [-q](https://copyparty.eu/cli/#g-q) disables logging to stdout, and may improve performance a little bit + * combine it with `-lo logfolder/cpp-%Y-%m-%d.txt` to log to a file instead + * the `%Y-%m-%d` makes it create a new logfile every day, with the date as filename +* `-lo whatever.txt` can be used without `-q` to log to both at the same time + * by default, the logfile will have colors if the terminal does (usually the case) + * use the [textfile-viewer](https://github.com/user-attachments/assets/8a828947-2fae-4df9-bd2a-3de46f42d478) or `less -R` in a terminal to see colors correctly +* if you want [no colors](https://youtu.be/biW5UVGkPMA?t=148): + * `--flo 2` disables colors for just the logfile + * `--no-ansi` disables colors for both the terminal and logfile + +config file example: + +```yaml +[global] + log-date: %Y-%m-%d # show dates on stdout too + lo: /var/log/cpp/%Y-%m-%d.txt # logfile path + flo: 2 # just text (no colors) in logfile + q # disable stdout; use logfile only +``` + + ## zeroconf announce enabled services on the LAN ([pic](https://user-images.githubusercontent.com/241032/215344737-0eae8d98-9496-4256-9aa8-cd2f6971810d.png)) -- `-z` enables both [mdns](#mdns) and [ssdp](#ssdp) diff --git a/copyparty/svchub.py b/copyparty/svchub.py index fd581c20..b3f409eb 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -196,7 +196,14 @@ class SvcHub(object): self.log_div = 10 ** (6 - args.log_tdec) self.log_efmt = "%02d:%02d:%02d.%0{}d".format(args.log_tdec) self.log_dfmt = "%04d-%04d-%06d.%0{}d".format(args.log_tdec) - self.log = self._log_disabled if args.q else self._log_enabled + + if args.q: + self.log = self._log_disabled + elif args.lo and args.flo == 2 and not self.no_ansi: + self.log = self._log_en_f2 + else: + self.log = self._log_enabled + if args.lo: self._setup_logfile(printed) @@ -1714,6 +1721,69 @@ class SvcHub(object): if not self.args.no_logflush: self.logf.flush() + def _log_en_f2(self, src: str, msg: str, c: Union[int, str] = 0) -> None: + with self.log_mutex: + dt = datetime.now(self.tz) + if dt.day != self.cday or dt.month != self.cmon: + if self.args.log_date: + zs = dt.strftime(self.args.log_date) + self.log_efmt = "%s %s" % (zs, self.log_efmt.split(" ")[-1]) + zs = "{}\n" if self.no_ansi else "\033[36m{}\033[0m\n" + zs = zs.format(dt.strftime("%Y-%m-%d")) + print(zs, end="") + self._set_next_day(dt) + if self.logf: + self.logf.write(zs) + + ts = self.log_efmt % ( + dt.hour, + dt.minute, + dt.second, + dt.microsecond // self.log_div, + ) + + # logfile: + if not c: + fmt = "%s %-21s LOG: %s\n" + elif c == 1: + fmt = "%s %-21s CRIT: %s\n" + elif c == 3: + fmt = "%s %-21s WARN: %s\n" + elif c == 6: + fmt = "%s %-21s BTW: %s\n" + else: + fmt = "%s %-21s LOG: %s\n" + fsrc = RE_ANSI.sub("", src) if "\033" in src else src + fmsg = RE_ANSI.sub("", msg) if "\033" in msg else msg + fmsg = fmt % (ts, fsrc, fmsg) + + # stdout ansi: + fmt = "\033[36m%s \033[33m%-21s \033[0m%s\n" + if not c: + pass + elif isinstance(c, int): + msg = "\033[3%sm%s\033[0m" % (c, msg) + elif "\033" not in c: + msg = "\033[%sm%s\033[0m" % (c, msg) + else: + msg = "%s%s\033[0m" % (c, msg) + + msg = fmt % (ts, src, msg) + try: + print(msg, end="") + except UnicodeEncodeError: + try: + print(msg.encode("utf-8", "replace").decode(), end="") + except: + print(msg.encode("ascii", "replace").decode(), end="") + except OSError as ex: + if ex.errno != errno.EPIPE: + raise + + self.logf.write(fmsg) + if not self.args.no_logflush: + self.logf.flush() + def pr(self, *a: Any, **ka: Any) -> None: try: with self.log_mutex: