Only log to syslog when run from a non-interactive console (e.g. a cron job). Related to #197.
This commit is contained in:
parent
032d4adee3
commit
90595e9c18
7 changed files with 62 additions and 14 deletions
3
NEWS
3
NEWS
|
@ -1,4 +1,5 @@
|
||||||
1.3.12.dev0
|
1.3.12
|
||||||
|
* Only log to syslog when run from a non-interactive console (e.g. a cron job).
|
||||||
* Remove unicode byte order mark from syslog output so it doesn't show up as a literal in rsyslog
|
* Remove unicode byte order mark from syslog output so it doesn't show up as a literal in rsyslog
|
||||||
output. See discussion on #197.
|
output. See discussion on #197.
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ def parse_arguments(*unparsed_arguments):
|
||||||
type=int,
|
type=int,
|
||||||
choices=range(0, 3),
|
choices=range(0, 3),
|
||||||
default=0,
|
default=0,
|
||||||
help='Display verbose progress to syslog (from none to lots: 0, 1, or 2)',
|
help='Display verbose progress to syslog (from none to lots: 0, 1, or 2). Ignored when console is interactive',
|
||||||
)
|
)
|
||||||
global_group.add_argument(
|
global_group.add_argument(
|
||||||
'--version',
|
'--version',
|
||||||
|
|
|
@ -21,6 +21,14 @@ def to_bool(arg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def interactive_console():
|
||||||
|
'''
|
||||||
|
Return whether the current console is "interactive". Meaning: Capable of
|
||||||
|
user input and not just something like a cron job.
|
||||||
|
'''
|
||||||
|
return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb'
|
||||||
|
|
||||||
|
|
||||||
def should_do_markup(no_color, configs):
|
def should_do_markup(no_color, configs):
|
||||||
'''
|
'''
|
||||||
Given the value of the command-line no-color argument, and a dict of configuration filename to
|
Given the value of the command-line no-color argument, and a dict of configuration filename to
|
||||||
|
@ -37,7 +45,7 @@ def should_do_markup(no_color, configs):
|
||||||
if py_colors is not None:
|
if py_colors is not None:
|
||||||
return to_bool(py_colors)
|
return to_bool(py_colors)
|
||||||
|
|
||||||
return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb'
|
return interactive_console()
|
||||||
|
|
||||||
|
|
||||||
LOG_LEVEL_TO_COLOR = {
|
LOG_LEVEL_TO_COLOR = {
|
||||||
|
@ -82,7 +90,7 @@ def configure_logging(console_log_level, syslog_log_level=None):
|
||||||
elif os.path.exists('/var/run/syslog'):
|
elif os.path.exists('/var/run/syslog'):
|
||||||
syslog_path = '/var/run/syslog'
|
syslog_path = '/var/run/syslog'
|
||||||
|
|
||||||
if syslog_path:
|
if syslog_path and not interactive_console():
|
||||||
syslog_handler = logging.handlers.SysLogHandler(address=syslog_path)
|
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)s %(message)s'))
|
||||||
syslog_handler.setLevel(syslog_log_level)
|
syslog_handler.setLevel(syslog_log_level)
|
||||||
|
|
|
@ -39,9 +39,10 @@ borgmatic info
|
||||||
## Logging
|
## Logging
|
||||||
|
|
||||||
By default, borgmatic logs to a local syslog-compatible daemon if one is
|
By default, borgmatic logs to a local syslog-compatible daemon if one is
|
||||||
present. Where those logs show up depends on your particular system. If you're
|
present and borgmatic is running in a non-interactive console. Where those
|
||||||
using systemd, try running `journalctl -xe`. Otherwise, try viewing
|
logs show up depends on your particular system. If you're using systemd, try
|
||||||
`/var/log/syslog` or similiar.
|
running `journalctl -xe`. Otherwise, try viewing `/var/log/syslog` or
|
||||||
|
similiar.
|
||||||
|
|
||||||
You can customize the log level used for syslog logging with the
|
You can customize the log level used for syslog logging with the
|
||||||
`--syslog-verbosity` flag, and this is independent from the console logging
|
`--syslog-verbosity` flag, and this is independent from the console logging
|
||||||
|
|
|
@ -38,8 +38,8 @@ hooks:
|
||||||
|
|
||||||
## Hook output
|
## Hook output
|
||||||
|
|
||||||
Any output produced by your hooks shows up both at the console and in syslog.
|
Any output produced by your hooks shows up both at the console and in syslog
|
||||||
For more information, read about <a
|
(when run in a non-interactive console). For more information, read about <a
|
||||||
href="https://torsion.org/borgmatic/docs/how-to/inspect-your-backups.md">inspecting
|
href="https://torsion.org/borgmatic/docs/how-to/inspect-your-backups.md">inspecting
|
||||||
your backups</a>.
|
your backups</a>.
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -1,6 +1,6 @@
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
VERSION = '1.3.12.dev0'
|
VERSION = '1.3.12'
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
|
|
@ -20,6 +20,29 @@ def test_to_bool_passes_none_through():
|
||||||
assert module.to_bool(None) is None
|
assert module.to_bool(None) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_interactive_console_false_when_not_isatty(capsys):
|
||||||
|
with capsys.disabled():
|
||||||
|
flexmock(module.sys.stdout).should_receive('isatty').and_return(False)
|
||||||
|
|
||||||
|
assert module.interactive_console() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_interactive_console_false_when_TERM_is_dumb(capsys):
|
||||||
|
with capsys.disabled():
|
||||||
|
flexmock(module.sys.stdout).should_receive('isatty').and_return(True)
|
||||||
|
flexmock(module.os.environ).should_receive('get').with_args('TERM').and_return('dumb')
|
||||||
|
|
||||||
|
assert module.interactive_console() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_interactive_console_true_when_isatty_and_TERM_is_not_dumb(capsys):
|
||||||
|
with capsys.disabled():
|
||||||
|
flexmock(module.sys.stdout).should_receive('isatty').and_return(True)
|
||||||
|
flexmock(module.os.environ).should_receive('get').with_args('TERM').and_return('smart')
|
||||||
|
|
||||||
|
assert module.interactive_console() is True
|
||||||
|
|
||||||
|
|
||||||
def test_should_do_markup_respects_no_color_value():
|
def test_should_do_markup_respects_no_color_value():
|
||||||
assert module.should_do_markup(no_color=True, configs={}) is False
|
assert module.should_do_markup(no_color=True, configs={}) is False
|
||||||
|
|
||||||
|
@ -75,15 +98,17 @@ def test_should_do_markup_prefers_no_color_value_to_PY_COLORS():
|
||||||
assert module.should_do_markup(no_color=True, configs={}) is False
|
assert module.should_do_markup(no_color=True, configs={}) is False
|
||||||
|
|
||||||
|
|
||||||
def test_should_do_markup_respects_stdout_tty_value():
|
def test_should_do_markup_respects_interactive_console_value():
|
||||||
flexmock(module.os.environ).should_receive('get').and_return(None)
|
flexmock(module.os.environ).should_receive('get').and_return(None)
|
||||||
|
flexmock(module).should_receive('interactive_console').and_return(True)
|
||||||
|
|
||||||
assert module.should_do_markup(no_color=False, configs={}) is False
|
assert module.should_do_markup(no_color=False, configs={}) is True
|
||||||
|
|
||||||
|
|
||||||
def test_should_do_markup_prefers_PY_COLORS_to_stdout_tty_value():
|
def test_should_do_markup_prefers_PY_COLORS_to_interactive_console_value():
|
||||||
flexmock(module.os.environ).should_receive('get').and_return('True')
|
flexmock(module.os.environ).should_receive('get').and_return('True')
|
||||||
flexmock(module).should_receive('to_bool').and_return(True)
|
flexmock(module).should_receive('to_bool').and_return(True)
|
||||||
|
flexmock(module).should_receive('interactive_console').and_return(False)
|
||||||
|
|
||||||
assert module.should_do_markup(no_color=False, configs={}) is True
|
assert module.should_do_markup(no_color=False, configs={}) is True
|
||||||
|
|
||||||
|
@ -108,11 +133,11 @@ def test_color_text_without_color_does_not_raise():
|
||||||
|
|
||||||
def test_configure_logging_probes_for_log_socket_on_linux():
|
def test_configure_logging_probes_for_log_socket_on_linux():
|
||||||
flexmock(module).should_receive('Console_color_formatter')
|
flexmock(module).should_receive('Console_color_formatter')
|
||||||
|
flexmock(module).should_receive('interactive_console').and_return(False)
|
||||||
flexmock(module.logging).should_receive('basicConfig').with_args(
|
flexmock(module.logging).should_receive('basicConfig').with_args(
|
||||||
level=logging.INFO, handlers=tuple
|
level=logging.INFO, handlers=tuple
|
||||||
)
|
)
|
||||||
flexmock(module.os.path).should_receive('exists').with_args('/dev/log').and_return(True)
|
flexmock(module.os.path).should_receive('exists').with_args('/dev/log').and_return(True)
|
||||||
flexmock(module.os.path).should_receive('exists').with_args('/var/run/syslog').and_return(False)
|
|
||||||
syslog_handler = logging.handlers.SysLogHandler()
|
syslog_handler = logging.handlers.SysLogHandler()
|
||||||
flexmock(module.logging.handlers).should_receive('SysLogHandler').with_args(
|
flexmock(module.logging.handlers).should_receive('SysLogHandler').with_args(
|
||||||
address='/dev/log'
|
address='/dev/log'
|
||||||
|
@ -123,6 +148,7 @@ def test_configure_logging_probes_for_log_socket_on_linux():
|
||||||
|
|
||||||
def test_configure_logging_probes_for_log_socket_on_macos():
|
def test_configure_logging_probes_for_log_socket_on_macos():
|
||||||
flexmock(module).should_receive('Console_color_formatter')
|
flexmock(module).should_receive('Console_color_formatter')
|
||||||
|
flexmock(module).should_receive('interactive_console').and_return(False)
|
||||||
flexmock(module.logging).should_receive('basicConfig').with_args(
|
flexmock(module.logging).should_receive('basicConfig').with_args(
|
||||||
level=logging.INFO, handlers=tuple
|
level=logging.INFO, handlers=tuple
|
||||||
)
|
)
|
||||||
|
@ -155,3 +181,15 @@ def test_configure_logging_skips_syslog_if_not_found():
|
||||||
flexmock(module.logging.handlers).should_receive('SysLogHandler').never()
|
flexmock(module.logging.handlers).should_receive('SysLogHandler').never()
|
||||||
|
|
||||||
module.configure_logging(console_log_level=logging.INFO)
|
module.configure_logging(console_log_level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
def test_configure_logging_skips_syslog_if_interactive_console():
|
||||||
|
flexmock(module).should_receive('Console_color_formatter')
|
||||||
|
flexmock(module).should_receive('interactive_console').and_return(True)
|
||||||
|
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()
|
||||||
|
|
||||||
|
module.configure_logging(console_log_level=logging.INFO)
|
||||||
|
|
Loading…
Reference in a new issue