Fix loglist

This commit is contained in:
XOF
2025-11-21 19:33:05 +08:00
parent 1f7aa70810
commit 6a0f344e5c
22 changed files with 380 additions and 357 deletions

View File

@@ -7,7 +7,9 @@ import (
"gemini-balancer/internal/response"
"gemini-balancer/internal/service"
"gemini-balancer/internal/task"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -160,6 +162,29 @@ func (h *APIKeyHandler) ListAPIKeys(c *gin.Context) {
response.Error(c, errors.NewAPIError(errors.ErrBadRequest, err.Error()))
return
}
if params.IDs != "" {
idStrs := strings.Split(params.IDs, ",")
ids := make([]uint, 0, len(idStrs))
for _, s := range idStrs {
id, err := strconv.ParseUint(s, 10, 64)
if err == nil {
ids = append(ids, uint(id))
}
}
if len(ids) > 0 {
keys, err := h.apiKeyService.GetKeysByIds(ids)
if err != nil {
response.Error(c, &errors.APIError{
HTTPStatus: http.StatusInternalServerError,
Code: "DATA_FETCH_ERROR",
Message: err.Error(),
})
return
}
response.Success(c, keys)
return
}
}
if params.Page <= 0 {
params.Page = 1
}

View File

@@ -3,14 +3,13 @@ package handlers
import (
"gemini-balancer/internal/errors"
"gemini-balancer/internal/models"
"gemini-balancer/internal/response"
"gemini-balancer/internal/service"
"strconv"
"github.com/gin-gonic/gin"
)
// LogHandler 负责处理与日志相关的HTTP请求
type LogHandler struct {
logService *service.LogService
}
@@ -20,14 +19,22 @@ func NewLogHandler(logService *service.LogService) *LogHandler {
}
func (h *LogHandler) GetLogs(c *gin.Context) {
// 直接将Gin的上下文传递给Service层让Service自己去解析查询参
logs, err := h.logService.GetLogs(c)
// 调用新的服务函数,接收日志列表和总
logs, total, err := h.logService.GetLogs(c)
if err != nil {
response.Error(c, errors.ErrDatabase)
return
}
if logs == nil {
logs = []models.RequestLog{}
}
response.Success(c, logs)
// 解析分页参数用于响应体
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
// 使用标准的分页响应结构
response.Success(c, gin.H{
"items": logs,
"total": total,
"page": page,
"page_size": pageSize,
})
}

View File

@@ -136,34 +136,48 @@ func (h *ProxyHandler) serveTransparentProxy(c *gin.Context, requestBody []byte,
var finalPromptTokens, finalCompletionTokens int
var actualRetries int = 0
defer func() {
// 如果一次尝试都未成功(例如,在第一次获取资源时就失败),则不记录日志
if lastUsedResources == nil {
h.logger.WithField("id", correlationID).Warn("No resources were used, skipping final log event.")
return
}
finalEvent := h.createLogEvent(c, startTime, correlationID, modelName, lastUsedResources, models.LogTypeFinal, isPreciseRouting)
finalEvent.LatencyMs = int(time.Since(startTime).Milliseconds())
finalEvent.IsSuccess = isSuccess
finalEvent.Retries = actualRetries
finalEvent.RequestLog.LatencyMs = int(time.Since(startTime).Milliseconds())
finalEvent.RequestLog.IsSuccess = isSuccess
finalEvent.RequestLog.Retries = actualRetries
if isSuccess {
finalEvent.PromptTokens = finalPromptTokens
finalEvent.CompletionTokens = finalCompletionTokens
finalEvent.RequestLog.PromptTokens = finalPromptTokens
finalEvent.RequestLog.CompletionTokens = finalCompletionTokens
}
// 确保即使在成功的情况下如果recorder存在也记录最终的状态码
if finalRecorder != nil {
finalEvent.StatusCode = finalRecorder.Code
finalEvent.RequestLog.StatusCode = finalRecorder.Code
}
if !isSuccess {
// 将 finalProxyErr 的信息填充到 RequestLog 中
if finalProxyErr != nil {
finalEvent.Error = finalProxyErr
finalEvent.ErrorCode = finalProxyErr.Code
finalEvent.ErrorMessage = finalProxyErr.Message
finalEvent.Error = finalProxyErr // Error 字段用于事件传递,不会被序列化到数据库
finalEvent.RequestLog.ErrorCode = finalProxyErr.Code
finalEvent.RequestLog.ErrorMessage = finalProxyErr.Message
} else if finalRecorder != nil {
apiErr := errors.NewAPIErrorWithUpstream(finalRecorder.Code, "PROXY_ERROR", "Request failed after all retries.")
// 降级处理:如果 finalProxyErr 为空但 recorder 存在且失败
apiErr := errors.NewAPIErrorWithUpstream(finalRecorder.Code, fmt.Sprintf("UPSTREAM_%d", finalRecorder.Code), "Request failed after all retries.")
finalEvent.Error = apiErr
finalEvent.ErrorCode = apiErr.Code
finalEvent.ErrorMessage = apiErr.Message
finalEvent.RequestLog.ErrorCode = apiErr.Code
finalEvent.RequestLog.ErrorMessage = apiErr.Message
}
}
eventData, _ := json.Marshal(finalEvent)
_ = h.store.Publish(models.TopicRequestFinished, eventData)
// 将完整的事件发布
eventData, err := json.Marshal(finalEvent)
if err != nil {
h.logger.WithField("id", correlationID).WithError(err).Error("Failed to marshal final log event.")
return
}
if err := h.store.Publish(models.TopicRequestFinished, eventData); err != nil {
h.logger.WithField("id", correlationID).WithError(err).Error("Failed to publish final log event.")
}
}()
var maxRetries int
if isPreciseRouting {
@@ -417,18 +431,24 @@ func (h *ProxyHandler) createLogEvent(c *gin.Context, startTime time.Time, corrI
}
if authTokenValue, exists := c.Get("authToken"); exists {
if authToken, ok := authTokenValue.(*models.AuthToken); ok {
event.AuthTokenID = &authToken.ID
event.RequestLog.AuthTokenID = &authToken.ID
}
}
if res != nil {
event.KeyID = res.APIKey.ID
event.GroupID = res.KeyGroup.ID
// [核心修正] 填充到内嵌的 RequestLog 结构体中
if res.APIKey != nil {
event.RequestLog.KeyID = &res.APIKey.ID
}
if res.KeyGroup != nil {
event.RequestLog.GroupID = &res.KeyGroup.ID
}
if res.UpstreamEndpoint != nil {
event.UpstreamID = &res.UpstreamEndpoint.ID
event.RequestLog.UpstreamID = &res.UpstreamEndpoint.ID
// UpstreamURL 是事件传递字段,不是数据库字段,所以在这里赋值是正确的
event.UpstreamURL = &res.UpstreamEndpoint.URL
}
if res.ProxyConfig != nil {
event.ProxyID = &res.ProxyConfig.ID
event.RequestLog.ProxyID = &res.ProxyConfig.ID
}
}
return event