前言
作为一名年迈的JavaBoy,我一直认为自己是一名后端工程师,语言只是工具这句话不仅仅是骗面试官的,有时候也得骗骗自己,毕竟在做一些小工具上用python、go之类的更高效。
最近自己的组长高升了,负责了一部分的运维部门的管理,都说新官上任三把火,第一把就连带着把我也烧了,领导觉得grafana绘制的监控花里胡哨华而不实,没办法直观的看到服务状况,运维的leader也是个不为五斗米折腰,愿为一锭金下跪的二波一,交付侧的同学吐槽了这么久也不改,大leader一说要改监控,接着就出排期,还要把每个服务的每分钟的接口平均耗时和每个服务的每分钟的接口请求总数都加上。
好好好,安排我跟运维小哥对一下具体方案,那我就看看怎么个事。
python
整体的逻辑是服务侧的服务都严格按照日志格式输出日志文件,只需要起一个性能采集服务去切对应的日志路径分析即可,我们把接口暴露给prometheus,prometheus按每分钟的频率去调用性能采集服务的接口,接口每次扫描当前时间向后推1分钟时间内日志内容并聚合数据返回。
本来我只是打个辅助,运维小哥出方案我实现,小哥上来就信心满满的给我说处理成类似这样的返回值就行了。
我一想那不简单了,python + flask接口直接text返回几分钟完事啊,结果搞完之后果然不行。prometheus采集不到,我问小哥你自己试过吗,结果小哥来了句。
好家伙,看不起谁呢,虽然我是一名年迈的Java练习生,虽然偶尔敲敲Python,但是Go这一块鄙人也是略懂一二,还轮不到运维指指点点好吧。
GO
用go的话一是可以有现成的prometheus包可以直接用api,二是可以直接打包成可执行文件,部署起来不需要在环境里安装多余的依赖,正当我在重温golang从入门到入土的时候,运维小哥直接来将我军。
敲里瓦,谁是你好兄弟,md最烦最后那个表情,你领导又不是我领导。。。。
口嗨归口嗨,该写的代码一行不能少。这篇文章完全是作为一个Go新手去写的,能实现功能就好了,有一些不优雅的地方还请各路大佬体谅老弟,也欢迎指正。
灵活配置日志路径
首先需要灵活配置读取日志路径,按SpringBoot那一套IDEA自动创建出yml直接写里完事,GoLand可没有,要么说golang轻量级呢,是真轻,需要在go.mod里单独引入gopkg.in/yaml.v3 v3.0.1 // indirect
依赖读取文件识别。Go中的struct可以简单理解为Java中的class,结构体变量首字母记得大写,我这种半道出家的写惯了小驼峰有次调试一直访问不到里面的变量被坑惨了。
// 定义一个配置类的结构体
type Config struct {
Collect struct {
Prometheus string `yaml:"prometheus"`
LogPath string `yaml:"log_path"`
Version int `yaml:"version"`
} `yaml:"collect"`
}
func loadConfig() Config {
// 读取 YAML 文件内容
yamlFile, err := ioutil.ReadFile("config.yaml")
if err != nil {
log.Fatalf("无法读取文件:%v", err)
}
// 解析 YAML
var config Config
err = yaml.Unmarshal(yamlFile, &config)
if err != nil {
log.Fatalf("无法解析 YAML:%v", err)
}
return config
}
上报prometheus
这样我们可以把地址配在yml里调用loadConfig方法即可获取配置信息。接下来开始注册prometheus接口,最终暴露一个2112端口。
func main() {
globalConfig := loadConfig()
// path请求数量统计
pathReqDuration := prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "path_request_count",
Help: "path请求数量统计",
}, []string{"server_name"})
err := prometheus.Register(pathReqDuration)
if err != nil {
log.Fatal("Failed to register metric:", err)
}
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
serverName := r.URL.Query().Get("server_name")
if serverName == "" {
http.Error(w, "server_name parameter is missing", http.StatusBadRequest)
return
}
// 扫描日志处理,返回具体数值
dateCountsTotal, _ := grepLog(globalConfig, serverName)
// 每次请求重置统计
pathReqDuration.Reset()
pathReqDuration.DeleteLabelValues("")
promhttp.Handler().ServeHTTP(w, r)
})
err = http.ListenAndServe(":2112", nil)
if err != nil {
log.Fatal(err)
}
}
这样就实现好了一个请求数量统计的收集,请求数的统计同理。Golang打包也十分方便,直接运行命令即可打包成可执行文件,不过静态资源是不会被打进包里的。
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o log-collector main.go
最终实现的效果为:
这样就可以直观的观察到某一分钟服务是否有请求进来。