diff --git a/cmd/padavan_exporter/main.go b/cmd/padavan_exporter/main.go index 575a142..4ca05d0 100644 --- a/cmd/padavan_exporter/main.go +++ b/cmd/padavan_exporter/main.go @@ -19,11 +19,16 @@ var ( la *string // Address on which to expose metrics and web interface ) +// init 函数用于初始化命令行参数。 +// 通过 kingpin 解析命令行参数,包括 web 服务监听地址、Padavan SSH 主机地址、用户名、密码等。 +// 并根据是否开启 debug 模式设置日志级别。 func init() { + // 定义命令行参数 la = kingpin.Flag("web.listen-address", "Address on which to expose metrics and web interface").Default(":9100").String() ph = kingpin.Flag("padavan.ssh.host", "Padavan ssh host").Default("127.0.0.1:22").String() pu = kingpin.Flag("padavan.ssh.username", "Padavan ssh username").Default("admin").String() pp = kingpin.Flag("padavan.ssh.password", "Padavan ssh password").Default("admin").String() + // 解析命令行参数,并在 debug 模式下设置日志级别 isDebug := kingpin.Flag("debug", "Debug mode").Bool() kingpin.Parse() @@ -34,31 +39,41 @@ func init() { log.Debugf("web.listen-address(%s) padavan.ssh.host(%s) padavan.ssh.username(%s) padavan.ssh.password(%s)", *la, *ph, *pu, *pp) } +// main 函数作为程序入口点,负责初始化 Prometheus 监控数据收集器、SSH 客户端,并启动 HTTP 服务提供监控数据。 func main() { + // 初始化 Prometheus 注册表和 SSH 客户端 reg := prometheus.NewPedanticRegistry() sc := initSshClient() + // 注册各种收集器到 Prometheus reg.MustRegister(collector.NewLoadAverageCollector(sc)) reg.MustRegister(collector.NewNetDevController(sc)) reg.MustRegister(collector.NewCpuCollector(sc)) + reg.MustRegister(collector.NewMemoryCollector(sc)) gatherers := prometheus.Gatherers{reg} h := promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{ ErrorLog: log.StandardLogger(), ErrorHandling: promhttp.ContinueOnError, }) + // 处理 /metrics 请求以提供监控数据 http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { h.ServeHTTP(w, r) }) + // 处理根路径请求,提供简单信息页面 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - b := ` padavan_exporter Here is padavan_exporter ` + b := ` padavan_exporter Here is /metrics
Here is padavan_exporter ` _, _ = fmt.Fprintln(w, b) }) log.Printf("Start server at %s", *la) + // 启动 HTTP 服务 log.Fatal(http.ListenAndServe(*la, nil)) } +// initSshClient 函数用于初始化并返回一个 SSH 客户端。 +// 该客户端用于与 Padavan 设备进行 SSH 连接以收集监控数据。 func initSshClient() *ssh.Client { + // 创建 SSH 客户端配置,包括用户名、密码和一些连接选项 sshConfig := ssh.ClientConfig{ User: *pu, Auth: []ssh.AuthMethod{ssh.Password(*pp)}, @@ -66,6 +81,7 @@ func initSshClient() *ssh.Client { HostKeyCallback: ssh.InsecureIgnoreHostKey(), } log.Printf("Connecting to %s", *ph) + // 尝试建立 SSH 连接 sshClient, err := ssh.Dial("tcp", *ph, &sshConfig) if err != nil { log.Fatalf("create ssh client failed: %+v", err) diff --git a/collector/memory.go b/collector/memory.go new file mode 100644 index 0000000..bab6fdc --- /dev/null +++ b/collector/memory.go @@ -0,0 +1,105 @@ +package collector + +import ( + "bufio" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/crypto/ssh" + "regexp" + "strconv" + "strings" +) + +/** + * @Author: 南宫乘风 + * @Description: + * @File: memory.go + * @Email: 1794748404@qq.com + * @Date: 2024-05-22 15:05 + */ + +var ( + meminfoReg = regexp.MustCompile(`(\w+):\s+(\d+) kB`) +) + +// memoryCollector 收集内存相关指标信息 +type memoryCollector struct { + metrics map[string]*prometheus.Desc // 存储指标描述信息 + sc *ssh.Client // SSH客户端,用于远程收集指标信息 +} + +// Describe 向通道发送描述指标的Desc对象 +// 这个方法不具体实现,因为metrics在Collect方法中动态创建。 +func (m *memoryCollector) Describe(ch chan<- *prometheus.Desc) { + // metrics created when Collect +} + +// Collect 向通道发送内存指标数据 +// @param ch 用于发送指标的通道 +func (m *memoryCollector) Collect(ch chan<- prometheus.Metric) { + // 通过SSH客户端获取远程机器的/proc/meminfo文件内容 + content := mustGetContent(m.sc, "/proc/meminfo") + // 创建Scanner用于逐行读取内容 + scanner := bufio.NewScanner(strings.NewReader(content)) + // 使用正则表达式匹配行内容,获取指标名和值 + for scanner.Scan() { + parts := meminfoReg.FindStringSubmatch(scanner.Text()) + + if len(parts) != 3 { + // 如果匹配不成功,则跳过当前行 + continue + } + // 指标名 + key := parts[1] + // 解析指标值 + value, err := strconv.ParseFloat(parts[2], 64) + if err != nil { + continue + // 如果解析失败,则跳过当前行 + } + value *= 1024 // 将值从kB转换为B + + var desc *prometheus.Desc + var ok bool + // 根据指标名,获取或创建对应的Desc对象 + switch key { + case "MemTotal": + desc, ok = m.metrics["MemTotal"] + if !ok { + desc = prometheus.NewDesc("node_memory_total_bytes", "Total memory in bytes.", nil, nil) + m.metrics["MemTotal"] = desc + } + case "MemFree": + desc, ok = m.metrics["MemFree"] + if !ok { + desc = prometheus.NewDesc("node_memory_free_bytes", "Free memory in bytes.", nil, nil) + m.metrics["MemFree"] = desc + } + case "Buffers": + desc, ok = m.metrics["Buffers"] + if !ok { + desc = prometheus.NewDesc("node_memory_buffers_bytes", "Buffers memory in bytes.", nil, nil) + m.metrics["Buffers"] = desc + } + case "Cached": + desc, ok = m.metrics["Cached"] + if !ok { + desc = prometheus.NewDesc("node_memory_cached_bytes", "Cached memory in bytes.", nil, nil) + m.metrics["Cached"] = desc + } + default: + continue // 如果不是我们关心的指标,则跳过当前行 + } + // 向通道发送指标 + ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, value) + } +} + +// NewMemoryCollector 创建一个新的memoryCollector实例 +// @param sc SSH客户端 +// @return 返回memoryCollector实例的指针 +func NewMemoryCollector(sc *ssh.Client) *memoryCollector { + return &memoryCollector{ + sc: sc, + metrics: map[string]*prometheus.Desc{}, + } +}