diff --git a/src/pydase/utils/logging.py b/src/pydase/utils/logging.py index 189ddb1..5d276c9 100644 --- a/src/pydase/utils/logging.py +++ b/src/pydase/utils/logging.py @@ -1,14 +1,45 @@ import asyncio import logging +import logging.config import sys from copy import copy import socketio # type: ignore[import-untyped] +import uvicorn.config import uvicorn.logging -from uvicorn.config import LOGGING_CONFIG import pydase.config +logger = logging.getLogger(__name__) + +if pydase.config.OperationMode().environment == "development": + LOG_LEVEL = logging.DEBUG +else: + LOG_LEVEL = logging.INFO + +LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "default": { + "()": "pydase.utils.logging.DefaultFormatter", + "fmt": "%(asctime)s.%(msecs)03d | %(levelprefix)s | " + "%(name)s:%(funcName)s:%(lineno)d - %(message)s", + "datefmt": "%Y-%m-%d %H:%M:%S", + }, + }, + "handlers": { + "default": { + "formatter": "default", + "class": "logging.StreamHandler", + "stream": "ext://sys.stderr", + }, + }, + "loggers": { + "pydase": {"handlers": ["default"], "level": LOG_LEVEL, "propagate": False}, + }, +} + class DefaultFormatter(uvicorn.logging.ColourizedFormatter): """ @@ -64,86 +95,31 @@ class SocketIOHandler(logging.Handler): ) -def setup_logging(level: str | int | None = None) -> None: +def setup_logging() -> None: """ Configures the logging settings for the application. This function sets up logging with specific formatting and colorization of log - messages. The log level is determined based on the application's operation mode, - with an option to override the level. By default, in a development environment, the - log level is set to DEBUG, whereas in other environments, it is set to INFO. - - Args: - level (Optional[str | int]): - A specific log level to set for the application. If None, the log level is - determined based on the application's operation mode. Accepts standard log - level names ('DEBUG', 'INFO', etc.) and corresponding numerical values. - - Example: - - ```python - >>> import logging - >>> setup_logging(logging.DEBUG) - >>> setup_logging("INFO") - ``` + messages. The log level is determined based on the application's operation mode. By + default, in a development environment, the log level is set to DEBUG, whereas in + other environments, it is set to INFO. """ - logger = logging.getLogger() + logger.debug("Configuring pydase logging.") - if pydase.config.OperationMode().environment == "development": - log_level = logging.DEBUG - else: - log_level = logging.INFO - - # If a level is specified, check whether it's a string or an integer. - if level is not None: - if isinstance(level, str): - # Convert known log level strings directly to their corresponding logging - # module constants. - level_name = level.upper() # Ensure level names are uppercase - if hasattr(logging, level_name): - log_level = getattr(logging, level_name) - else: - raise ValueError( - f"Invalid log level: {level}. Must be one of 'DEBUG', 'INFO', " - "'WARNING', 'ERROR', etc." - ) - elif isinstance(level, int): - log_level = level # Directly use integer levels - else: - raise ValueError("Log level must be a string or an integer.") - - # Set the logger's level. - logger.setLevel(log_level) - - # create console handler and set level to debug - ch = logging.StreamHandler() - - # add formatter to ch - ch.setFormatter( - DefaultFormatter( - fmt=( - "%(asctime)s.%(msecs)03d | %(levelprefix)s | " - "%(name)s:%(funcName)s:%(lineno)d - %(message)s" - ), - datefmt="%Y-%m-%d %H:%M:%S", - ) - ) - - # add ch to logger - logger.addHandler(ch) - - logger.debug("Configuring service logging.") - logging.getLogger("asyncio").setLevel(logging.INFO) - logging.getLogger("urllib3").setLevel(logging.INFO) + logging.config.dictConfig(LOGGING_CONFIG) # configuring uvicorn logger - LOGGING_CONFIG["formatters"]["default"][ - "fmt" - ] = "%(asctime)s.%(msecs)03d | %(levelprefix)s %(message)s" - LOGGING_CONFIG["formatters"]["default"]["datefmt"] = "%Y-%m-%d %H:%M:%S" - LOGGING_CONFIG["formatters"]["access"]["fmt"] = ( + uvicorn.config.LOGGING_CONFIG["formatters"]["default"]["fmt"] = ( + "%(asctime)s.%(msecs)03d | %(levelprefix)s %(message)s" + ) + uvicorn.config.LOGGING_CONFIG["formatters"]["default"]["datefmt"] = ( + "%Y-%m-%d %H:%M:%S" + ) + uvicorn.config.LOGGING_CONFIG["formatters"]["access"]["fmt"] = ( "%(asctime)s.%(msecs)03d | %(levelprefix)s %(client_addr)s " '- "%(request_line)s" %(status_code)s' ) - LOGGING_CONFIG["formatters"]["access"]["datefmt"] = "%Y-%m-%d %H:%M:%S" + uvicorn.config.LOGGING_CONFIG["formatters"]["access"]["datefmt"] = ( + "%Y-%m-%d %H:%M:%S" + ) diff --git a/tests/components/test_number_slider.py b/tests/components/test_number_slider.py index 0eb5ff8..f255541 100644 --- a/tests/components/test_number_slider.py +++ b/tests/components/test_number_slider.py @@ -8,7 +8,8 @@ from pydase.data_service.data_service_observer import DataServiceObserver from pydase.data_service.state_manager import StateManager from pytest import LogCaptureFixture -logger = logging.getLogger(__name__) +logger = logging.getLogger("pydase") +logger.propagate = True def test_number_slider(caplog: LogCaptureFixture) -> None: diff --git a/tests/data_service/test_task_manager.py b/tests/data_service/test_task_manager.py index 6793fe9..08ffc65 100644 --- a/tests/data_service/test_task_manager.py +++ b/tests/data_service/test_task_manager.py @@ -7,7 +7,8 @@ from pydase.data_service.data_service_observer import DataServiceObserver from pydase.data_service.state_manager import StateManager from pytest import LogCaptureFixture -logger = logging.getLogger() +logger = logging.getLogger("pydase") +logger.propagate = True @pytest.mark.asyncio diff --git a/tests/observer_pattern/observable/test_observable.py b/tests/observer_pattern/observable/test_observable.py index 4bd7e9f..d7a8788 100644 --- a/tests/observer_pattern/observable/test_observable.py +++ b/tests/observer_pattern/observable/test_observable.py @@ -5,7 +5,8 @@ import pytest from pydase.observer_pattern.observable import Observable from pydase.observer_pattern.observer import Observer -logger = logging.getLogger(__name__) +logger = logging.getLogger("pydase") +logger.propagate = True class MyObserver(Observer): diff --git a/tests/observer_pattern/observable/test_observable_dict.py b/tests/observer_pattern/observable/test_observable_dict.py index f06c00d..de8f66f 100644 --- a/tests/observer_pattern/observable/test_observable_dict.py +++ b/tests/observer_pattern/observable/test_observable_dict.py @@ -5,7 +5,8 @@ import pytest from pydase.observer_pattern.observable import Observable from pydase.observer_pattern.observer import Observer -logger = logging.getLogger(__name__) +logger = logging.getLogger("pydase") +logger.propagate = True class MyObserver(Observer): diff --git a/tests/observer_pattern/observable/test_observable_object.py b/tests/observer_pattern/observable/test_observable_object.py index a5b6d6b..804c8d1 100644 --- a/tests/observer_pattern/observable/test_observable_object.py +++ b/tests/observer_pattern/observable/test_observable_object.py @@ -5,7 +5,8 @@ import pytest from pydase.observer_pattern.observable import Observable from pydase.observer_pattern.observer import Observer -logger = logging.getLogger(__name__) +logger = logging.getLogger("pydase") +logger.propagate = True class MyObserver(Observer): diff --git a/tests/utils/test_logging.py b/tests/utils/test_logging.py index 6599d0d..5273a71 100644 --- a/tests/utils/test_logging.py +++ b/tests/utils/test_logging.py @@ -1,12 +1,12 @@ import logging -from pydase.utils.logging import setup_logging from pytest import LogCaptureFixture def test_log_error(caplog: LogCaptureFixture): - setup_logging("ERROR") - logger = logging.getLogger() + logger = logging.getLogger("pydase") + logger.setLevel(logging.ERROR) + logger.debug("This is a debug message") logger.info("This is an info message") logger.warning("This is a warning message") @@ -21,8 +21,9 @@ def test_log_error(caplog: LogCaptureFixture): def test_log_warning(caplog: LogCaptureFixture): - setup_logging("WARNING") - logger = logging.getLogger() + logger = logging.getLogger("pydase") + logger.setLevel(logging.WARNING) + logger.debug("This is a debug message") logger.info("This is an info message") logger.warning("This is a warning message") @@ -37,10 +38,9 @@ def test_log_warning(caplog: LogCaptureFixture): def test_log_debug(caplog: LogCaptureFixture): - setup_logging("DEBUG") - logger = ( - logging.getLogger() - ) # Get the root logger or replace with the appropriate logger. + logger = logging.getLogger("pydase") + logger.setLevel(logging.DEBUG) + logger.debug("This is a debug message") logger.info("This is an info message") logger.warning("This is a warning message") @@ -54,10 +54,9 @@ def test_log_debug(caplog: LogCaptureFixture): def test_log_info(caplog: LogCaptureFixture): - setup_logging("INFO") - logger = ( - logging.getLogger() - ) # Get the root logger or replace with the appropriate logger. + logger = logging.getLogger("pydase") + logger.setLevel(logging.INFO) + logger.debug("This is a debug message") logger.info("This is an info message") logger.warning("This is a warning message")