我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何使用go自定义prometheus的exporter

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何使用go自定义prometheus的exporter

这篇文章主要介绍“如何使用go自定义prometheus的exporter”,在日常操作中,相信很多人在如何使用go自定义prometheus的exporter问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用go自定义prometheus的exporter”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

介绍

prometheus中如果要监控服务器和应用的各种指标,需要用各种各样的exporter服务,例如node_exportesmysql_exportespgsql_exportes等。这些都是官方或者第三方已经提供好的。但是如果自己想要监控一些其它exportes没有的指标,则就需要自己去构建一个属于自己的exportes,好在官方提供相关的库,目前支持以下语言:

官方支持语言:

  • Go

  • Java or Scala

  • Python

  • Ruby

  • Rust

metric的类型

在开始之前需要了解下metric的类型划分

  • Counter(计数器):只增不减的计数器,用于记录事件发生的次数,例如请求数量、错误数量等。

  • Gauge(仪表盘):可增可减的指标,用于记录当前的状态,例如 CPU 使用率、内存使用量等。

  • Histogram(直方图):用于记录数据的分布情况,例如请求响应时间的分布情况。

  • Summary(摘要):与 Histogram 类似,但是它会在客户端计算出一些摘要信息,例如平均值、标准差等。

类型详解

Guage

Gauge的特点:1. 可以任意上升或下降,没有固定的范围限制。2. 可以被设置为任何值,不像Counter只能递增。3. 可以被用来表示瞬时值或累计值。4. 可以被用来表示单个实体的状态,例如单个服务器的CPU使用率。5. 可以被用来表示多个实体的总体状态,例如整个集群的CPU使用率。Gauge的使用:1. Gauge的值可以通过set()方法进行设置。2. Gauge的值可以通过inc()和dec()方法进行增加或减少。3. Gauge的值可以通过add()方法进行增加或减少指定的值。4. Gauge的值可以通过set_to_current_time()方法设置为当前时间戳。5. Gauge的值可以通过observe()方法进行设置,这个方法可以用来记录样本值和时间戳。

Counter

Counter的特点:1. Counter只能增加,不能减少或重置。2. Counter的值是一个非负整数。3. Counter的值可以随时间增加,但不会减少。4. Counter的值在重启Prometheus时会重置为0。5. Counter的值可以被多个Goroutine同时增加,不需要加锁。6. Counter的值可以被推送到Pushgateway中,用于监控非Prometheus监控的数据。Counter的使用方法:1. 在程序中定义一个Counter对象,并初始化为0。2. 当需要记录计数时,调用Counter的Inc()方法增加计数器的值。3. 将Counter对象暴露给Prometheus,使其能够收集数据。4. 在Prometheus中定义一个相应的指标,并将Counter对象与该指标关联。

示例代码:

import (    "github.com/prometheus/client_golang/prometheus"    "github.com/prometheus/client_golang/prometheus/promauto")// 定义一个Counter对象var requestCounter = promauto.NewCounter(prometheus.CounterOpts{    Name: "http_requests_total",    Help: "The total number of HTTP requests",})// 记录请求计数func handleRequest() {    requestCounter.Inc()    // 处理请求}

在上面的代码中,我们定义了一个名为http_requests_totalCounter对象,用于记录HTTP请求的总数。每当处理一个请求时,我们调用requestCounter.Inc()方法增加计数器的值。最后,我们将Counter对象暴露给Prometheus,并在Prometheus中定义了一个名为http_requests_total的指标,将Counter对象与该指标关联。这样,Prometheus就能够收集和展示http_requests_total指标的数据了

Histogram

Histogram是一种Prometheus指标类型,用于度量数据的分布情况。它将数据分成一系列桶(bucket),每个桶代表一段范围内的数据。每个桶都有一个计数器(counter),用于记录该范围内的数据数量。在Prometheus中,Histogram指标类型的名称以“_bucket”结尾。Histogram指标类型通常用于度量请求延迟、响应大小等连续型数据。例如,我们可以使用Histogram指标类型来度量Web应用程序的请求延迟。我们可以将请求延迟分成几个桶,例如0.1秒、0.5秒、1秒、5秒、10秒、30秒等。每个桶都记录了在该范围内的请求延迟的数量。Histogram指标类型还有两个重要的计数器:sum和count。sum用于记录所有数据的总和,count用于记录数据的数量。通过这两个计数器,我们可以计算出平均值和其他统计信息。在Prometheus中,我们可以使用histogram_quantile函数来计算某个百分位数的值。例如,我们可以使用histogram_quantile(0.9, my_histogram)来计算my_histogram指标类型中90%的请求延迟的值。总之,Histogram指标类型是一种非常有用的指标类型,可以帮助我们了解数据的分布情况,从而更好地监控和优化应用程序的性能。

