基于Prometheus的client_golang库实现应用的自定义可观测监控

文章目录

    • 1. 安装client_golang库
    • 2. 编写可观测监控代码
    • 3. 运行效果
    • 4. jar、graalvm、golang编译运行版本对比

前文使用java+graalvm实现原生应用可观测监控: prometheus client_java实现进程的CPU、内存、IO、流量的可观测,但是部分java依赖包使用了复杂的反射功能,Graalvm编译可能失败,无法在运行过程中获取反射类,需要手动添加反射类,比较麻烦,容易出现编译原生失败的情况。
这里介绍基于Prometheus的client_golang库实现应用可观测监控,使用Go语言实现,编译更简单。
官方教程:[https://prometheus.io/docs/guides/go-application/](Instrumenting a Go application for Prometheus)
client_golang: https://github.com/prometheus/client_golang

1. 安装client_golang库

当前最新版本v1.20.5

go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promauto
go get github.com/prometheus/client_golang/prometheus/promhttp

2. 编写可观测监控代码

实现进程的CPU、内存、IO、流量的可观测监控,部分实现代码如下:

package metricsimport ("bufio""container/list""encoding/json""errors""fmt""github.com/prometheus/client_golang/prometheus""os/exec""task-exporter/common""task-exporter/models""regexp""runtime""strconv""strings"
)type TopMetrics struct{}var Registry = prometheus.NewRegistry()var upGauge = prometheus.NewGauge(prometheus.GaugeOpts{Name: METRICS_UP_USE,Help: "help",
})var processCpuGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: METRICS_CPU_USE,Help: "help",
}, []string{LABEL_PID, LABEL_USER, LABEL_CMD, LABEL_INCLUDE})var processMemGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: METRICS_MEM_USE,Help: "help",
}, []string{LABEL_PID, LABEL_USER, LABEL_CMD, LABEL_INCLUDE})var processResGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: METRICS_RES_USE,Help: "help",
}, []string{LABEL_PID, LABEL_USER, LABEL_CMD, LABEL_INCLUDE})func init() {upGauge.Set(1)Registry.MustRegister(upGauge)Registry.MustRegister(processCpuGauge)Registry.MustRegister(processMemGauge)Registry.MustRegister(processResGauge)fmt.Println("=====init=====")
}func (top *TopMetrics) Run() {topData, _ := top.handle()processList := topData.ProcessListvar elements []models.ProcessInfofor e := processList.Front(); e != nil; e = e.Next() {elements = append(elements, e.Value.(models.ProcessInfo))}elements = top.topCpuMem(elements, "cpu")processCpuGauge.Reset()for row, v := range elements {if row < common.ConfigInfo.Limit {processCpuGauge.WithLabelValues(v.Pid, v.User, v.Cmd, v.In).Set(v.Cpu)}}// MEM,RESelements = top.topCpuMem(elements, "res")processMemGauge.Reset()processResGauge.Reset()for row, v := range elements {if row < common.ConfigInfo.Limit {processMemGauge.WithLabelValues(v.Pid, v.User, v.Cmd, v.In).Set(v.Mem)processResGauge.WithLabelValues(v.Pid, v.User, v.Cmd, v.In).Set(v.Res)}}}func (top *TopMetrics) handle() (models.TopData, error) {var topData = models.TopData{ProcessList: list.New()}// 创建一个 Command 对象,指定要执行的外部命令及其参数cmd := exec.Command("top", "-c", "-b", "-n", "1", "-w", "512")// 获取命令的标准输出stdout, err := cmd.StdoutPipe()defer stdout.Close()if err != nil {fmt.Println("Error creating StdoutPipe:", err)return topData, errors.New("Error creating StdoutPipe:")}// 启动命令if err := cmd.Start(); err != nil {fmt.Println("Error starting command:", err)return topData, errors.New("Error starting command")}// 使用 bufio.Scanner 逐行读取命令的输出scanner := bufio.NewScanner(stdout)//fmt.Println("\n=====start=====")row := 1for scanner.Scan() {line := scanner.Text()//fmt.Println(row, "======:", line)if row == 1 {//top.handleUptime(line, row, &topData)} else if row == 2 {//top.handleTask(line, row, &topData)} else if row == 3 {//top.handleCpu(line, row, &topData)} else if row == 4 {//top.handleMem(line, row, &topData)} else if row == 5 {//top.handleSwap(line, row, &topData)} else if row >= 8 {top.handleProcess(line, row, &topData)}row++}// 等待命令执行完成if err := cmd.Wait(); err != nil {fmt.Println("Error waiting for top command to finish:", err)return topData, errors.New("exec: not started")}// 检查扫描过程中是否有错误if err := scanner.Err(); err != nil {fmt.Println("Error reading output:", err)}return topData, nil
}func (top *TopMetrics) handleProcess(line string, row int, topData *models.TopData) {//                                       pid      user      PR       NI    VIRT      RES     SHR     S        %CPU        %MEM         TIME    COMMANDprocessRegex := regexp.MustCompile("(\\d+)\\s+(\\S+)\\s+\\S+\\s+\\S+\\s+\\S+\\s+(\\S+)\\s+\\S+\\s+\\S+\\s+([\\d.]+)\\s+([\\d.]+)\\s+\\S+\\s(.+)")processMatches := processRegex.FindStringSubmatch(line)if len(processMatches) == 7 {process := models.ProcessInfo{In: "n"}process.Pid = processMatches[1]process.User = processMatches[2]res := processMatches[3]if strings.HasSuffix(res, "m") {process.Res, _ = strconv.ParseFloat(strings.Trim(res, "m"), 64)process.Res = process.Res * 1024} else if strings.HasSuffix(res, "g") {process.Res, _ = strconv.ParseFloat(strings.Trim(res, "g"), 64)process.Res = process.Res * 1024 * 1024} else {process.Res, _ = strconv.ParseFloat(res, 64)}process.Cpu, _ = strconv.ParseFloat(processMatches[4], 64)process.Mem, _ = strconv.ParseFloat(processMatches[5], 64)process.Cmd = strings.TrimSpace(processMatches[6])// 检查别名process.Cmd = common.FilterAlias(process.Cmd)topData.ProcessList.PushBack(process)} else {strB, _ := json.Marshal(processMatches)fmt.Println(row, "======processRegex found:", string(strB))}
}/*** 采集进程信息CPU\MEM\RES*/
func (top *TopMetrics) topCpuMem(processes []models.ProcessInfo, ptype string) []models.ProcessInfo {len := len(processes)//fmt.Println("======topCpuMem len:", len)for i := 0; i < len-1; i++ {for r := i + 1; r < len; r++ {if ptype == "cpu" {if processes[r].Cpu > processes[i].Cpu {processes[i], processes[r] = processes[r], processes[i]}} else if ptype == "mem" {if processes[r].Mem > processes[i].Mem {processes[i], processes[r] = processes[r], processes[i]}} else if ptype == "res" {if processes[r].Res > processes[i].Res {//fmt.Println("======res change:", i, processes[i], r, processes[r])processes[i], processes[r] = processes[r], processes[i]}}}}return processes
}

