feat: add configurable logging level for backend and Mini App
- Introduced a new `LOG_LEVEL` configuration option in the `.env.example` file to allow users to set the logging level (DEBUG, INFO, WARNING, ERROR). - Updated the `Settings` class to include the `log_level` attribute, normalizing its value to ensure valid logging levels are used. - Modified the logging setup in `run.py` to utilize the configured log level, enhancing flexibility in log management. - Enhanced the Mini App to include the logging level in the JavaScript configuration, allowing for consistent logging behavior across the application. - Added a new `logger.js` module for frontend logging, implementing level-based filtering and console delegation. - Included unit tests for the new logger functionality to ensure proper behavior and level handling.
This commit is contained in:
@@ -93,7 +93,11 @@ class NoCacheStaticMiddleware:
|
||||
headers[idx] = (b"vary", existing + b", " + vary_val)
|
||||
else:
|
||||
headers.append((b"vary", vary_val))
|
||||
message = {"type": "http.response.start", "status": message["status"], "headers": headers}
|
||||
message = {
|
||||
"type": "http.response.start",
|
||||
"status": message["status"],
|
||||
"headers": headers,
|
||||
}
|
||||
await send(message)
|
||||
|
||||
await self.app(scope, receive, send_wrapper)
|
||||
@@ -104,13 +108,16 @@ app.add_middleware(NoCacheStaticMiddleware)
|
||||
|
||||
@app.get(
|
||||
"/app/config.js",
|
||||
summary="Mini App config (language)",
|
||||
description="Returns JS that sets window.__DT_LANG from DEFAULT_LANGUAGE. Loaded before main.js.",
|
||||
summary="Mini App config (language, log level)",
|
||||
description=(
|
||||
"Returns JS that sets window.__DT_LANG and window.__DT_LOG_LEVEL. Loaded before main.js."
|
||||
),
|
||||
)
|
||||
def app_config_js() -> Response:
|
||||
"""Return JS assigning window.__DT_LANG for the webapp. No caching."""
|
||||
"""Return JS assigning window.__DT_LANG and window.__DT_LOG_LEVEL for the webapp. No caching."""
|
||||
lang = config.DEFAULT_LANGUAGE
|
||||
body = f'window.__DT_LANG = "{lang}";'
|
||||
log_level = config.LOG_LEVEL_STR.lower()
|
||||
body = f'window.__DT_LANG = "{lang}";\nwindow.__DT_LOG_LEVEL = "{log_level}";'
|
||||
return Response(
|
||||
content=body,
|
||||
media_type="application/javascript; charset=utf-8",
|
||||
|
||||
@@ -20,6 +20,7 @@ from duty_teller.utils.dates import DateRangeValidationError, validate_date_rang
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _lang_from_accept_language(header: str | None) -> str:
|
||||
"""Return the application language: always config.DEFAULT_LANGUAGE.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ BOT_TOKEN is not validated on import; call require_bot_token() in the entry poin
|
||||
when running the bot.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
@@ -46,6 +47,14 @@ def _parse_phone_list(raw: str) -> set[str]:
|
||||
return result
|
||||
|
||||
|
||||
def _normalize_log_level(raw: str) -> str:
|
||||
"""Return a valid log level name (DEBUG, INFO, WARNING, ERROR); default INFO."""
|
||||
level = (raw or "").strip().upper()
|
||||
if level in ("DEBUG", "INFO", "WARNING", "ERROR"):
|
||||
return level
|
||||
return "INFO"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Settings:
|
||||
"""Injectable settings built from environment. Used in tests or when env is overridden."""
|
||||
@@ -67,6 +76,7 @@ class Settings:
|
||||
duty_display_tz: str
|
||||
default_language: str
|
||||
duty_pin_notify: bool
|
||||
log_level: str
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "Settings":
|
||||
@@ -118,6 +128,7 @@ class Settings:
|
||||
default_language=normalize_lang(os.getenv("DEFAULT_LANGUAGE", "en")),
|
||||
duty_pin_notify=os.getenv("DUTY_PIN_NOTIFY", "1").strip().lower()
|
||||
not in ("0", "false", "no"),
|
||||
log_level=_normalize_log_level(os.getenv("LOG_LEVEL", "INFO")),
|
||||
)
|
||||
|
||||
|
||||
@@ -141,6 +152,8 @@ EXTERNAL_CALENDAR_ICS_URL = _settings.external_calendar_ics_url
|
||||
DUTY_DISPLAY_TZ = _settings.duty_display_tz
|
||||
DEFAULT_LANGUAGE = _settings.default_language
|
||||
DUTY_PIN_NOTIFY = _settings.duty_pin_notify
|
||||
LOG_LEVEL = getattr(logging, _settings.log_level.upper(), logging.INFO)
|
||||
LOG_LEVEL_STR = _settings.log_level
|
||||
|
||||
|
||||
def is_admin(username: str) -> bool:
|
||||
|
||||
@@ -24,7 +24,7 @@ async def _resolve_bot_username(application) -> None:
|
||||
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
level=logging.INFO,
|
||||
level=config.LOG_LEVEL,
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user