ccpoad / internal /model /log.go
anyalerob's picture
Upload folder using huggingface_hub
2986042 verified
Raw
History Blame Contribute Delete
4.81 kB
package model
import (
"strconv"
"strings"
"time"
)
// LogSource* constants define persisted log sources plus special filter aliases.
const (
LogSourceProxy = "proxy"
LogSourceScheduledCheck = "scheduled_check"
LogSourceManualTest = "manual_test"
LogSourceDetection = "detection"
LogSourceAll = "all"
)
// NormalizeStoredLogSource maps stored or legacy log sources to supported persisted values.
func NormalizeStoredLogSource(raw string) string {
switch strings.TrimSpace(raw) {
case "", LogSourceProxy:
return LogSourceProxy
case LogSourceScheduledCheck:
return LogSourceScheduledCheck
case LogSourceManualTest:
return LogSourceManualTest
default:
return LogSourceProxy
}
}
// JSONTime 自定义时间类型,使用Unix时间戳进行JSON序列化
// 设计原则:与数据库格式统一,减少转换复杂度(KISS原则)
type JSONTime struct {
time.Time
}
// MarshalJSON 实现JSON序列化
func (jt JSONTime) MarshalJSON() ([]byte, error) {
if jt.IsZero() {
return []byte("0"), nil
}
return []byte(strconv.FormatInt(jt.Unix(), 10)), nil
}
// UnmarshalJSON 实现JSON反序列化
func (jt *JSONTime) UnmarshalJSON(data []byte) error {
if string(data) == "null" || string(data) == "0" {
jt.Time = time.Time{}
return nil
}
ts, err := strconv.ParseInt(string(data), 10, 64)
if err != nil {
return err
}
jt.Time = time.Unix(ts, 0)
return nil
}
// LogEntry 请求日志条目
type LogEntry struct {
ID int64 `json:"id"`
Time JSONTime `json:"time"`
Model string `json:"model"`
ActualModel string `json:"actual_model,omitempty"` // 实际转发的模型(空表示未重定向)
LogSource string `json:"log_source,omitempty"`
ChannelID int64 `json:"channel_id"`
ChannelName string `json:"channel_name,omitempty"`
StatusCode int `json:"status_code"`
Message string `json:"message"`
Duration float64 `json:"duration"` // 总耗时(秒)
IsStreaming bool `json:"is_streaming"` // 是否为流式请求
FirstByteTime float64 `json:"first_byte_time"` // 上游首字节响应时间(秒)
APIKeyUsed string `json:"api_key_used"` // 使用的API Key(写入时强制脱敏为 abc.xyz 格式,数据库不存明文)
APIKeyHash string `json:"api_key_hash,omitempty"` // API Key 的 SHA256(仅用于后台精确定位 key_index,不泄露明文)
AuthTokenID int64 `json:"auth_token_id"` // 客户端使用的API令牌ID(新增2025-12,0表示未使用token)
AuthTokenDescription string `json:"auth_token_description"` // API令牌描述(查询时从auth_tokens表JOIN获取)
ClientIP string `json:"client_ip"` // 客户端IP地址(新增2025-12)
BaseURL string `json:"base_url,omitempty"` // 请求使用的上游URL(多URL场景)
ServiceTier string `json:"service_tier,omitempty"` // OpenAI service_tier: "priority"(2x)/"flex"(0.5x)
// Token统计(2025-11新增,支持Claude API usage字段)
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
CacheReadInputTokens int `json:"cache_read_input_tokens"`
CacheCreationInputTokens int `json:"cache_creation_input_tokens"` // 5m+1h缓存总和(兼容字段)
Cache5mInputTokens int `json:"cache_5m_input_tokens"` // 5分钟缓存写入Token数(新增2025-12)
Cache1hInputTokens int `json:"cache_1h_input_tokens"` // 1小时缓存写入Token数(新增2025-12)
Cost float64 `json:"cost"` // 请求成本(美元,标准成本)
CostMultiplier float64 `json:"cost_multiplier"` // 写日志时快照的渠道倍率,默认1
// 瞬态字段:不持久化到 logs 表,仅用于传递 debug 数据到写入管道
DebugData *DebugLogEntry `json:"-"`
}
// LogFilter 日志查询过滤条件
type LogFilter struct {
ChannelID *int64
ChannelName string
ChannelNameLike string
Model string
ModelLike string
StatusCode *int
ChannelType string // 渠道类型过滤(anthropic/openai/gemini/codex)
AuthTokenID *int64 // API令牌ID过滤
LogSource string
}
// ChannelURLLogStat 是基于持久化日志聚合出的 URL 启动快照。
// 用途:程序启动时从 logs.base_url 回填 URLSelector 的当日成功/失败计数与延迟。
type ChannelURLLogStat struct {
ChannelID int64
BaseURL string
Requests int64
Failures int64
LatencyMs float64
LastSeen time.Time
}