Add better logging
This commit is contained in:
@@ -3,7 +3,7 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
@@ -250,8 +250,10 @@ func (m *mockLogger) getLogs() []string {
|
||||
return append([]string{}, m.logs...)
|
||||
}
|
||||
|
||||
func (m *mockLogger) asLogger() *log.Logger {
|
||||
return log.New(m, "", 0)
|
||||
func (m *mockLogger) asLogger() *slog.Logger {
|
||||
return slog.New(slog.NewTextHandler(m, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
}
|
||||
|
||||
func (m *mockLogger) Write(p []byte) (n int, err error) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user