diff --git a/doc/source/reference/watchers.rst b/doc/source/reference/watchers.rst deleted file mode 100644 index 0d6c41b5..00000000 --- a/doc/source/reference/watchers.rst +++ /dev/null @@ -1,12 +0,0 @@ -================== - oslo_log.watchers -================== - -.. automodule:: oslo_log.watchers - :members: - :undoc-members: - :show-inheritance: - -.. seealso:: - - :ref:`using` diff --git a/oslo_log/log.py b/oslo_log/log.py index 8899de08..4bb24424 100644 --- a/oslo_log/log.py +++ b/oslo_log/log.py @@ -377,10 +377,9 @@ def _setup_logging_from_conf(conf, project, version): logpath = _get_log_file_path(conf) if logpath: # On Windows, in-use files cannot be moved or deleted. - if conf.watch_log_file and platform.system() == 'Linux': - from oslo_log import watchers - file_handler = watchers.FastWatchedFileHandler - filelog = file_handler(logpath) + if conf.watch_log_file: + raise RuntimeError('watch_log_file feature was removed because ' + 'it has been broken for multiple releases.') elif conf.log_rotation_type.lower() == "interval": file_handler = logging.handlers.TimedRotatingFileHandler when = conf.log_rotate_interval_type.lower() diff --git a/oslo_log/tests/unit/test_log.py b/oslo_log/tests/unit/test_log.py index 4c34d4c4..d17d20ad 100644 --- a/oslo_log/tests/unit/test_log.py +++ b/oslo_log/tests/unit/test_log.py @@ -22,7 +22,6 @@ import datetime import io import logging import os -import platform import shutil import sys try: @@ -154,24 +153,6 @@ class CommonLoggerTestsMixIn(object): mock_logger = loggers_mock.return_value.logger mock_logger.addHandler.assert_any_call(handler_mock.return_value) - @mock.patch('oslo_log.watchers.FastWatchedFileHandler') - @mock.patch('oslo_log.log._get_log_file_path', return_value='test.conf') - @mock.patch('platform.system', return_value='Linux') - def test_watchlog_on_linux(self, platfotm_mock, path_mock, handler_mock): - self.config(watch_log_file=True) - log._setup_logging_from_conf(self.CONF, 'test', 'test') - handler_mock.assert_called_once_with(path_mock.return_value) - self.assertEqual(self.log_handlers[0], handler_mock.return_value) - - @mock.patch('logging.handlers.WatchedFileHandler') - @mock.patch('oslo_log.log._get_log_file_path', return_value='test.conf') - @mock.patch('platform.system', return_value='Windows') - def test_watchlog_on_windows(self, platform_mock, path_mock, handler_mock): - self.config(watch_log_file=True) - log._setup_logging_from_conf(self.CONF, 'test', 'test') - handler_mock.assert_called_once_with(path_mock.return_value) - self.assertEqual(self.log_handlers[0], handler_mock.return_value) - @mock.patch('logging.handlers.TimedRotatingFileHandler') @mock.patch('oslo_log.log._get_log_file_path', return_value='test.conf') def test_timed_rotate_log(self, path_mock, handler_mock): @@ -1363,54 +1344,6 @@ class SetDefaultsTestCase(BaseTestCase): self.assertIsNone(self.conf.log_file) -@testtools.skipIf(platform.system() != 'Linux', - 'pyinotify library works on Linux platform only.') -class FastWatchedFileHandlerTestCase(BaseTestCase): - - def setUp(self): - super(FastWatchedFileHandlerTestCase, self).setUp() - - def _config(self): - os_level, log_path = tempfile.mkstemp() - log_dir_path = os.path.dirname(log_path) - log_file_path = os.path.basename(log_path) - self.CONF(['--log-dir', log_dir_path, '--log-file', log_file_path]) - self.config(use_stderr=False) - self.config(watch_log_file=True) - log.setup(self.CONF, 'test', 'test') - return log_path - - def test_instantiate(self): - self._config() - logger = log._loggers[None].logger - self.assertEqual(1, len(logger.handlers)) - from oslo_log import watchers - self.assertIsInstance(logger.handlers[0], - watchers.FastWatchedFileHandler) - - def test_log(self): - log_path = self._config() - logger = log._loggers[None].logger - text = 'Hello World!' - logger.info(text) - with open(log_path, 'r') as f: - file_content = f.read() - self.assertIn(text, file_content) - - def test_move(self): - log_path = self._config() - os_level_dst, log_path_dst = tempfile.mkstemp() - os.rename(log_path, log_path_dst) - time.sleep(6) - self.assertTrue(os.path.exists(log_path)) - - def test_remove(self): - log_path = self._config() - os.remove(log_path) - time.sleep(6) - self.assertTrue(os.path.exists(log_path)) - - class MutateTestCase(BaseTestCase): def setUp(self): super(MutateTestCase, self).setUp() diff --git a/oslo_log/watchers.py b/oslo_log/watchers.py deleted file mode 100644 index d970f9d7..00000000 --- a/oslo_log/watchers.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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 errno -import logging -import logging.config -import logging.handlers -import os -import pyinotify -import stat -import time -try: - import syslog -except ImportError: - syslog = None - -"""Linux specific pyinotify based logging handlers""" - - -class _FileKeeper(pyinotify.ProcessEvent): - def my_init(self, watched_handler, watched_file): - self._watched_handler = watched_handler - self._watched_file = watched_file - - def process_default(self, event): - if event.name == self._watched_file: - self._watched_handler.reopen_file() - - -class _EventletThreadedNotifier(pyinotify.ThreadedNotifier): - - def loop(self): - """Eventlet friendly ThreadedNotifier - - EventletFriendlyThreadedNotifier contains additional time.sleep() - call insude loop to allow switching to other thread when eventlet - is used. - It can be used with eventlet and native threads as well. - """ - - while not self._stop_event.is_set(): - self.process_events() - time.sleep(0) - ref_time = time.time() - if self.check_events(): - self._sleep(ref_time) - self.read_events() - - -class FastWatchedFileHandler(logging.handlers.WatchedFileHandler, object): - """Frequency of reading events. - - Watching thread sleeps max(0, READ_FREQ - (TIMEOUT / 1000)) seconds. - """ - READ_FREQ = 5 - - """Poll timeout in milliseconds. - - See https://docs.python.org/2/library/select.html#select.poll.poll""" - TIMEOUT = 5 - - def __init__(self, logpath, *args, **kwargs): - self._log_file = os.path.basename(logpath) - self._log_dir = os.path.dirname(logpath) - super(FastWatchedFileHandler, self).__init__(logpath, *args, **kwargs) - self._watch_file() - - def _watch_file(self): - mask = pyinotify.IN_MOVED_FROM | pyinotify.IN_DELETE - watch_manager = pyinotify.WatchManager() - handler = _FileKeeper(watched_handler=self, - watched_file=self._log_file) - notifier = _EventletThreadedNotifier( - watch_manager, - default_proc_fun=handler, - read_freq=FastWatchedFileHandler.READ_FREQ, - timeout=FastWatchedFileHandler.TIMEOUT) - notifier.daemon = True - watch_manager.add_watch(self._log_dir, mask) - notifier.start() - - def reopen_file(self): - try: - # stat the file by path, checking for existence - sres = os.stat(self.baseFilename) - except OSError as err: - if err.errno == errno.ENOENT: - sres = None - else: - raise - # compare file system stat with that of our stream file handle - if (not sres or - sres[stat.ST_DEV] != self.dev or - sres[stat.ST_INO] != self.ino): - if self.stream is not None: - # we have an open file handle, clean it up - self.stream.flush() - self.stream.close() - self.stream = None - # open a new file handle and get new stat info from that fd - self.stream = self._open() - self._statstream() diff --git a/releasenotes/notes/deprecate-watch-log-file-ba9524ae872e7c01.yaml b/releasenotes/notes/deprecate-watch-log-file-ba9524ae872e7c01.yaml index b72f8c77..9b96de6a 100644 --- a/releasenotes/notes/deprecate-watch-log-file-ba9524ae872e7c01.yaml +++ b/releasenotes/notes/deprecate-watch-log-file-ba9524ae872e7c01.yaml @@ -3,3 +3,9 @@ deprecations: - | The ``watch_log_file`` option has been deprecated and will be removed in a future release. + +upgrade: + - | + Setting ``[DEFAULT] watch_log_file = True`` now raises RuntimeError while + configuring logger. The feature has been broken for multiple cycles and + depends on pyinotify library which is not maintained now.