TL;DR
コメントいただいた方法でアップデートしました!
とりあえずこうしてみたけど、もっといいやり方ありそうなので、有識者な方教えてください。
f := *(*float64)(unsafe.Pointer(&out[0]))
モチベーションと背景
Prometheusを使ったサーバリソースモニタの環境整備を進めており、既存exporterでカバーできないMetricsが欲しくなったのでexporterを自作してみることにしました。
golang自体がまったく初めてで、先週末に初めてhello worldしてみたところです。
exporterを作成するにあたりいくつかの言語でフォーマットやライブラリがありましたが、どうせならgolangでやってみようと思い入門し、公式のチュートリアルを一通りやってから、参考にした記事を見つつexporterを作成してみました。
成果物は コチラ
Metrics取得にbashコマンドを利用
golangでの低レイヤの情報取得は別の機会に学習しようと思い、LinuxサーバのMetrics取得についてはbashのコマンドを使いました。
参考記事:
Golangで外部コマンドを実行する方法まとめ
Goで外部コマンドをパイプして実行する
golang で UNIX コマンドパイプラインを扱う(Big Sky)
最終的には、mattnさんが公開されている go-pipeline を使って、UNIXコマンドのパイプ処理を書きました。
go-pipeline の戻り値が []byte
bashコマンドを実行できたのはよいものの、その戻り値が []byte(uint8) で返ってくることがわかりました。
exporterの出力には、数値をfloat64で渡さないといけないらしく、[]byteからfloat64へのconvertが必要になりました。
[]byte から float64 への convert 方法
とりあえずざざざっとググってみると、
Convert byte array “[]uint8” to float64 in GoLang(StackOverFlow)
Convert a byte array to float64(StackOverFlow)
というStackOverFlowの記事があり、どうやらなんかライブラリとか関数一発でできなくて、バイナリ列をどうにかこうにかしてる模様、、ええあまりしたくないなぁ。
ライブラリでいうと、 strconv を使うのが近そうというのがわかりました。
試行錯誤した結果、[]byte を一度stringにして、その際にできてしまう末尾の \n をchompして、得られたstringをstrconvでfloat64にParseすることで、対応できました。が、ほんとにこれだけのためにこんないろいろしなきゃいけないの?というのはちょっと懐疑的。。
package main
import (
// "fmt"
"flag"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"log"
"net/http"
"github.com/mattn/go-pipeline" // go-pipeline : https://github.com/mattn/go-pipeline
"strconv"
"strings"
"unsafe"
)
~~ 中略 ~~
func get443Estab() float64 {
out, err := pipeline.Output(
[]string{"netstat", "-n"},
[]string{"grep", "ESTABLISHED"},
[]string{"grep", ":443"},
[]string{"wc", "-l"},
)
if err != nil {
// PIPESTATUSの中に一つでも0以外が含まれると err : exit status ${status} になるので注意
return float64(0)
}
f, err := strconv.ParseFloat(strings.TrimRight(*(*string)(unsafe.Pointer(&out)), "\n"), 64)
if err != nil {
return float64(0)
}
return float64(f)
}
~~ 略 ~~
もっといいやり方あったら、教えてください m(_ _)m
versionなど
# go version
go version go1.10.4 linux/amd64