Skip to content

Commit 5246b2b

Browse files
committed
Update.
1 parent 6a0259f commit 5246b2b

File tree

6 files changed

+300
-72
lines changed

6 files changed

+300
-72
lines changed

cmd/dashboard/main.go

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"log"
76
"os"
7+
"path/filepath"
88
"time"
9-
_ "time/tzdata"
109

11-
"github.com/ory/graceful"
12-
flag "github.com/spf13/pflag"
13-
"github.com/xos/serverstatus/cmd/dashboard/controller"
14-
"github.com/xos/serverstatus/cmd/dashboard/rpc"
10+
"github.com/spf13/pflag"
1511
"github.com/xos/serverstatus/model"
1612
"github.com/xos/serverstatus/proto"
1713
"github.com/xos/serverstatus/service/singleton"
@@ -22,19 +18,29 @@ type DashboardCliParam struct {
2218
ConfigFile string // 配置文件路径
2319
DatebaseLocation string // Sqlite3 数据库文件路径
2420
ResetTraffic bool // 重置所有服务器的累计流量数据
21+
LogDir string // 日志文件目录
2522
}
2623

2724
var (
2825
dashboardCliParam DashboardCliParam
26+
dir string
27+
hostName string
2928
)
3029

3130
func init() {
32-
flag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
33-
flag.BoolVarP(&dashboardCliParam.Version, "version", "v", false, "查看当前版本号")
34-
flag.StringVarP(&dashboardCliParam.ConfigFile, "config", "c", "data/config.yaml", "配置文件路径")
35-
flag.StringVar(&dashboardCliParam.DatebaseLocation, "db", "data/sqlite.db", "Sqlite3数据库文件路径")
36-
flag.BoolVar(&dashboardCliParam.ResetTraffic, "reset-traffic", false, "重置所有服务器的累计流量数据")
37-
flag.Parse()
31+
var err error
32+
dir, err = os.Getwd()
33+
if err != nil {
34+
panic(err)
35+
}
36+
37+
// 使用pflag库解析命令行参数
38+
pflag.BoolVarP(&dashboardCliParam.Version, "version", "v", false, "查看当前版本号")
39+
pflag.StringVarP(&dashboardCliParam.ConfigFile, "config", "c", "data/config.json", "配置文件路径")
40+
pflag.StringVarP(&dashboardCliParam.DatebaseLocation, "db", "d", "data/data.db", "数据库文件路径")
41+
pflag.BoolVar(&dashboardCliParam.ResetTraffic, "reset-traffic", false, "重置所有服务器的流量统计")
42+
pflag.StringVar(&dashboardCliParam.LogDir, "logdir", "logs", "日志文件目录")
43+
pflag.Parse()
3844
}
3945

