实现日志收集功能
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
{
|
||||
"server_url": "http://10.35.10.12:8080/api",
|
||||
"id": "agent-fnos1",
|
||||
"name": "fnos1",
|
||||
"device_id": "agent-fnos1",
|
||||
"token": "eea3ffc9b3bb6b2a9f2e5bf228a2c7db",
|
||||
"id": "agent-1764858612504948034-3525156",
|
||||
"name": "yunc",
|
||||
"token": "84f0657f9075f63e3964aeb7e3a9e59b",
|
||||
"interval": "10s",
|
||||
"debug": true,
|
||||
"api_port": 8081
|
||||
|
||||
7121
agent/agent.log
Normal file
7121
agent/agent.log
Normal file
File diff suppressed because it is too large
Load Diff
189
agent/main.go
189
agent/main.go
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -23,13 +24,12 @@ import (
|
||||
// Config Agent配置
|
||||
type Config struct {
|
||||
ServerURL string `json:"server_url"`
|
||||
ID string `json:"id"` // Agent唯一标识,自动生成
|
||||
Name string `json:"name"` // Agent显示名称
|
||||
DeviceID string `json:"device_id"` // 向后兼容,保留
|
||||
Token string `json:"token"` // 设备认证令牌
|
||||
Interval string `json:"interval"` // 采集间隔
|
||||
Debug bool `json:"debug"` // 调试模式
|
||||
APIPort int `json:"api_port"` // API端口
|
||||
ID string `json:"id"` // Agent唯一标识,自动生成
|
||||
Name string `json:"name"` // Agent显示名称
|
||||
Token string `json:"token"` // 设备认证令牌
|
||||
Interval string `json:"interval"` // 采集间隔
|
||||
Debug bool `json:"debug"` // 调试模式
|
||||
APIPort int `json:"api_port"` // API端口
|
||||
}
|
||||
|
||||
// NetworkInterfaceMetrics 网卡监控指标
|
||||
@@ -92,10 +92,9 @@ type Metrics struct {
|
||||
RxRate uint64 `json:"rx_rate"` // 所有网卡实时接收速率总和 (bytes/s)
|
||||
TxRate uint64 `json:"tx_rate"` // 所有网卡实时发送速率总和 (bytes/s)
|
||||
// 设备信息字段
|
||||
DeviceID string `json:"device_id"` // 设备ID
|
||||
AgentID string `json:"agent_id"` // Agent唯一标识
|
||||
Name string `json:"name"` // 设备名称
|
||||
IP string `json:"ip"` // 设备IP地址
|
||||
AgentID string `json:"agent_id"` // Agent唯一标识
|
||||
Name string `json:"name"` // 设备名称
|
||||
IP string `json:"ip"` // 设备IP地址
|
||||
}
|
||||
|
||||
// 全局配置
|
||||
@@ -183,10 +182,9 @@ func initConfig() {
|
||||
// 默认配置
|
||||
config = Config{
|
||||
ServerURL: "http://localhost:8080/api",
|
||||
ID: "", // 自动生成
|
||||
Name: "", // 自动生成或从配置读取
|
||||
DeviceID: "default", // 向后兼容,保留
|
||||
Token: "", // 设备认证令牌,从配置或环境变量读取
|
||||
ID: "", // 自动生成
|
||||
Name: "", // 自动生成或从配置读取
|
||||
Token: "", // 设备认证令牌,从配置或环境变量读取
|
||||
Interval: "10s",
|
||||
Debug: false, // 默认非调试模式
|
||||
APIPort: 8081, // 默认API端口8081
|
||||
@@ -217,9 +215,9 @@ func initConfig() {
|
||||
|
||||
// 打印配置信息
|
||||
if config.Debug {
|
||||
log.Printf("Agent ID: %s, Name: %s, DeviceID: %s, Debug: %v, API Port: %d", config.ID, config.Name, config.DeviceID, config.Debug, config.APIPort)
|
||||
log.Printf("Agent ID: %s, Name: %s, Debug: %v, API Port: %d", config.ID, config.Name, config.Debug, config.APIPort)
|
||||
} else {
|
||||
log.Printf("Agent ID: %s, Name: %s, DeviceID: %s", config.ID, config.Name, config.DeviceID)
|
||||
log.Printf("Agent ID: %s, Name: %s", config.ID, config.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,10 +235,6 @@ func loadFromEnv() {
|
||||
config.Name = name
|
||||
}
|
||||
|
||||
if deviceID := os.Getenv("AGENT_DEVICE_ID"); deviceID != "" {
|
||||
config.DeviceID = deviceID
|
||||
}
|
||||
|
||||
if token := os.Getenv("AGENT_TOKEN"); token != "" {
|
||||
config.Token = token
|
||||
}
|
||||
@@ -356,10 +350,6 @@ func readConfigFile() {
|
||||
config.Name = fileConfig.Name
|
||||
}
|
||||
|
||||
if fileConfig.DeviceID != "" {
|
||||
config.DeviceID = fileConfig.DeviceID
|
||||
}
|
||||
|
||||
if fileConfig.Token != "" {
|
||||
config.Token = fileConfig.Token
|
||||
}
|
||||
@@ -746,6 +736,116 @@ func collectDiskDetails() ([]DiskDetailMetrics, error) {
|
||||
return diskDetails, nil
|
||||
}
|
||||
|
||||
// 采集系统日志
|
||||
func collectLogs() ([]LogEntry, error) {
|
||||
// 日志文件路径
|
||||
logFile := "/var/log/messages"
|
||||
log.Printf("Attempting to collect logs from %s", logFile)
|
||||
|
||||
// 检查文件是否存在和权限
|
||||
fileInfo, err := os.Stat(logFile)
|
||||
if err != nil {
|
||||
log.Printf("Failed to stat log file %s: %v", logFile, err)
|
||||
return nil, fmt.Errorf("failed to stat log file %s: %w", logFile, err)
|
||||
}
|
||||
log.Printf("Log file %s exists, size: %d bytes", logFile, fileInfo.Size())
|
||||
|
||||
// 打开日志文件
|
||||
file, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
log.Printf("Failed to open log file %s: %v", logFile, err)
|
||||
return nil, fmt.Errorf("failed to open log file %s: %w", logFile, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 读取文件末尾内容
|
||||
const maxReadSize = 1024 * 1024 // 最多读取1MB
|
||||
readSize := maxReadSize
|
||||
if fileInfo.Size() < int64(maxReadSize) {
|
||||
readSize = int(fileInfo.Size())
|
||||
}
|
||||
log.Printf("Reading %d bytes from log file", readSize)
|
||||
|
||||
buf := make([]byte, readSize)
|
||||
bytesRead, err := file.ReadAt(buf, fileInfo.Size()-int64(readSize))
|
||||
if err != nil {
|
||||
log.Printf("Failed to read log file: %v", err)
|
||||
return nil, fmt.Errorf("failed to read log file: %w", err)
|
||||
}
|
||||
log.Printf("Successfully read %d bytes from log file", bytesRead)
|
||||
|
||||
// 分割日志行
|
||||
lines := bytes.Split(buf[:bytesRead], []byte("\n"))
|
||||
log.Printf("Found %d lines in log file", len(lines))
|
||||
|
||||
// 创建日志条目切片
|
||||
logs := make([]LogEntry, 0, 50) // 最多保存50条日志
|
||||
|
||||
// 从后往前解析日志行
|
||||
for i := len(lines) - 1; i >= 0 && len(logs) < 50; i-- {
|
||||
line := bytes.TrimSpace(lines[i])
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 打印前5行日志行,用于调试
|
||||
if i >= len(lines)-5 {
|
||||
log.Printf("Processing log line %d: %s", i, string(line))
|
||||
}
|
||||
|
||||
// 使用字符串处理,更方便处理空格
|
||||
lineStr := string(line)
|
||||
|
||||
// 使用strings.Fields分割日志行,自动处理连续空格
|
||||
fields := strings.Fields(lineStr)
|
||||
log.Printf("Line %d fields: %v, length: %d", i, fields, len(fields))
|
||||
if len(fields) < 6 {
|
||||
log.Printf("Skipping line %d: not enough fields (%d) after splitting", i, len(fields))
|
||||
continue
|
||||
}
|
||||
|
||||
// 构建时间字符串:月份 日期 时间
|
||||
timeStr := fmt.Sprintf("%s %s %s", fields[0], fields[1], fields[2])
|
||||
log.Printf("Line %d timeStr: '%s'", i, timeStr)
|
||||
|
||||
// 解析时间
|
||||
t, err := time.Parse("Jan 2 15:04:05", timeStr)
|
||||
if err != nil {
|
||||
log.Printf("Skipping line %d: failed to parse time '%s': %v", i, timeStr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 设置当前年份
|
||||
year, _, _ := time.Now().Date()
|
||||
t = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, time.Local)
|
||||
|
||||
// 寻找第一个冒号,用于分割source和message
|
||||
colonIndex := strings.Index(lineStr, ": ")
|
||||
if colonIndex == -1 {
|
||||
log.Printf("Skipping line %d: no colon found to split source and message", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析source和message
|
||||
source := lineStr[:colonIndex]
|
||||
message := lineStr[colonIndex+2:]
|
||||
|
||||
// 创建日志条目
|
||||
logEntry := LogEntry{
|
||||
Sequence: len(logs) + 1,
|
||||
Source: source,
|
||||
Time: t,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
// 添加到日志切片(注意顺序,后面的日志先解析,所以需要插入到前面)
|
||||
logs = append([]LogEntry{logEntry}, logs...)
|
||||
}
|
||||
|
||||
log.Printf("Successfully collected %d logs", len(logs))
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func collectMetrics() (*Metrics, error) {
|
||||
metrics := &Metrics{}
|
||||
|
||||
@@ -753,11 +853,6 @@ func collectMetrics() (*Metrics, error) {
|
||||
metrics.Network = make(map[string]NetworkInterfaceMetrics)
|
||||
|
||||
// 设置设备信息
|
||||
deviceID := config.DeviceID
|
||||
if deviceID == "" {
|
||||
deviceID = config.ID
|
||||
}
|
||||
metrics.DeviceID = deviceID
|
||||
metrics.AgentID = config.ID
|
||||
metrics.Name = config.Name
|
||||
// 尝试获取本机IP地址
|
||||
@@ -825,6 +920,15 @@ func collectMetrics() (*Metrics, error) {
|
||||
metrics.RxRate = rxRate
|
||||
metrics.TxRate = txRate
|
||||
|
||||
// 采集系统日志
|
||||
logs, err := collectLogs()
|
||||
if err != nil {
|
||||
// 日志采集失败时使用空切片
|
||||
log.Printf("Failed to collect logs: %v, using empty slice", err)
|
||||
logs = make([]LogEntry, 0)
|
||||
}
|
||||
metrics.Logs = logs
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
@@ -849,12 +953,6 @@ func sendMetrics(metricsList []*Metrics) error {
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
// 使用DeviceID作为设备唯一标识,与设备管理中的ID匹配
|
||||
deviceID := config.DeviceID
|
||||
if deviceID == "" {
|
||||
deviceID = config.ID
|
||||
}
|
||||
req.Header.Set("X-Device-ID", deviceID)
|
||||
// 设置Agent名称
|
||||
req.Header.Set("X-Agent-Name", config.Name)
|
||||
// 设置设备认证令牌
|
||||
@@ -928,16 +1026,15 @@ func startHTTPServer() {
|
||||
disk, _ := collectDisk()
|
||||
|
||||
status := map[string]interface{}{
|
||||
"status": "running",
|
||||
"agent_id": config.ID,
|
||||
"name": config.Name,
|
||||
"device_id": config.DeviceID,
|
||||
"debug": config.Debug,
|
||||
"interval": config.Interval,
|
||||
"cpu": cpu,
|
||||
"cpu_hz": cpuHz,
|
||||
"memory": memory,
|
||||
"disk": disk,
|
||||
"status": "running",
|
||||
"agent_id": config.ID,
|
||||
"name": config.Name,
|
||||
"debug": config.Debug,
|
||||
"interval": config.Interval,
|
||||
"cpu": cpu,
|
||||
"cpu_hz": cpuHz,
|
||||
"memory": memory,
|
||||
"disk": disk,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user