Files
life-echo/api/app/core/logging.py

271 lines
10 KiB
Python
Raw Normal View History

"""
loguru 统一日志配置 + InterceptHandler 拦截第三方库的标准库 logging
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
约定
- **业务代码**`app` 包内一律使用 ``get_logger(__name__)``返回 **loguru** ``logger.bind(module=...)``
直接走 loguru sink占位符用 **``{}``**勿用 ``%s``否则不会插值
**禁止** ``import logging`` 取业务 logger适配器层与第三方 SDK 除外
- **第三方**uvicornceleryhttpxlangchain 仍用标准库 ``logging`` ``InterceptHandler`` 汇入 loguru
默认将 ``celery*````httpx``/``httpcore`` 调到 WARNING避免刷屏任务边界见 ``app.tasks.celery_app`` ``event=celery_task_*``
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
级别
- INFO面向运维的稳定摘要生产/预发推荐长期保持
- DEBUG可含 prompt/响应预览或哈希会显著增噪与体积仅短时排障可与 ``AGENT_LOG_MAX_CHARS`` / ``AGENT_LOG_PROMPT_MODE`` 配合
``Settings.log_level`` 控制 sink``LOG_LEVEL````LOG_LEVEL=DEBUG`` 时业务 ``logger.debug`` 可见
不打开全局 DEBUG 也可设 ``LOG_AGENT_VERBOSE=1`` 查看 Agent 单行耗时与规模 ``app.core.agent_logging``
**实践说明**开发/终端用人类可读单行格式若上生产聚合ELKLokiCloudWatch建议**另加** JSON sink``serialize=True`` 或自定义 ``format``与现有 stderr 并存便于检索与关联而不是在控制台格式里硬塞结构化字段
**字段约定可读性**
- 机读键用英文 ``snake_case``优先 ``event=...``其余 ``key=value`` 空格分隔与人相关的说明用 ``msg=中文短句``可含空格放在行尾或紧邻 ``event``
- **HTTP**``request_id`` 由中间件 ``contextualize``业务处可 ``logger.bind(**correlation_bind_kwargs(user_id=..., memoir_correlation_id=...))`` ``app.core.log_events``
- **Celery**``task_prerun`` 会通过 ``app.core.celery_log_context`` 注入 ``user_id`` / ``correlation_id`` / ``task_id`` 等到 loguru ``extra``不覆盖已有 ``bind````task_postrun`` 清除避免串任务
- **耗时**业务里程碑的结束行带 ``duration_ms````perf_counter`` × 1000LLM 细粒度见 ``app.core.agent_logging`` ``agent_span`` / ``LOG_AGENT_VERBOSE``
- **级别**INFO=里程碑与任务起止DEBUG=体积与路径WARNING=可恢复失败与降级
Agent / LLM 诊断见 ``app.core.agent_logging````LOG_AGENT_VERBOSE````AGENT_LOG_MAX_CHARS````AGENT_LOG_PROMPT_MODE````AGENT_LOG_PROMPT_DEDUP`` ``api/.env.example`` ``Settings``
"""
from __future__ import annotations
import logging
import os
import sys
from typing import TYPE_CHECKING, Any
from loguru import logger
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
from app.core.config import settings
from app.core.log_events import (
celery_prerun_extras,
correlation_bind_kwargs,
format_log_event,
)
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
if TYPE_CHECKING:
from loguru import Logger
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
def _sink_min_level() -> str:
raw = (settings.log_level or "INFO").strip().upper()
if raw in ("TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"):
return raw
return "INFO"
def _parse_stdlib_level(name: str) -> int | None:
s = name.strip().upper()
if not s:
return None
if s == "TRACE":
return logging.DEBUG
return logging._nameToLevel.get(s) # type: ignore[attr-defined]
def _stdlib_logging_package_dir() -> str:
"""标准库 ``logging`` 包目录(``logging.__file__`` 的父目录),用于路径判断。"""
return os.path.dirname(os.path.abspath(logging.__file__))
def _path_is_stdlib_logging_source(path: str) -> bool:
"""是否落在 CPython 自带的 ``logging`` 包源码下(避免用 ``/logging/`` 子串误伤业务目录名)。"""
if not path:
return False
try:
root = os.path.normcase(_stdlib_logging_package_dir())
norm = os.path.normcase(os.path.abspath(path))
except OSError:
return False
sep = os.sep
return norm == root or norm.startswith(root + sep)
# CPython 未暴露「真实调用方」与「logging 内部 dispatch」的区分 API。部分链路里
# pathname 已是业务文件(如 celery/.../trace.py但 funcName 仍被记成 logging
# 内部入口callHandlers / Handler.emit 等)。此处枚举与 ``Lib/logging`` 中常见
# 帧名对齐;若未来版本改名,可据栈样本增补。
_LOG_DISPATCH_FUNC_NAMES: frozenset[str] = frozenset(
{"callHandlers", "emit", "handle", "makeRecord", "callWithContext"}
)
def _stdlib_emit_display(log_record: logging.LogRecord) -> tuple[str, int]:
"""从 LogRecord 解析更可读的 function / line供 InterceptHandler 写入 loguru"""
fn = log_record.funcName or "?"
ln = log_record.lineno
path = log_record.pathname or ""
if _path_is_stdlib_logging_source(path):
return "-", 0
if fn in _LOG_DISPATCH_FUNC_NAMES:
base = os.path.basename(path)
stem = base[:-3] if base.endswith(".py") else base
return stem or "?", ln
return fn, ln
def _stderr_format(record: Any) -> str:
"""控制台 sinkrequest_id / correlation_id / user_id 有值时才显示对应列。"""
rid = str(record["extra"].get("request_id") or "").strip()
rid_part = "<dim>rid={extra[request_id]}</dim> | " if rid and rid != "-" else ""
cid = str(record["extra"].get("correlation_id") or "").strip()
cid_part = "<dim>corr={extra[correlation_id]}</dim> | " if cid else ""
uid = str(record["extra"].get("user_id") or "").strip()
uid_part = "<dim>uid={extra[user_id]}</dim> | " if uid else ""
return (
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level.name: <8}</level> | "
"<cyan>{extra[module]}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
f"{rid_part}{cid_part}{uid_part}"
"<level>{message}</level>\n{exception}"
)
def _merge_celery_worker_extra(record: Any) -> None:
"""把 ContextVar 中的 Celery 上下文字段并入本条 loguru 记录(不覆盖已有非空 extra"""
try:
from app.core.celery_log_context import get_celery_log_extras
ctx = get_celery_log_extras()
if not ctx:
return
except Exception:
return
ex = record["extra"]
for k, v in ctx.items():
if not v:
continue
cur = ex.get(k)
if cur is None or str(cur).strip() in ("", "-"):
ex[k] = v
def _apply_third_party_log_levels() -> None:
"""压低 Celery/httpx 框架日志噪声。
logger NOTSET logger 若也为 NOTSET有效级别会变成 0NOTSETINFO 会全部通过
因此这里**必须**写死默认级别不能依赖 NOTSET继承
默认未设 CELERY_LOG_LEVEL / HTTPX_LOG_LEVEL
- ``LOG_LEVEL`` TRACE/DEBUGCeleryINFOhttpx/httpcoreWARNING
- 否则Celery httpx/httpcoreWARNING保留业务 loguru ``event=celery_task_*`` 摘要
需要框架原始行时设置 ``CELERY_LOG_LEVEL=INFO````HTTPX_LOG_LEVEL=INFO``
"""
sink = _sink_min_level()
verbose = sink in ("TRACE", "DEBUG")
# 无效环境变量时的回退:与「未设置变量」分支一致,禁止 NOTSET
default_celery = logging.INFO if verbose else logging.WARNING
default_httpx = logging.WARNING
raw_c = (settings.celery_log_level or "").strip()
if raw_c:
parsed = _parse_stdlib_level(raw_c)
cel_level = parsed if parsed is not None else default_celery
else:
cel_level = default_celery
for name in ("celery", "celery.worker"):
logging.getLogger(name).setLevel(cel_level)
raw_h = (settings.httpx_log_level or "").strip()
if raw_h:
parsed = _parse_stdlib_level(raw_h)
httpx_level = parsed if parsed is not None else default_httpx
else:
httpx_level = default_httpx
for name in ("httpx", "httpcore"):
logging.getLogger(name).setLevel(httpx_level)
class InterceptHandler(logging.Handler):
"""Route standard-library logging messages into loguru.
使用 stdlib LogRecord 真实文件/覆盖 loguru function/line
module 使用 record.name celery.app.trace若只能解析到 logging 内部则显示 ``-:0``
"""
def emit(self, log_record: logging.LogRecord) -> None:
try:
level = logger.level(log_record.levelname).name
except ValueError:
level = log_record.levelno
modname = log_record.name or "logging"
fn, ln = _stdlib_emit_display(log_record)
def patch_record(record: object) -> None:
r = record # loguru Record, dict-like
r["function"] = fn # type: ignore[index]
r["line"] = ln # type: ignore[index]
r["extra"]["module"] = modname # type: ignore[index]
msg = log_record.getMessage()
patched = logger.patch(patch_record)
if log_record.exc_info:
patched.opt(exception=log_record.exc_info).log(level, msg)
else:
patched.log(level, msg)
def setup_logging() -> None:
"""Call once at process entryAPI`main`Worker`celery_app` 首行)。
2026-03-20 15:15:35 +08:00
Celery ``worker_hijack_root_logger=False``否则会覆盖根 logger
2026-03-20 15:15:35 +08:00
"""
global logger
logger.remove()
logger.add(
sys.stderr,
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
level=_sink_min_level(),
format=_stderr_format,
backtrace=True,
diagnose=False,
)
json_path = (settings.log_json_file or "").strip()
if json_path:
logger.add(
json_path,
level=_sink_min_level(),
serialize=True,
rotation="20 MB",
retention="7 days",
encoding="utf-8",
enqueue=True,
)
logger.configure(extra={"request_id": "-", "module": "-"})
logger = logger.patch(_merge_celery_worker_extra)
# 仅 root 挂 InterceptHandler避免子 logger 与 root 各处理一次导致重复行
root = logging.getLogger()
root.handlers = [InterceptHandler()]
root.setLevel(logging.NOTSET)
_apply_third_party_log_levels()
def get_logger(name: str) -> Logger:
"""返回带 ``module`` 上下文的 loguru Logger业务模块应 ``get_logger(__name__)``。"""
return logger.bind(module=name)
# 供 middleware 等使用 ``contextualize`` 的同一 loguru 实例(与 get_logger 同源)
__all__ = [
"logger",
"setup_logging",
"get_logger",
"InterceptHandler",
"format_log_event",
"correlation_bind_kwargs",
"celery_prerun_extras",
]