Browse Source

Handle log file error more consistently with other error. Add --log-file-verbosity flag. Add docs.

Dan Helfman 5 years ago
parent
commit
a9104ed090

+ 3 - 0
NEWS

@@ -1,3 +1,6 @@
+1.4.5
+ * Log to file instead of syslog via command-line "--log-file" flag.
+
 1.4.4
  * #234: Support for Borg --keep-exclude-tags and --exclude-nodump options.
 

+ 9 - 2
borgmatic/commands/arguments.py

@@ -147,13 +147,20 @@ def parse_arguments(*unparsed_arguments):
         type=int,
         choices=range(0, 3),
         default=0,
-        help='Display verbose progress to syslog (from none to lots: 0, 1, or 2). Ignored when console is interactive',
+        help='Log verbose progress to syslog (from none to lots: 0, 1, or 2). Ignored when console is interactive or --log-file is given',
+    )
+    global_group.add_argument(
+        '--log-file-verbosity',
+        type=int,
+        choices=range(0, 3),
+        default=1,
+        help='Log verbose progress to log file (from none to lots: 0, 1, or 2). Only used when --log-file is given',
     )
     global_group.add_argument(
         '--log-file',
         type=str,
         default=None,
-        help='Write log messages to this file instead of concole and syslog',
+        help='Write log messages to this file instead of syslog',
     )
     global_group.add_argument(
         '--version',

+ 11 - 5
borgmatic/commands/borgmatic.py

@@ -484,11 +484,17 @@ def main():  # pragma: no cover
     configs, parse_logs = load_configurations(config_filenames)
 
     colorama.init(autoreset=True, strip=not should_do_markup(global_arguments.no_color, configs))
-    configure_logging(
-        verbosity_to_log_level(global_arguments.verbosity),
-        verbosity_to_log_level(global_arguments.syslog_verbosity),
-        global_arguments.log_file,
-    )
+    try:
+        configure_logging(
+            verbosity_to_log_level(global_arguments.verbosity),
+            verbosity_to_log_level(global_arguments.syslog_verbosity),
+            verbosity_to_log_level(global_arguments.log_file_verbosity),
+            global_arguments.log_file,
+        )
+    except (FileNotFoundError, PermissionError) as error:
+        configure_logging(logging.CRITICAL)
+        logger.critical('Error configuring logging: {}'.format(error))
+        exit_with_help_link()
 
     logger.debug('Ensuring legacy configuration is upgraded')
     convert.guard_configuration_upgraded(LEGACY_CONFIG_PATH, config_filenames)

+ 14 - 12
borgmatic/logger.py

@@ -73,12 +73,19 @@ def color_text(color, message):
     return '{}{}{}'.format(color, message, colorama.Style.RESET_ALL)
 
 
-def configure_logging(console_log_level, syslog_log_level=None, log_file=None):
+def configure_logging(
+    console_log_level, syslog_log_level=None, log_file_log_level=None, log_file=None
+):
     '''
-    Configure logging to go to both the console and syslog. Use the given log levels, respectively.
+    Configure logging to go to both the console and (syslog or log file). Use the given log levels,
+    respectively.
+
+    Raise FileNotFoundError or PermissionError if the log file could not be opened for writing.
     '''
     if syslog_log_level is None:
         syslog_log_level = console_log_level
+    if log_file_log_level is None:
+        log_file_log_level = console_log_level
 
     console_handler = logging.StreamHandler()
     console_handler.setFormatter(Console_color_formatter())
@@ -97,18 +104,13 @@ def configure_logging(console_log_level, syslog_log_level=None, log_file=None):
         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 = logging.FileHandler(log_file)
         file_handler.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s'))
-        file_handler.setLevel(syslog_log_level)
+        file_handler.setLevel(log_file_log_level)
         handlers = (console_handler, file_handler)
     else:
         handlers = (console_handler,)
 
-    logging.basicConfig(level=min(console_log_level, syslog_log_level), handlers=handlers)
+    logging.basicConfig(
+        level=min(console_log_level, syslog_log_level, log_file_log_level), handlers=handlers
+    )

+ 14 - 0
docs/how-to/inspect-your-backups.md

@@ -70,6 +70,20 @@ Or to increase syslog logging to include debug spew:
 borgmatic --syslog-verbosity 2
 ```
 
+### Logging to file
+
+If you don't want to use syslog, and you'd rather borgmatic log to a plain
+file, use the `--log-file` flag:
+
+```bash
+borgmatic --log-file /path/to/file.log
+```
+
+Note that if you use the `--log-file` flag, you are responsible for rotating
+the log file so it doesn't grow too large. Also, there is also
+`--log-file-verbosity` flag to customize the log file's log level.
+
+
 ### systemd journal
 
 If your local syslog daemon is systemd's journal, be aware that journald by

+ 1 - 1
setup.py

@@ -1,6 +1,6 @@
 from setuptools import find_packages, setup
 
-VERSION = '1.4.4'
+VERSION = '1.4.5'
 
 
 setup(

+ 5 - 3
tests/unit/test_logger.py

@@ -195,11 +195,11 @@ def test_configure_logging_skips_syslog_if_interactive_console():
     module.configure_logging(console_log_level=logging.INFO)
 
 
-def test_configure_logging_to_logfile_instead_syslog():
+def test_configure_logging_to_logfile_instead_of_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
+        level=logging.DEBUG, handlers=tuple
     )
     flexmock(module.os.path).should_receive('exists').with_args('/dev/log').and_return(True)
     flexmock(module.logging.handlers).should_receive('SysLogHandler').never()
@@ -208,7 +208,9 @@ def test_configure_logging_to_logfile_instead_syslog():
         file_handler
     ).once()
 
-    module.configure_logging(console_log_level=logging.INFO, log_file='/tmp/logfile')
+    module.configure_logging(
+        console_log_level=logging.INFO, log_file_log_level=logging.DEBUG, log_file='/tmp/logfile'
+    )
 
 
 def test_configure_logging_skips_logfile_if_argument_is_none():