main.go

package mainimport ("flag""github.com/prometheus/client_golang/prometheus/promhttp""log""net/http""task-exporter/metrics""time"
)var addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")func main() {registry := metrics.Registrygo func() {for {top := metrics.TopMetrics{}top.Run()//io := metrics.IoMetrics{}//io.Run()//net := metrics.NethogsMetrics{}//net.Run()//port := metrics.NetstatMetrics{}//port.Run()time.Sleep(15 * time.Second)}}()http.Handle("/metrics", promhttp.HandlerFor(registry,promhttp.HandlerOpts{EnableOpenMetrics: true,}),)log.Printf("Listening on http://localhost%s/metrics", *addr)log.Fatal(http.ListenAndServe(*addr, nil))
}

3. 运行效果

在这里插入图片描述
Grafana展示效果参看前文。

4. jar、graalvm、golang编译运行版本对比

jar、graalvm、golang生成文件大小对比
在这里插入图片描述
jar、graalvm、golang运行占用CPU、内存对比
在这里插入图片描述

go编译版本:task_exporter-linux-x86.zip

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/9840.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【C++课程学习】:继承(上)(详细讲解)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一.继承的概念和定义 &#x1f384;继承的概念&#xff1a; &#x1f384;继承的定义&#xff1a; …

PVE纵览-备份与快照指南

