diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/__init__.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/__init__.py index b18336b7..7311abf2 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/__init__.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/__init__.py @@ -101,7 +101,7 @@ from .channel_adapter_protocol import ChannelAdapterProtocol from .turn_context_protocol import TurnContextProtocol -from ._load_configuration import load_configuration_from_env +from .config import load_configuration_from_env __all__ = [ "AgentsModel", diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/config/__init__.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/__init__.py new file mode 100644 index 00000000..73eadc2b --- /dev/null +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/__init__.py @@ -0,0 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from ._load_configuration import load_configuration_from_env + +__all__ = [ + "load_configuration_from_env", +] diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_configure_logging.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_configure_logging.py new file mode 100644 index 00000000..eb76594e --- /dev/null +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_configure_logging.py @@ -0,0 +1,55 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import logging + +from ..errors import configuration_errors + +# in Python 3.11, we can move to using +# logging.getLevelNamesMapping() +_NAME_TO_LEVEL = { + "CRITICAL": logging.CRITICAL, + "FATAL": logging.FATAL, + "ERROR": logging.ERROR, + "WARN": logging.WARNING, + "WARNING": logging.WARNING, + "INFO": logging.INFO, + "INFORMATION": logging.INFO, # .NET parity + "DEBUG": logging.DEBUG, + "NOTSET": logging.NOTSET, +} + + +def _configure_logging(logging_config: dict): + """Configures logging based on the provided logging configuration dictionary. + + :param logging_config: A dictionary containing logging configuration. + :raises ValueError: If an invalid log level is provided in the configuration. + """ + + log_levels = logging_config.get("LOGLEVEL", {}) + + console_handler = logging.StreamHandler() + console_handler.setFormatter( + logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)" + ) + ) + + for key in log_levels: + level_name = log_levels[key].upper() + level = _NAME_TO_LEVEL.get(level_name) + if level is None: + raise ValueError( + configuration_errors.InvalidLoggingConfiguration.format(key, level_name) + ) + + namespace = key.lower() + if namespace == "default": + logger = logging.getLogger() + else: + logger = logging.getLogger(namespace) + + logger.handlers.clear() # Remove existing handlers to prevent duplicates + logger.addHandler(console_handler) + logger.setLevel(level) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/_load_configuration.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py similarity index 91% rename from libraries/microsoft-agents-activity/microsoft_agents/activity/_load_configuration.py rename to libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py index cfc8a420..ef0de9f3 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/_load_configuration.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/config/_load_configuration.py @@ -3,6 +3,8 @@ from typing import Any +from ._configure_logging import _configure_logging + def load_configuration_from_env(env_vars: dict[str, Any]) -> dict: """ @@ -26,6 +28,8 @@ def load_configuration_from_env(env_vars: dict[str, Any]) -> dict: conn for conn in result.get("CONNECTIONSMAP", {}).values() ] + _configure_logging(result.get("LOGGING", {})) + return { "AGENTAPPLICATION": result.get("AGENTAPPLICATION", {}), "CONNECTIONS": result.get("CONNECTIONS", {}), diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/__init__.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/__init__.py index 90283f53..a9f63d69 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/__init__.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/__init__.py @@ -6,9 +6,16 @@ """ from .error_message import ErrorMessage -from .error_resources import ActivityErrorResources +from .error_resources import ActivityErrorResources, ConfigurationErrorResources # Singleton instance activity_errors = ActivityErrorResources() +configuration_errors = ConfigurationErrorResources() -__all__ = ["ErrorMessage", "ActivityErrorResources", "activity_errors"] +__all__ = [ + "ErrorMessage", + "ActivityErrorResources", + "ConfigurationErrorResources", + "activity_errors", + "configuration_errors", +] diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/error_resources.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/error_resources.py index 0b0b68e9..455f7bc4 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/error_resources.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/errors/error_resources.py @@ -50,3 +50,16 @@ class ActivityErrorResources: def __init__(self): """Initialize ActivityErrorResources.""" pass + + +class ConfigurationErrorResources: + """ + Error messages for environment configuration operations. + + Error codes are organized in the range -66000 to -66999. + """ + + InvalidLoggingConfiguration = ErrorMessage( + "Invalid logging level configured: {0} = {1}", + -66011, + )