Only log to syslog when run from a non-interactive console (e.g. a cron job). Related to #197.
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details

This commit is contained in:
Dan Helfman 2019-06-27 14:41:21 -07:00
parent 032d4adee3
commit 90595e9c18
7 changed files with 62 additions and 14 deletions

3
NEWS
View File

@ -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
output. See discussion on #197.

View File

@ -108,7 +108,7 @@ 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)',
help='Display verbose progress to syslog (from none to lots: 0, 1, or 2). Ignored when console is interactive',
)
global_group.add_argument(
'--version',

View File

@ -21,6 +21,14 @@ def to_bool(arg):
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):
'''
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:
return to_bool(py_colors)
return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb'
return interactive_console()
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'):
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.setFormatter(logging.Formatter('borgmatic: %(levelname)s %(message)s'))
syslog_handler.setLevel(syslog_log_level)

View File

@ -39,9 +39,10 @@ borgmatic info
## Logging
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
using systemd, try running `journalctl -xe`. Otherwise, try viewing
`/var/log/syslog` or similiar.
present and borgmatic is running in a non-interactive console. Where those
logs show up depends on your particular system. If you're using systemd, try
running `journalctl -xe`. Otherwise, try viewing `/var/log/syslog` or
similiar.
You can customize the log level used for syslog logging with the
`--syslog-verbosity` flag, and this is independent from the console logging

View File

@ -38,8 +38,8 @@ hooks:
## Hook output
Any output produced by your hooks shows up both at the console and in syslog.
For more information, read about <a
Any output produced by your hooks shows up both at the console and in syslog
(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
your backups</a>.

View File

@ -1,6 +1,6 @@
from setuptools import find_packages, setup
VERSION = '1.3.12.dev0'
VERSION = '1.3.12'
setup(

View File

@ -20,6 +20,29 @@ def test_to_bool_passes_none_through():
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():
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
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).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).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
@ -108,11 +133,11 @@ def test_color_text_without_color_does_not_raise():
def test_configure_logging_probes_for_log_socket_on_linux():
flexmock(module).should_receive('Console_color_formatter')
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.os.path).should_receive('exists').with_args('/var/run/syslog').and_return(False)
syslog_handler = logging.handlers.SysLogHandler()
flexmock(module.logging.handlers).should_receive('SysLogHandler').with_args(
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():
flexmock(module).should_receive('Console_color_formatter')
flexmock(module).should_receive('interactive_console').and_return(False)
flexmock(module.logging).should_receive('basicConfig').with_args(
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()
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)