Files
GopherGate/internal/server/logging.go
hobokenchicken 6b10d4249c
Some checks failed
CI / Check (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Release Build (push) Has been cancelled
feat: migrate backend from rust to go
This commit replaces the Axum/Rust backend with a Gin/Go implementation. The original Rust code has been archived in the 'rust' branch.
2026-03-19 10:30:05 -04:00

114 lines
3.2 KiB
Go

package server
import (
"log"
"time"
"llm-proxy/internal/db"
)
type RequestLog struct {
Timestamp time.Time `json:"timestamp"`
ClientID string `json:"client_id"`
Provider string `json:"provider"`
Model string `json:"model"`
PromptTokens uint32 `json:"prompt_tokens"`
CompletionTokens uint32 `json:"completion_tokens"`
ReasoningTokens uint32 `json:"reasoning_tokens"`
TotalTokens uint32 `json:"total_tokens"`
CacheReadTokens uint32 `json:"cache_read_tokens"`
CacheWriteTokens uint32 `json:"cache_write_tokens"`
Cost float64 `json:"cost"`
HasImages bool `json:"has_images"`
Status string `json:"status"`
ErrorMessage string `json:"error_message,omitempty"`
DurationMS int64 `json:"duration_ms"`
}
type RequestLogger struct {
database *db.DB
hub *Hub
logChan chan RequestLog
}
func NewRequestLogger(database *db.DB, hub *Hub) *RequestLogger {
return &RequestLogger{
database: database,
hub: hub,
logChan: make(chan RequestLog, 100),
}
}
func (l *RequestLogger) Start() {
go func() {
for entry := range l.logChan {
l.processLog(entry)
}
}()
}
func (l *RequestLogger) LogRequest(entry RequestLog) {
select {
case l.logChan <- entry:
default:
log.Println("Request log channel full, dropping log entry")
}
}
func (l *RequestLogger) processLog(entry RequestLog) {
// Broadcast to dashboard
l.hub.broadcast <- map[string]interface{}{
"type": "request",
"channel": "requests",
"payload": entry,
}
// Insert into DB
tx, err := l.database.Begin()
if err != nil {
log.Printf("Failed to begin transaction for logging: %v", err)
return
}
defer tx.Rollback()
// Ensure client exists
_, _ = tx.Exec("INSERT OR IGNORE INTO clients (client_id, name, description) VALUES (?, ?, 'Auto-created from request')",
entry.ClientID, entry.ClientID)
// Insert log
_, err = tx.Exec(`
INSERT INTO llm_requests
(timestamp, client_id, provider, model, prompt_tokens, completion_tokens, reasoning_tokens, total_tokens, cache_read_tokens, cache_write_tokens, cost, has_images, status, error_message, duration_ms)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, entry.Timestamp, entry.ClientID, entry.Provider, entry.Model,
entry.PromptTokens, entry.CompletionTokens, entry.ReasoningTokens, entry.TotalTokens,
entry.CacheReadTokens, entry.CacheWriteTokens, entry.Cost, entry.HasImages,
entry.Status, entry.ErrorMessage, entry.DurationMS)
if err != nil {
log.Printf("Failed to insert request log: %v", err)
return
}
// Update client stats
_, _ = tx.Exec(`
UPDATE clients SET
total_requests = total_requests + 1,
total_tokens = total_tokens + ?,
total_cost = total_cost + ?,
updated_at = CURRENT_TIMESTAMP
WHERE client_id = ?
`, entry.TotalTokens, entry.Cost, entry.ClientID)
// Update provider balance
if entry.Cost > 0 {
_, _ = tx.Exec("UPDATE provider_configs SET credit_balance = credit_balance - ? WHERE id = ? AND (billing_mode IS NULL OR billing_mode != 'postpaid')",
entry.Cost, entry.Provider)
}
err = tx.Commit()
if err != nil {
log.Printf("Failed to commit logging transaction: %v", err)
}
}