fix(channels): QQ 适配器接上注入 logger(帧取证复活)+ 兜底不再静默降级(Warn 说明 Debug 会被丢)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
AskaEth committed 2026-10-05 12:53:17 +08:00
1 parent e1bd7c3009
commit b82870f41e
4 files changed
+272 -8

No files matched your search

+48 -7
View File
@@ -29,6 +29,9 @@ import (
)
const (
// adapterLogSource 是本包产出的日志的来源标签(AGENTS §10:日志必须标来源)。
adapterLogSource = "plugins/channels/qq_obv11"
// defaultMaxMessageLength bounds a single outgoing QQ message length.
defaultMaxMessageLength = 4000
// defaultRecommendBurstMax is the safe burst size for one reply.
@@ -142,9 +145,25 @@ type Adapter struct {
// Compile-time conformance with the channel contract.
var _ adapter.PlatformAdapter = (*Adapter)(nil)
// Option customizes an Adapter at construction time.
type Option func(*Adapter)
// WithLogger injects the logger the adapter logs through — the wiring path the composition
// root uses (cmd/cyrene/channels.go), mirroring kook/telegram/webhook. A Config decoded from
// channels.json never carries a logger, so without this option an adapter would fall back to
// resolveLogger's default and every DEBUG record (the inbound frame forensics) would vanish.
// It takes precedence over Config.Logger when both are given.
func WithLogger(l *slog.Logger) Option {
return func(a *Adapter) {
if l != nil {
a.log = logger.WithSource(l, adapterLogSource)
}
}
}
// NewAdapter builds a qq_obv11 Adapter from cfg, applying safe defaults. 默认值口径
// (裁决 1.4):transport=ws、mode=server,即**不给出任何新配置时行为与改造前一致**。
func NewAdapter(cfg Config) *Adapter {
func NewAdapter(cfg Config, opts ...Option) *Adapter {
cfg.Transport = normalizeTransport(cfg.Transport)
cfg.Mode = normalizeMode(cfg.Mode)
cfg.ReportPath = normalizeReportPath(cfg.ReportPath)
@@ -154,13 +173,8 @@ func NewAdapter(cfg Config) *Adapter {
if cfg.ProactiveWindow <= 0 {
cfg.ProactiveWindow = defaultProactiveWindow
}
base := cfg.Logger
if base == nil {
base = logger.New(logger.Options{})
}
return &Adapter{
a := &Adapter{
cfg: cfg,
log: logger.WithSource(base, "plugins/channels/qq_obv11"),
groups: map[string]string{},
newConn: make(chan struct{}, 1),
limiter: newRateLimiter(cfg.MaxProactivePerWindow, cfg.ProactiveWindow),
@@ -169,6 +183,33 @@ func NewAdapter(cfg Config) *Adapter {
probeEvery: defaultProbeInterval,
probeTimeout: defaultProbeTimeout,
}
for _, opt := range opts {
opt(a)
}
a.log = resolveLogger(a.log, cfg.Logger)
return a
}
// resolveLogger 选定适配器的日志器(并统一加来源标注)。优先级:WithLogger 选项 →
// Config.Logger → 兜底。
//
// 兜底**不静默**:它用默认 INFO logger 并当场记一条 WARN。理由是入站帧取证
// (inbound_debug.go,级固定 DEBUG)是排查「群友 @ 没回」的地基——静默兜底会让「没人
// 注入 logger」在日志里毫无痕迹,与「上游压根没上报」无法区分(2026-10-05 实测:组合根
// 漏传 logger,全部归档里 `msg="inbound frame"` 一条都没有)。因此宁可留一条噪音,也要
// 让「Debug 会被丢弃」显式可见。兜底只服务裸适配器(测试/库内直构),生产路径由组合根
// 经 WithLogger 正式接线。
func resolveLogger(option, cfg *slog.Logger) *slog.Logger {
if option != nil {
return option
}
if cfg != nil {
return logger.WithSource(cfg, adapterLogSource)
}
fallback := logger.WithSource(logger.New(logger.Options{}), adapterLogSource)
fallback.Warn("qq-obv11: no logger injected, Debug forensics (inbound frame) will be dropped",
"hint", "inject one via qq_obv11.WithLogger from the composition root")
return fallback
}
// Name returns the platform identifier "qq-obv11".