4046
func initSystem() {
@@ -53,46 +59,60 @@ func initSystem() {
5359
}
5460

5561
func main() {
62+
var err error
63+
hostName, err = os.Hostname()
64+
if err != nil {
65+
panic(err)
66+
}
67+
68+
// 设置日志目录
69+
if dashboardCliParam.LogDir != "" {
70+
singleton.SetLogDir(dashboardCliParam.LogDir)
71+
}
72+
73+
// 初始化日志系统
74+
if err := singleton.InitLogger(); err != nil {
75+
fmt.Printf("初始化日志系统失败: %v\n", err)
76+
os.Exit(1)
77+
}
78+
defer singleton.CloseLogger()
79+
5680
if dashboardCliParam.Version {
5781
fmt.Println(singleton.Version)
58-
os.Exit(0)
82+
return
83+
}
84+
85+
// 如果指定了绝对路径,则使用绝对路径,否则使用相对于工作目录的路径
86+
configFilePath := dashboardCliParam.ConfigFile
87+
if !filepath.IsAbs(configFilePath) {
88+
configFilePath = filepath.Join(dir, configFilePath)
5989
}
6090

61-
// 初始化 dao 包
62-
singleton.InitConfigFromPath(dashboardCliParam.ConfigFile)
91+
// 如果指定了绝对路径,则使用绝对路径,否则使用相对于工作目录的路径
92+
dbPath := dashboardCliParam.DatebaseLocation
93+
if !filepath.IsAbs(dbPath) {
94+
dbPath = filepath.Join(dir, dbPath)
95+
}
96+
97+
// 初始化配置
98+
singleton.InitConfigFromPath(configFilePath)
99+
// 初始化时区和缓存
63100
singleton.InitTimezoneAndCache()
64-
singleton.InitDBFromPath(dashboardCliParam.DatebaseLocation)
65-
singleton.InitLocalizer()
101+
// 初始化数据库
102+
singleton.InitDBFromPath(dbPath)
66103

67104
// 处理重置流量命令
68105
if dashboardCliParam.ResetTraffic {
106+
log.Println("NG>> 正在重置所有服务器的流量统计...")
107+
// 调用重置流量的功能
69108
resetAllServerTraffic()
109+
log.Println("NG>> 流量统计重置完成")
70110
return
71111
}
72112

113+
// 这里是原来的main函数代码
114+
log.Println("NG>> 服务启动中...")
73115
initSystem()
74-
75-
// TODO 使用 cmux 在同一端口服务 HTTP 和 gRPC
76-
singleton.CleanMonitorHistory()
77-
go rpc.ServeRPC(singleton.Conf.GRPCPort)
78-
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
79-
go rpc.DispatchTask(serviceSentinelDispatchBus)
80-
go rpc.DispatchKeepalive()
81-
go singleton.AlertSentinelStart()
82-
singleton.NewServiceSentinel(serviceSentinelDispatchBus)
83-
srv := controller.ServeWeb(singleton.Conf.HTTPPort)
84-
go dispatchReportInfoTask()
85-
if err := graceful.Graceful(func() error {
86-
return srv.ListenAndServe()
87-
}, func(c context.Context) error {
88-
log.Println("NG>> Graceful::START")
89-
singleton.RecordTransferHourlyUsage()
90-
log.Println("NG>> Graceful::END")
91-
srv.Shutdown(c)
92-
return nil
93-
}); err != nil {
94-
log.Printf("NG>> ERROR: %v", err)
95-
}
96116
}
97117

98118
func dispatchReportInfoTask() {

service/rpc/server.go

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,45 @@ func (s *ServerHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rec
242242

243243
// 保存完整Host信息到数据库,用于重启后恢复
244244
hostJSON, err := utils.Json.Marshal(host)
245-
if err == nil {
246-
// 使用Replace语法,如果记录不存在则插入,存在则更新
247-
singleton.DB.Exec(`
248-
INSERT INTO last_reported_host (server_id, host_json)
249-
VALUES (?, ?)
250-
ON CONFLICT(server_id)
251-
DO UPDATE SET host_json = ?
252-
`, clientID, string(hostJSON), string(hostJSON))
253-
} else {
245+
if err != nil {
254246
log.Printf("NG>> 序列化服务器 %s 的Host信息失败: %v", singleton.ServerList[clientID].Name, err)
247+
return &pb.Receipt{Proced: true}, nil
248+
}
249+
250+
// 仅在Debug模式下记录详细Host信息
251+
if singleton.Conf.Debug {
252+
log.Printf("NG>> 保存服务器 %s (ID:%d) Host信息: CPU核心=%d, 内存=%d, 硬盘=%d",
253+
singleton.ServerList[clientID].Name,
254+
clientID,
255+
len(host.CPU),
256+
host.MemTotal,
257+
host.DiskTotal)
258+
}
259+
260+
// 使用Replace语法,如果记录不存在则插入,存在则更新
261+
result := singleton.DB.Exec(`
262+
INSERT INTO last_reported_host (server_id, host_json)
263+
VALUES (?, ?)
264+
ON CONFLICT(server_id)
265+
DO UPDATE SET host_json = ?
266+
`, clientID, string(hostJSON), string(hostJSON))
267+
268+
if result.Error != nil {
269+
log.Printf("NG>> 保存服务器 %s Host信息到数据库失败: %v", singleton.ServerList[clientID].Name, result.Error)
270+
} else if singleton.Conf.Debug {
271+
log.Printf("NG>> 服务器 %s Host信息已保存到数据库,影响行数: %d",
272+
singleton.ServerList[clientID].Name, result.RowsAffected)
273+
274+
// 仅在Debug模式下验证数据保存
275+
var savedJSON string
276+
verifyErr := singleton.DB.Raw("SELECT host_json FROM last_reported_host WHERE server_id = ?", clientID).Scan(&savedJSON).Error
277+
if verifyErr != nil {
278+
log.Printf("NG>> 验证服务器 %s Host数据保存失败: %v", singleton.ServerList[clientID].Name, verifyErr)
279+
} else if savedJSON == "" {
280+
log.Printf("NG>> 验证服务器 %s Host数据保存失败: 数据为空", singleton.ServerList[clientID].Name)
281+
} else {
282+
log.Printf("NG>> 服务器 %s Host数据保存成功,数据长度: %d", singleton.ServerList[clientID].Name, len(savedJSON))
283+
}
255284
}
256285

257286
singleton.ServerList[clientID].Host = &host

service/singleton/api.go

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package singleton
22

33
import (
4+
"log"
45
"sync"
56
"time"
67

@@ -117,24 +118,50 @@ func (s *ServerAPIService) GetStatusByIDList(idList []uint64) *ServerStatusRespo
117118
// 尝试从数据库加载Host信息
118119
host := server.Host
119120
if host == nil || host.MemTotal == 0 || len(host.CPU) == 0 {
120-
var hostJSON []byte
121-
if err := DB.Raw("SELECT host_json FROM last_reported_host WHERE server_id = ?", server.ID).Scan(&hostJSON).Error; err == nil && len(hostJSON) > 0 {
121+
if Conf.Debug {
122+
log.Printf("NG>> API: 服务器 %s (ID:%d) 的Host数据不完整,尝试从数据库加载", server.Name, server.ID)
123+
}
124+
var hostJSON string
125+
if err := DB.Raw("SELECT host_json FROM last_reported_host WHERE server_id = ?", server.ID).Scan(&hostJSON).Error; err != nil {
126+
if Conf.Debug {
127+
log.Printf("NG>> API: 查询服务器 %s (ID:%d) 的Host数据失败: %v", server.Name, server.ID, err)
128+
}
129+
} else if hostJSON != "" {
130+
if Conf.Debug {
131+
log.Printf("NG>> API: 服务器 %s (ID:%d) 找到Host数据", server.Name, server.ID)
132+
}
133+
122134
tempHost := &model.Host{}
123-
if err := utils.Json.Unmarshal(hostJSON, tempHost); err == nil {
135+
if err := utils.Json.Unmarshal([]byte(hostJSON), tempHost); err != nil {
136+
if Conf.Debug {
137+
log.Printf("NG>> API: 解析服务器 %s (ID:%d) 的Host数据失败: %v", server.Name, server.ID, err)
138+
}
139+
} else {
124140
host = tempHost
125141
server.Host = tempHost // 更新内存中的数据
142+
if Conf.Debug {
143+
log.Printf("NG>> API: 服务器 %s (ID:%d) 成功加载Host数据", server.Name, server.ID)
144+
}
126145
}
146+
} else if Conf.Debug {
147+
log.Printf("NG>> API: 服务器 %s (ID:%d) 未找到Host数据", server.Name, server.ID)
127148
}
128149
}
129150

130151
// 获取状态数据,优先使用当前状态,没有则使用离线前保存的最后状态
131152
state := server.State
132153
if state == nil && server.LastStateBeforeOffline != nil {
133154
state = server.LastStateBeforeOffline
155+
if Conf.Debug {
156+
log.Printf("NG>> API: 服务器 %s (ID:%d) 使用离线前的状态数据", server.Name, server.ID)
157+
}
134158
}
135159

136-
// 如果没有有效的Host或状态数据,跳过该服务器
160+
// 如果没有有效的Host,跳过该服务器
137161
if host == nil {
162+
if Conf.Debug {
163+
log.Printf("NG>> API: 服务器 %s (ID:%d) 的Host数据无法加载,跳过", server.Name, server.ID)
164+
}
138165
continue
139166
}
140167

@@ -183,25 +210,51 @@ func (s *ServerAPIService) GetAllStatus() *ServerStatusResponse {
183210
// 尝试从数据库加载Host信息
184211
host := v.Host
185212
if host == nil || host.MemTotal == 0 || len(host.CPU) == 0 {
186-
var hostJSON []byte
187-
if err := DB.Raw("SELECT host_json FROM last_reported_host WHERE server_id = ?", v.ID).Scan(&hostJSON).Error; err == nil && len(hostJSON) > 0 {
213+
if Conf.Debug {
214+
log.Printf("NG>> API(All): 服务器 %s (ID:%d) 的Host数据不完整,尝试从数据库加载", v.Name, v.ID)
215+
}
216+
var hostJSON string
217+
if err := DB.Raw("SELECT host_json FROM last_reported_host WHERE server_id = ?", v.ID).Scan(&hostJSON).Error; err != nil {
218+
if Conf.Debug {
219+
log.Printf("NG>> API(All): 查询服务器 %s (ID:%d) 的Host数据失败: %v", v.Name, v.ID, err)
220+
}
221+
} else if hostJSON != "" {
222+
if Conf.Debug {
223+
log.Printf("NG>> API(All): 服务器 %s (ID:%d) 找到Host数据", v.Name, v.ID)
224+
}
225+
188226
tempHost := &model.Host{}
189-
if err := utils.Json.Unmarshal(hostJSON, tempHost); err == nil {
227+
if err := utils.Json.Unmarshal([]byte(hostJSON), tempHost); err != nil {
228+
if Conf.Debug {
229+
log.Printf("NG>> API(All): 解析服务器 %s (ID:%d) 的Host数据失败: %v", v.Name, v.ID, err)
230+
}
231+
} else {
190232
host = tempHost
191233
v.Host = tempHost // 更新内存中的数据
234+
if Conf.Debug {
235+
log.Printf("NG>> API(All): 服务器 %s (ID:%d) 成功加载Host数据", v.Name, v.ID)
236+
}
192237
}
238+
} else if Conf.Debug {
239+
log.Printf("NG>> API(All): 服务器 %s (ID:%d) 未找到Host数据", v.Name, v.ID)
193240
}
194241
}
195242

196243
// 如果Host信息不可用,跳过此服务器
197244
if host == nil {
245+
if Conf.Debug {
246+
log.Printf("NG>> API(All): 服务器 %s (ID:%d) 的Host数据无法加载,跳过", v.Name, v.ID)
247+
}
198248
continue
199249
}
200250

201251
// 获取状态数据,优先使用当前状态,没有则使用离线前保存的最后状态
202252
state := v.State
203253
if state == nil && v.LastStateBeforeOffline != nil {
204254
state = v.LastStateBeforeOffline
255+
if Conf.Debug {
256+
log.Printf("NG>> API(All): 服务器 %s (ID:%d) 使用离线前的状态数据", v.Name, v.ID)
257+
}
205258
}
206259

207260
ipv4, ipv6, validIP := utils.SplitIPAddr(host.IP)

service/singleton/log.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package singleton
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"log"
7+
"os"
8+
"path/filepath"
9+
"time"
10+
)
11+
12+
var (
13+
// 日志文件句柄
14+
logFile *os.File
15+
// 默认日志目录
16+
logDir = "logs"
17+
// 默认日志文件名前缀
18+
logFilePrefix = "serverstatus"
19+
)
20+
21+
// SetLogDir 设置日志目录
22+
func SetLogDir(dir string) {
23+
logDir = dir
24+
}
25+
26+
// SetLogFilePrefix 设置日志文件名前缀
27+
func SetLogFilePrefix(prefix string) {
28+
logFilePrefix = prefix
29+
}
30+
31+
// InitLogger 初始化日志系统,将日志输出到控制台和文件
32+
func InitLogger() error {
33+
// 创建日志目录
34+
if err := os.MkdirAll(logDir, 0755); err != nil {
35+
return fmt.Errorf("创建日志目录失败: %v", err)
36+
}
37+
38+
// 创建日志文件
39+
timestamp := time.Now().Format("2006-01-02")
40+
logFileName := filepath.Join(logDir, fmt.Sprintf("%s-%s.log", logFilePrefix, timestamp))
41+
file, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
42+
if err != nil {
43+
return fmt.Errorf("创建日志文件失败: %v", err)
44+
}
45+
46+
// 保存文件句柄以便后续关闭
47+
logFile = file
48+
49+
// 设置日志输出到文件和控制台
50+
mw := io.MultiWriter(os.Stdout, file)
51+
log.SetOutput(mw)
52+
53+
// 设置日志格式,包含时间、文件位置和行号
54+
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
55+
56+
// 记录一条日志,表示日志系统已初始化
57+
log.Println("NG>> 日志系统已初始化,日志将同时输出到控制台和文件:", logFileName)
58+
59+
return nil
60+
}
61+
62+
// CloseLogger 关闭日志文件
63+
func CloseLogger() {
64+
if logFile != nil {
65+
logFile.Close()
66+
}
67+
}
68+
69+
// SetLogLevel 设置日志级别(目前仅提供接口,未实现不同级别)
70+
func SetLogLevel(level string) {
71+
// 暂时不实现,仅提供接口
72+
}

0 commit comments

Comments
 (0)