import sys
import os
from loguru import logger
from app.core.config import settings
from pathlib import Path
from app.core.request_id import get_request_id
# ── 1. Remove default loguru handler ──────────────────────────────────────────
logger.remove()
# ── 2. Inject request_id into every log record ───────────────────────────────
def _inject_request_id(record):
req_id = get_request_id()
record["extra"]["request_id"] = req_id if req_id else "SYSTEM"
logger.configure(patcher=_inject_request_id)
import logging
logging.getLogger("uvicorn.access").disabled = True
# ── 3. Log directory ──────────────────────────────────────────────────────────
log_dir = Path("app_logs")
log_dir.mkdir(parents=True, exist_ok=True)
# ── 4. Console — structured JSON for production, colorized text for local ─────
IS_PROD = os.environ.get("ENVIRONMENT", "development").lower() in ("production", "prod")
if IS_PROD:
# Production: pure JSON, machine-readable — timestamp in 12-hour AM/PM UTC
logger.add(
sys.stdout,
format="{message}",
serialize=True, # emits {"text":..., "record":{...}} JSON lines
level=settings.LOG_LEVEL,
enqueue=True,
)
else:
# Local dev: human-readable with color, 12-hour AM/PM format
logger.add(
sys.stdout,
format=(
"{time:MM-DD-YYYY hh:mm:ss A} | "
"{level:<8} | "
"[{extra[request_id]}] "
"{name}:{function}:{line} — {message}"
),
colorize=True,
level=settings.LOG_LEVEL,
enqueue=True,
)
# ── 5. File logger — 12-hour AM/PM timestamp ─────────────────────────────────
logger.add(
"app_logs/api_{time:YYYY-MM-DD}.log",
rotation="00:00",
retention="30 days",
compression="zip",
level="INFO",
# hh = 12-hour clock, A = AM/PM
format="{time:MM-DD-YYYY hh:mm:ss A} | {level:<8} | [{extra[request_id]}] {name}:{function}:{line} — {message}",
enqueue=True,
)
# ── 6. New Relic sink (optional — never crashes the app) ──────────────────────
_NR_KEY = os.environ.get("NEW_RELIC_LICENSE_KEY", "")
if _NR_KEY:
try:
import newrelic.agent
try:
newrelic.agent.initialize()
except Exception:
pass
def _newrelic_sink(message):
record = message.record
try:
newrelic.agent.record_log_event(
message=record["message"],
level=record["level"].name,
timestamp=int(record["time"].timestamp() * 1000),
attributes={
"request_id": record["extra"].get("request_id", "SYSTEM"),
"method": record["extra"].get("method", ""),
"path": record["extra"].get("path", ""),
"status": record["extra"].get("status", ""),
"duration_ms": record["extra"].get("duration_ms", ""),
"logger": record["name"],
"function": record["function"],
"line": record["line"],
}
)
except Exception:
pass # Cloud logging must never affect the app
logger.add(_newrelic_sink, level="INFO", enqueue=True)
except ImportError:
pass
except Exception:
pass