日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

的使用go_使用 Go 开发 Prometheus Exporter

發布時間:2024/8/23 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 的使用go_使用 Go 开发 Prometheus Exporter 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Exporter 是 Prometheus 監控的核心,如果你遇到一些應用不存在相應的 Exporter,那么我們可以自己去編寫 Exporter。下面我們簡單介紹如何使用 Golang 來快速編寫一個 Exporter。

1. 安裝 GO 和依賴包

按照 https://golang.org/doc/install 上的步驟進行安裝配置 GO 環境,創建一個名為 my_first_exporter 的文件夾。

$?go?mod?init?my_first_exporter?
$?go?get?github.com/prometheus/client_golang?
$?go?get?github.com/joho/godotenv
-->?creates?go.mod?file
-->?Installs?dependency?into?the?go.mod?file

2. 創建入口點和導入依賴包

package?main

import?(
?"github.com/joho/godotenv"
?"github.com/prometheus/client_golang/prometheus"
?"github.com/prometheus/client_golang/prometheus/promhttp"
)

3. 創建 main() 函數

func?main()

4. 添加 prometheus metrics 端點,并在某個服務端口上監聽

func?main()?{
???http.Handle("/metrics",?promhttp.Handler())
???log.Fatal(http.ListenAndServe(":9101",?nil))
}

5. 使用 curl 請求外部服務接口

比如我們這里監控的應用程序是 MirthConnect,所以我需要進行兩個 API 接口調用:

  • 獲取 channel 統計數據
  • 獲取 channel id 和名稱映射
curl?-k?--location?--request?GET?'https://apihost/api/channels/statistics'?\
--user?admin:admin

curl?-k?--location?--request?GET?'https://apihost/api/channels/idsAndNames'?\
--user?admin:admin

6. 將 curl 調用轉換為 go http 調用,并解析結果

如果你是 Go 新手,這應該是最困難的一步。對于我這里的例子,端點返回的是 XML 格式的數據,所以我必須用 "encoding/xml" 包來反序列化 XML。轉換成功后意味著我的 GO 程序可以執行和 curl 命令一樣的 API 調用。

7. 聲明 Prometheus metrics

在 Prometheus 中,每個 metric 指標都由以下幾個部分組成:metric name/metric label values/metric help text/metric type/measurement ,例如:

Example:
#?HELP?promhttp_metric_handler_requests_total?Total?number?of?scrapes?by?HTTP?status?code.
#?TYPE?promhttp_metric_handler_requests_total?counter
promhttp_metric_handler_requests_total{code=”200"}?1.829159e+06
promhttp_metric_handler_requests_total{code=”500"}?0
promhttp_metric_handler_requests_total{code=”503"}?0

對于應用 scrapers,我們將定義 Prometheus metrics 描述信息,其中包括 metric 名稱、metric label 標簽以及 metric 幫助信息。

messagesReceived?=?prometheus.NewDesc(
?prometheus.BuildFQName(namespace,?"",?"messages_received_total"),
?"How?many?messages?have?been?received?(per?channel).",
?[]string{"channel"},?nil,
)

8. 定義一個結構體實現 Prometheus 的 Collector 接口

Prometheus 的 client 庫提供了實現自定義 Exportor 的接口,Collector 接口定義了兩個方法 Describe 和 Collect,實現這兩個方法就可以暴露自定義的數據:

  • Describe(chan
  • Collect(chan

如下所示:

type?Exporter?struct?{
?mirthEndpoint,?mirthUsername,?mirthPassword?string
}

func?NewExporter(mirthEndpoint?string,?mirthUsername?string,?mirthPassword?string)?*Exporter?{
?return?&Exporter{
??mirthEndpoint:?mirthEndpoint,
??mirthUsername:?mirthUsername,
??mirthPassword:?mirthPassword,
?}
}
func?(e?*Exporter)?Describe(ch?chan?{
}func?(e?*Exporter)?Collect(ch?chan?{
}

9. 在 Describe 函數中,把第7步的 metric 描述信息發送給它

func?(e?*Exporter)?Describe(ch?chan?{
?ch??ch??ch??ch??ch??ch?}

10. 將接口調用邏輯從第6步移到 Collect 函數中

直接將采集的數據發送到 prometheus.Metric 通道中。

func?(e?*Exporter)?Collect(ch?chan?{
?channelIdNameMap,?err?:=?e.LoadChannelIdNameMap()if?err?!=?nil?{
??ch????up,?prometheus.GaugeValue,?0,
??)
??log.Println(err)return
?}
?ch???up,?prometheus.GaugeValue,?1,
?)
?e.HitMirthRestApisAndUpdateMetrics(channelIdNameMap,?ch)
}

當執行 api 調用時,確保使用prometheus.MustNewConstMetric(prometheus.Desc, metric type, measurement)發送測量值,如果你需要傳入額外的標簽,可以像下面這樣在參數列表的后面加入:

channelError,?_?:=?strconv.ParseFloat(channelStatsList.Channels[i].Error,?64)
ch??messagesErrored,?prometheus.GaugeValue,?channelError,?channelName,
)

11. 在 main 函數中聲明 exporter

exporter?:=?NewExporter(mirthEndpoint,?mirthUsername,?mirthPassword)
prometheus.MustRegister(exporter)

到這里其實這個 Exporter 就可以使用了,每次訪問 metrics 路由的時候,它會執行 api 調用,并以 Prometheus Text 文本格式返回數據。下面的步驟主要是方便部署了。

12. 將硬編碼的 api 路徑放到 flag 中

前面我們硬編碼了好多參數,比如應用程序的網址、metrics 路由地址以及 exporter 端口,我們可以通過從命令行參數中來解析這些值使程序更加靈活。

var?(
listenAddress?=?flag.String("web.listen-address",?":9141",
?"Address?to?listen?on?for?telemetry")
metricsPath?=?flag.String("web.telemetry-path",?"/metrics",
?"Path?under?which?to?expose?metrics")
)
func?main()?{
???flag.Parse()
???...
???http.Handle(*metricsPath,?promhttp.Handler())
???log.Fatal(http.ListenAndServe(*listenAddress,?nil))
}

13. 將憑證放入環境變量

如果應用端點改變了或者登錄憑證改變了怎么辦?我們可以從環境變量中來加載這些數據,在這個例子中,我們使用 godotenv 這個包來幫助將變量值存儲在本地的一個目錄中:

import?(
??"os"
)
func?main()?{
?err?:=?godotenv.Load()
?if?err?!=?nil?{
??log.Println("Error?loading?.env?file,?assume?env?variables?are?set.")
?}
?mirthEndpoint?:=?os.Getenv("MIRTH_ENDPOINT")
?mirthUsername?:=?os.Getenv("MIRTH_USERNAME")
?mirthPassword?:=?os.Getenv("MIRTH_PASSWORD")
}

整個 Exporter 完整的代碼如下所示:

package?main

import?(
?"crypto/tls"
?"encoding/xml"
?"flag"
?"io/ioutil"
?"log"
?"net/http"
?"os"
?"strconv"

?"github.com/joho/godotenv"
?"github.com/prometheus/client_golang/prometheus"
?"github.com/prometheus/client_golang/prometheus/promhttp"
)

/*101af57f-f26c-40d3-86a3-309e74b93512Send-Email-Notification
*/
type?ChannelIdNameMap?struct?{
?XMLName?xml.Name???????`xml:"map"`
?Entries?[]ChannelEntry?`xml:"entry"`
}
type?ChannelEntry?struct?{
?XMLName?xml.Name?`xml:"entry"`
?Values??[]string?`xml:"string"`
}

/*c5e6a736-0e88-46a7-bf32-5b4908c4d859101af57f-f26c-40d3-86a3-309e74b9351200000
*/
type?ChannelStatsList?struct?{
?XMLName??xml.Name???????`xml:"list"`
?Channels?[]ChannelStats?`xml:"channelStatistics"`
}
type?ChannelStats?struct?{
?XMLName???xml.Name?`xml:"channelStatistics"`
?ServerId??string???`xml:"serverId"`
?ChannelId?string???`xml:"channelId"`
?Received??string???`xml:"received"`
?Sent??????string???`xml:"sent"`
?Error?????string???`xml:"error"`
?Filtered??string???`xml:"filtered"`
?Queued????string???`xml:"queued"`
}

const?namespace?=?"mirth"
const?channelIdNameApi?=?"/api/channels/idsAndNames"
const?channelStatsApi?=?"/api/channels/statistics"

var?(
?tr?=?&http.Transport{
??TLSClientConfig:?&tls.Config{InsecureSkipVerify:?true},
?}
?client?=?&http.Client{Transport:?tr}

?listenAddress?=?flag.String("web.listen-address",?":9141",
??"Address?to?listen?on?for?telemetry")
?metricsPath?=?flag.String("web.telemetry-path",?"/metrics",
??"Path?under?which?to?expose?metrics")

?//?Metrics
?up?=?prometheus.NewDesc(
??prometheus.BuildFQName(namespace,?"",?"up"),
??"Was?the?last?Mirth?query?successful.",
??nil,?nil,
?)
?messagesReceived?=?prometheus.NewDesc(
??prometheus.BuildFQName(namespace,?"",?"messages_received_total"),
??"How?many?messages?have?been?received?(per?channel).",
??[]string{"channel"},?nil,
?)
?messagesFiltered?=?prometheus.NewDesc(
??prometheus.BuildFQName(namespace,?"",?"messages_filtered_total"),
??"How?many?messages?have?been?filtered?(per?channel).",
??[]string{"channel"},?nil,
?)
?messagesQueued?=?prometheus.NewDesc(
??prometheus.BuildFQName(namespace,?"",?"messages_queued"),
??"How?many?messages?are?currently?queued?(per?channel).",
??[]string{"channel"},?nil,
?)
?messagesSent?=?prometheus.NewDesc(
??prometheus.BuildFQName(namespace,?"",?"messages_sent_total"),
??"How?many?messages?have?been?sent?(per?channel).",
??[]string{"channel"},?nil,
?)
?messagesErrored?=?prometheus.NewDesc(
??prometheus.BuildFQName(namespace,?"",?"messages_errored_total"),
??"How?many?messages?have?errored?(per?channel).",
??[]string{"channel"},?nil,
?)
)

type?Exporter?struct?{
?mirthEndpoint,?mirthUsername,?mirthPassword?string
}

func?NewExporter(mirthEndpoint?string,?mirthUsername?string,?mirthPassword?string)?*Exporter?{
?return?&Exporter{
??mirthEndpoint:?mirthEndpoint,
??mirthUsername:?mirthUsername,
??mirthPassword:?mirthPassword,
?}
}

func?(e?*Exporter)?Describe(ch?chan?{
?ch??ch??ch??ch??ch??ch?}func?(e?*Exporter)?Collect(ch?chan?{
?channelIdNameMap,?err?:=?e.LoadChannelIdNameMap()if?err?!=?nil?{
??ch????up,?prometheus.GaugeValue,?0,
??)
??log.Println(err)return
?}
?ch???up,?prometheus.GaugeValue,?1,
?)
?e.HitMirthRestApisAndUpdateMetrics(channelIdNameMap,?ch)
}func?(e?*Exporter)?LoadChannelIdNameMap()?(map[string]string,?error)?{//?Create?the?map?of?channel?id?to?names
?channelIdNameMap?:=?make(map[string]string)
?req,?err?:=?http.NewRequest("GET",?e.mirthEndpoint+channelIdNameApi,?nil)if?err?!=?nil?{return?nil,?err
?}//?This?one?line?implements?the?authentication?required?for?the?task.
?req.SetBasicAuth(e.mirthUsername,?e.mirthPassword)//?Make?request?and?show?output.
?resp,?err?:=?client.Do(req)if?err?!=?nil?{return?nil,?err
?}
?body,?err?:=?ioutil.ReadAll(resp.Body)
?resp.Body.Close()if?err?!=?nil?{return?nil,?err
?}//?fmt.Println(string(body))//?we?initialize?our?arrayvar?channelIdNameMapXML?ChannelIdNameMap//?we?unmarshal?our?byteArray?which?contains?our//?xmlFiles?content?into?'users'?which?we?defined?above
?err?=?xml.Unmarshal(body,?&channelIdNameMapXML)if?err?!=?nil?{return?nil,?err
?}for?i?:=?0;?i?len(channelIdNameMapXML.Entries);?i++?{
??channelIdNameMap[channelIdNameMapXML.Entries[i].Values[0]]?=?channelIdNameMapXML.Entries[i].Values[1]
?}return?channelIdNameMap,?nil
}func?(e?*Exporter)?HitMirthRestApisAndUpdateMetrics(channelIdNameMap?map[string]string,?ch?chan?{//?Load?channel?stats
?req,?err?:=?http.NewRequest("GET",?e.mirthEndpoint+channelStatsApi,?nil)if?err?!=?nil?{
??log.Fatal(err)
?}//?This?one?line?implements?the?authentication?required?for?the?task.
?req.SetBasicAuth(e.mirthUsername,?e.mirthPassword)//?Make?request?and?show?output.
?resp,?err?:=?client.Do(req)if?err?!=?nil?{
??log.Fatal(err)
?}
?body,?err?:=?ioutil.ReadAll(resp.Body)
?resp.Body.Close()if?err?!=?nil?{
??log.Fatal(err)
?}//?fmt.Println(string(body))//?we?initialize?our?arrayvar?channelStatsList?ChannelStatsList//?we?unmarshal?our?byteArray?which?contains?our//?xmlFiles?content?into?'users'?which?we?defined?above
?err?=?xml.Unmarshal(body,?&channelStatsList)if?err?!=?nil?{
??log.Fatal(err)
?}for?i?:=?0;?i?len(channelStatsList.Channels);?i++?{
??channelName?:=?channelIdNameMap[channelStatsList.Channels[i].ChannelId]
??channelReceived,?_?:=?strconv.ParseFloat(channelStatsList.Channels[i].Received,?64)
??ch????messagesReceived,?prometheus.GaugeValue,?channelReceived,?channelName,
??)
??channelSent,?_?:=?strconv.ParseFloat(channelStatsList.Channels[i].Sent,?64)
??ch????messagesSent,?prometheus.GaugeValue,?channelSent,?channelName,
??)
??channelError,?_?:=?strconv.ParseFloat(channelStatsList.Channels[i].Error,?64)
??ch????messagesErrored,?prometheus.GaugeValue,?channelError,?channelName,
??)
??channelFiltered,?_?:=?strconv.ParseFloat(channelStatsList.Channels[i].Filtered,?64)
??ch????messagesFiltered,?prometheus.GaugeValue,?channelFiltered,?channelName,
??)
??channelQueued,?_?:=?strconv.ParseFloat(channelStatsList.Channels[i].Queued,?64)
??ch????messagesQueued,?prometheus.GaugeValue,?channelQueued,?channelName,
??)
?}
?log.Println("Endpoint?scraped")
}func?main()?{
?err?:=?godotenv.Load()if?err?!=?nil?{
??log.Println("Error?loading?.env?file,?assume?env?variables?are?set.")
?}
?flag.Parse()
?mirthEndpoint?:=?os.Getenv("MIRTH_ENDPOINT")
?mirthUsername?:=?os.Getenv("MIRTH_USERNAME")
?mirthPassword?:=?os.Getenv("MIRTH_PASSWORD")
?exporter?:=?NewExporter(mirthEndpoint,?mirthUsername,?mirthPassword)
?prometheus.MustRegister(exporter)
?http.Handle(*metricsPath,?promhttp.Handler())
?http.HandleFunc("/",?func(w?http.ResponseWriter,?r?*http.Request)?{
??w.Write([]byte(`Mirth?Channel?Exporter

Mirth?Channel?Exporter

?+?*metricsPath?+?`'>Metrics


?????????????`))
?})
?log.Fatal(http.ListenAndServe(*listenAddress,?nil))
}

14. 編寫一個 Makefile 文件,方便在不同平臺上快速構建

Makefile 可以讓你在開發過程中省去很多多余的操作,比如我們要構建多個平臺的構建程序,可以創建如下所示的 Makefile 文件。

linux:
???GOOS=linux?GOARCH=amd64?go?build
mac:
???GOOS=darwin?GOARCH=amd64?go?build

只要調用 make mac 或 make linux 命令就可以看到不同的可執行文件出現。

15. 編寫一個 service 文件,將這個 go 應用作為守護進程運行

我們可以為這個 Exporter 編寫一個 service 文件或者 Dockerfile 文件來管理該應用,比如這里我們直接在 Centos 7 上用 systemd 來管理該應用。這可以編寫一個如下所示的 service 文件:

[Unit]
Description=mirth?channel?exporter
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
WorkingDirectory=/mirth/mirthconnect
EnvironmentFile=/etc/sysconfig/mirth_channel_exporter
ExecStart=/mirth/mirthconnect/mirth_channel_exporter

[Install]
WantedBy=multi-user.target

到這里就完成了使用 Golang 編寫一個簡單的 Prometheus Exporter。

原文鏈接:https://medium.com/teamzerolabs/15-steps-to-write-an-application-prometheus-exporter-in-go-9746b4520e26

”?點擊屏末?|??|?即刻學習

總結

以上是生活随笔為你收集整理的的使用go_使用 Go 开发 Prometheus Exporter的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。