Esempio n. 1
0
    def test_logging_filters_with_known_limitations(
        self,
        capfd: pytest.CaptureFixture,
        log_filters_input: str,
        mocker: MockerFixture,
    ) -> None:
        """Test known limitations of log message filters.

        - Filters in the input string should be separated with commas, not spaces.
        - Filters are applied with string matching, so a filter of `/health` will
          also filter out messages including `/healthy`.
        """
        filters = logging_conf.LogFilter.set_filters(log_filters_input)
        mocker.patch.dict(
            logging_conf.LOGGING_CONFIG["filters"]["filter_log_message"],
            {
                "()": logging_conf.LogFilter,
                "filters": filters
            },
            clear=True,
        )
        logger = logging_conf.logging.getLogger(
            "test.logging_conf.output.filtererrors")
        logging_conf.configure_logging(logger=logger)
        logger.info(log_filters_input)
        logger.info("/healthy")
        captured = capfd.readouterr()
        assert log_filters_input not in captured.out
        if log_filters_input == "/health":
            assert "/healthy" not in captured.out
        else:
            assert "/healthy" in captured.out
Esempio n. 2
0
 def test_logging_filters(
     self,
     capfd: pytest.CaptureFixture,
     log_filters_input: str,
     log_filters_output: set[str],
     mocker: MockerFixture,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test that log message filters are applied as expected."""
     monkeypatch.setenv("LOG_FILTERS", log_filters_input)
     mocker.patch.object(logging_conf, "LOG_FILTERS",
                         logging_conf.LogFilter.set_filters())
     mocker.patch.dict(
         logging_conf.LOGGING_CONFIG["filters"]["filter_log_message"],
         {
             "()": logging_conf.LogFilter,
             "filters": logging_conf.LOG_FILTERS
         },
         clear=True,
     )
     path_to_log = "/status"
     logger = logging_conf.logging.getLogger(
         "test.logging_conf.output.filters")
     logging_conf.configure_logging(logger=logger)
     logger.info(*self._uvicorn_access_log_args(path_to_log))
     logger.info(log_filters_input)
     for log_filter in log_filters_output:
         logger.info(*self._uvicorn_access_log_args(log_filter))
     captured = capfd.readouterr()
     assert logging_conf.LOG_FILTERS == log_filters_output
     assert path_to_log in captured.out
     for log_filter in log_filters_output:
         assert log_filter not in captured.out
Esempio n. 3
0
 def test_logging_output_default(self,
                                 capfd: pytest.CaptureFixture) -> None:
     """Test logger output with default format."""
     logger = logging_conf.logging.getLogger()
     logging_conf.configure_logging()
     logger.info("Hello, World!")
     captured = capfd.readouterr()
     assert "INFO" in captured.out
     assert "Hello, World!" in captured.out
Esempio n. 4
0
 def test_configure_logging_module(self, logging_conf_module_path: str,
                                   mocker: MockerFixture) -> None:
     """Test logging configuration with correct logging config module path."""
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     logging_conf.configure_logging(logger=logger,
                                    logging_conf=logging_conf_module_path)
     logger.debug.assert_called_once_with(
         f"Logging dict config loaded from {logging_conf_module_path}.")
Esempio n. 5
0
 def test_configure_logging_tmp_file(self, logging_conf_tmp_file_path: Path,
                                     mocker: MockerFixture) -> None:
     """Test logging configuration with temporary logging config file path."""
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     logging_conf_file = f"{logging_conf_tmp_file_path}/tmp_log.py"
     logging_conf.configure_logging(logger=logger,
                                    logging_conf=logging_conf_file)
     logger.debug.assert_called_once_with(
         f"Logging dict config loaded from {logging_conf_file}.")
Esempio n. 6
0
 def test_configure_logging_module_incorrect(self,
                                             mocker: MockerFixture) -> None:
     """Test logging configuration with incorrect logging config module path."""
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     logger_error_msg = "Error when setting logging module"
     with pytest.raises(ModuleNotFoundError):
         logging_conf.configure_logging(logger=logger,
                                        logging_conf="no.module.here")
     assert logger_error_msg in logger.error.call_args.args[0]
     assert "ModuleNotFoundError" in logger.error.call_args.args[0]
Esempio n. 7
0
 def test_configure_logging_tmp_module(
     self,
     logging_conf_tmp_file_path: Path,
     mocker: MockerFixture,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test logging configuration with temporary logging config path."""
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     monkeypatch.syspath_prepend(logging_conf_tmp_file_path)
     monkeypatch.setenv("LOGGING_CONF", "tmp_log")
     assert os.getenv("LOGGING_CONF") == "tmp_log"
     logging_conf.configure_logging(logger=logger, logging_conf="tmp_log")
     logger.debug.assert_called_once_with(
         "Logging dict config loaded from tmp_log.")
Esempio n. 8
0
 def test_logging_output_custom_format(
     self,
     capfd: pytest.CaptureFixture,
     log_format: str,
     log_level_output: str,
     logging_conf_tmp_file_path: Path,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test logger output with custom format."""
     logging_conf_file = f"{logging_conf_tmp_file_path}/tmp_log.py"
     monkeypatch.setenv("LOG_FORMAT", "gunicorn")
     monkeypatch.setenv("LOG_LEVEL", "debug")
     logger = logging_conf.logging.getLogger()
     logging_conf.configure_logging(logging_conf=logging_conf_file)
     logger.debug("Hello, Customized World!")
     captured = capfd.readouterr()
     assert log_format not in captured.out
     assert log_level_output in captured.out
     assert f"Logging dict config loaded from {logging_conf_file}." in captured.out
     assert "Hello, Customized World!" in captured.out
Esempio n. 9
0
 def test_configure_logging_tmp_module_no_dict(
     self,
     logging_conf_tmp_path_no_dict: Path,
     mocker: MockerFixture,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test logging configuration with temporary logging config path.
     - Correct module name
     - No `LOGGING_CONFIG` object
     """
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     monkeypatch.syspath_prepend(logging_conf_tmp_path_no_dict)
     monkeypatch.setenv("LOGGING_CONF", "no_dict")
     logger_error_msg = "Error when setting logging module"
     attribute_error_msg = "No LOGGING_CONFIG in no_dict"
     assert os.getenv("LOGGING_CONF") == "no_dict"
     with pytest.raises(AttributeError):
         logging_conf.configure_logging(logger=logger,
                                        logging_conf="no_dict")
     logger.error.assert_called_once_with(
         f"{logger_error_msg}: AttributeError {attribute_error_msg}.")
Esempio n. 10
0
 def test_configure_logging_tmp_module_incorrect_type(
     self,
     logging_conf_tmp_path_incorrect_type: Path,
     mocker: MockerFixture,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test logging configuration with temporary logging config path.
     - Correct module name
     - `LOGGING_CONFIG` object with incorrect type
     """
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     monkeypatch.syspath_prepend(logging_conf_tmp_path_incorrect_type)
     monkeypatch.setenv("LOGGING_CONF", "incorrect_type")
     logger_error_msg = "Error when setting logging module"
     type_error_msg = "LOGGING_CONFIG is not a dictionary instance"
     assert os.getenv("LOGGING_CONF") == "incorrect_type"
     with pytest.raises(TypeError):
         logging_conf.configure_logging(logger=logger,
                                        logging_conf="incorrect_type")
     logger.error.assert_called_once_with(
         f"{logger_error_msg}: TypeError {type_error_msg}.")
Esempio n. 11
0
 def test_configure_logging_tmp_file_incorrect_extension(
     self,
     logging_conf_tmp_path_incorrect_extension: Path,
     mocker: MockerFixture,
 ) -> None:
     """Test logging configuration with incorrect temporary file type."""
     logger = mocker.patch.object(logging_conf.logging,
                                  "root",
                                  autospec=True)
     incorrect_logging_conf = logging_conf_tmp_path_incorrect_extension.joinpath(
         "tmp_logging_conf")
     logger_error_msg = "Error when setting logging module"
     import_error_msg = f"Unable to import {incorrect_logging_conf}"
     with pytest.raises(ImportError) as e:
         logging_conf.configure_logging(
             logger=logger,
             logging_conf=str(incorrect_logging_conf),
         )
     assert str(e.value) in import_error_msg
     logger.error.assert_called_once_with(
         f"{logger_error_msg}: ImportError {import_error_msg}.")
     with open(incorrect_logging_conf, "r") as f:
         contents = f.read()
         assert "This file doesn't have the correct extension" in contents
Esempio n. 12
0
    max_workers: str | None = None,
    total_workers: str | None = None,
    workers_per_core: str = "1",
) -> int:
    """Calculate the number of Gunicorn worker processes."""
    cores = multiprocessing.cpu_count()
    default = max(int(float(workers_per_core) * cores), 2)
    use_max = m if max_workers and (m := int(max_workers)) > 0 else False
    use_total = t if total_workers and (t := int(total_workers)) > 0 else False
    use_least = min(use_max, use_total) if use_max and use_total else False
    use_default = min(use_max, default) if use_max else default
    return use_least or use_total or use_default


# Gunicorn settings
bind = os.getenv(
    "BIND") or f'{os.getenv("HOST", "0.0.0.0")}:{os.getenv("PORT", "80")}'
accesslog = os.getenv("ACCESS_LOG", "-")
errorlog = os.getenv("ERROR_LOG", "-")
graceful_timeout = int(os.getenv("GRACEFUL_TIMEOUT", "120"))
keepalive = int(os.getenv("KEEP_ALIVE", "5"))
logconfig_dict = configure_logging()
loglevel = os.getenv("LOG_LEVEL", "info")
timeout = int(os.getenv("TIMEOUT", "120"))
worker_tmp_dir = "/dev/shm"
workers = calculate_workers(
    os.getenv("MAX_WORKERS"),
    os.getenv("WEB_CONCURRENCY"),
    workers_per_core=os.getenv("WORKERS_PER_CORE", "1"),
)
Esempio n. 13
0
    """Start the Uvicorn or Gunicorn server."""
    try:
        if process_manager == "gunicorn":
            logger.debug("Running Uvicorn with Gunicorn.")
            gunicorn_options: list = set_gunicorn_options(app_module)
            subprocess.run(gunicorn_options)
        elif process_manager == "uvicorn":
            logger.debug("Running Uvicorn without Gunicorn.")
            uvicorn_options: dict = set_uvicorn_options(
                log_config=logging_conf_dict)
            uvicorn.run(app_module, **uvicorn_options)
        else:
            raise NameError(
                "Process manager needs to be either uvicorn or gunicorn")
    except Exception as e:
        logger.error(
            f"Error when starting server: {e.__class__.__name__} {e}.")
        raise


if __name__ == "__main__":  # pragma: no cover
    logger = logging.getLogger()
    logging_conf_dict = configure_logging(logger=logger)
    run_pre_start_script(logger=logger)
    start_server(
        str(os.getenv("PROCESS_MANAGER", "gunicorn")),
        app_module=set_app_module(logger=logger),
        logger=logger,
        logging_conf_dict=logging_conf_dict,
    )