Skip to content

Commit

Permalink
feat: 适配路由器的内存指标 (#14)
Browse files Browse the repository at this point in the history
* feat:增加地址调转和内存监控的指标

* feat:适配路由器的指标
  • Loading branch information
nangongchengfeng committed May 22, 2024
1 parent 3d066d8 commit d58498c
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 1 deletion.
18 changes: 17 additions & 1 deletion cmd/padavan_exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -34,38 +39,49 @@ 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 := ` <html> <head> <title>padavan_exporter</title> </head> <body> <span>Here is <a href="https://github.com/Bpazy/padavan_exporter">padavan_exporter</a></span> </body> </html>`
b := ` <html> <head> <title>padavan_exporter</title> </head> <body> <span> Here is <a href="/metrics">/metrics</a> </span> <br><span>Here is <a href="https://github.com/Bpazy/padavan_exporter">padavan_exporter</a></span> </body> </html>`
_, _ = 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)},
Timeout: 5 * time.Second,
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)
Expand Down
105 changes: 105 additions & 0 deletions collector/memory.go
Original file line number Diff line number Diff line change
@@ -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{},
}
}

0 comments on commit d58498c

Please sign in to comment.