Просмотр исходного кода

Add "--log-file-format" flag for customizing the log message format (#658).

Dan Helfman 2 лет назад
Родитель
Сommit
7e6bee84b0

+ 3 - 0
NEWS

@@ -7,6 +7,9 @@
    https://torsion.org/borgmatic/docs/how-to/make-per-application-backups/#archive-naming
  * #479, #588: The "prefix" options have been deprecated in favor of the new "archive_name_format"
    auto-matching behavior and the "match_archives" option.
+ * #658: Add "--log-file-format" flag for customizing the log message format. See the documentation
+   for more information:
+   https://torsion.org/borgmatic/docs/how-to/inspect-your-backups/#logging-to-file
  * #662: Fix regression in which the "check_repositories" option failed to match repositories.
  * #663: Fix regression in which the "transfer" action produced a traceback.
  * Add spellchecking of source code during test runs.

+ 5 - 3
borgmatic/commands/arguments.py

@@ -178,10 +178,12 @@ def make_parsers():
         help='Log verbose progress to monitoring integrations that support logging (from only errors to very verbose: -1, 0, 1, or 2)',
     )
     global_group.add_argument(
-        '--log-file',
+        '--log-file', type=str, help='Write log messages to this file instead of syslog',
+    )
+    global_group.add_argument(
+        '--log-file-format',
         type=str,
-        default=None,
-        help='Write log messages to this file instead of syslog',
+        help='Log format string used for log messages written to the log file',
     )
     global_group.add_argument(
         '--override',

+ 1 - 0
borgmatic/commands/borgmatic.py

@@ -700,6 +700,7 @@ def main():  # pragma: no cover
             verbosity_to_log_level(global_arguments.log_file_verbosity),
             verbosity_to_log_level(global_arguments.monitoring_verbosity),
             global_arguments.log_file,
+            global_arguments.log_file_format,
         )
     except (FileNotFoundError, PermissionError) as error:
         configure_logging(logging.CRITICAL)

+ 9 - 2
borgmatic/logger.py

@@ -156,6 +156,7 @@ def configure_logging(
     log_file_log_level=None,
     monitoring_log_level=None,
     log_file=None,
+    log_file_format=None,
 ):
     '''
     Configure logging to go to both the console and (syslog or log file). Use the given log levels,
@@ -200,12 +201,18 @@ def configure_logging(
 
     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.setFormatter(
+            logging.Formatter('borgmatic: {levelname} {message}', style='{')  # noqa: FS003
+        )
         syslog_handler.setLevel(syslog_log_level)
         handlers = (console_handler, syslog_handler)
     elif log_file:
         file_handler = logging.handlers.WatchedFileHandler(log_file)
-        file_handler.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s'))
+        file_handler.setFormatter(
+            logging.Formatter(
+                log_file_format or '[{asctime}] {levelname}: {message}', style='{'  # noqa: FS003
+            )
+        )
         file_handler.setLevel(log_file_log_level)
         handlers = (console_handler, file_handler)
     else:

+ 36 - 2
docs/how-to/inspect-your-backups.md

@@ -154,5 +154,39 @@ 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, for example with
-[logrotate](https://wiki.archlinux.org/index.php/Logrotate). Also, there is a
-`--log-file-verbosity` flag to customize the log file's log level.
+[logrotate](https://wiki.archlinux.org/index.php/Logrotate).
+
+You can the `--log-file-verbosity` flag to customize the log file's log level:
+
+```bash
+borgmatic --log-file /path/to/file.log --log-file-verbosity 2
+```
+
+<span class="minilink minilink-addedin">New in borgmatic version 1.7.11</span>
+Use the `--log-file-format` flag to override the default log message format.
+This format string can contain a series of named placeholders wrapped in curly
+brackets. For instance, the default log format is: `[{asctime}] {levelname}:
+{message}`. This means each log message is recorded as the log time (in square
+brackets), a logging level name, a colon, and the actual log message.
+
+So if you just want each log message to get logged *without* a timestamp or a
+logging level name:
+
+```bash
+borgmatic --log-file /path/to/file.log --log-file-format "{message}"
+```
+
+Here is a list of available placeholders:
+
+ * `{asctime}`: time the log message was created
+ * `{levelname}`: level of the log message (`INFO`, `DEBUG`, etc.)
+ * `{lineno}`: line number in the source file where the log message originated
+ * `{message}`: actual log message
+ * `{pathname}`: path of the source file where the log message originated
+
+See the [Python logging
+documentation](https://docs.python.org/3/library/logging.html#logrecord-attributes)
+for additional placeholders.
+
+Note that this `--log-file-format` flg only applies to the specified
+`--log-file` and not to syslog or other logging.

+ 31 - 2
tests/unit/test_logger.py

@@ -285,7 +285,7 @@ def test_configure_logging_skips_syslog_if_interactive_console():
     module.configure_logging(console_log_level=logging.INFO)
 
 
-def test_configure_logging_to_logfile_instead_of_syslog():
+def test_configure_logging_to_log_file_instead_of_syslog():
     flexmock(module).should_receive('add_custom_log_levels')
     flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(
@@ -309,7 +309,36 @@ def test_configure_logging_to_logfile_instead_of_syslog():
     )
 
 
-def test_configure_logging_skips_logfile_if_argument_is_none():
+def test_configure_logging_to_log_file_formats_with_custom_log_format():
+    flexmock(module).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.ANSWER
+    flexmock(module.logging).should_receive('Formatter').with_args(
+        '{message}', style='{'  # noqa: FS003
+    ).once()
+    flexmock(module).should_receive('Multi_stream_handler').and_return(
+        flexmock(setFormatter=lambda formatter: None, setLevel=lambda level: None)
+    )
+
+    flexmock(module).should_receive('interactive_console').and_return(False)
+    flexmock(module.logging).should_receive('basicConfig').with_args(
+        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()
+    file_handler = logging.handlers.WatchedFileHandler('/tmp/logfile')
+    flexmock(module.logging.handlers).should_receive('WatchedFileHandler').with_args(
+        '/tmp/logfile'
+    ).and_return(file_handler).once()
+
+    module.configure_logging(
+        console_log_level=logging.INFO,
+        log_file_log_level=logging.DEBUG,
+        log_file='/tmp/logfile',
+        log_file_format='{message}',  # noqa: FS003
+    )
+
+
+def test_configure_logging_skips_log_file_if_argument_is_none():
     flexmock(module).should_receive('add_custom_log_levels')
     flexmock(module.logging).ANSWER = module.ANSWER
     flexmock(module).should_receive('Multi_stream_handler').and_return(