Files
mfd-monitor-agent/cmd/agent/main.go
codex 877e4cf41d docs(agent): 统一中文注释并补充部署文档
将代码与测试注释改为中文以符合仓库规范;新增中文部署指南、
示例配置,并完善构建脚本对 VERSION 注入的说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 02:30:49 +00:00

138 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"errors"
"flag"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"mfd-monitor-agent/internal/collector"
"mfd-monitor-agent/internal/config"
"mfd-monitor-agent/internal/httpapi"
"mfd-monitor-agent/internal/reporter"
"mfd-monitor-agent/internal/service"
"mfd-monitor-agent/internal/snapshot"
)
var version = "0.1.0"
// main 运行 CLI,命令失败时以非零状态退出。
func main() {
if err := runCLI(os.Args[1:]); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
// runCLI 分发 agent 子命令,并在调用处理函数前加载配置。
func runCLI(args []string) error {
if len(args) == 0 {
args = []string{"run"}
}
switch args[0] {
case "run":
fs := flag.NewFlagSet("run", flag.ExitOnError)
configPath := fs.String("config", "", "JSON 配置文件路径")
if err := fs.Parse(args[1:]); err != nil {
return err
}
cfg, err := config.Load(*configPath)
if err != nil {
return err
}
return runAgent(cfg)
case "install":
cfg, executable, configPath, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Install(executable, cfg, service.InstallOptions{ConfigPath: configPath})
case "uninstall":
cfg, _, _, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Uninstall(cfg)
case "start":
cfg, _, _, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Start(cfg)
case "stop":
cfg, _, _, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Stop(cfg)
case "version":
fmt.Println(version)
return nil
default:
return fmt.Errorf("unknown command %q; expected run, install, uninstall, start, stop, version", args[0])
}
}
// commandConfig 解析服务相关子命令的标志并解析可执行文件路径。
func commandConfig(args []string) (config.Config, string, string, error) {
fs := flag.NewFlagSet("service", flag.ExitOnError)
configPath := fs.String("config", "", "JSON 配置文件路径")
executableFlag := fs.String("exe", "", "agent 可执行文件路径(默认当前二进制)")
if err := fs.Parse(args); err != nil {
return config.Config{}, "", "", err
}
cfg, err := config.Load(*configPath)
if err != nil {
return cfg, "", "", err
}
executable := *executableFlag
if executable == "" {
executable, err = os.Executable()
if err != nil {
return cfg, "", "", err
}
}
return cfg, executable, *configPath, nil
}
// runAgent 启动采集、可选上报与本地 HTTP API,直至收到退出信号。
func runAgent(cfg config.Config) error {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
startedAt := time.Now().UTC()
cache := snapshot.NewCache(collector.New(cfg.HostID), version, startedAt)
go cache.Run(ctx, cfg.Collect.Interval.Duration(), cfg.Collect.Timeout.Duration())
if cfg.Report.Enabled {
client := reporter.NewClient(cfg.Report.Endpoint, cfg.Report.Timeout.Duration())
go reporter.Run(ctx, cache, client, cfg.Report.Interval.Duration(), logger)
}
server := &http.Server{
Addr: cfg.HTTP.ListenAddress,
Handler: httpapi.New(cache),
ReadHeaderTimeout: 5 * time.Second,
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = server.Shutdown(shutdownCtx)
}()
logger.Info("agent listening", "address", cfg.HTTP.ListenAddress)
err := server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}