Skip to content
3 changes: 3 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,9 @@ module webSite 'modules/web-sites.bicep' = {
UWSGI_THREADS: '2'
APP_ENV: appEnvironment
AZURE_CLIENT_ID: userAssignedIdentity.outputs.clientId
AZURE_BASIC_LOGGING_LEVEL: 'INFO'
AZURE_PACKAGE_LOGGING_LEVEL: 'WARNING'
AZURE_LOGGING_PACKAGES: ''
}
// WAF aligned configuration for Monitoring
applicationInsightResourceId: enableMonitoring ? applicationInsights!.outputs.resourceId : null
Expand Down
89 changes: 47 additions & 42 deletions infra/main.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions infra/main_custom.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,9 @@ module webSite 'modules/web-sites.bicep' = {
UWSGI_THREADS: '2'
APP_ENV: appEnvironment
AZURE_CLIENT_ID: userAssignedIdentity.outputs.clientId
AZURE_BASIC_LOGGING_LEVEL: 'INFO'
AZURE_PACKAGE_LOGGING_LEVEL: 'WARNING'
AZURE_LOGGING_PACKAGES: ''
}
// WAF aligned configuration for Monitoring
applicationInsightResourceId: enableMonitoring ? applicationInsights!.outputs.resourceId : null
Expand Down
10 changes: 8 additions & 2 deletions src/.env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Chat
DEBUG=True
# Basic application logging (default: INFO level)
AZURE_BASIC_LOGGING_LEVEL=INFO
# Azure package logging (default: WARNING level to suppress INFO)
AZURE_PACKAGE_LOGGING_LEVEL=WARNING
# Comma-separated list of specific logger names to configure (default: empty - no custom loggers)
# Example: AZURE_LOGGING_PACKAGES=azure.identity.aio._internal,azure.monitor.opentelemetry.exporter.export._base
AZURE_LOGGING_PACKAGES=

AZURE_AI_AGENT_API_VERSION=
AZURE_AI_AGENT_ENDPOINT=
AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME=
Expand Down
24 changes: 5 additions & 19 deletions src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
MINIMUM_SUPPORTED_AZURE_OPENAI_PREVIEW_API_VERSION, app_settings)
from backend.utils import (ChatType, format_as_ndjson,
format_non_streaming_response,
format_stream_response)
format_stream_response, configure_logging)
from event_utils import track_event_if_configured
from azure.monitor.opentelemetry import configure_azure_monitor
from opentelemetry import trace
Expand All @@ -40,6 +40,9 @@

bp = Blueprint("routes", __name__, static_folder="static", template_folder="static")

# Configure logging based on environment variables
configure_logging(app_settings.logging)

# Check if the Application Insights Instrumentation Key is set in the environment variables
instrumentation_key = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING")
if instrumentation_key:
Expand All @@ -50,20 +53,6 @@
# Log a warning if the Instrumentation Key is not found
logging.warning("No Application Insights Instrumentation Key found. Skipping configuration")

# Configure logging
logging.basicConfig(level=logging.INFO)

# Suppress INFO logs from 'azure.core.pipeline.policies.http_logging_policy'
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
logging.WARNING
)
logging.getLogger("azure.identity.aio._internal").setLevel(logging.WARNING)

# Suppress info logs from OpenTelemetry exporter
logging.getLogger("azure.monitor.opentelemetry.exporter.export._base").setLevel(
logging.WARNING
)


def create_app():
app = Quart(__name__)
Expand Down Expand Up @@ -115,10 +104,7 @@ async def assets(path):
return await send_from_directory("static/assets", path)


# Debug settings
DEBUG = os.environ.get("DEBUG", "false")
if DEBUG.lower() == "true":
logging.basicConfig(level=logging.DEBUG)
# Debug settings are now handled by the logging configuration above

USER_AGENT = "GitHubSampleWebApp/AsyncAzureOpenAI/1.0.0"

Expand Down
26 changes: 26 additions & 0 deletions src/backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,31 @@ class _UiSettings(BaseSettings):
show_share_button: bool = False


class _LoggingSettings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="AZURE_", env_file=DOTENV_PATH, extra="ignore", env_ignore_empty=True
)

basic_logging_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO"
package_logging_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "WARNING"
logging_packages: Optional[List[str]] = []

@field_validator("logging_packages", mode="before")
@classmethod
def split_logging_packages(cls, packages) -> Optional[List[str]]:
if isinstance(packages, str) and len(packages.strip()) > 0:
return [pkg.strip() for pkg in packages.split(",") if pkg.strip()]
return None

def get_basic_log_level(self) -> int:
"""Convert string log level to logging constant"""
return getattr(logging, self.basic_logging_level.upper())

def get_package_log_level(self) -> int:
"""Convert string package log level to logging constant"""
return getattr(logging, self.package_logging_level.upper())


class _ChatHistorySettings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="AZURE_COSMOSDB_",
Expand Down Expand Up @@ -367,6 +392,7 @@ class _AppSettings(BaseModel):
azure_ai: _AzureAISettings = _AzureAISettings()
search: _SearchCommonSettings = _SearchCommonSettings()
ui: Optional[_UiSettings] = _UiSettings()
logging: _LoggingSettings = _LoggingSettings()

# Constructed properties
chat_history: Optional[_ChatHistorySettings] = None
Expand Down
25 changes: 21 additions & 4 deletions src/backend/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
import uuid
import time

DEBUG = os.environ.get("DEBUG", "false")
if DEBUG.lower() == "true":
logging.basicConfig(level=logging.DEBUG)

AZURE_SEARCH_PERMITTED_GROUPS_COLUMN = os.environ.get(
"AZURE_SEARCH_PERMITTED_GROUPS_COLUMN"
)
Expand Down Expand Up @@ -153,3 +149,24 @@ def comma_separated_string_to_list(s: str) -> List[str]:
Split comma-separated values into a list.
"""
return s.strip().replace(" ", "").split(",")


def configure_logging(logging_settings):
"""
Configure logging based on the provided logging settings.

Args:
logging_settings: Instance of _LoggingSettings containing logging configuration
"""
# Configure basic logging
logging.basicConfig(
level=logging_settings.get_basic_log_level(),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
force=True # Override any existing configuration
)

azure_log_level = logging_settings.get_package_log_level()
for logger_name in logging_settings.logging_packages or []:
logging.getLogger(logger_name).setLevel(azure_log_level)

logging.info(f"Logging configured - Basic: {logging_settings.basic_logging_level}, Azure packages: {logging_settings.package_logging_level}, Packages: {logging_settings.logging_packages}")