def _get_log_file_paths(self, workdir, pants_run): pid_specific_log_file = ExceptionSink.exceptions_log_path(for_pid=pants_run.pid, in_dir=workdir) self.assertTrue(os.path.isfile(pid_specific_log_file)) shared_log_file = ExceptionSink.exceptions_log_path(in_dir=workdir) self.assertTrue(os.path.isfile(shared_log_file)) self.assertNotEqual(pid_specific_log_file, shared_log_file) return (pid_specific_log_file, shared_log_file)
def get_log_file_paths(workdir: str, pid: int) -> Tuple[str, str]: pid_specific_log_file = ExceptionSink.exceptions_log_path(for_pid=pid, in_dir=workdir) assert os.path.isfile(pid_specific_log_file) shared_log_file = ExceptionSink.exceptions_log_path(in_dir=workdir) assert os.path.isfile(shared_log_file) assert pid_specific_log_file != shared_log_file return (pid_specific_log_file, shared_log_file)
def _extract_remote_exception(self, pantsd_pid, nailgun_error): """Given a NailgunError, returns a Terminated exception with additional info (where possible). This method will include the entire exception log for either the `pid` in the NailgunError, or failing that, the `pid` of the pantsd instance. """ sources = [pantsd_pid] if nailgun_error.pid is not None: sources = [abs(nailgun_error.pid)] + sources exception_text = None for source in sources: log_path = ExceptionSink.exceptions_log_path(for_pid=source) exception_text = maybe_read_file(log_path) if exception_text: break exception_suffix = ( "\nRemote exception:\n{}".format(exception_text) if exception_text else "" ) return self.Terminated( "abruptly lost active connection to pantsd runner: {!r}{}".format( nailgun_error, exception_suffix ) )
def test_log_exception(self): fake_process_title = "fake_title" msg = "XXX" sink = self._gen_sink_subclass() pid = os.getpid() with temporary_dir() as tmpdir: # Check that tmpdir exists, and log an exception into that directory. sink.reset_log_location(tmpdir) with unittest.mock.patch("setproctitle.getproctitle", autospec=True, spec_set=True) as getproctitle_mock: getproctitle_mock.return_value = fake_process_title sink.log_exception(msg) getproctitle_mock.assert_called_once() # This should have created two log files, one specific to the current pid. self.assertEqual(os.listdir(tmpdir), [".pids"]) cur_process_error_log_path = ExceptionSink.exceptions_log_path( for_pid=pid, in_dir=tmpdir) self.assertTrue(os.path.isfile(cur_process_error_log_path)) shared_error_log_path = ExceptionSink.exceptions_log_path( in_dir=tmpdir) self.assertTrue(os.path.isfile(shared_error_log_path)) # Ensure we're creating two separate files. self.assertNotEqual(cur_process_error_log_path, shared_error_log_path) # We only logged a single error, so the files should both contain only that single log entry. err_rx = """\ timestamp: ([^\n]+) process title: {fake_process_title} sys.argv: ([^\n]+) pid: {pid} {msg} """.format(fake_process_title=re.escape(fake_process_title), pid=pid, msg=msg) with open(cur_process_error_log_path, "r") as cur_pid_file: self.assertRegex(cur_pid_file.read(), err_rx) with open(shared_error_log_path, "r") as shared_log_file: self.assertRegex(shared_log_file.read(), err_rx)
def test_log_exception(): sink = _gen_sink_subclass() with temporary_dir() as tmpdir: # Check that tmpdir exists, and log an exception into that directory. sink.reset_log_location(tmpdir) pid = os.getpid() with unittest.mock.patch("setproctitle.getproctitle", autospec=True, spec_set=True) as getproctitle_mock: getproctitle_mock.return_value = "fake_title" sink._log_exception("XXX") getproctitle_mock.assert_called_once() # This should have created two log files, one specific to the current pid. logfiles = os.listdir(tmpdir) assert len(logfiles) == 2 assert "exceptions.log" in logfiles cur_process_error_log_path = ExceptionSink.exceptions_log_path( for_pid=pid, in_dir=tmpdir) assert os.path.isfile(cur_process_error_log_path) is True shared_error_log_path = ExceptionSink.exceptions_log_path( in_dir=tmpdir) assert os.path.isfile(shared_error_log_path) is True # Ensure we're creating two separate files. assert cur_process_error_log_path != shared_error_log_path # We only logged a single error, so the files should both contain only that single log entry. err_rx = f"""\ timestamp: ([^\n]+) process title: fake_title sys.argv: ([^\n]+) pid: {pid} XXX """ with open(cur_process_error_log_path) as cur_pid_file: assert bool(re.search(err_rx, cur_pid_file.read())) with open(shared_error_log_path) as shared_log_file: assert bool(re.search(err_rx, shared_log_file.read()))