解决日志api的message字段为空的问题
This commit is contained in:
BIN
agent/agent
BIN
agent/agent
Binary file not shown.
110
agent/main.go
110
agent/main.go
@@ -812,37 +812,105 @@ func collectLogs() ([]LogEntry, error) {
|
|||||||
// 使用字符串处理,更方便处理空格
|
// 使用字符串处理,更方便处理空格
|
||||||
lineStr := string(line)
|
lineStr := string(line)
|
||||||
|
|
||||||
// 使用strings.Fields分割日志行,自动处理连续空格
|
// 尝试多种时间格式解析
|
||||||
|
t := time.Time{}
|
||||||
|
err := error(nil)
|
||||||
|
timestampEndIndex := -1
|
||||||
|
|
||||||
|
// 格式1: "Jan 2 15:04:05" 格式(如messages文件)
|
||||||
fields := strings.Fields(lineStr)
|
fields := strings.Fields(lineStr)
|
||||||
if len(fields) < 6 {
|
if len(fields) >= 3 {
|
||||||
// 尝试其他时间格式或跳过
|
timeStr := fmt.Sprintf("%s %s %s", fields[0], fields[1], fields[2])
|
||||||
continue
|
t, err = time.Parse("Jan 2 15:04:05", timeStr)
|
||||||
|
if err == nil {
|
||||||
|
// 设置当前年份
|
||||||
|
year, _, _ := time.Now().Date()
|
||||||
|
t = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, time.Local)
|
||||||
|
// 找到时间戳结束位置:找到第三个字段后的第一个空格
|
||||||
|
pos := 0
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
// 找到下一个字段的开始位置
|
||||||
|
pos = strings.Index(lineStr[pos:], fields[i]) + len(fields[i])
|
||||||
|
if pos >= len(lineStr) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 跳过后面的空格
|
||||||
|
for pos < len(lineStr) && lineStr[pos] == ' ' {
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
timestampEndIndex = pos
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建时间字符串:月份 日期 时间
|
// 格式2: ISO 8601格式(如dnf.log文件)
|
||||||
timeStr := fmt.Sprintf("%s %s %s", fields[0], fields[1], fields[2])
|
if err != nil {
|
||||||
|
// 查找第一个空格的位置
|
||||||
// 解析时间
|
spaceIndex := strings.Index(lineStr, " ")
|
||||||
t, err := time.Parse("Jan 2 15:04:05", timeStr)
|
if spaceIndex > 0 {
|
||||||
|
timestampStr := lineStr[:spaceIndex]
|
||||||
|
t, err = time.Parse(time.RFC3339, timestampStr)
|
||||||
|
if err != nil {
|
||||||
|
// 尝试另一种ISO 8601格式(不含时区)
|
||||||
|
t, err = time.Parse("2006-01-02T15:04:05", timestampStr)
|
||||||
|
if err != nil {
|
||||||
|
// 尝试带时区的另一种格式
|
||||||
|
t, err = time.Parse("2006-01-02T15:04:05Z07:00", timestampStr)
|
||||||
|
if err != nil {
|
||||||
|
// 尝试DNF日志格式(带时区但没有冒号)
|
||||||
|
t, err = time.Parse("2006-01-02T15:04:05Z0700", timestampStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
timestampEndIndex = spaceIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果所有格式都解析失败,跳过该日志行
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 尝试其他时间格式
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置当前年份
|
// 解析source和message
|
||||||
year, _, _ := time.Now().Date()
|
var source string
|
||||||
t = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, time.Local)
|
var message string
|
||||||
|
|
||||||
// 寻找第一个冒号,用于分割source和message
|
if timestampEndIndex > 0 {
|
||||||
colonIndex := strings.Index(lineStr, ": ")
|
// 跳过时间戳部分
|
||||||
if colonIndex == -1 {
|
afterTimestamp := strings.TrimSpace(lineStr[timestampEndIndex:])
|
||||||
continue
|
afterFields := strings.Fields(afterTimestamp)
|
||||||
|
|
||||||
|
// 寻找冒号分隔符
|
||||||
|
colonIndex := strings.Index(afterTimestamp, ":")
|
||||||
|
|
||||||
|
if colonIndex > 0 {
|
||||||
|
// 如果找到冒号,使用冒号前的部分作为source
|
||||||
|
sourcePart := strings.TrimSpace(afterTimestamp[:colonIndex])
|
||||||
|
source = fmt.Sprintf("%s %s", filepath.Base(logFile), sourcePart)
|
||||||
|
// 冒号后的部分作为message
|
||||||
|
message = strings.TrimSpace(afterTimestamp[colonIndex+1:])
|
||||||
|
} else if len(afterFields) > 0 {
|
||||||
|
// 如果没有冒号,使用第一个字段作为source
|
||||||
|
source = fmt.Sprintf("%s %s", filepath.Base(logFile), afterFields[0])
|
||||||
|
// 剩余部分作为message
|
||||||
|
if len(afterFields) > 1 {
|
||||||
|
message = strings.Join(afterFields[1:], " ")
|
||||||
|
} else {
|
||||||
|
message = afterTimestamp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 简单处理
|
||||||
|
source = filepath.Base(logFile)
|
||||||
|
message = afterTimestamp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 简单处理,使用文件名作为source,整行作为message
|
||||||
|
source = filepath.Base(logFile)
|
||||||
|
message = lineStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析source和message,添加文件名作为source的一部分
|
|
||||||
source := fmt.Sprintf("%s %s", filepath.Base(logFile), lineStr[:colonIndex])
|
|
||||||
message := lineStr[colonIndex+2:]
|
|
||||||
|
|
||||||
// 创建日志条目
|
// 创建日志条目
|
||||||
logEntry := LogEntry{
|
logEntry := LogEntry{
|
||||||
Sequence: len(logs) + 1,
|
Sequence: len(logs) + 1,
|
||||||
|
|||||||
Binary file not shown.
2
agent/test-log.log
Normal file
2
agent/test-log.log
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Dec 5 00:30:00 test-host test-service: This is a test log message using traditional format
|
||||||
|
2025-12-05T00:31:00+0800 test-service: This is a test log message using ISO format
|
||||||
3
agent/test.log
Normal file
3
agent/test.log
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Dec 5 12:00:00 test-server test-service[1234]: This is a test log message 1
|
||||||
|
Dec 5 12:01:00 test-server test-service[1234]: This is a test log message 2
|
||||||
|
Dec 5 12:02:00 test-server test-service[1234]: This is a test log message 3
|
||||||
@@ -337,6 +337,16 @@ func HandleMetricsPost(c *gin.Context) {
|
|||||||
compatDisk[mountpoint] = diskMetrics.UsedPercent
|
compatDisk[mountpoint] = diskMetrics.UsedPercent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 转换日志格式为LogMetrics
|
||||||
|
logMetricsList := make([]LogMetrics, 0, len(req.Logs))
|
||||||
|
for _, logEntry := range req.Logs {
|
||||||
|
logMetricsList = append(logMetricsList, LogMetrics{
|
||||||
|
Source: logEntry.Source,
|
||||||
|
Time: logEntry.Time.Format(time.RFC3339),
|
||||||
|
Message: logEntry.Message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
metrics := map[string]interface{}{
|
metrics := map[string]interface{}{
|
||||||
"cpu": req.CPU,
|
"cpu": req.CPU,
|
||||||
"cpu_hz": req.CPUHz,
|
"cpu_hz": req.CPUHz,
|
||||||
@@ -349,6 +359,7 @@ func HandleMetricsPost(c *gin.Context) {
|
|||||||
"rx_bytes": totalRxBytes,
|
"rx_bytes": totalRxBytes,
|
||||||
},
|
},
|
||||||
"network_interfaces": req.Network,
|
"network_interfaces": req.Network,
|
||||||
|
"logs": logMetricsList,
|
||||||
}
|
}
|
||||||
broadcastMetricsUpdate(deviceID, metrics)
|
broadcastMetricsUpdate(deviceID, metrics)
|
||||||
}
|
}
|
||||||
@@ -475,12 +486,12 @@ func broadcastMetricsUpdate(deviceID string, metrics map[string]interface{}) {
|
|||||||
// GetCPUMetrics 获取CPU指标
|
// GetCPUMetrics 获取CPU指标
|
||||||
func GetCPUMetrics(c *gin.Context) {
|
func GetCPUMetrics(c *gin.Context) {
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
||||||
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
||||||
endTime := c.DefaultQuery("end_time", "now()")
|
endTime := c.DefaultQuery("end_time", "now()")
|
||||||
aggregation := c.DefaultQuery("aggregation", "average")
|
aggregation := c.DefaultQuery("aggregation", "average")
|
||||||
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
||||||
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
||||||
limit, _ := strconv.Atoi(limitStr)
|
limit, _ := strconv.Atoi(limitStr)
|
||||||
|
|
||||||
// 查询数据
|
// 查询数据
|
||||||
@@ -505,12 +516,12 @@ func GetCPUMetrics(c *gin.Context) {
|
|||||||
// GetMemoryMetrics 获取内存指标
|
// GetMemoryMetrics 获取内存指标
|
||||||
func GetMemoryMetrics(c *gin.Context) {
|
func GetMemoryMetrics(c *gin.Context) {
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
||||||
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
||||||
endTime := c.DefaultQuery("end_time", "now()")
|
endTime := c.DefaultQuery("end_time", "now()")
|
||||||
aggregation := c.DefaultQuery("aggregation", "average")
|
aggregation := c.DefaultQuery("aggregation", "average")
|
||||||
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
||||||
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
||||||
limit, _ := strconv.Atoi(limitStr)
|
limit, _ := strconv.Atoi(limitStr)
|
||||||
|
|
||||||
// 查询数据
|
// 查询数据
|
||||||
@@ -535,12 +546,12 @@ func GetMemoryMetrics(c *gin.Context) {
|
|||||||
// GetDiskMetrics 获取磁盘指标
|
// GetDiskMetrics 获取磁盘指标
|
||||||
func GetDiskMetrics(c *gin.Context) {
|
func GetDiskMetrics(c *gin.Context) {
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
||||||
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
||||||
endTime := c.DefaultQuery("end_time", "now()")
|
endTime := c.DefaultQuery("end_time", "now()")
|
||||||
aggregation := c.DefaultQuery("aggregation", "average")
|
aggregation := c.DefaultQuery("aggregation", "average")
|
||||||
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
||||||
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
||||||
limit, _ := strconv.Atoi(limitStr)
|
limit, _ := strconv.Atoi(limitStr)
|
||||||
|
|
||||||
// 查询数据
|
// 查询数据
|
||||||
@@ -580,12 +591,12 @@ func GetDiskMetrics(c *gin.Context) {
|
|||||||
// GetNetworkMetrics 获取网络指标
|
// GetNetworkMetrics 获取网络指标
|
||||||
func GetNetworkMetrics(c *gin.Context) {
|
func GetNetworkMetrics(c *gin.Context) {
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
||||||
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
startTime := c.DefaultQuery("start_time", "-1h") // 缩短默认查询时间范围到1小时,减少默认数据量
|
||||||
endTime := c.DefaultQuery("end_time", "now()")
|
endTime := c.DefaultQuery("end_time", "now()")
|
||||||
aggregation := c.DefaultQuery("aggregation", "average")
|
aggregation := c.DefaultQuery("aggregation", "average")
|
||||||
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
interval := c.DefaultQuery("interval", "10s") // 添加interval参数,默认10秒
|
||||||
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
limitStr := c.DefaultQuery("limit", "5000") // 添加limit参数,默认5000条记录
|
||||||
limit, _ := strconv.Atoi(limitStr)
|
limit, _ := strconv.Atoi(limitStr)
|
||||||
|
|
||||||
// 查询发送和接收的网络速率指标
|
// 查询发送和接收的网络速率指标
|
||||||
@@ -779,18 +790,18 @@ func GetAllDeviceStatus(c *gin.Context) {
|
|||||||
allDevices := deviceStorage.GetDevices()
|
allDevices := deviceStorage.GetDevices()
|
||||||
|
|
||||||
// 查询每个设备的状态
|
// 查询每个设备的状态
|
||||||
result := make([]map[string]interface{}, 0, len(allDevices))
|
result := make([]map[string]interface{}, 0, len(allDevices))
|
||||||
for _, device := range allDevices {
|
for _, device := range allDevices {
|
||||||
// 查询设备监控数据
|
// 查询设备监控数据
|
||||||
_, status, _ := globalStorage.QueryDeviceStatus(context.Background(), device.ID)
|
_, status, _ := globalStorage.QueryDeviceStatus(context.Background(), device.ID)
|
||||||
|
|
||||||
// 总是返回设备信息,无论是否有监控数据
|
// 总是返回设备信息,无论是否有监控数据
|
||||||
result = append(result, map[string]interface{}{
|
result = append(result, map[string]interface{}{
|
||||||
"id": device.ID,
|
"id": device.ID,
|
||||||
"name": device.Name,
|
"name": device.Name,
|
||||||
"status": status,
|
"status": status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"devices": result,
|
"devices": result,
|
||||||
@@ -1011,7 +1022,13 @@ func GetDiskDetails(c *gin.Context) {
|
|||||||
// GetLogs 获取系统日志
|
// GetLogs 获取系统日志
|
||||||
func GetLogs(c *gin.Context) {
|
func GetLogs(c *gin.Context) {
|
||||||
// 获取查询参数
|
// 获取查询参数
|
||||||
deviceID := c.Query("device_id") // 不使用默认值,空值表示查询所有设备
|
deviceID := c.Query("device_id") // 必须参数,不能为空
|
||||||
|
if deviceID == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "device_id is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
startTime := c.DefaultQuery("start_time", "-24h")
|
startTime := c.DefaultQuery("start_time", "-24h")
|
||||||
endTime := c.DefaultQuery("end_time", "now()")
|
endTime := c.DefaultQuery("end_time", "now()")
|
||||||
|
|
||||||
@@ -1063,6 +1080,6 @@ func GetLogs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"data": logs,
|
"logs": logs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -82,7 +83,7 @@ func (s *Storage) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 写入数据到InfluxDB,带重试机制
|
// 写入数据到InfluxDB,带重试机制
|
||||||
func (s *Storage) writeData(ctx context.Context, measurement string, tags map[string]string, fields map[string]interface{}, deviceID, metricType string) error {
|
func (s *Storage) writeData(ctx context.Context, measurement string, tags map[string]string, fields map[string]interface{}, deviceID, metricType string, timestamp ...time.Time) error {
|
||||||
// 重试配置 - 减少重试次数和延迟,确保在超时时间内完成
|
// 重试配置 - 减少重试次数和延迟,确保在超时时间内完成
|
||||||
maxRetries := 2
|
maxRetries := 2
|
||||||
baseDelay := 200 * time.Millisecond
|
baseDelay := 200 * time.Millisecond
|
||||||
@@ -113,7 +114,14 @@ func (s *Storage) writeData(ctx context.Context, measurement string, tags map[st
|
|||||||
}
|
}
|
||||||
fieldList = append(fieldList, fieldStr)
|
fieldList = append(fieldList, fieldStr)
|
||||||
}
|
}
|
||||||
line := fmt.Sprintf("%s,%s %s %d", measurement, formatTags(tags), strings.Join(fieldList, ","), time.Now().UnixNano())
|
// 确定时间戳
|
||||||
|
var ts int64
|
||||||
|
if len(timestamp) > 0 {
|
||||||
|
ts = timestamp[0].UnixNano()
|
||||||
|
} else {
|
||||||
|
ts = time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
line := fmt.Sprintf("%s,%s %s %d", measurement, formatTags(tags), strings.Join(fieldList, ","), ts)
|
||||||
err := writeAPI.WriteRecord(ctx, line)
|
err := writeAPI.WriteRecord(ctx, line)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -248,8 +256,8 @@ func (s *Storage) WriteLogMetric(ctx context.Context, deviceID string, sequence
|
|||||||
"message": message,
|
"message": message,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用新的writeData方法
|
// 使用新的writeData方法,传入日志的实际时间
|
||||||
return s.writeData(ctx, "logs", allTags, fields, deviceID, "log")
|
return s.writeData(ctx, "logs", allTags, fields, deviceID, "log", time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryMetrics 查询监控指标,支持采样
|
// QueryMetrics 查询监控指标,支持采样
|
||||||
@@ -577,7 +585,7 @@ func (s *Storage) QueryLogMetrics(ctx context.Context, deviceID string, startTim
|
|||||||
// 按时间倒序排列,获取最新的日志
|
// 按时间倒序排列,获取最新的日志
|
||||||
query += `
|
query += `
|
||||||
|> sort(columns: ["_time"], desc: true)
|
|> sort(columns: ["_time"], desc: true)
|
||||||
|> limit(n: 100)` // 限制返回100条最新日志
|
|> limit(n: 200)` // 限制返回200条记录(因为message和sequence是分开存储的)
|
||||||
|
|
||||||
// 执行查询
|
// 执行查询
|
||||||
queryResult, err := queryAPI.Query(ctx, query)
|
queryResult, err := queryAPI.Query(ctx, query)
|
||||||
@@ -586,37 +594,66 @@ func (s *Storage) QueryLogMetrics(ctx context.Context, deviceID string, startTim
|
|||||||
}
|
}
|
||||||
defer queryResult.Close()
|
defer queryResult.Close()
|
||||||
|
|
||||||
// 存储日志数据
|
// 使用map存储日志数据,key是时间戳和source的组合
|
||||||
logs := make([]map[string]interface{}, 0)
|
logMap := make(map[string]map[string]interface{})
|
||||||
|
|
||||||
// 处理查询结果
|
// 处理查询结果
|
||||||
for queryResult.Next() {
|
for queryResult.Next() {
|
||||||
if queryResult.TableChanged() {
|
|
||||||
// 表结构变化,跳过
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取记录
|
// 获取记录
|
||||||
record := queryResult.Record()
|
record := queryResult.Record()
|
||||||
|
|
||||||
// 构建日志数据
|
// 获取时间和source作为唯一键
|
||||||
logData := map[string]interface{}{
|
timeStr := record.Time().Format(time.RFC3339Nano)
|
||||||
"time": record.Time(),
|
source := record.ValueByKey("source").(string)
|
||||||
"device_id": record.ValueByKey("device_id"),
|
key := timeStr + "-" + source
|
||||||
"source": record.ValueByKey("source"),
|
|
||||||
"sequence": record.ValueByKey("sequence"),
|
// 检查是否已经有这个日志条目的基础信息
|
||||||
"message": record.ValueByKey("message"),
|
logData, exists := logMap[key]
|
||||||
"agent_name": record.ValueByKey("agent_name"),
|
if !exists {
|
||||||
|
// 创建新的日志条目
|
||||||
|
logData = map[string]interface{}{
|
||||||
|
"time": record.Time(),
|
||||||
|
"device_id": record.ValueByKey("device_id"),
|
||||||
|
"source": source,
|
||||||
|
"sequence": nil,
|
||||||
|
"message": nil,
|
||||||
|
"agent_name": record.ValueByKey("agent_name"),
|
||||||
|
}
|
||||||
|
logMap[key] = logData
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加到日志列表
|
// 根据字段类型更新相应的值
|
||||||
logs = append(logs, logData)
|
field := record.Field()
|
||||||
|
switch field {
|
||||||
|
case "message":
|
||||||
|
logData["message"] = record.Value()
|
||||||
|
case "sequence":
|
||||||
|
logData["sequence"] = record.Value()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if queryResult.Err() != nil {
|
if queryResult.Err() != nil {
|
||||||
return nil, queryResult.Err()
|
return nil, queryResult.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将map转换为切片
|
||||||
|
logs := make([]map[string]interface{}, 0, len(logMap))
|
||||||
|
for _, logData := range logMap {
|
||||||
|
logs = append(logs, logData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按时间倒序排序
|
||||||
|
sort.Slice(logs, func(i, j int) bool {
|
||||||
|
timeI := logs[i]["time"].(time.Time)
|
||||||
|
timeJ := logs[j]["time"].(time.Time)
|
||||||
|
return timeI.After(timeJ)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 限制返回100条最新日志
|
||||||
|
if len(logs) > 100 {
|
||||||
|
logs = logs[:100]
|
||||||
|
}
|
||||||
|
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/monitor/backend/config"
|
"github.com/monitor/backend/config"
|
||||||
@@ -34,9 +36,17 @@ func isDefaultDBConfig(cfg *config.Config) bool {
|
|||||||
|
|
||||||
// main 函数启动服务器
|
// main 函数启动服务器
|
||||||
func main() {
|
func main() {
|
||||||
// 配置日志:只输出必要的信息,禁用调试日志
|
// 配置日志:同时输出到文件和标准输出
|
||||||
|
logFileName := fmt.Sprintf("monitor-backend-%s.log", time.Now().Format("2006-01-02"))
|
||||||
|
logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warning: Failed to open log file %s, logging only to stdout: %v", logFileName, err)
|
||||||
|
} else {
|
||||||
|
defer logFile.Close()
|
||||||
|
// 创建一个多输出写入器,同时写入文件和标准输出
|
||||||
|
log.SetOutput(io.MultiWriter(os.Stdout, logFile))
|
||||||
|
}
|
||||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
log.SetOutput(os.Stdout)
|
|
||||||
|
|
||||||
// 加载配置
|
// 加载配置
|
||||||
cfg, err := config.LoadConfig()
|
cfg, err := config.LoadConfig()
|
||||||
@@ -96,8 +106,9 @@ func main() {
|
|||||||
r := gin.New()
|
r := gin.New()
|
||||||
// 添加必要的中间件
|
// 添加必要的中间件
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
// 禁用Gin的默认日志
|
// 设置Gin的日志输出到文件和标准输出
|
||||||
r.Use(gin.LoggerWithWriter(gin.DefaultWriter, "/health"))
|
ginLogger := log.New(io.MultiWriter(os.Stdout, logFile), "[GIN] ", log.Ldate|log.Ltime)
|
||||||
|
r.Use(gin.LoggerWithWriter(ginLogger.Writer()))
|
||||||
|
|
||||||
// 设置CORS
|
// 设置CORS
|
||||||
r.Use(func(c *gin.Context) {
|
r.Use(func(c *gin.Context) {
|
||||||
|
|||||||
183878
backend/monitor-backend-2025-12-05.log
Normal file
183878
backend/monitor-backend-2025-12-05.log
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
8947
backend/monitor-server.log
Normal file
8947
backend/monitor-server.log
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user