Repository: sdss/sdsstools

Issue:
Allow rotate log when starting file logger
When the file logger starts, if `rotating=True`, the new log is appended. In some cases one may want the new log to start a new file and the previous log to be rolled over.


<!-- Edit the body of your new issue then click the ✓ "Create Issue" button in the top right of the editor. The first line will be the issue title. Assignees and Labels follow after a blank line. Leave an empty line before beginning the body of the issue. -->

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8fb9b18..86a6720 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## [0.4.8](https://github.com/sdss/sdsstools/compare/0.4.7...HEAD)
+
+- Feature [#26]: allow rollover of file logger on `start_file_logger`.
+
+
 ## [0.4.7](https://github.com/sdss/sdsstools/compare/0.4.6...0.4.7) - 2021-02-12
 
 - Bug [#24]: update dependencies so that `importlib_metadata` is installed for Python<=3.7 (it was Python<3.7).
diff --git a/src/sdsstools/logger.py b/src/sdsstools/logger.py
index 5808389..0867a42 100644
--- a/src/sdsstools/logger.py
+++ b/src/sdsstools/logger.py
@@ -7,6 +7,7 @@
 # @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)
 
 import copy
+import datetime
 import logging
 import os
 import re
@@ -199,22 +200,29 @@ class SDSSLogger(logging.Logger):
         shutil.copyfile(self.log_filename, os.path.expanduser(path))
 
     def start_file_logger(self, path, log_level=logging.DEBUG,
-                          mode='a', rotating=True):
+                          mode='a', rotating=True, rollover=False):
         """Start file logging."""
 
         log_file_path = os.path.expanduser(path)
         logdir = os.path.dirname(log_file_path)
 
+        SUFFIX = '%Y-%m-%d_%H:%M:%S'
+
         try:
 
             if not os.path.exists(logdir):
                 os.makedirs(logdir)
 
+            if os.path.exists(log_file_path) and rotating and rollover:
+                now = datetime.datetime.utcnow()
+                dst = str(log_file_path) + '.' + now.strftime(SUFFIX)
+                shutil.move(str(log_file_path), dst)
+
             if rotating:
                 self.fh = TimedRotatingFileHandler(str(log_file_path),
                                                    when='midnight',
                                                    utc=True)
-                self.fh.suffix = '%Y-%m-%d_%H:%M:%S'
+                self.fh.suffix = SUFFIX
             else:
                 self.fh = logging.FileHandler(str(log_file_path), mode=mode)
 


Test patch:
diff --git a/test/test_logger.py b/test/test_logger.py
index eca0dc6..ffa16ab 100644
--- a/test/test_logger.py
+++ b/test/test_logger.py
@@ -170,3 +170,18 @@ async def test_asyncio_exception_handler(logger, caplog, event_loop):
 
     assert caplog.record_tuples[0][1] == logging.ERROR
     assert 'An error in a task.' in caplog.record_tuples[0][2]
+
+
+def test_logger_rotating_rollover(tmp_path):
+
+    log_file = tmp_path / 'logs' / 'test_log.log'
+
+    logger1 = get_logger(str(uuid.uuid4()))
+    logger1.start_file_logger(log_file)
+
+    assert len(list((tmp_path / 'logs').glob('*'))) == 1
+
+    logger2 = get_logger(str(uuid.uuid4()))
+    logger2.start_file_logger(log_file, rollover=True)
+
+    assert len(list((tmp_path / 'logs').glob('*'))) == 2