在 tRPC-Python 中,框架的日志后端固定为 loguru。配置文件中的 plugins.log 用于定义各个 logger 的输出目标(sink)列表。如果需要自定义日志输出方式,可以通过扩展点 PluginType.LOG_WRITER 实现 Writer 插件(框架内置了 console 和 file 两种实现)。
插件通过 @register_plugin 装饰器在模块导入时完成注册:当执行 import trpc_sdk.log 时,包初始化逻辑会加载 _loguru.py,进而导入 _writer.py,触发内置 console 和 file 插件的注册。因此,自定义的 Writer 模块也需要在服务启动入口处被 import,确保装饰器在日志初始化前执行。
在 PluginType.LOG 类型下有两个内置插件,但运行时工厂固定使用名为 loguru 的后端;扩展日志输出通道时,请注册 PluginType.LOG_WRITER 而非 PluginType.LOG:
DefaultLoggerInitPlugin(注册名"default"):在init()阶段调用get_log_config()加载配置,仅执行一次。LoguruFactoryPlugin(注册名"loguru",即DEFAULT_LOG_BACKEND_NAME):通过create_logger根据配置的输出列表创建具体的 logger 实例。
get_log_config() 会读取配置文件中的 plugins.log 节点,其结构为「logger 名称 → 输出配置列表」的映射。默认 logger 的名称为 "default"(DEFAULT_LOG_NAME)——注意不要将 YAML 中的默认 logger 名 "default" 与上面负责初始化配置的 DefaultLoggerInitPlugin 混淆。
列表中的每一项输出配置都对应一个 OutputConfig 实例(若包含未知字段,构造时会抛出 TypeError)。各字段含义如下:
writer:PluginType.LOG_WRITER插件的注册名(如console、file)。level:日志级别,支持trace、debug、info、warning、error、fatal。其中trace和debug均映射为标准库logging.DEBUG;未配置时默认为debug。formatter:格式化方式。空字符串或"json"使用文本布局;"noop"和"full_json"使用_format.py中对应的 Formatter 类;其他未知名称会回退为"json"。format_config:对应FormatConfig。Writer 实际读取其中的fmt和time_fmt字段。能够直接转换为loguru原生模板的百分号字段仅包括asctime、levelname、filename、lineno、process、message以及%(msecs)03d;若包含其他字段或格式修饰符,则会回退为函数回调形式的 formatter。writer_config:对应WriterConfig。内置的FileWriter仅读取其中的filename、log_path、max_size、max_backups和compress:- 文件路径由
log_path + "/" + filename拼接而成(若log_path为空则直接使用filename)。 max_size以 MB 为单位,乘以1000000后传给loguru的rotation参数(为0时不轮转)。max_backups传给loguru的retention参数(为0时不限制保留份数)。compress: true时启用gz压缩。WriterConfig上的max_age、roll_type、time_unit字段目前未被FileWriter读取。
- 文件路径由
caller_skip:定义在OutputConfig上,默认值为2,当前内置 Writer 未读取该字段。capture_third_party:是否捕获第三方库日志,必须填写 YAML 布尔值true或false(注意:带引号的"false"是非空字符串,在布尔判断时会被视为True)。third_party_level:第三方库日志级别,缺省时沿用当前输出项的level。
在创建日志绑定时,_LoguruSinkManager.create_binding 会根据 output_config.writer 的名称查找已注册的 PluginType.LOG_WRITER 插件:
- 若未找到同名插件,打印
cannot find writer plugin并跳过该条输出配置。 - 若找到的插件实例继承自
LoggerWriterPlugin,则调用其create_log_sink(output_config)方法。 create_log_sink内部会调用子类实现的_get_sink_kwargs(output_config);若返回None,同样跳过该条输出。
因此,开发自定义 Writer 只需继承 LoggerWriterPlugin 并实现 _get_sink_kwargs 方法即可。注意 LoggerWriterPlugin 未在 trpc_sdk.log 顶层导出,需从 trpc_sdk.log._writer 导入。
我们可以直接参考内置 ConsoleWriter 的实现。基类的 _common_sink_kwargs(output_config) 已经组装好了 level、format 以及 enqueue=True,子类只需在此基础上补充 sink 等 loguru.logger.add 所需的参数:
import sys
from trpc_sdk.abc import PluginType
from trpc_sdk.log._writer import LoggerWriterPlugin
from trpc_sdk.plugin import register_plugin
@register_plugin(PluginType.LOG_WRITER, "console")
class ConsoleWriter(LoggerWriterPlugin):
def _get_sink_kwargs(self, output_config):
sink_kwargs = self._common_sink_kwargs(output_config)
sink_kwargs["sink"] = sys.stderr
sink_kwargs["colorize"] = False
return sink_kwargs如果编写自己的 Writer 插件,只需将装饰器中的注册名改为自定义名称(例如 "stderr"),并在服务入口导入该模块。
随后在配置文件中指定 writer 为对应的注册名。由于同一个 logger 支持同时输出到多个目标,default 下配置为一个列表:
plugins:
log:
default:
- writer: console
level: info配置加载完成后,调用 trpc_sdk.log 的日志接口(如 info(...) 或 get_logger("default"))即可将日志写入对应的输出插件。