ソースを参照

Log to file instead of syslog via command-line "--log-file" flag (#233).

Dan Helfman 5 年 前
コミット
06f134cc71

+ 6 - 0
borgmatic/commands/arguments.py

@@ -149,6 +149,12 @@ def parse_arguments(*unparsed_arguments):
         default=0,
         help='Display verbose progress to syslog (from none to lots: 0, 1, or 2). Ignored when console is interactive',
     )
+    global_group.add_argument(
+        '--log-file',
+        type=str,
+        default=None,
+        help='Write log messages to this file instead of concole and syslog',
+    )
     global_group.add_argument(
         '--version',
         dest='version',

+ 1 - 0
borgmatic/commands/borgmatic.py

@@ -487,6 +487,7 @@ def main():  # pragma: no cover
     configure_logging(
         verbosity_to_log_level(global_arguments.verbosity),
         verbosity_to_log_level(global_arguments.syslog_verbosity),
+        global_arguments.log_file,
     )
 
     logger.debug('Ensuring legacy configuration is upgraded')

+ 18 - 5
borgmatic/logger.py

@@ -73,7 +73,7 @@ def color_text(color, message):
     return '{}{}{}'.format(color, message, colorama.Style.RESET_ALL)
 
 
-def configure_logging(console_log_level, syslog_log_level=None):
+def configure_logging(console_log_level, syslog_log_level=None, log_file=None):
     '''
     Configure logging to go to both the console and syslog. Use the given log levels, respectively.
     '''
@@ -85,16 +85,29 @@ def configure_logging(console_log_level, syslog_log_level=None):
     console_handler.setLevel(console_log_level)
 
     syslog_path = None
-    if os.path.exists('/dev/log'):
-        syslog_path = '/dev/log'
-    elif os.path.exists('/var/run/syslog'):
-        syslog_path = '/var/run/syslog'
+    if log_file is None:
+        if os.path.exists('/dev/log'):
+            syslog_path = '/dev/log'
+        elif os.path.exists('/var/run/syslog'):
+            syslog_path = '/var/run/syslog'
 
     if syslog_path and not interactive_console():
         syslog_handler = logging.handlers.SysLogHandler(address=syslog_path)
         syslog_handler.setFormatter(logging.Formatter('borgmatic: %(levelname)s %(message)s'))
         syslog_handler.setLevel(syslog_log_level)
         handlers = (console_handler, syslog_handler)
+    elif log_file:
+        try:
+            file_handler = logging.FileHandler(log_file)
+        except FileNotFoundError:
+            print("ERROR: Path to log-file doesn't exist: {}".format(log_file))
+            sys.exit(1)
+        except PermissionError:
+            print("ERROR: No write access to log-file: {}".format(log_file))
+            sys.exit(1)
+        file_handler.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s'))
+        file_handler.setLevel(syslog_log_level)
+        handlers = (console_handler, file_handler)
     else:
         handlers = (console_handler,)
 

+ 28 - 0
tests/unit/test_logger.py

@@ -193,3 +193,31 @@ def test_configure_logging_skips_syslog_if_interactive_console():
     flexmock(module.logging.handlers).should_receive('SysLogHandler').never()
 
     module.configure_logging(console_log_level=logging.INFO)
+
+
+def test_configure_logging_to_logfile_instead_syslog():
+    # syslog skipped in non-interactive console if --log-file argument provided
+    flexmock(module).should_receive('interactive_console').and_return(False)
+    flexmock(module.logging).should_receive('basicConfig').with_args(
+        level=logging.INFO, handlers=tuple
+    )
+    flexmock(module.os.path).should_receive('exists').with_args('/dev/log').and_return(True)
+    flexmock(module.logging.handlers).should_receive('SysLogHandler').never()
+    file_handler = logging.FileHandler('/tmp/logfile')
+    flexmock(module.logging).should_receive('FileHandler').with_args('/tmp/logfile').and_return(
+        file_handler
+    ).once()
+
+    module.configure_logging(console_log_level=logging.INFO, log_file='/tmp/logfile')
+
+
+def test_configure_logging_skips_logfile_if_argument_is_none():
+    # No FileHandler added if argument --log-file is None
+    flexmock(module).should_receive('interactive_console').and_return(False)
+    flexmock(module.logging).should_receive('basicConfig').with_args(
+        level=logging.INFO, handlers=tuple
+    )
+    flexmock(module.os.path).should_receive('exists').and_return(False)
+    flexmock(module.logging).should_receive('FileHandler').never()
+
+    module.configure_logging(console_log_level=logging.INFO, log_file=None)