From d58498c007e2eeafa7bf4116a0bb558b21bfb921 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=97=E5=AE=AB=E4=B9=98=E9=A3=8E?=
<46562911+nangongchengfeng@users.noreply.github.com>
Date: Wed, 22 May 2024 22:58:00 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8D=E8=B7=AF=E7=94=B1?=
=?UTF-8?q?=E5=99=A8=E7=9A=84=E5=86=85=E5=AD=98=E6=8C=87=E6=A0=87=20(#14)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat:增加地址调转和内存监控的指标
* feat:适配路由器的指标
---
cmd/padavan_exporter/main.go | 18 +++++-
collector/memory.go | 105 +++++++++++++++++++++++++++++++++++
2 files changed, 122 insertions(+), 1 deletion(-)
create mode 100644 collector/memory.go
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{},
+ }
+}