feat(observability): enrich request logs with auth and knowledge context
- Collect per-request auth diagnostics (header presence, scheme, token fingerprint, failure stage/reason, subject, expiry) in the auth middleware and expose them through RequestLogContextFromGin so the shared logger middleware can emit them. - Add route, query, user-agent, and referer fields to the request log. - Log knowledge resolve outcome per stage (no_search_points, no_candidate_text, no_active_candidates, rerank_failed_fallback, resolved) with candidate/selected snippet previews and precise facts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -790,6 +790,7 @@ func (s *KnowledgeService) ResolveContext(
|
||||
return nil, response.ErrServiceUnavailable(50354, "qdrant_search_failed", err.Error())
|
||||
}
|
||||
if len(searchPoints) == 0 {
|
||||
s.logKnowledgeResolve(baseContext, tenantID, query, "no_search_points", nil, nil)
|
||||
return baseContext, nil
|
||||
}
|
||||
|
||||
@@ -812,6 +813,7 @@ func (s *KnowledgeService) ResolveContext(
|
||||
documents = append(documents, text)
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
s.logKnowledgeResolve(baseContext, tenantID, query, "no_candidate_text", nil, nil)
|
||||
return baseContext, nil
|
||||
}
|
||||
|
||||
@@ -820,6 +822,7 @@ func (s *KnowledgeService) ResolveContext(
|
||||
return nil, err
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
s.logKnowledgeResolve(baseContext, tenantID, query, "no_active_candidates", nil, nil)
|
||||
return baseContext, nil
|
||||
}
|
||||
|
||||
@@ -834,6 +837,7 @@ func (s *KnowledgeService) ResolveContext(
|
||||
selected = s.enrichKnowledgePromptSnippets(ctx, tenantID, selected)
|
||||
baseContext.Snippets = selected
|
||||
baseContext.PreciseFacts = s.collectKnowledgeContextPreciseFacts(ctx, tenantID, groupIDs, selected)
|
||||
s.logKnowledgeResolve(baseContext, tenantID, query, "rerank_failed_fallback", candidates, selected)
|
||||
return baseContext, nil
|
||||
}
|
||||
|
||||
@@ -853,9 +857,97 @@ func (s *KnowledgeService) ResolveContext(
|
||||
selected = s.enrichKnowledgePromptSnippets(ctx, tenantID, selected)
|
||||
baseContext.Snippets = selected
|
||||
baseContext.PreciseFacts = s.collectKnowledgeContextPreciseFacts(ctx, tenantID, groupIDs, selected)
|
||||
s.logKnowledgeResolve(baseContext, tenantID, query, "resolved", candidates, selected)
|
||||
return baseContext, nil
|
||||
}
|
||||
|
||||
func (s *KnowledgeService) logKnowledgeResolve(
|
||||
knowledgeCtx *KnowledgeContext,
|
||||
tenantID int64,
|
||||
query string,
|
||||
stage string,
|
||||
candidates []KnowledgeSnippet,
|
||||
selected []KnowledgeSnippet,
|
||||
) {
|
||||
if s == nil || s.logger == nil || knowledgeCtx == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("knowledge resolve result",
|
||||
zap.String("stage", strings.TrimSpace(stage)),
|
||||
zap.Int64("tenant_id", tenantID),
|
||||
zap.Int64s("group_ids", knowledgeCtx.GroupIDs),
|
||||
zap.Strings("group_names", knowledgeCtx.GroupNames),
|
||||
zap.String("query", knowledgeLogPreview(query, 600)),
|
||||
zap.Int("candidate_count", len(candidates)),
|
||||
zap.Int("selected_count", len(selected)),
|
||||
zap.Int("precise_fact_count", len(knowledgeCtx.PreciseFacts)),
|
||||
zap.Any("candidates", summarizeKnowledgeSnippetsForLog(candidates)),
|
||||
zap.Any("selected_snippets", summarizeKnowledgeSnippetsForLog(selected)),
|
||||
zap.Strings("precise_facts", summarizeKnowledgeFactsForLog(knowledgeCtx.PreciseFacts)),
|
||||
)
|
||||
}
|
||||
|
||||
func summarizeKnowledgeSnippetsForLog(snippets []KnowledgeSnippet) []map[string]any {
|
||||
if len(snippets) == 0 {
|
||||
return []map[string]any{}
|
||||
}
|
||||
|
||||
summary := make([]map[string]any, 0, len(snippets))
|
||||
for _, snippet := range snippets {
|
||||
entry := map[string]any{
|
||||
"point_id": snippet.PointID,
|
||||
"group_id": snippet.GroupID,
|
||||
"group_name": strings.TrimSpace(snippet.GroupName),
|
||||
"item_id": snippet.KnowledgeItemID,
|
||||
"item_name": strings.TrimSpace(snippet.ItemName),
|
||||
"score": snippet.Score,
|
||||
"text": knowledgeLogPreview(snippet.Text, 240),
|
||||
}
|
||||
if sourceType := strings.TrimSpace(snippet.SourceType); sourceType != "" {
|
||||
entry["source_type"] = sourceType
|
||||
}
|
||||
if sourceURI := strings.TrimSpace(derefKnowledgeString(snippet.SourceURI)); sourceURI != "" {
|
||||
entry["source_uri"] = sourceURI
|
||||
}
|
||||
if len(snippet.PreciseFacts) > 0 {
|
||||
entry["precise_facts"] = summarizeKnowledgeFactsForLog(snippet.PreciseFacts)
|
||||
}
|
||||
summary = append(summary, entry)
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
func summarizeKnowledgeFactsForLog(facts []string) []string {
|
||||
if len(facts) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
limit := len(facts)
|
||||
if limit > 12 {
|
||||
limit = 12
|
||||
}
|
||||
|
||||
summary := make([]string, 0, limit)
|
||||
for _, fact := range facts[:limit] {
|
||||
summary = append(summary, knowledgeLogPreview(fact, 240))
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
func knowledgeLogPreview(value string, maxRunes int) string {
|
||||
text := strings.TrimSpace(value)
|
||||
if text == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
runes := []rune(text)
|
||||
if maxRunes <= 0 || len(runes) <= maxRunes {
|
||||
return text
|
||||
}
|
||||
return strings.TrimSpace(string(runes[:maxRunes])) + "..."
|
||||
}
|
||||
|
||||
func (s *KnowledgeService) RenderPromptSection(ctx *KnowledgeContext) string {
|
||||
if ctx == nil || (len(ctx.Snippets) == 0 && len(ctx.PreciseFacts) == 0) {
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user