106 lines
2.7 KiB
Go
106 lines
2.7 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"encoding/csv"
|
||
"fmt"
|
||
"io"
|
||
"time"
|
||
|
||
"github.com/company/ai-ops/internal/domain/model"
|
||
"github.com/company/ai-ops/internal/domain/repository"
|
||
"github.com/company/ai-ops/internal/redis"
|
||
goredis "github.com/redis/go-redis/v9"
|
||
)
|
||
|
||
// LogService 是日志业务逻辑层
|
||
type LogService struct {
|
||
logRepo repository.LogRepository
|
||
}
|
||
|
||
func NewLogService(lr repository.LogRepository) *LogService {
|
||
return &LogService{logRepo: lr}
|
||
}
|
||
|
||
// QueryLogs 查询日志
|
||
func (s *LogService) QueryLogs(ctx context.Context, filter model.LogQueryFilter) ([]model.RequestLog, int, error) {
|
||
// Redis 缓存键:基于筛选条件构建
|
||
cacheKey := s.buildCacheKey(filter)
|
||
|
||
// 尝试从缓存获取
|
||
if redis.Client != nil {
|
||
var cached []model.RequestLog
|
||
var total int
|
||
err := redis.Client.Get(ctx, cacheKey+":items").Scan(&cached)
|
||
if err == nil {
|
||
redis.Client.Get(ctx, cacheKey+":total").Scan(&total)
|
||
return cached, total, nil
|
||
}
|
||
if err != goredis.Nil {
|
||
// 缓存错误不阻断业务,继续查数据库
|
||
}
|
||
}
|
||
|
||
// 超时控制
|
||
queryCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||
defer cancel()
|
||
|
||
logs, total, err := s.logRepo.Query(queryCtx, filter)
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("query logs: %w", err)
|
||
}
|
||
|
||
// 写入缓存(5分钟 TTL)
|
||
if redis.Client != nil {
|
||
redis.Client.Set(ctx, cacheKey+":items", logs, 5*time.Minute)
|
||
redis.Client.Set(ctx, cacheKey+":total", total, 5*time.Minute)
|
||
}
|
||
|
||
return logs, total, nil
|
||
}
|
||
|
||
// ExportLogsCSV 导出日志为 CSV
|
||
func (s *LogService) ExportLogsCSV(ctx context.Context, filter model.LogQueryFilter, w io.Writer) error {
|
||
filter.Page = 1
|
||
filter.PageSize = 10000 // 导出上限
|
||
|
||
logs, _, err := s.logRepo.Query(ctx, filter)
|
||
if err != nil {
|
||
return fmt.Errorf("query logs for export: %w", err)
|
||
}
|
||
|
||
csvWriter := csv.NewWriter(w)
|
||
defer csvWriter.Flush()
|
||
|
||
// 写入表头
|
||
if err := csvWriter.Write([]string{"时间", "服务名", "路径", "方法", "状态码", "延迟(ms)", "用户ID", "供应商ID", "错误码"}); err != nil {
|
||
return fmt.Errorf("write csv header: %w", err)
|
||
}
|
||
|
||
// 写入数据
|
||
for _, l := range logs {
|
||
row := []string{
|
||
l.Timestamp.Format(time.RFC3339),
|
||
l.Service,
|
||
l.Path,
|
||
l.Method,
|
||
fmt.Sprintf("%d", l.StatusCode),
|
||
fmt.Sprintf("%.2f", l.LatencyMs),
|
||
l.UserID,
|
||
l.SupplierID,
|
||
l.ErrorCode,
|
||
}
|
||
if err := csvWriter.Write(row); err != nil {
|
||
return fmt.Errorf("write csv row: %w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *LogService) buildCacheKey(filter model.LogQueryFilter) string {
|
||
return fmt.Sprintf("ai-ops:logs:%s:%s:%d:%s:%s:%d:%d",
|
||
filter.Service, filter.Path, filter.StatusCode,
|
||
filter.UserID, filter.SupplierID, filter.Page, filter.PageSize)
|
||
}
|