Add better logging

This commit is contained in:
2026-03-03 05:32:37 +00:00
parent c2b6945cab
commit 27dfe7298d
9 changed files with 263 additions and 39 deletions

View File

@@ -3,7 +3,7 @@ package server
import (
"encoding/json"
"fmt"
"log"
"log/slog"
"net/http"
"strings"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/ajac-zero/latticelm/internal/api"
"github.com/ajac-zero/latticelm/internal/conversation"
"github.com/ajac-zero/latticelm/internal/logger"
"github.com/ajac-zero/latticelm/internal/providers"
)
@@ -27,11 +28,11 @@ type ProviderRegistry interface {
type GatewayServer struct {
registry ProviderRegistry
convs conversation.Store
logger *log.Logger
logger *slog.Logger
}
// New creates a GatewayServer bound to the provider registry.
func New(registry ProviderRegistry, convs conversation.Store, logger *log.Logger) *GatewayServer {
func New(registry ProviderRegistry, convs conversation.Store, logger *slog.Logger) *GatewayServer {
return &GatewayServer{
registry: registry,
convs: convs,
@@ -94,11 +95,19 @@ func (s *GatewayServer) handleResponses(w http.ResponseWriter, r *http.Request)
if req.PreviousResponseID != nil && *req.PreviousResponseID != "" {
conv, err := s.convs.Get(*req.PreviousResponseID)
if err != nil {
s.logger.Printf("error retrieving conversation: %v", err)
s.logger.ErrorContext(r.Context(), "failed to retrieve conversation",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("conversation_id", *req.PreviousResponseID),
slog.String("error", err.Error()),
)
http.Error(w, "error retrieving conversation", http.StatusInternalServerError)
return
}
if conv == nil {
s.logger.WarnContext(r.Context(), "conversation not found",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("conversation_id", *req.PreviousResponseID),
)
http.Error(w, "conversation not found", http.StatusNotFound)
return
}
@@ -140,7 +149,12 @@ func (s *GatewayServer) handleResponses(w http.ResponseWriter, r *http.Request)
func (s *GatewayServer) handleSyncResponse(w http.ResponseWriter, r *http.Request, provider providers.Provider, providerMsgs []api.Message, resolvedReq *api.ResponseRequest, origReq *api.ResponseRequest, storeMsgs []api.Message) {
result, err := provider.Generate(r.Context(), providerMsgs, resolvedReq)
if err != nil {
s.logger.Printf("provider %s error: %v", provider.Name(), err)
s.logger.ErrorContext(r.Context(), "provider generation failed",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("provider", provider.Name()),
slog.String("model", resolvedReq.Model),
slog.String("error", err.Error()),
)
http.Error(w, "provider error", http.StatusBadGateway)
return
}
@@ -155,10 +169,24 @@ func (s *GatewayServer) handleSyncResponse(w http.ResponseWriter, r *http.Reques
}
allMsgs := append(storeMsgs, assistantMsg)
if _, err := s.convs.Create(responseID, result.Model, allMsgs); err != nil {
s.logger.Printf("error storing conversation: %v", err)
s.logger.ErrorContext(r.Context(), "failed to store conversation",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("response_id", responseID),
slog.String("error", err.Error()),
)
// Don't fail the response if storage fails
}
s.logger.InfoContext(r.Context(), "response generated",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("provider", provider.Name()),
slog.String("model", result.Model),
slog.String("response_id", responseID),
slog.Int("input_tokens", result.Usage.InputTokens),
slog.Int("output_tokens", result.Usage.OutputTokens),
slog.Bool("has_tool_calls", len(result.ToolCalls) > 0),
)
// Build spec-compliant response
resp := s.buildResponse(origReq, result, provider.Name(), responseID)
@@ -335,13 +363,20 @@ loop:
}
break loop
case <-r.Context().Done():
s.logger.Printf("client disconnected")
s.logger.InfoContext(r.Context(), "client disconnected",
slog.String("request_id", logger.FromContext(r.Context())),
)
return
}
}
if streamErr != nil {
s.logger.Printf("stream error: %v", streamErr)
s.logger.ErrorContext(r.Context(), "stream error",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("provider", provider.Name()),
slog.String("model", origReq.Model),
slog.String("error", streamErr.Error()),
)
failedResp := s.buildResponse(origReq, &api.ProviderResult{
Model: origReq.Model,
}, provider.Name(), responseID)
@@ -477,9 +512,21 @@ loop:
}
allMsgs := append(storeMsgs, assistantMsg)
if _, err := s.convs.Create(responseID, model, allMsgs); err != nil {
s.logger.Printf("error storing conversation: %v", err)
s.logger.ErrorContext(r.Context(), "failed to store conversation",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("response_id", responseID),
slog.String("error", err.Error()),
)
// Don't fail the response if storage fails
}
s.logger.InfoContext(r.Context(), "streaming response completed",
slog.String("request_id", logger.FromContext(r.Context())),
slog.String("provider", provider.Name()),
slog.String("model", model),
slog.String("response_id", responseID),
slog.Bool("has_tool_calls", len(toolCalls) > 0),
)
}
}
@@ -488,7 +535,10 @@ func (s *GatewayServer) sendSSE(w http.ResponseWriter, flusher http.Flusher, seq
*seq++
data, err := json.Marshal(event)
if err != nil {
s.logger.Printf("failed to marshal SSE event: %v", err)
s.logger.Error("failed to marshal SSE event",
slog.String("event_type", eventType),
slog.String("error", err.Error()),
)
return
}
fmt.Fprintf(w, "event: %s\ndata: %s\n\n", eventType, data)