package controller import ( "encoding/json" "net/http" "time" "ezops/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // LogHandler 日志监控处理器 // Logan.Li type LogHandler struct { db *gorm.DB } func NewLogHandler(db *gorm.DB) *LogHandler { return &LogHandler{db: db} } // QueryLogs 查询日志条目(支持 level/service/host/keyword 过滤) // Logan.Li func (h *LogHandler) QueryLogs(c *gin.Context) { var query model.PageQuery c.ShouldBindQuery(&query) db := h.db.Model(&model.LogEntry{}) // 过滤 if level := c.Query("level"); level != "" { db = db.Where("level = ?", level) } if service := c.Query("service"); service != "" { db = db.Where("service = ?", service) } if host := c.Query("host"); host != "" { db = db.Where("host = ?", host) } if kw := c.Query("keyword"); kw != "" { db = db.Where("message LIKE ?", "%"+kw+"%") } if sourceID := c.Query("source_id"); sourceID != "" { db = db.Where("source_id = ?", sourceID) } var total int64 db.Count(&total) var logs []model.LogEntry db.Order("timestamp DESC").Offset(query.GetOffset()).Limit(query.GetLimit()).Find(&logs) c.JSON(http.StatusOK, model.Response{Code: 200, Data: model.ListResponse{List: logs, Total: total}}) } // ListSources 获取日志源列表 // Logan.Li func (h *LogHandler) ListSources(c *gin.Context) { var sources []model.LogSource h.db.Order("updated_at DESC").Find(&sources) c.JSON(http.StatusOK, model.Response{Code: 200, Data: model.ListResponse{List: sources, Total: int64(len(sources))}}) } // CreateSource 创建日志源 // Logan.Li func (h *LogHandler) CreateSource(c *gin.Context) { var req struct { Name string `json:"name" binding:"required"` Type string `json:"type" binding:"required"` // application/syslog/docker/kubernetes Config map[string]interface{} `json:"config"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()}) return } configJSON, _ := json.Marshal(req.Config) now := time.Now() item := model.LogSource{ Name: req.Name, Type: req.Type, Status: "active", Config: string(configJSON), LastSeen: &now, } h.db.Create(&item) c.JSON(http.StatusOK, model.Response{Code: 200, Data: item}) } // DeleteSource 删除日志源 // Logan.Li func (h *LogHandler) DeleteSource(c *gin.Context) { if err := h.db.Delete(&model.LogSource{}, c.Param("id")); err.Error != nil { c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "删除失败"}) return } c.JSON(http.StatusOK, model.Response{Code: 200, Message: "已删除"}) } // IngestLogs Agent 上报日志(无需 JWT,供 Agent 调用) // Logan.Li func (h *LogHandler) IngestLogs(c *gin.Context) { var req struct { Source string `json:"source"` Host string `json:"host"` Service string `json:"service"` Entries []map[string]interface{} `json:"entries"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()}) return } // 查找或创建日志源 var source model.LogSource h.db.Where("name = ?", req.Source).FirstOrCreate(&source, model.LogSource{ Name: req.Source, Type: "agent", Status: "active", }) count := 0 for _, entry := range req.Entries { level, _ := entry["level"].(string) msg, _ := entry["message"].(string) raw, _ := entry["raw"].(string) traceID, _ := entry["trace_id"].(string) ts := time.Now() if t, ok := entry["timestamp"].(string); ok { if parsed, err := time.Parse(time.RFC3339, t); err == nil { ts = parsed } } log := model.LogEntry{ SourceID: source.ID, Timestamp: ts, Level: level, Service: req.Service, Host: req.Host, Message: msg, TraceID: traceID, Raw: raw, } if err := h.db.Create(&log); err.Error == nil { count++ } } // 更新日志源统计 now := time.Now() h.db.Model(&source).Updates(map[string]interface{}{ "last_seen": &now, }).UpdateColumn("log_count", source.LogCount+int64(count)) c.JSON(http.StatusOK, model.Response{Code: 200, Message: "成功写入", Data: map[string]int{"count": count}}) }