PVE纵览-备份与快照指南 文章目录 PVE纵览-备份与快照指南摘要1 备份与快照概述定义与区别备份与快照在PVE中的应用场景 2 PVE 备份功能详解备份类型与策略配置备份任务自动化备份管理 3 PVE 快照功能详解快照的工作原理快照的创建与恢复机制快照对系统性能的影响快照的使用场景…

Android JNI 技术入门指南

引言 在Android开发中&#xff0c;Java是一种主要的编程语言&#xff0c;然而&#xff0c;对于一些性能要求较高的场景&#xff08;如音视频处理、图像处理、计算密集型任务等&#xff09;&#xff0c;我们可能需要使用到C或C等语言来编写底层的高效代码。为了实现Java代码与C…

供应商srm管理,招投标管理,电子采购管理,在线询价,在线报价,供应商准入审核(java代码)

前言&#xff1a; 随着互联网和数字技术的不断发展&#xff0c;企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式&#xff0c;能够提高采购效率、降低采购成本、优化供应商合作效率&#xff0c;已成为企业实现效益提升的关键手段。系统获取在文末…

Java 函数接口Supplier【供给型接口】简介与示例

Java中四个重要的函数式接口&#xff1a;Function、Predicate、Consumer和Supplier。这些接口是函数式编程的基础&#xff0c;Function用于转换操作&#xff0c;Predicate用于进行条件判断&#xff0c;Consumer用于消费输入而不产生输出&#xff0c;而Supplier则用于提供值但不…

线程与进程的区别(面试)

一.进程 进程&#xff1a;一个程序启动起来&#xff0c;就会对应一个进程&#xff0c;进程就是系统分配资源的基本单位。 上面一部分进程是我们自己去执行应用的可执行文件, 而另一部分是操作系统自动启动的进程. 二.线程 线程&#xff1a;线程是进程中的一个执行单元&#xff…

VMware调整窗口为可以缩小但不改变显示内容的大小

也就是缩小窗口不会影响内容的大小 这样设置就好

OpenAI 发布了新的事实性基准——SimpleQA

SimpleQA 简介 名为 SimpleQA 的事实性基准&#xff0c;用于衡量语言模型回答简短的事实性问题的能力。 人工智能领域的一个悬而未决的问题是如何训练模型&#xff0c;使其产生符合事实的回答。 目前的语言模型有时会产生错误的输出或没有证据证明的答案&#xff0c;这个问题…

酒店民宿小程序,探索行业数字化管理发展

在数字化发展时代&#xff0c;各行各业都开始向数字化转型发展&#xff0c;酒店民宿作为热门行业也逐渐趋向数字、智能化发展。 对于酒店民宿来说&#xff0c;如何将酒店特色服务优势等更加快速运营推广是重中之重。酒店民宿小程序作为一款集结预约、房源管理、客户订单管理等…

[C++11] 可变参数模板

文章目录 基本语法及原理可变参数模板的基本语法参数包的两种类型可变参数模板的定义 sizeof... 运算符可变参数模板的实例化原理可变参数模板的意义 包扩展包扩展的基本概念包扩展的实现原理编译器如何展开参数包包扩展的高级应用 emplace 系列接口emplace_back 和 emplace 的…

使用Ubuntu快速部署MinIO对象存储

