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

news2024/11/14 11:03:56

文章目录

    • 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 metrics

import (
	"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.ProcessList
	var elements []models.ProcessInfo
	for 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,RES
	elements = 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 := 1
	for 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    COMMAND
	processRegex := 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 main

import (
	"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.Registry
	go 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.coloradmin.cn/o/2236479.html

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

相关文章

Docker网络概述

1. Docker 网络概述 1.1 网络组件 Docker网络的核心组件包括网络驱动程序、网络、容器以及IP地址管理&#xff08;IPAM&#xff09;。这些组件共同工作&#xff0c;为容器提供网络连接和通信能力。 网络驱动程序&#xff1a;Docker支持多种网络驱动程序&#xff0c;每种驱动程…

新160个crackme - 094-TheBigMan-crackme6

运行分析 需破解Name和Serial PE分析 LCC win32程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida搜索字符串&#xff0c;进入关键函数 ida动调&#xff0c;发现关键判断函数func_1 进入后&#xff0c;发现Name长度需满足一定要求&#xff0c;且func_2返回值不能…

大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【Django】视图函数

【Django】视图函数 视图函数的本质是Python中的函数&#xff0c;视图函数负责处理用户的请求并返回响应&#xff0c;该响应可以是网页的HTML内容、重定向、404错误、XML文档、图像或者任何东西&#xff0c;一般在应用中的views.py编写&#xff0c;示例代码如下&#xff1a; …

kafka+zookeeper的搭建

kafka从2.8版本开始&#xff0c;就可以不用配置zookeeper了&#xff0c;但是也可以继续配置。我目前使用的kafka版本是kafka_2.12-3.0.0.tgz&#xff0c;其中前面的2.12表示是使用该版本的scala语言进行编写的&#xff0c;而后面的3.00才是kafka当前的版本。 通过百度网盘分享…

了解bootstrap改造asp.net core MVC的样式模板

我们都知道&#xff0c;在使用默认的asp.net core MVC模板建立项目的时候&#xff0c;里面的样式是已经事先被写好了的。一般来说都在css目录下的site.css和bootstrap.css及下面的bootstrap.min.css中。我们打开bootstrap这些样式文件&#xff0c;里面有大量的样式类的定义&…

HTMLCSS:旋转的动态卡片

效果演示 这段代码创建了一个具有动态背景和渐变效果的卡片。卡片背景有一个无限循环的旋转动画&#xff0c;增加了视觉吸引力。这种效果可以用于展示个人信息、项目介绍或其他需要吸引用户注意的内容。 HTML <div class"card"><h3>前端Hardy</h3&…

深入Pillow:处理图像下载中的意外挑战

在当今数字化时代&#xff0c;获取和处理图像数据已经成为了许多应用程序的核心功能。从社交媒体到电子商务&#xff0c;图像的获取和处理对于用户体验至关重要。下载图片不仅能够丰富我们的内容&#xff0c;还能够通过分析图像数据为我们的应用提供更多价值。然而&#xff0c;…

管理 Elasticsearch 变得更容易了,非常容易!

作者&#xff1a;来自 Elastic Ken Exner Elasticsearch 用户&#xff0c;我们听到了你的心声。管理 Elasticsearch 有时会变得很复杂&#xff0c;面临的挑战包括性能调整、问题检测和资源优化。我们一直致力于简化你的体验。今天&#xff0c;我们宣布了自收购 Opster 以来的一…

Linux挖矿病毒(kswapd0进程使cpu爆满)

一、摘要 事情起因:有台测试服务器很久没用了&#xff0c;突然监控到CPU飙到了95以上&#xff0c;并且阿里云服务器厂商还发送了通知消息&#xff0c;【阿里云】尊敬的xxh: 经检测您的阿里云服务&#xff08;ECS实例&#xff09;i-xxx存在挖矿活动。因此很明确服务器中挖矿病毒…

【go从零单排】迭代器(Iterators)

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;迭代器的实现通常不是通过语言内置的迭代器类型&#x…

(混乱版)数据冒险-ld,sub和and

第一张图没有数据转发 从这张图来看&#xff0c;如果没有数据转发机制&#xff0c;流水线的执行会出现更多的停顿。这种情况下&#xff0c;数据依赖只能通过**插入停顿周期&#xff08;stalls&#xff09;**来解决。具体分析如下&#xff1a; 指令序列 ld r1, 0(r2)&#xf…

成都睿明智科技有限公司抖音电商服务效果如何?

在这个短视频风起云涌的时代&#xff0c;抖音电商以其独特的魅力&#xff0c;成为了众多商家竞相追逐的新蓝海。而在这片波澜壮阔的商海中&#xff0c;成都睿明智科技有限公司犹如一艘稳健的航船&#xff0c;引领着无数企业驶向成功的彼岸。今天&#xff0c;就让我们一起揭开成…

ssm071北京集联软件科技有限公司信息管理系统+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;北京集联软件科技有限公司信息管理系统 \ 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本信息…

shodan[3](泷羽sec)

声明 学习视频来自B站UP主 泷羽sec,如涉及侵泷羽sec权马上删除文章。 笔记只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 这节课旨在扩大自己在网络安全方面的知识面&#xff0c;了解网络安全领域的见闻&#xff0c;了…

【OD-支持在线评测】数字涂色(100分)

📎 在线评测链接 https://app5938.acapp.acwing.com.cn/contest/11/problem/OD1081 🍓 OJ题目截图 🍿 最新机试E卷,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 94 分 🌍 评测功能需要 ⇒ 订阅专栏 ⇐ 后私信联系解锁~ 文章目录 📎…

k8s 上如何跑 Dolphins 模型

接着上一篇的介绍&#xff0c;这一篇就来跑跑 Dolphins 模型&#xff0c;本篇会记录&#xff0c;跑模型常见的阬点。 1 在 k8s 上创建 pod 将外部数据挂载在 pod 里&#xff0c;并申请 gpu 资源。同时修改代码里对应的引入数据的路径 # dolphins.yaml apiVersion: v1 kind: …

如何避免数据倾斜

1、数据倾斜的表现 数据倾斜是由于数据分布不均匀&#xff0c;造成数据大量的集中到一点&#xff0c;造成数据热点的现象。 主要表现&#xff1a;任务进度长时间维持在 99%或者 100%的附近&#xff0c;查看任务监控页面&#xff0c;发现只有少量 reduce 子任务未完成&#xff0…

计算机网络综合题

IP数据报的划分 CRC差错检测 冗余码的计算 因此&#xff0c;余数是1110&#xff0c;传输的数为11010110111110。在传输过程中最后两位变成o&#xff0c;接收端能够发现&#xff0c;因为11010110111110除以10011余数不为0。 子网划分 暴力求解法 &#xff08;定长子网划分大量…

O-RAN前传Spilt Option 7-2x

Spilt Option 7-2x 下行比特处理上行比特处理相关文章&#xff1a; Open Fronthaul wrt ORAN 联盟被称为下层拆分(LLS)&#xff0c;其目标是提高电信市场的灵活性和竞争力。下层拆分是指无线电单元(RU) 和分布式单元(DU) 之间的拆分。 O-RAN前传接口可以在 eCPRI 上传输。eCPR…