Summary

Summary是Prometheus中的一种指标类型,用于记录一组样本的总和、计数和分位数。它适用于记录耗时、请求大小等具有较大变化范围的指标。Summary指标类型包含以下几个指标:1. sum:样本值的总和。2. count:样本值的计数。3. quantile:分位数。其中,sum和count是必须的,而quantile是可选的。在使用Summary指标类型时,需要注意以下几点:1. 每个Summary指标类型都会记录所有样本的总和和计数,因此它们的值会随时间变化而变化。2. 每个Summary指标类型都可以记录多个分位数,例如50%、90%、95%、99%等。3. 每个Summary指标类型都可以设置一个时间窗口,用于计算分位数。4. 每个Summary指标类型都可以设置一个最大样本数,用于限制内存使用。5. 每个Summary指标类型都可以设置一个标签集,用于区分不同的实例。总之,Summary指标类型是一种非常有用的指标类型,可以帮助我们更好地了解系统的性能和健康状况

示例

以下示例实现了通过传入的端口号监听对应的进程,并输出进程的一些信息,如pid、cmdline、exe、ppid、内存使用等信息(通过读/proc/pid/目录下的文件来实现),后面如果有其他需要可自行修改。因为写的比较仓促,这里也不详细介绍代码中的含义,有兴趣的可以留言,或者直接拿走代码试试。

目录结构是

|-main.go|-go.mod|-go.sum|-collector   |-- exec.go   |-- port.go

main.go

package mainimport ("fmt""net/http""time""exporter/collector""github.com/alecthomas/kingpin""github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promhttp")// 定义命令行参数var (ticker = kingpin.Flag("ticker", "Interval for obtaining indicators.").Short('t').Default("5").Int()mode   = kingpin.Flag("mode", "Using netstat or lsof for specified port pid information.").Short('m').Default("netstat").String()port   = kingpin.Flag("port", "This service is to listen the port.").Short('p').Default("9527").String()ports  = kingpin.Arg("ports", "The process of listening on ports.").Required().Strings())func main() {kingpin.Version("1.1")kingpin.Parse()// 注册自身采集器prometheus.MustRegister(collector.NewPortCollector(*ports, *mode))// fmt.Printf("Would ping: %s with timeout %s \n", *mode, *ports)go func() {for {collector.NewPortCollector(*ports, *mode).Updata()time.Sleep(time.Duration(*ticker) * time.Second)}}()http.Handle("/metrics", promhttp.Handler())fmt.Println("Ready to listen on port:", *port)if err := http.ListenAndServe("0.0.0.0:"+*port, nil); err != nil {fmt.Printf("Error occur when start server %v", err)}}

exec.go