想拥有自己的私有云存储&#xff0c;安全可靠又高效&#xff1f;MinIO是你的理想选择&#xff01;这篇文章将手把手教你如何在Ubuntu 22.04服务器上部署MinIO&#xff0c;并使用Nginx反向代理和Let’s Encrypt证书进行安全加固。 即使你是新手&#xff0c;也能轻松完成&#xf…

贝尔不等式,路径积分与AB(Aharonov-Bohm)效应

贝尔不等式、路径积分与Aharonov-Bohm&#xff08;AB&#xff09;效应 这些概念分别源于量子力学不同的理论分支和思想实验&#xff0c;但它们都揭示了量子力学的奇异性质&#xff0c;包括非局域性、相位效应和波粒二象性。以下详细解析每一概念&#xff0c;并探讨其相互联系。…

用友U8接口-isHasCounterSignPiid错误

错误消息 调用U813的审批流方法报错&#xff0c;找不到方法:“Boolean UFIDA.U8.Audit.BusinessService.ManualAudit.isHasCounterSignPiid System.Web.Services.Protocols.SoapException:服务器无法处理请求。 ---> System.MissingMethodException: 找不到方法:“Boolean…

QJson-趟过的各种坑(先坑后用法)

QJson-趟过的各种坑【先坑后用法】 Chapter1 QJson-趟过的各种坑【先坑后用法】一、不能处理大数据量&#xff0c;如果你的数据量有百兆左右(特别是有的小伙伴还喜欢json格式化输出的)&#xff0c;不要用Qjson&#xff0c;否则会报错 DocumentTooLarge二、json格式化输出1.构建…

flink实战-- flink任务的火焰图如何使用

火焰图 Flame Graphs 是一种有效的可视化工具,可以帮助我们排查如下问题: 目前哪些方法正在消耗 CPU 资源?一个方法的消耗与其他方法相比如何?哪一系列的堆栈调用导致了特定方法的执行?y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的…

.Net Core 6.0 WebApi在Centos中部署

查看已经开发的端口的列表 firewall-cmd --zonepublic --list-ports .net core sdk密匙 sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm sudo yum update .net core sdk安装 sudo yum install -y dotnet-sdk-6.0 sudo dnf in…

Java基于SpringBoot+Vue的农产品电商平台

大家好&#xff0c;我是Java徐师兄&#xff0c;今天为大家带来的是Java基于SpringBoot 的农产品电商平台。该系统采用 Java 语言 开发&#xff0c;MySql 作为数据库&#xff0c;系统功能完善 &#xff0c;实用性强 &#xff0c;可供大学生实战项目参考使用。 博主介绍&#xff…

一文读懂系列:结合抓包分析,详解SSH协议通信原理

SSH协议通过建立加密通道来提供安全的远程访问、文件传输和执行远程命令等操作。接下来我们就通过具体示例和抓包分析&#xff0c;让大家清楚地了解SSH协议的神秘面纱&#xff01;如有更多疑问&#xff0c;欢迎讨论区留言讨论~ 1. SSH简介 SSH&#xff08;Secure Shell&#x…

数据冒险-ld和add(又称load-use冒险)

第一张图没有使用前递&#xff0c;第二张图使用前递&#xff0c;chatgpt分析第二张图 这张图展示了一个流水线的执行过程&#xff0c;其中存在读后写&#xff08;RAW&#xff09;数据冒险。我们可以通过**前递&#xff08;Forwarding&#xff09;**技术来解决这个数据冒险&…

Java 的 Scanner 类:控制台输入与文件扫描

Java 的 Scanner 类是一个非常方便的工具类&#xff0c;主要用于从控制台或文件中扫描输入数据。虽然它也可以用于扫描文件内容&#xff0c;但我们通常更喜欢它用于控制台输入&#xff0c;因为扫描文件可以通过文件流来完成。接下来&#xff0c;我们将通过几个简单的示例来讲解…