diff --git a/validations_libs/cli/parseractions.py b/validations_libs/cli/parseractions.py index 2a7a93bc..492d3031 100644 --- a/validations_libs/cli/parseractions.py +++ b/validations_libs/cli/parseractions.py @@ -16,7 +16,9 @@ import argparse -from validations_libs.utils import LOG +from validations_libs import utils + +LOG = utils.getLogger(__name__ + '.parseractions') class CommaListAction(argparse.Action): diff --git a/validations_libs/logger.py b/validations_libs/logger.py new file mode 100644 index 00000000..4cc08c58 --- /dev/null +++ b/validations_libs/logger.py @@ -0,0 +1,47 @@ +# Copyright 2022 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import logging +import os +from logging.handlers import SysLogHandler + + +def getLogger(loggerName, stream_lvl=logging.WARN): + """Create logger instance. + + :param loggerName: name of the new Logger instance + :type loggerName: `str` + :param stream_lvl: minimum level at which the messages will be printed to stream + :type stream_lvl: `int` + :rtype: `Logger` + """ + new_logger = logging.getLogger(loggerName) + + formatter = logging.Formatter("%(asctime)s %(module)s %(message)s") + + s_handler = logging.StreamHandler() + s_handler.setFormatter(formatter) + s_handler.setLevel(stream_lvl) + + new_logger.addHandler(s_handler) + + if os.path.exists('/dev/log'): + sys_handler = SysLogHandler(address='/dev/log') + sys_handler.setFormatter(formatter) + + new_logger.addHandler(sys_handler) + else: + new_logger.warning("Journal socket does not exist. Logs will not be processed by syslog.") + + return new_logger diff --git a/validations_libs/tests/test_logger.py b/validations_libs/tests/test_logger.py new file mode 100644 index 00000000..c1211445 --- /dev/null +++ b/validations_libs/tests/test_logger.py @@ -0,0 +1,44 @@ +# Copyright 2022 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +try: + from unittest import mock +except ImportError: + import mock +from unittest import TestCase + +import logging + +from validations_libs import logger + + +class TestLogger(TestCase): + + def setUp(self) -> None: + super().setUp() + + @mock.patch('os.path.exists', reutrn_value=True) + def test_logger_init(self, mock_exists): + new_logger = logger.getLogger("fooo") + mock_exists.assert_called_once_with('/dev/log') + self.assertEqual(logging.Logger, type(new_logger)) + + @mock.patch('logging.Logger.warning') + @mock.patch('os.path.exists', return_value=False) + def test_logger_init_no_journal(self, mock_exists, mock_warning): + new_logger = logger.getLogger("fooo") + mock_exists.assert_called_once_with('/dev/log') + mock_warning.assert_called_once() + self.assertEqual(logging.Logger, type(new_logger)) diff --git a/validations_libs/tests/test_utils.py b/validations_libs/tests/test_utils.py index f64e5ffd..e9cb2ff3 100644 --- a/validations_libs/tests/test_utils.py +++ b/validations_libs/tests/test_utils.py @@ -40,6 +40,7 @@ class TestUtils(TestCase): def setUp(self): super(TestUtils, self).setUp() + self.logger = mock.patch('validations_libs.logger.getLogger') @mock.patch('validations_libs.validation.Validation._get_content', return_value=fakes.FAKE_PLAYBOOK[0]) @@ -399,19 +400,16 @@ class TestUtils(TestCase): [], []) self.assertEqual(result, {}) - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch('validations_libs.utils.os.makedirs') @mock.patch( 'validations_libs.utils.os.access', side_effect=[False, True]) @mock.patch('validations_libs.utils.os.path.exists', return_value=True) def test_create_log_dir_access_issue(self, mock_exists, - mock_access, mock_mkdirs, - mock_log): + mock_access, mock_mkdirs): log_path = utils.create_log_dir("/foo/bar") self.assertEqual(log_path, constants.VALIDATIONS_LOG_BASEDIR) - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch( 'validations_libs.utils.os.makedirs', side_effect=PermissionError) @@ -424,8 +422,7 @@ class TestUtils(TestCase): autospec=True, side_effect=fakes._accept_default_log_path) def test_create_log_dir_existence_issue(self, mock_exists, - mock_access, mock_mkdirs, - mock_log): + mock_access, mock_mkdirs): """Tests behavior after encountering non-existence of the the selected log folder, failed attempt to create it (raising PermissionError), and finally resorting to a fallback. @@ -433,32 +430,27 @@ class TestUtils(TestCase): log_path = utils.create_log_dir("/foo/bar") self.assertEqual(log_path, constants.VALIDATIONS_LOG_BASEDIR) - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch('validations_libs.utils.os.makedirs') @mock.patch('validations_libs.utils.os.access', return_value=True) @mock.patch('validations_libs.utils.os.path.exists', return_value=True) def test_create_log_dir_success(self, mock_exists, - mock_access, mock_mkdirs, - mock_log): + mock_access, mock_mkdirs): """Test successful log dir retrieval on the first try. """ log_path = utils.create_log_dir("/foo/bar") self.assertEqual(log_path, "/foo/bar") - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch( 'validations_libs.utils.os.makedirs', side_effect=PermissionError) @mock.patch('validations_libs.utils.os.access', return_value=False) @mock.patch('validations_libs.utils.os.path.exists', return_value=False) def test_create_log_dir_runtime_err(self, mock_exists, - mock_access, mock_mkdirs, - mock_log): + mock_access, mock_mkdirs): """Test if failure of the fallback raises 'RuntimeError' """ self.assertRaises(RuntimeError, utils.create_log_dir, "/foo/bar") - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch( 'validations_libs.utils.os.makedirs', side_effect=PermissionError) @@ -468,19 +460,16 @@ class TestUtils(TestCase): side_effect=fakes._accept_default_log_path) def test_create_log_dir_default_perms_runtime_err( self, mock_exists, - mock_access, mock_mkdirs, - mock_log): + mock_access, mock_mkdirs): """Test if the inaccessible fallback raises 'RuntimeError' """ self.assertRaises(RuntimeError, utils.create_log_dir, "/foo/bar") - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch('validations_libs.utils.os.makedirs') @mock.patch('validations_libs.utils.os.access', return_value=False) @mock.patch('validations_libs.utils.os.path.exists', return_value=False) def test_create_log_dir_mkdirs(self, mock_exists, - mock_access, mock_mkdirs, - mock_log): + mock_access, mock_mkdirs): """Test successful creation of the directory if the first access fails. """ @@ -532,7 +521,6 @@ class TestUtils(TestCase): results['ansible_environment']['ANSIBLE_STDOUT_CALLBACK'], fakes.ANSIBLE_ENVIRONNMENT_CONFIG['ANSIBLE_STDOUT_CALLBACK']) - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch('{}.Path.exists'.format(PATHLIB), return_value=False) @mock.patch('{}.Path.is_dir'.format(PATHLIB), @@ -543,8 +531,7 @@ class TestUtils(TestCase): def test_check_creation_community_validations_dir(self, mock_mkdir, mock_iterdir, mock_isdir, - mock_exists, - mock_log): + mock_exists): basedir = PosixPath('/foo/bar/community-validations') subdir = fakes.COVAL_SUBDIR result = utils.check_community_validations_dir(basedir, subdir) @@ -556,7 +543,6 @@ class TestUtils(TestCase): PosixPath("/foo/bar/community-validations/lookup_plugins")] ) - @mock.patch('validations_libs.utils.LOG', autospec=True) @mock.patch('{}.Path.is_dir'.format(PATHLIB), return_value=True) @mock.patch('{}.Path.exists'.format(PATHLIB), return_value=True) @mock.patch('{}.Path.iterdir'.format(PATHLIB), @@ -566,8 +552,7 @@ class TestUtils(TestCase): mock_mkdir, mock_iterdir, mock_exists, - mock_isdir, - mock_log): + mock_isdir): basedir = PosixPath('/foo/bar/community-validations') subdir = fakes.COVAL_SUBDIR result = utils.check_community_validations_dir(basedir, subdir) diff --git a/validations_libs/utils.py b/validations_libs/utils.py index a3ba161d..b2b20ecc 100644 --- a/validations_libs/utils.py +++ b/validations_libs/utils.py @@ -16,7 +16,6 @@ import ast import configparser import datetime import glob -import logging import os import site import subprocess @@ -31,8 +30,9 @@ except ImportError: from validations_libs import constants from validations_libs.group import Group from validations_libs.validation import Validation +from validations_libs.logger import getLogger -LOG = logging.getLogger(__name__ + ".utils") +LOG = getLogger(__name__ + ".utils") def current_time():