自定义 prometheus_exporter

本文介绍了如何使用Go语言编写一个通用的Prometheusexporter,通过定时任务而非每次请求更新自定义监控指标,包括从配置文件读取监控项和执行命令获取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        通过golang 编写一个通用的 prometheus_exporter,数据更新非每次访问更新自定义指标的数据。而是通过定时的方式进行更新。

        代码写的不好,见谅哈

package main

import (
	"bytes"
	"errors"
	"flag"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/text/encoding/simplifiedchinese"
	"gopkg.in/yaml.v2"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"
)

var conf Config

// up 服务状态
var up = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "up", Help: "Service status"}, []string{"job", "namespace", "server"})

// grafana筛选项
var Custom = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "Custom", Help: "Grafana Custom"}, []string{"job", "namespace", "server"})

// metrics 监控数据(多个监控项)
var metric = make(map[string]*prometheus.GaugeVec)

// 配置文件
type Config struct {
	Monitoring MonitoringConfiguration `yaml:"monitoring_configuration"`
	Promconf   struct {
		Job       string        `yaml:"job"`
		Path      string        `yaml:"path"`
		Port      string        `yaml:"port"`
		Namespace string        `yaml:"namespace"`
		Server    string        `yaml:"server"`
		Sleep     time.Duration `yaml:"sleep"`
	} `yaml:"prometheus"`
}

// MonitoringConfiguration 监控项配置
type MonitoringConfiguration struct {
	CustomMonitors []Monitoring `yaml:"custom_monitors"`
}

// Monitoring 监控项
type Monitoring struct {
	Name        string      `yaml:"name"`
	Description interface{} `yaml:"description"`
	Command     string      `yaml:"command"`
}

// 判断一个字符串是否由数字组成
func isNumericString(s string) bool {
	_, err := strconv.Atoi(removeExtraSpace(s))
	return err == nil
}

// 去除字符串首尾的空白字符和引号
func removeExtraSpace(input string) string {
	input = strings.TrimSpace(input)
	reg := regexp.MustCompile(`\s+`)
	input = reg.ReplaceAllString(input, "")
	if len(input) == 0 {
		log.Println("原始数字为空")
		return "0"
	}
	return strings.Trim(input, `"`)
}

// 执行 command 命令
func getValue(cmd string) (float64, error) {
	defer func() {
		err := recover()
		if err != nil {
			log.Println("Error", err)
		}
	}()
	var command *exec.Cmd
	if runtime.GOOS == "linux" {
		// 获取入口通道
		command = exec.Command("/bin/bash", "-c", cmd)
	} else if runtime.GOOS == "windows" {
		// 获取入口通道
		command = exec.Command("cmd.exe", "/C", cmd)
	}

	var out bytes.Buffer
	var stderr bytes.Buffer
	command.Stdout = &out
	command.Stderr = &stderr
	var strerr string
	err := command.Run()
	if err != nil {
		if runtime.GOOS == "windows" {
			// 使用GBK解码,否则 windows 执行命令会乱码
			decoder := simplifiedchinese.GBK.NewDecoder()
			decodedStr, _ := decoder.Bytes(stderr.Bytes())
			strerr = string(decodedStr)
		} else if runtime.GOOS == "linux" {
			strerr = string(stderr.Bytes())
		}
		//服务状态判断为异常
		return 0, errors.New(strerr)
	}
	if !isNumericString(out.String()) {
		return 0, errors.New("值不是纯数字组成: " + out.String())
	}
	ifloat, err := strconv.ParseFloat(removeExtraSpace(out.String()), 64)
	if err != nil {
		log.Println("数据类型转化失败", err)
		return 0, err
	}
	return ifloat, nil
}

// 加载配置文件
func LoadXml(filepath string) error {
	file, err := os.ReadFile(filepath)
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(file, &conf)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	defer func() {
		err := recover()
		if err != nil {
			log.Println("Error", err)
		}
	}()

	//启动参数
	conffile := flag.String("e", "/etc/general_export.yaml", "指定配置文件 默认: /etc/general_export.yaml")
	flag.Parse()
	err := LoadXml(*conffile)
	if err != nil {
		log.Println("配置文件加载失败", err)
		return
	}

	//初始化监控指标
	for _, monitor := range conf.Monitoring.CustomMonitors {
		// 监控指标
		metric[monitor.Name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: monitor.Name, Help: monitor.Description.(string)}, []string{"job", "namespace", "server"})
		if ifloat, metricserr := getValue(monitor.Command); metricserr == nil {
			metric[monitor.Name].WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(ifloat)
			//设置服务状态
			up.WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(1)
			//注册自定义监控指标
			prometheus.MustRegister(metric[monitor.Name])
		} else {
			log.Printf("[%s]监控值获取失败: %v", monitor.Name, metricserr)
			metric[monitor.Name].WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(ifloat)
			up.WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(0)
		}
	}

	//筛选
	Custom.WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(0)
	//注册up指标
	prometheus.MustRegister(up, Custom)
	//更新数据指标
	go func() {
		for range time.Tick(time.Second * conf.Promconf.Sleep) {
			for _, monitor := range conf.Monitoring.CustomMonitors {
				// 获取监控值
				ifloat, err := getValue(monitor.Command)
				if err != nil {
					log.Printf("[%s]监控值获取失败: %v", monitor.Name, err)
					metric[monitor.Name].WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(ifloat)
					up.WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(0) //更新服务状态为DOWN
				} else {
					metric[monitor.Name].WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(ifloat)
					//设置服务状态 UP
					up.WithLabelValues(conf.Promconf.Job, conf.Promconf.Namespace, conf.Promconf.Server).Set(1)
				}
			}
		}
	}()

	// 暴露指标
	http.Handle(conf.Promconf.Path, promhttp.Handler())
	log.Println("server up http://0.0.0.0:" + conf.Promconf.Port + conf.Promconf.Path)
	err = http.ListenAndServe(":"+conf.Promconf.Port, nil)
	if err != nil {
		log.Println("Error", err)
		return
	}
}

配置文件

monitoring_configuration:
  #custom_monitors 可以自定义多个自定义监控项(可执行脚本)
  custom_monitors:
    - name: "自定义监控项名称1"
      description: "监控项描述"
      command: "windows 或 linux 指令,需要返回数值"
    - name: "Args2"
      description: "用于监控入口并发"
      command: "type D:\\工作目录\\工作目录-整理\\test2.txt"
prometheus:
  job: "测试JOB"
  path: "/metrics"            #exporter 监听路径
  port: 9901                  #exporter 监听端口
  sleep: 10                   #数据更新的间隔时间时间
  namespace: "测试项目"        #项目名称空间
  server: "服务名称"           #服务器类型或服务名称

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值