mirror of
https://github.com/2930134478/AI-CS.git
synced 2026-06-15 00:44:30 +08:00
204 lines
5.0 KiB
Go
204 lines
5.0 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/2930134478/AI-CS/backend/models"
|
|
"github.com/2930134478/AI-CS/backend/repository"
|
|
)
|
|
|
|
type CreateSystemLogInput struct {
|
|
Level string
|
|
Category string
|
|
Event string
|
|
Source string
|
|
TraceID string
|
|
ConversationID *uint
|
|
UserID *uint
|
|
VisitorID *uint
|
|
Message string
|
|
Meta map[string]interface{}
|
|
Timestamp *time.Time
|
|
}
|
|
|
|
type QuerySystemLogsInput struct {
|
|
From string
|
|
To string
|
|
Level string
|
|
Category string
|
|
Event string
|
|
Source string
|
|
ConversationID *uint
|
|
Keyword string
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
type QuerySystemLogsResult struct {
|
|
Items []models.SystemLog `json:"items"`
|
|
Total int64 `json:"total"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
}
|
|
|
|
// SystemLogService 结构化日志服务(查询 + 写入)。
|
|
type SystemLogService struct {
|
|
repo *repository.SystemLogRepository
|
|
minPersistLevel atomic.Int32 // 见 SYSTEM_LOG_MIN_LEVEL / 数据库覆盖;logRankNone 表示关闭全部落库
|
|
}
|
|
|
|
func NewSystemLogService(repo *repository.SystemLogRepository, minPersistLevel int) *SystemLogService {
|
|
s := &SystemLogService{repo: repo}
|
|
s.SetMinPersistLevelRank(minPersistLevel)
|
|
return s
|
|
}
|
|
|
|
// SetMinPersistLevelRank 运行时调整最低落库级别(线程安全)。
|
|
func (s *SystemLogService) SetMinPersistLevelRank(rank int) {
|
|
if s == nil {
|
|
return
|
|
}
|
|
s.minPersistLevel.Store(int32(rank))
|
|
}
|
|
|
|
// MinPersistLevelRank 当前生效的最低落库级别数值。
|
|
func (s *SystemLogService) MinPersistLevelRank() int {
|
|
if s == nil {
|
|
return logRankInfo
|
|
}
|
|
return int(s.minPersistLevel.Load())
|
|
}
|
|
|
|
func (s *SystemLogService) shouldPersistLevel(level string) bool {
|
|
if s == nil {
|
|
return false
|
|
}
|
|
cur := int(s.minPersistLevel.Load())
|
|
if cur == logRankNone {
|
|
return false
|
|
}
|
|
return logLevelRank(level) >= cur
|
|
}
|
|
|
|
// ShouldPersistLevel 供中间件等在组装大 payload 前判断,避免无谓开销。
|
|
func (s *SystemLogService) ShouldPersistLevel(level string) bool {
|
|
return s.shouldPersistLevel(strings.ToLower(strings.TrimSpace(level)))
|
|
}
|
|
|
|
func (s *SystemLogService) Create(input CreateSystemLogInput) error {
|
|
level := strings.ToLower(strings.TrimSpace(input.Level))
|
|
if level == "" {
|
|
level = "info"
|
|
}
|
|
if !s.shouldPersistLevel(level) {
|
|
return nil
|
|
}
|
|
category := strings.ToLower(strings.TrimSpace(input.Category))
|
|
if category == "" {
|
|
category = "system"
|
|
}
|
|
source := strings.ToLower(strings.TrimSpace(input.Source))
|
|
if source == "" {
|
|
source = "backend"
|
|
}
|
|
event := strings.TrimSpace(input.Event)
|
|
if event == "" {
|
|
event = "general"
|
|
}
|
|
message := strings.TrimSpace(input.Message)
|
|
if message == "" {
|
|
return fmt.Errorf("message 不能为空")
|
|
}
|
|
|
|
metaJSON := ""
|
|
if input.Meta != nil {
|
|
if b, err := json.Marshal(input.Meta); err == nil {
|
|
metaJSON = string(b)
|
|
}
|
|
}
|
|
ts := time.Now()
|
|
if input.Timestamp != nil {
|
|
ts = *input.Timestamp
|
|
}
|
|
|
|
item := &models.SystemLog{
|
|
Timestamp: ts,
|
|
Level: level,
|
|
Category: category,
|
|
Event: event,
|
|
Source: source,
|
|
TraceID: strings.TrimSpace(input.TraceID),
|
|
ConversationID: input.ConversationID,
|
|
UserID: input.UserID,
|
|
VisitorID: input.VisitorID,
|
|
Message: message,
|
|
MetaJSON: metaJSON,
|
|
}
|
|
return s.repo.Create(item)
|
|
}
|
|
|
|
func (s *SystemLogService) Query(input QuerySystemLogsInput) (*QuerySystemLogsResult, error) {
|
|
page := input.Page
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
pageSize := input.PageSize
|
|
if pageSize <= 0 {
|
|
pageSize = 50
|
|
}
|
|
if pageSize > 200 {
|
|
pageSize = 200
|
|
}
|
|
|
|
db := s.repo.DB().Model(&models.SystemLog{})
|
|
if input.From != "" {
|
|
if t, err := time.Parse("2006-01-02", input.From); err == nil {
|
|
db = db.Where("timestamp >= ?", t)
|
|
}
|
|
}
|
|
if input.To != "" {
|
|
if t, err := time.Parse("2006-01-02", input.To); err == nil {
|
|
db = db.Where("timestamp < ?", t.AddDate(0, 0, 1))
|
|
}
|
|
}
|
|
if v := strings.TrimSpace(input.Level); v != "" {
|
|
db = db.Where("level = ?", strings.ToLower(v))
|
|
}
|
|
if v := strings.TrimSpace(input.Category); v != "" {
|
|
db = db.Where("category = ?", strings.ToLower(v))
|
|
}
|
|
if v := strings.TrimSpace(input.Event); v != "" {
|
|
db = db.Where("event = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(input.Source); v != "" {
|
|
db = db.Where("source = ?", strings.ToLower(v))
|
|
}
|
|
if input.ConversationID != nil {
|
|
db = db.Where("conversation_id = ?", *input.ConversationID)
|
|
}
|
|
if v := strings.TrimSpace(input.Keyword); v != "" {
|
|
like := "%" + v + "%"
|
|
db = db.Where("(message LIKE ? OR meta_json LIKE ? OR trace_id LIKE ?)", like, like, like)
|
|
}
|
|
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]models.SystemLog, 0, pageSize)
|
|
if err := db.Order("timestamp DESC").Offset((page-1)*pageSize).Limit(pageSize).Find(&items).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &QuerySystemLogsResult{
|
|
Items: items,
|
|
Total: total,
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
}, nil
|
|
}
|
|
|