feat(platform): 日志管理 - 统一日志出口并屏蔽查询与 Agent 流式事件日志

新增 internal/logging,slog、标准库 log、GORM 与 Gin 访问日志共用同一 Handler,由 CANVAS_LOG_LEVEL / CANVAS_LOG_FORMAT 控制。info 级别不再输出成功的查询类请求、成功 SQL、message_end 等 Agent 流式事件和常规画布自动保存;4xx/5xx、慢请求、慢 SQL、SQL 失败与 panic 仍会记录,SQL 不内联参数值。
This commit is contained in:
ddcat666 committed 2026-09-30 03:33:34 +08:00
1 parent b7ff958c2d
commit a0f025aa96
21 files changed
+602 -44

No files matched your search

+2 -2
View File
@@ -5,7 +5,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"log"
"log/slog"
"math"
"strings"
"time"
@@ -403,7 +403,7 @@ func (s *Service) CreateCloudAgentRun(userID string, req CloudAgentRequest, pare
return nil, err
}
if superseded {
log.Printf("agent run %s cannot resume after a contract change; continuing in a new turn", parentID)
slog.Info("agent run cannot resume after a contract change; continuing in a new turn", "run", parentID)
} else if !cloudAgentRunTerminal(parentRun.Status) || parentRun.CleanupPending {
return nil, kernel.NewAppError(409, "上一轮 Agent 尚未结束")
}
@@ -5,7 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"log/slog"
"math"
"sort"
"strings"
@@ -510,7 +510,7 @@ func (e *CanvasSearchEngine) generateHighlight(node model.CanvasNode, query Canv
// handleMessageStart 处理消息开始事件
func (s *Service) handleMessageStart(userID, runID string, data map[string]any) (any, error) {
// 记录消息开始
log.Printf("[Agent] message_start run=%s", runID)
slog.Debug("agent message_start", "run", runID)
// 更新运行状态
run, err := s.repo.CloudAgent(userID, runID)
@@ -537,14 +537,14 @@ func (s *Service) handleMessageStart(userID, runID string, data map[string]any)
func (s *Service) handleMessageDelta(userID, runID string, data map[string]any) (any, error) {
delta, _ := data["delta"].(string)
messageID, _ := data["messageId"].(string)
log.Printf("[Agent] message_delta run=%s messageId=%s len=%d", runID, messageID, len(delta))
slog.Debug("agent message_delta", "run", runID, "message_id", messageID, "len", len(delta))
// 推送实时增量到前端
if err := s.broadcastAgentEvent(userID, runID, "message_delta", map[string]any{
"delta": delta,
"messageId": messageID,
}); err != nil {
log.Printf("[Agent] failed to broadcast message_delta: %v", err)
slog.Warn("agent message_delta broadcast failed", "run", runID, "error", err)
}
return map[string]any{"ok": true}, nil
@@ -552,19 +552,19 @@ func (s *Service) handleMessageDelta(userID, runID string, data map[string]any)
// handleMessageEnd 处理消息结束事件(关键修复点)
func (s *Service) handleMessageEnd(userID, runID string, data map[string]any) (any, error) {
log.Printf("[Agent] message_end run=%s data=%+v", runID, data)
slog.Debug("agent message_end", "run", runID, "role", data["role"])
// 检查消息角色,防止将用户消息误认为助手消息
role, _ := data["role"].(string)
if role == "user" {
// 这是用户消息结束,不是助手响应,直接返回
log.Printf("[Agent] ignoring user message_end run=%s", runID)
slog.Debug("agent message_end ignored", "run", runID, "role", "user")
return map[string]any{"ok": true, "ignored": true, "reason": "user message"}, nil
}
// 只处理助手消息结束
if role != "assistant" {
log.Printf("[Agent] ignoring non-assistant message_end run=%s role=%s", runID, role)
slog.Debug("agent message_end ignored", "run", runID, "role", role)
return map[string]any{"ok": true, "ignored": true, "reason": fmt.Sprintf("non-assistant role: %s", role)}, nil
}
@@ -585,7 +585,7 @@ func (s *Service) handleMessageEnd(userID, runID string, data map[string]any) (a
// 计数真实的助手响应
state.PiAssistantResponses++
log.Printf("[Agent] assistant response completed run=%s count=%d", runID, state.PiAssistantResponses)
slog.Debug("agent assistant response completed", "run", runID, "count", state.PiAssistantResponses)
if err := s.saveCloudAgentRuntimeState(userID, runID, &state); err != nil {
return nil, err
@@ -597,7 +597,7 @@ func (s *Service) handleMessageEnd(userID, runID string, data map[string]any) (a
// handleToolCall 处理工具调用事件
func (s *Service) handleToolCall(userID, runID string, data map[string]any) (any, error) {
toolName, _ := data["toolName"].(string)
log.Printf("[Agent] tool_call run=%s tool=%s", runID, toolName)
slog.Debug("agent tool_call", "run", runID, "tool", toolName)
return map[string]any{"ok": true}, nil
}
@@ -605,7 +605,7 @@ func (s *Service) handleToolCall(userID, runID string, data map[string]any) (any
// handleError 处理错误事件
func (s *Service) handleError(userID, runID string, data map[string]any) (any, error) {
errorMsg, _ := data["error"].(string)
log.Printf("[Agent] error run=%s error=%s", runID, errorMsg)
slog.Warn("agent runtime error", "run", runID, "error", errorMsg)
// 更新运行状态为失败
run, err := s.repo.CloudAgent(userID, runID)
@@ -687,7 +687,7 @@ func (s *Service) updatePiRunWithStep(userID, runID string, revision int64, step
// routeStandardTool 路由标准工具(非画布工具)
func (s *Service) routeStandardTool(ctx context.Context, userID, runID, toolName string, payload map[string]json.RawMessage) (any, error) {
log.Printf("[Agent] routing standard tool: %s", toolName)
slog.Debug("agent routing standard tool", "run", runID, "tool", toolName)
// 将参数从 json.RawMessage 转换为 map[string]any
args := make(map[string]any)
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"log"
"log/slog"
"os"
"path/filepath"
"strings"
@@ -380,7 +381,7 @@ type EnhancedPiRequestParams struct {
// buildEnhancedPiRequest 构建增强的 Pi 请求(核心方法)
func (s *Service) buildEnhancedPiRequest(ctx context.Context, params EnhancedPiRequestParams) (cloudAgentPiProcessRequest, error) {
log.Printf("[Agent] building enhanced request for canvas=%s", params.CanvasID)
slog.Debug("agent building request", "canvas", params.CanvasID)
// 1. 构建工具列表(包括画布工具)
tools := s.buildCompletePiTools(params.Canonical.Tools)
@@ -389,7 +390,7 @@ func (s *Service) buildEnhancedPiRequest(ctx context.Context, params EnhancedPiR
skillPaths := s.buildSkillPaths(params.RuntimeState.Skills)
enabledSkills := s.buildSkillManifests(params.RuntimeState.Skills)
log.Printf("[Agent] enabled skills: %d", len(enabledSkills))
slog.Debug("agent enabled skills", "count", len(enabledSkills))
// 3. 构建 Profile 配置
profile := s.buildProfileConfig(params.RuntimeState.Profile)
@@ -409,11 +410,11 @@ func (s *Service) buildEnhancedPiRequest(ctx context.Context, params EnhancedPiR
return cloudAgentPiProcessRequest{}, fmt.Errorf("marshal canvas: %w", err)
}
log.Printf("[Agent] canvas intelligence: nodes=%d, focus=%d, relationships=%d, clusters=%d",
slog.Debug("agent canvas intelligence", "nodes",
enhancedCanvas.Snapshot.TotalNodes,
len(enhancedCanvas.Snapshot.FocusNodes),
len(enhancedCanvas.Snapshot.Relationships),
len(enhancedCanvas.Snapshot.Layout.Clusters))
"focus", len(enhancedCanvas.Snapshot.FocusNodes),
"relationships", len(enhancedCanvas.Snapshot.Relationships),
"clusters", len(enhancedCanvas.Snapshot.Layout.Clusters))
// 6. 构建 Features 配置
features := s.buildFeaturesConfig(params.RuntimeState)
@@ -461,7 +462,7 @@ func (s *Service) buildEnhancedPiRequest(ctx context.Context, params EnhancedPiR
Model: modelConfig,
}
log.Printf("[Agent] request built successfully")
slog.Debug("agent request built", "canvas", params.CanvasID)
return request, nil
}
@@ -4,7 +4,7 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log"
"log/slog"
"infinite-canvas/backend/internal/model"
)
@@ -191,17 +191,17 @@ func formatDuration(ms int64) string {
// logPiAgentStep 记录 Pi Agent 步骤
func logPiAgentStep(runID, step, message string) {
log.Printf("[Agent Step] run=%s step=%s: %s", runID, step, message)
slog.Debug("agent step", "run", runID, "step", step, "message", message)
}
// logPiAgentError 记录 Pi Agent 错误
func logPiAgentError(runID string, err error) {
log.Printf("[Agent Error] run=%s: %v", runID, err)
slog.Warn("agent error", "run", runID, "error", err)
}
// logPiAgentMetric 记录 Pi Agent 指标
func logPiAgentMetric(runID, metric string, value interface{}) {
log.Printf("[Agent Metric] run=%s %s=%v", runID, metric, value)
slog.Debug("agent metric", "run", runID, "metric", metric, "value", value)
}
// Type Conversions
@@ -2,7 +2,7 @@ package app
import (
"encoding/json"
"log"
"log/slog"
"infinite-canvas/backend/internal/model"
)
@@ -70,7 +70,7 @@ func (s *Service) cloudAgentRefreshStepSnapshotHash(run *model.CloudAgentExecuti
return call
}
call = cloudAgentRewriteCallSnapshotHash(call, latest)
log.Printf("agent step hash filled: run=%s step=%d index=%d tool=%s", run.ID, state.Step, state.CallIndex, call.Function.Name)
slog.Debug("agent step hash filled", "run", run.ID, "step", state.Step, "index", state.CallIndex, "tool", call.Function.Name)
return call
}
baseline := state.StepSnapshotHash
@@ -118,6 +118,6 @@ func (s *Service) cloudAgentRefreshStepSnapshotHash(run *model.CloudAgentExecuti
return call
}
call = cloudAgentRewriteCallSnapshotHash(call, latest)
log.Printf("agent step hash refreshed: run=%s step=%d index=%d tool=%s", run.ID, state.Step, state.CallIndex, call.Function.Name)
slog.Debug("agent step hash refreshed", "run", run.ID, "step", state.Step, "index", state.CallIndex, "tool", call.Function.Name)
return call
}
+9 -3
View File
@@ -3,8 +3,10 @@ package app
import (
"encoding/json"
"fmt"
"log"
"log/slog"
"time"
"infinite-canvas/backend/internal/logging"
"infinite-canvas/backend/internal/model"
)
@@ -111,7 +113,9 @@ func (s *Service) publicSystemChannelCatalog(intent *ModelRequestIntent) ([]Publ
if intent != nil {
matched, matchErr := s.channelModelMatchesIntent(&cm, intent)
if matchErr != nil {
log.Printf("system channel model omitted from catalog id=%s: invalid capability: %v", cm.ID, matchErr)
if logging.Every("catalog-omit:"+cm.ID, 10*time.Minute) {
slog.Warn("system channel model omitted from catalog", "id", cm.ID, "reason", "invalid capability", "error", matchErr)
}
continue
}
if !matched {
@@ -121,7 +125,9 @@ func (s *Service) publicSystemChannelCatalog(intent *ModelRequestIntent) ([]Publ
publicModel, sanitizeErr := s.sanitizeChannelModel(&cm)
if sanitizeErr != nil {
log.Printf("system channel model omitted from catalog id=%s: %v", cm.ID, sanitizeErr)
if logging.Every("catalog-omit:"+cm.ID, 10*time.Minute) {
slog.Warn("system channel model omitted from catalog", "id", cm.ID, "error", sanitizeErr)
}
continue
}
publicModels = append(publicModels, publicModel)
@@ -3,6 +3,7 @@ package app
import (
"context"
"log"
"log/slog"
"math"
"strings"
"time"
@@ -124,7 +125,7 @@ func (s *Service) cleanupDetachedUserResources(userID string, candidates []model
if err := s.repo.DeleteDetachedResources(detached, deletionJobs); err != nil {
return err
}
log.Printf("detached resource cleanup: removed %d resource rows for user %s", len(detached), userID)
slog.Info("detached resource cleanup", "removed", len(detached), "user", userID)
if len(deletionJobs) > 0 {
s.runWorkerTask(func() { s.drainResourceDeletionJobs(len(deletionJobs)) })
}
@@ -157,7 +158,7 @@ func (s *Service) cleanupExpiredArchivedAssets() {
deleted++
}
if deleted > 0 {
log.Printf("recycle bin cleanup: deleted %d expired assets (retention: %d days)", deleted, retentionDays)
slog.Info("recycle bin cleanup", "deleted", deleted, "retention_days", retentionDays)
}
}