package collectorimport ("bufio""fmt""io""os""os/exec""strings")var (order  intawkMap = make(map[int]string)result = make(map[string]string)// 定义要在status文件里筛选的关键字targetList   = []string{"Name", "State", "PPid", "Uid", "Gid", "VmHWM", "VmRSS"}targetResult = make(map[string]map[string]string))func stringGrep(s string, d string) (bool, error) {for k, v := range d {if v != rune(s[k]) {return false, fmt.Errorf("string does not match")}}order = 1resolv, err := stringAWK(s[len(d):])if len(resolv) == 0 {return false, err}order = 0return true, nil}func stringAWK(s string) (map[int]string, error) {i := 0for k, v := range s {if v != rune(9) && v != rune(32) && v != rune(10) {i = 1awkMap[order] += string(v)} else {if i > 0 {order++i = 0}stringAWK(s[k+1:])return awkMap, nil}}return awkMap, fmt.Errorf("awk error")}func GetProcessInfo(p []string, m string) map[string]map[string]string {for _, port := range p {// 通过端口号获取进程pid信息// 通过组合命令行的方式执行linux命令,筛选出pidcmd := "sudo " + m + " -tnlp" + "|grep :" + port + "|awk '{print $NF}'|awk -F'/' '{print $1}'"getPid := exec.Command("bash", "-c", cmd)out, err := getPid.Output()if err != nil {fmt.Println("exec command failed", err)return nil}dir := strings.ReplaceAll(string(out), "\n", "")if len(dir) == 0 {fmt.Println("'dir' string is empty")return nil// panic("'dir' string is empty")}// fmt.Println("test_dir", dir)result["pid"] = dir// 获取命令行绝地路径cmdRoot := "sudo ls -l /proc/" + dir + "/exe |awk '{print $NF}'"getCmdRoot := exec.Command("bash", "-c", cmdRoot)out, err = getCmdRoot.Output()if err != nil {fmt.Println("exec getCmdRoot command failed", err)}// fmt.Println("test_cmdroot", strings.ReplaceAll(string(out), "\n", ""))result["cmdroot"] = strings.ReplaceAll(string(out), "\n", "")// 获取/proc/pid/cmdline文件内信息cmdline, err := os.Open("/proc/" + dir + "/cmdline")if err != nil {fmt.Println("open cmdline file error :", err)panic(err)}cmdlineReader, err := bufio.NewReader(cmdline).ReadString('\n')if err != nil && err != io.EOF {fmt.Println(err)}result["cmdline"] = strings.ReplaceAll(cmdlineReader, "\x00", " ")// 获取/proc/pid/status文件内信息status, err := os.Open("/proc/" + dir + "/status")if err != nil {fmt.Println("open status file error :", err)}// 执行函数返回前关闭打开的文件defer cmdline.Close()defer status.Close()statusReader := bufio.NewReader(status)if err != nil {fmt.Println(err)}for {line, err := statusReader.ReadString('\n') //注意是字符if err == io.EOF {if len(line) != 0 {fmt.Println(line)}break}if err != nil {fmt.Println("read file failed, err:", err)// return}for _, v := range targetList {istrue, _ := stringGrep(line, v)if istrue {result[v] = awkMap[2]// fmt.Printf("%v结果是:%v\n", v, awkMap[2])awkMap = make(map[int]string)}}}// fmt.Println("数据的和:", result)// fmt.Println("test_result", result)targetResult[port] = result// 给result map重新赋值,要不然使用的是同一个map指针,targetResult结果是一样的result = make(map[string]string)}// fmt.Println("test_total", targetResult)return targetResult}

port.go

package collectorimport ("sync""github.com/prometheus/client_golang/prometheus""github.com/shirou/gopsutil/host")var (isexist   float64 = 1namespace         = "own_process"endetail          = "datails"endmems           = "mems")// 定义收集指标结构体// 分为进程信息和内存信息type PortCollector struct {ProcessDetail portMetricsProcessMems   portMetricsmutex         sync.Mutex // 使用于多个协程访问共享资源的场景// value         prometheus.Gauge}type portMetrics []struct {desc  *prometheus.Descvalue map[string]string}func (p *PortCollector) Describe(ch chan<- *prometheus.Desc) {for _, metric := range p.ProcessDetail {ch <- metric.desc}for _, metric := range p.ProcessMems {ch <- metric.desc}// ch <- p.ProcessMems}func (p *PortCollector) Collect(ch chan<- prometheus.Metric) {p.mutex.Lock()defer p.mutex.Unlock()// ch <- prometheus.MustNewConstMetric(p.ProcessMems, prometheus.GaugeValue, 0)for _, metric := range p.ProcessDetail {ch <- prometheus.MustNewConstMetric(metric.desc, prometheus.GaugeValue, isexist, metric.value["cmdroot"], metric.value["cmdline"], metric.value["Name"], metric.value["State"], metric.value["PPid"], metric.value["Uid"], metric.value["Gid"])}for _, metric := range p.ProcessMems {ch <- prometheus.MustNewConstMetric(metric.desc, prometheus.GaugeValue, isexist, metric.value["Name"], metric.value["pid"], metric.value["VmHWM"], metric.value["VmRSS"])}}func (p *PortCollector) Updata() {// Do nothing here as the value is generated in the Collect() function}func newMetrics(p []string, s map[string]map[string]string, u string) *portMetrics {host, _ := host.Info()hostname := host.Hostnamevar detailList, memsList portMetricsfor _, v := range p {// fmt.Println(k, v)detailList = append(detailList, struct {desc  *prometheus.Descvalue map[string]string}{desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, v, endetail),"Process-related information of port "+v,[]string{"cmdroot", "cmdline", "process_name", "status", "ppid", "ownuser", "owngroup"}, // 设置动态labels,collect函数里传来的就是这个变量的值prometheus.Labels{"host_name": hostname}),                                               // 设置静态labelsvalue: s[v],})memsList = append(memsList, struct {desc  *prometheus.Descvalue map[string]string}{desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, v, endmems),"Process memory usage information of port "+v,[]string{"process_name", "pid", "vmhwm", "vmrss"}, // 设置动态labels,collect函数里传来的就是这个变量的值prometheus.Labels{"host_name": hostname}),         // 设置静态labelsvalue: s[v],})}if u == "detail" {return &detailList} else {return &memsList}}// NewPortCollector 创建port收集器,返回指标信息func NewPortCollector(p []string, m string) *PortCollector {final := GetProcessInfo(p, m)// fmt.Printf("test_fanal:%#v", len(final))if len(final) == 0 {isexist = 0} else {isexist = 1}return &PortCollector{ProcessDetail: *newMetrics(p, final, "detail"),ProcessMems:   *newMetrics(p, final, "mems"),}}

到此,关于“如何使用go自定义prometheus的exporter”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何使用go自定义prometheus的exporter

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何使用go自定义prometheus的exporter

这篇文章主要介绍“如何使用go自定义prometheus的exporter”,在日常操作中,相信很多人在如何使用go自定义prometheus的exporter问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如
2023-07-05

如何自定义Prometheus监控指标

本篇内容介绍了“如何自定义Prometheus监控指标”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目前大部分使用Spring Boot构
2023-06-17

如何使用Prometheus针对自己的服务器采集自定义的参数

如何使用Prometheus针对自己的服务器采集自定义的参数,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。用一个简单的例子来说明。我用express和http搭了一个最简单的服
2023-06-04

如何在Prometheus中创建自定义指标

要在Prometheus中创建自定义指标,您需要遵循以下步骤:创建一个用于暴露指标的应用程序或服务。您可以使用Prometheus客户端库来帮助您在应用程序中实现指标暴露的功能。在您的应用程序中定义您想要暴露的自定义指标,并在适当的地方将其
如何在Prometheus中创建自定义指标
2024-03-04

如何在Prometheus系统中创建自定义指标

要在Prometheus系统中创建自定义指标,您需要执行以下步骤:创建一个新的指标定义文件,通常使用PromQL语言编写。您可以使用文本编辑器创建一个以.prom为后缀的文件,该文件包含新指标的定义和计算规则。将指标定义文件保存到Prome
如何在Prometheus系统中创建自定义指标
2024-03-04

如何为Prometheus添加自定义告警通知渠道

要为Prometheus添加自定义告警通知渠道,可以按照以下步骤操作:创建一个新的告警接收器:首先,需要在Prometheus配置文件中添加一个新的告警接收器,可以参考官方文档中的示例进行配置。编写通知脚本:根据自定义的告警通知需求,编写一
如何为Prometheus添加自定义告警通知渠道
2024-03-04

如何使用SpringBoot自定义starter

这篇文章主要介绍了如何使用SpringBoot自定义starter,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。springboot是什么springboot一种全新的编程规
2023-06-14

Android自定义组件:2、如何实现和使用自定义组件、自定义属性

声明:本教程不收取任何费用,欢迎转载,尊重作者劳动成果,不得用于商业用途,侵权必究!!! 目录 一、前言 二、如何实现自定义组件 步骤1:写 attrs.xml 资源文件 1、attrs.xml 文件 和 R 文件对应关系 2、attrs.
2022-06-06

自定义资源CRD如何使用

今天小编给大家分享一下自定义资源CRD如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。介绍Custom Resourc
2023-06-30

Java接口如何自定义使用

本篇内容主要讲解“Java接口如何自定义使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java接口如何自定义使用”吧!一、引入一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方
2023-06-30

如何使用vant自定义弹框

本文小编为大家详细介绍“如何使用vant自定义弹框”,内容详细,步骤清晰,细节处理妥当,希望这篇“如何使用vant自定义弹框”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。vant 自定义弹框使用vant制作弹框,
2023-06-30

vue中的自定义指令如何使用

这篇文章主要介绍“vue中的自定义指令如何使用”,在日常操作中,相信很多人在vue中的自定义指令如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue中的自定义指令如何使用”的疑惑有所帮助!接下来,请跟
2023-06-29

如何自定义Go Json的序列化方法

这篇文章主要讲解了“如何自定义Go Json的序列化方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何自定义Go Json的序列化方法”吧!我们知道,通过tag,可以有条件地实现定制Go
2023-07-02

Flex中如何使用自定义事件

Flex中如何使用自定义事件,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Flex自定义事件事件是一个非常有用的功能,通常用于信息传递交互大大提高程序编写的灵活性。在高级语言中
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录