logging: tool.call.error/end 接入主 logger (双写终端 + spark-mcp.log)
之前所有 tool 调用的 error 和 end 事件只写进 per-tool 文件
(data/logs/tools/<ts>_<tool>_<id>.log), 主日志文件 spark-mcp.log 完全
看不到。tail -f spark-mcp.log 排查问题时形同虚设, 必须去找具体的
per-tool 文件才能看到错误。
修复: toolCallLogger struct 加 base *slog.Logger 字段, StartToolCall
把传入的 base 写进 struct. WithError 在记录错误的同时调 base.Error
("tool.call.error", tool, name, err), End 在写完 per-tool 文件后
调 base.Info("tool.call.end", tool, name, duration_ms, error).
base 走的是 main logger (multiHandler 包装), 自动同时写终端 (遵循
LOG_FORMAT, 默认 text) 和主日志文件 (永远 JSON). LOG_LEVEL 过滤对
这两条事件仍生效, 行为统一.
新增 3 个单元测试 (internal/logging/tool_call_test.go):
- TestStartToolCall_EmitsErrorAndEnd: 验证错误路径两条事件都进
主 logger (用 bytes.Buffer 捕获 slog 输出, 解码 JSON 校验字段)
- TestStartToolCall_NilBaseSafe: 验证 base=nil (测试场景) 不 panic
且 per-tool 文件仍写入
- TestStartToolCall_SuccessfulCall_EmitsEndOnly: 验证成功路径
只发 end 不发 error, duration_ms >= 0
附带: gofmt 重排 internal/logging/tool_call.go import 块 (按字母序)
未提交: spark-mcp-linux-amd64 (本地 build 产物, .gitignore 未拦,
需另决定); .env (gitignored 内容)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -41,6 +41,7 @@ func shortHexID(n int) (string, error) {
|
||||
|
||||
type toolCallLogger struct {
|
||||
file *os.File
|
||||
base *slog.Logger
|
||||
toolName string
|
||||
startedAt time.Time
|
||||
resultRaw json.RawMessage
|
||||
@@ -54,6 +55,12 @@ func (t *toolCallLogger) WithResult(result any) {
|
||||
|
||||
func (t *toolCallLogger) WithError(err error) {
|
||||
t.err = err
|
||||
if t.base != nil && err != nil {
|
||||
t.base.Error("tool.call.error",
|
||||
"tool", t.toolName,
|
||||
"err", err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *toolCallLogger) End() {
|
||||
@@ -82,6 +89,14 @@ func (t *toolCallLogger) End() {
|
||||
}
|
||||
_, _ = t.file.Write(b)
|
||||
_, _ = t.file.WriteString("\n")
|
||||
|
||||
if t.base != nil {
|
||||
t.base.Info("tool.call.end",
|
||||
"tool", t.toolName,
|
||||
"duration_ms", durationMs,
|
||||
"error", errStr,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -145,6 +160,7 @@ func StartToolCall(ctx context.Context, base *slog.Logger, toolName string, para
|
||||
|
||||
return &toolCallLogger{
|
||||
file: f,
|
||||
base: base,
|
||||
toolName: toolName,
|
||||
startedAt: startedAt,
|
||||
}, nil
|
||||
|
||||
Reference in New Issue
Block a user