From 8b986888a9ba5567803fc0d5fd0e6ddfc904e7a0 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 24 Apr 2026 20:09:52 +0000 Subject: [PATCH] logrotate-counter format --- README.md | 1 + copyparty/__main__.py | 18 ++++++++++++++++++ copyparty/svchub.py | 31 +++++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3e459bf1..4277f9b5 100644 --- a/README.md +++ b/README.md @@ -1360,6 +1360,7 @@ serverlog is sent to stdout by default (but logging to a file is also possible) * [-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 + * global-option [--rlo](https://copyparty.eu/cli/#rlo-help-page) decides what happens if the filename is taken * `-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 diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 36ab35c7..9e9f13f4 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1020,6 +1020,23 @@ def get_sects(): """ ), ], + [ + "rlo", + "logrotate format", + dedent( + """ + a logrotate-counter is added if the logfile filename is taken; + by default at the end, unless \033[32m%R\033[0m is somewhere in the \033[36m-lo\033[0m pattern, + for example: -lo /var/log/cpp/%Y-%m-%d%R.txt + + \033[36m--rlo\033[0m configures the logrotate format; examples: + .1 = when necessary, append a dot followed by a single digit + .1! = counter is always added, even when not necessary + -3 = a hyphen followed by three-digit counter + (blank) = disable counter; overwrite existing logfile + """ + ), + ], [ "ls", "volume inspection", @@ -1681,6 +1698,7 @@ def add_logging(ap): ap2.add_argument("-q", action="store_true", help="quiet; disable most STDOUT messages") ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile; use .txt for plaintext or .xz for compressed. Example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)") ap2.add_argument("--flo", metavar="N", type=int, default=1, help="log format for \033[33m-lo\033[0m; [\033[32m1\033[0m]=classic/colors, [\033[32m2\033[0m]=no-color") + ap2.add_argument("--rlo", metavar="TXT", type=u, default=".1", help="logrotate counter format; see \033[33m--help-rlo\033[0m") ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR") ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR") ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster") diff --git a/copyparty/svchub.py b/copyparty/svchub.py index 939bff9a..fc0a9db9 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -209,7 +209,20 @@ class SvcHub(object): else: self.log = self._log_enabled + self.lo1 = self.lo2 = "" if args.lo: + if "%" in args.lo and "%R" not in args.lo: + args.lo += "%R" + if not args.rlo: + args.lo = args.lo.replace("%R", "") + try: + self.lo1, self.lo2 = args.lo.split("%R") + except: + self.lo1 = args.lo + try: + self.rot_fmt = "%%s%s%%0%sd%s" % (args.rlo[:1], args.rlo[1:2], self.lo2) + except: + self.rot_fmt = "%s.%d" self._setup_logfile() LOG[0] = self.log @@ -1360,7 +1373,7 @@ class SvcHub(object): def _logname(self) -> str: dt = datetime.now(self.tz) - fn = str(self.args.lo) + fn = str(self.lo1) for fs in "YmdHMS": fs = "%" + fs if fs in fn: @@ -1369,15 +1382,17 @@ class SvcHub(object): return fn def _setup_logfile(self) -> None: - base_fn = fn = sel_fn = self._logname() - do_xz = fn.lower().endswith(".xz") - if fn != self.args.lo: - ctr = 0 + base_fn = fn = self._logname() + sel_fn = fn + self.lo2 + do_xz = sel_fn.lower().endswith(".xz") + if "%R" in self.args.lo: # yup this is a race; if started sufficiently concurrently, two # copyparties can grab the same logfile (considered and ignored) - while os.path.exists(sel_fn): - ctr += 1 - sel_fn = "{}.{}".format(fn, ctr) + for n in range(9999): + if n or "!" in self.args.rlo: + sel_fn = self.rot_fmt % (fn, n) + if not os.path.exists(sel_fn): + break fn = sel_fn try: