diff --git a/.gitignore b/.gitignore index 6afa7c6..cdbe891 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .idea *.iml -.vscode/ +.vscode/settings.json artifacts/ vendor/ logs/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..dd334bb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Process", + "type": "go", + "request": "launch", + "mode": "auto", + "cwd": "${workspaceFolder}", + "program": "${fileDirname}", + "args": [ + "--log.level=debug", + "--config.file=temp/network_exporter.yml", + ], + "showLog": true, + "asRoot": true, + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index b54030a..2410b93 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ This exporter gathers either ICMP, MTR, TCP Port or HTTP Get stats and exports t - `ping_rtt_seconds{type=usd}`: Standard deviation without correction in seconds - `ping_rtt_seconds{type=csd}`: Standard deviation with correction (Bessel's) in seconds - `ping_rtt_seconds{type=range}`: Range in seconds +- `ping_rtt_snt_count`: Packet sent count total +- `ping_rtt_snt_fail_count`: Packet sent fail count total +- `ping_rtt_snt_seconds`: Packet sent time total in seconds - `ping_loss_percent`: Packet loss in percent --- @@ -51,6 +54,9 @@ This exporter gathers either ICMP, MTR, TCP Port or HTTP Get stats and exports t - `mtr_rtt_seconds{type=csd}`: Standard deviation with correction (Bessel's) in seconds - `mtr_rtt_seconds{type=range}`: Range in seconds - `mtr_rtt_seconds{type=loss}`: Packet loss in percent +- `mtr_rtt_snt_count`: Packet sent count total +- `mtr_rtt_snt_fail_count`: Packet sent fail count total +- `mtr_rtt_snt_seconds`: Packet sent time total in seconds --- diff --git a/collector/collector_mtr.go b/collector/collector_mtr.go index c4c3f6d..db298e1 100644 --- a/collector/collector_mtr.go +++ b/collector/collector_mtr.go @@ -2,6 +2,7 @@ package collector import ( "strconv" + "strings" "sync" "github.com/prometheus/client_golang/prometheus" @@ -12,6 +13,9 @@ import ( var ( mtrLabelNames = []string{"name", "target", "ttl", "path"} mtrDesc = prometheus.NewDesc("mtr_rtt_seconds", "Round Trip Time in seconds", append(mtrLabelNames, "type"), nil) + mtrSntDesc = prometheus.NewDesc("mtr_rtt_snt_count", "Round Trip Send Package Total", append(mtrLabelNames, "type"), nil) + mtrSntFailDesc = prometheus.NewDesc("mtr_rtt_snt_fail_count", "Round Trip Send Package Fail Total", append(mtrLabelNames, "type"), nil) + mtrSntTimeDesc = prometheus.NewDesc("mtr_rtt_snt_seconds", "Round Trip Send Package Time Total", append(mtrLabelNames, "type"), nil) mtrHopsDesc = prometheus.NewDesc("mtr_hops", "Number of route hops", []string{"name", "target"}, nil) mtrTargetsDesc = prometheus.NewDesc("mtr_targets", "Number of active targets", nil, nil) mtrStateDesc = prometheus.NewDesc("mtr_up", "Exporter state", nil, nil) @@ -76,6 +80,18 @@ func (p *MTR) Collect(ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric(mtrDesc, prometheus.GaugeValue, hop.RangeTime.Seconds(), append(ll, "range")...) ch <- prometheus.MustNewConstMetric(mtrDesc, prometheus.GaugeValue, float64(hop.Loss), append(ll, "loss")...) } + + mtrSntDesc = prometheus.NewDesc("mtr_rtt_snt_count", "Round Trip Send Package Total", mtrLabelNames, l2) + mtrSntFailDesc = prometheus.NewDesc("mtr_rtt_snt_fail_count", "Round Trip Send Package Fail Total", mtrLabelNames, l2) + mtrSntTimeDesc = prometheus.NewDesc("mtr_rtt_snt_seconds", "Round Trip Send Package Time Total", mtrLabelNames, l2) + + for ttl, summary := range metric.HopSummaryMap { + ll := append(l, strings.Split(ttl, "_")[0]) + ll = append(ll, summary.AddressTo) + ch <- prometheus.MustNewConstMetric(mtrSntDesc, prometheus.CounterValue, float64(summary.Snt), ll...) + ch <- prometheus.MustNewConstMetric(mtrSntFailDesc, prometheus.CounterValue, float64(summary.SntFail), ll...) + ch <- prometheus.MustNewConstMetric(mtrSntTimeDesc, prometheus.CounterValue, summary.SntTime.Seconds(), ll...) + } } ch <- prometheus.MustNewConstMetric(mtrTargetsDesc, prometheus.GaugeValue, float64(len(targets))) } diff --git a/collector/collector_ping.go b/collector/collector_ping.go index bc02e83..fd877f3 100644 --- a/collector/collector_ping.go +++ b/collector/collector_ping.go @@ -10,13 +10,16 @@ import ( ) var ( - icmpLabelNames = []string{"name", "target", "target_ip"} - icmpStatusDesc = prometheus.NewDesc("ping_status", "Ping Status", icmpLabelNames, nil) - icmpRttDesc = prometheus.NewDesc("ping_rtt_seconds", "Round Trip Time in seconds", append(icmpLabelNames, "type"), nil) - icmpLossDesc = prometheus.NewDesc("ping_loss_percent", "Packet loss in percent", icmpLabelNames, nil) - icmpTargetsDesc = prometheus.NewDesc("ping_targets", "Number of active targets", nil, nil) - icmpStateDesc = prometheus.NewDesc("ping_up", "Exporter state", nil, nil) - icmpMutex = &sync.Mutex{} + icmpLabelNames = []string{"name", "target", "target_ip"} + icmpStatusDesc = prometheus.NewDesc("ping_status", "Ping Status", icmpLabelNames, nil) + icmpRttDesc = prometheus.NewDesc("ping_rtt_seconds", "Round Trip Time in seconds", append(icmpLabelNames, "type"), nil) + icmpSntSummaryDesc = prometheus.NewDesc("ping_rtt_snt_count", "Packet sent count", icmpLabelNames, nil) + icmpSntFailSummaryDesc = prometheus.NewDesc("ping_rtt_snt_fail_count", "Packet sent fail count", icmpLabelNames, nil) + icmpSntTimeSummaryDesc = prometheus.NewDesc("ping_rtt_snt_seconds", "Packet sent time total", icmpLabelNames, nil) + icmpLossDesc = prometheus.NewDesc("ping_loss_percent", "Packet loss in percent", icmpLabelNames, nil) + icmpTargetsDesc = prometheus.NewDesc("ping_targets", "Number of active targets", nil, nil) + icmpStateDesc = prometheus.NewDesc("ping_up", "Exporter state", nil, nil) + icmpMutex = &sync.Mutex{} ) // PING prom @@ -64,6 +67,9 @@ func (p *PING) Collect(ch chan<- prometheus.Metric) { icmpStatusDesc = prometheus.NewDesc("ping_status", "Ping Status", icmpLabelNames, l2) icmpRttDesc = prometheus.NewDesc("ping_rtt_seconds", "Round Trip Time in seconds", append(icmpLabelNames, "type"), l2) + icmpSntSummaryDesc = prometheus.NewDesc("ping_rtt_snt_count", "Packet sent count", icmpLabelNames, l2) + icmpSntFailSummaryDesc = prometheus.NewDesc("ping_rtt_snt_fail_count", "Packet sent fail count", icmpLabelNames, l2) + icmpSntTimeSummaryDesc = prometheus.NewDesc("ping_rtt_snt_seconds", "Packet sent time total", icmpLabelNames, l2) icmpLossDesc = prometheus.NewDesc("ping_loss_percent", "Packet loss in percent", icmpLabelNames, l2) if metric.Success { @@ -80,6 +86,9 @@ func (p *PING) Collect(ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric(icmpRttDesc, prometheus.GaugeValue, metric.UncorrectedSDTime.Seconds(), append(l, "usd")...) ch <- prometheus.MustNewConstMetric(icmpRttDesc, prometheus.GaugeValue, metric.CorrectedSDTime.Seconds(), append(l, "csd")...) ch <- prometheus.MustNewConstMetric(icmpRttDesc, prometheus.GaugeValue, metric.RangeTime.Seconds(), append(l, "range")...) + ch <- prometheus.MustNewConstMetric(icmpSntSummaryDesc, prometheus.GaugeValue, float64(metric.SntSummary), l...) + ch <- prometheus.MustNewConstMetric(icmpSntFailSummaryDesc, prometheus.GaugeValue, float64(metric.SntFailSummary), l...) + ch <- prometheus.MustNewConstMetric(icmpSntTimeSummaryDesc, prometheus.GaugeValue, metric.SntTimeSummary.Seconds(), l...) ch <- prometheus.MustNewConstMetric(icmpLossDesc, prometheus.GaugeValue, metric.DropRate, l...) } ch <- prometheus.MustNewConstMetric(icmpTargetsDesc, prometheus.GaugeValue, float64(len(targets))) diff --git a/dist/deploy/docker-compose.yml b/dist/deploy/docker-compose.yml index 88347b9..f923a99 100644 --- a/dist/deploy/docker-compose.yml +++ b/dist/deploy/docker-compose.yml @@ -41,13 +41,12 @@ services: - "-storageDataPath=/storage" - "-loggerLevel=INFO" - "-httpListenAddr=:8428" - - "-retentionPeriod=24" - - "-memory.allowedPercent=70" + - "-retentionPeriod=1y" - "-influxSkipSingleField" - "-selfScrapeInterval=30s" station1_agt: - image: victoriametrics/vmagent + image: victoriametrics/vmagent:latest container_name: station1_agt hostname: station1_agt restart: always @@ -63,11 +62,9 @@ services: - "-promscrape.config=/vmagent_prometheus.yml" - "-remoteWrite.tmpDataPath=/storage" - "-loggerLevel=INFO" - - "-memory.allowedPercent=70" - - "-insert.maxQueueDuration=360m0s" - "-remoteWrite.url=http://central_vm:8428/api/v1/write" station1_exporter: - image: syepes/network_exporter + image: syepes/network_exporter:latest container_name: station1_exporter hostname: station1_exporter restart: always diff --git a/go.mod b/go.mod index 4fcb72a..1b5f19a 100644 --- a/go.mod +++ b/go.mod @@ -6,23 +6,28 @@ require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/go-kit/log v0.2.1 github.com/golang/protobuf v1.5.3 // indirect - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.15.0 github.com/prometheus/common v0.42.0 github.com/prometheus/procfs v0.9.0 // indirect - golang.org/x/net v0.8.0 - golang.org/x/sys v0.6.0 // indirect + golang.org/x/net v0.9.0 + golang.org/x/sys v0.7.0 // indirect gopkg.in/yaml.v3 v3.0.1 ) -require github.com/alecthomas/kingpin/v2 v2.3.2 +require ( + github.com/alecthomas/kingpin/v2 v2.3.2 + github.com/felixge/fgprof v0.9.3 +) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect + github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/prometheus/client_model v0.3.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect google.golang.org/protobuf v1.30.0 // indirect ) diff --git a/go.sum b/go.sum index 5ab89e5..cf89464 100644 --- a/go.sum +++ b/go.sum @@ -6,9 +6,15 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -20,17 +26,19 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ= +github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= @@ -40,15 +48,19 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -58,5 +70,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 1637a16..37dfd91 100644 --- a/main.go +++ b/main.go @@ -2,15 +2,16 @@ package main import ( "context" + "expvar" "fmt" "net" "net/http" + "net/http/pprof" "os" "time" - _ "net/http/pprof" - "github.com/alecthomas/kingpin/v2" + "github.com/felixge/fgprof" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" @@ -24,18 +25,19 @@ import ( "github.com/syepes/network_exporter/pkg/common" ) -const version string = "1.6.8" +const version string = "1.7.0" var ( - listenAddress = kingpin.Flag("web.listen-address", "The address to listen on for HTTP requests").Default(":9427").String() - configFile = kingpin.Flag("config.file", "Exporter configuration file").Default("/app/cfg/network_exporter.yml").String() - sc = &config.SafeConfig{Cfg: &config.Config{}} - logger log.Logger - icmpID *common.IcmpID // goroutine shared counter - monitorPING *monitor.PING - monitorMTR *monitor.MTR - monitorTCP *monitor.TCPPort - monitorHTTPGet *monitor.HTTPGet + listenAddress = kingpin.Flag("web.listen-address", "The address to listen on for HTTP requests").Default(":9427").String() + configFile = kingpin.Flag("config.file", "Exporter configuration file").Default("/app/cfg/network_exporter.yml").String() + enableProfileing = kingpin.Flag("profiling", "Enable Profiling (pprof + fgprof)").Default("false").Bool() + sc = &config.SafeConfig{Cfg: &config.Config{}} + logger log.Logger + icmpID *common.IcmpID // goroutine shared counter + monitorPING *monitor.PING + monitorMTR *monitor.MTR + monitorTCP *monitor.TCPPort + monitorHTTPGet *monitor.HTTPGet indexHTML = ` Network Exporter (Version ` + version + `)

Network Exporter

Metrics

` ) @@ -108,12 +110,8 @@ func startConfigRefresh() { } func startServer() { + mux := http.NewServeMux() metricsPath := "/metrics" - level.Info(logger).Log("msg", "Starting ping exporter", "version", version) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, indexHTML, metricsPath) - }) reg := prometheus.NewRegistry() reg.MustRegister(collectors.NewGoCollector()) @@ -123,10 +121,25 @@ func startServer() { reg.MustRegister(&collector.TCP{Monitor: monitorTCP}) reg.MustRegister(&collector.HTTPGet{Monitor: monitorHTTPGet}) h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) - http.Handle(metricsPath, h) + mux.Handle(metricsPath, h) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, indexHTML, metricsPath) + }) + if *enableProfileing { + level.Info(logger).Log("msg", "Profiling enabled") + mux.Handle("/debug/vars", http.HandlerFunc(expVars)) + mux.HandleFunc("/debug/fgprof", fgprof.Handler().(http.HandlerFunc)) + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + } + + level.Info(logger).Log("msg", "Starting ping exporter", "version", version) level.Info(logger).Log("msg", fmt.Sprintf("Listening for %s on %s", metricsPath, *listenAddress)) - level.Error(logger).Log("msg", "Could not start http", "err", http.ListenAndServe(*listenAddress, nil)) + level.Error(logger).Log("msg", "Could not start http", "err", http.ListenAndServe(*listenAddress, mux)) } func getResolver() *net.Resolver { @@ -142,3 +155,17 @@ func getResolver() *net.Resolver { } return &net.Resolver{PreferGo: true, Dial: dialer} } + +func expVars(w http.ResponseWriter, r *http.Request) { + first := true + w.Header().Set("Content-Type", "application/json; charset=utf-8") + fmt.Fprintf(w, "{\n") + expvar.Do(func(kv expvar.KeyValue) { + if !first { + fmt.Fprintf(w, ",\n") + } + first = false + fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) + }) + fmt.Fprintf(w, "\n}\n") +} diff --git a/pkg/common/type.go b/pkg/common/type.go index decb333..da70297 100644 --- a/pkg/common/type.go +++ b/pkg/common/type.go @@ -36,6 +36,15 @@ type IcmpReturn struct { Elapsed time.Duration } +// IcmpSummary ICMP HOP Summary +type IcmpSummary struct { + AddressFrom string `json:"address_from"` + AddressTo string `json:"address_to"` + Snt int `json:"snt"` + SntFail int `json:"snt_fail"` + SntTime time.Duration `json:"snt_time"` +} + // IcmpHop ICMP HOP Response time details type IcmpHop struct { Success bool `json:"success"` @@ -44,6 +53,7 @@ type IcmpHop struct { N int `json:"n"` TTL int `json:"ttl"` Snt int `json:"snt"` + SntFail int `json:"snt_fail"` LastTime time.Duration `json:"last"` SumTime time.Duration `json:"sum"` AvgTime time.Duration `json:"avg"` diff --git a/pkg/mtr/mtr.go b/pkg/mtr/mtr.go index 1ade0d3..5e8a8a3 100644 --- a/pkg/mtr/mtr.go +++ b/pkg/mtr/mtr.go @@ -153,6 +153,7 @@ func runMtr(destAddr string, srcAddr string, icmpID int, options *MtrOptions) (r hop.RangeTime = time.Duration(common.TimeRange(mtrReturn.allTime)) failSum := options.Count() - mtrReturn.succSum + hop.SntFail = failSum loss := (float64)(failSum) / (float64)(options.Count()) hop.Loss = float64(loss) diff --git a/pkg/mtr/type.go b/pkg/mtr/type.go index 8037493..3559cbc 100644 --- a/pkg/mtr/type.go +++ b/pkg/mtr/type.go @@ -15,6 +15,7 @@ const defaultCount = 10 type MtrResult struct { DestAddr string `json:"dest_address"` Hops []common.IcmpHop `json:"hops"` + HopSummaryMap map[string]*common.IcmpSummary `json:"hop_summary_map"` } // MtrReturn MTR Response diff --git a/pkg/ping/ping.go b/pkg/ping/ping.go index 0d1f1ba..004f5c9 100644 --- a/pkg/ping/ping.go +++ b/pkg/ping/ping.go @@ -64,13 +64,8 @@ func runPing(ipAddr string, ip string, srcAddr string, icmpID int, option *PingO seq := 0 for cnt := 0; cnt < option.Count(); cnt++ { icmpReturn, err := icmp.Icmp(ip, srcAddr, ttl, pid, timeout, seq) - if err != nil { - pingResult.Success = false - pingResult.DropRate = 1.0 - return pingResult, err - } - if !icmpReturn.Success || !common.IsEqualIP(ip, icmpReturn.Addr) { + if err != nil || !icmpReturn.Success || !common.IsEqualIP(ip, icmpReturn.Addr) { continue } @@ -91,12 +86,6 @@ func runPing(ipAddr string, ip string, srcAddr string, icmpID int, option *PingO time.Sleep(interval) } - if !pingReturn.success { - pingResult.Success = false - pingResult.DropRate = 1.0 - return pingResult, nil - } - pingResult.Success = pingReturn.success pingResult.DropRate = float64(option.Count()-pingReturn.succSum) / float64(option.Count()) pingResult.SumTime = pingReturn.sumTime @@ -107,6 +96,9 @@ func runPing(ipAddr string, ip string, srcAddr string, icmpID int, option *PingO pingResult.UncorrectedSDTime = time.Duration(common.TimeUncorrectedDeviation(pingReturn.allTime)) pingResult.CorrectedSDTime = time.Duration(common.TimeCorrectedDeviation(pingReturn.allTime)) pingResult.RangeTime = time.Duration(common.TimeRange(pingReturn.allTime)) + pingResult.SntSummary = option.Count() + pingResult.SntFailSummary = option.Count() - pingReturn.succSum + pingResult.SntTimeSummary = time.Duration(common.TimeRange(pingReturn.allTime)) return pingResult, nil } diff --git a/pkg/ping/type.go b/pkg/ping/type.go index 549001b..2b28629 100644 --- a/pkg/ping/type.go +++ b/pkg/ping/type.go @@ -22,6 +22,9 @@ type PingResult struct { UncorrectedSDTime time.Duration `json:"usd"` CorrectedSDTime time.Duration `json:"csd"` RangeTime time.Duration `json:"range"` + SntSummary int `json:"snt_summary"` + SntFailSummary int `json:"snt_fail_summary"` + SntTimeSummary time.Duration `json:"snt_time_summary"` } // PingReturn ICMP Response diff --git a/target/target_http.go b/target/target_http.go index b2d77ad..f1fc0a3 100644 --- a/target/target_http.go +++ b/target/target_http.go @@ -56,6 +56,7 @@ func (t *HTTPGet) run(startupDelay time.Duration) { } } + waitChan := make(chan struct{}, MaxConcurrentJobs) tick := time.NewTicker(t.interval) for { select { @@ -64,7 +65,11 @@ func (t *HTTPGet) run(startupDelay time.Duration) { t.wg.Done() return case <-tick.C: - go t.httpGetCheck() + waitChan <- struct{}{} + go func() { + t.httpGetCheck() + <-waitChan + }() } } } @@ -99,8 +104,8 @@ func (t *HTTPGet) httpGetCheck() { level.Debug(t.logger).Log("type", "HTTPGet", "func", "httpGetCheck", "msg", bytes) t.Lock() + defer t.Unlock() t.result = data - t.Unlock() } // Compute returns the results of the HTTP metrics diff --git a/target/target_mtr.go b/target/target_mtr.go index 712c183..4554454 100644 --- a/target/target_mtr.go +++ b/target/target_mtr.go @@ -3,6 +3,7 @@ package target import ( "encoding/json" "fmt" + "strconv" "sync" "time" @@ -47,6 +48,7 @@ func NewMTR(logger log.Logger, icmpID *common.IcmpID, startupDelay time.Duration count: count, labels: labels, stop: make(chan struct{}), + result: &mtr.MtrResult{HopSummaryMap: map[string]*common.IcmpSummary{}}, } t.wg.Add(1) go t.run(startupDelay) @@ -61,6 +63,7 @@ func (t *MTR) run(startupDelay time.Duration) { } } + waitChan := make(chan struct{}, MaxConcurrentJobs) tick := time.NewTicker(t.interval) for { select { @@ -69,7 +72,11 @@ func (t *MTR) run(startupDelay time.Duration) { t.wg.Done() return case <-tick.C: - go t.mtr() + waitChan <- struct{}{} + go func() { + t.mtr() + <-waitChan + }() } } } @@ -87,15 +94,29 @@ func (t *MTR) mtr() { level.Error(t.logger).Log("type", "MTR", "func", "mtr", "msg", fmt.Sprintf("%s", err)) } - bytes, err2 := json.Marshal(data) + t.Lock() + defer t.Unlock() + summaryMap := t.result.HopSummaryMap + t.result = data + for _, hop := range data.Hops { + summary := summaryMap[strconv.Itoa(hop.TTL)+"_"+hop.AddressTo] + if summary == nil { + summary = &common.IcmpSummary{} + summaryMap[strconv.Itoa(hop.TTL)+"_"+hop.AddressTo] = summary + } + summary.AddressFrom = hop.AddressFrom + summary.AddressTo = hop.AddressTo + summary.Snt += hop.Snt + summary.SntTime += hop.SumTime + summary.SntFail += hop.SntFail + } + t.result.HopSummaryMap = summaryMap + + bytes, err2 := json.Marshal(t.result) if err2 != nil { level.Error(t.logger).Log("type", "MTR", "func", "mtr", "msg", fmt.Sprintf("%s", err2)) } level.Debug(t.logger).Log("type", "MTR", "func", "mtr", "msg", bytes) - - t.Lock() - t.result = data - t.Unlock() } // Compute returns the results of the MTR metrics diff --git a/target/target_ping.go b/target/target_ping.go index 1f62516..fb04e7c 100644 --- a/target/target_ping.go +++ b/target/target_ping.go @@ -12,6 +12,8 @@ import ( "github.com/syepes/network_exporter/pkg/ping" ) +const MaxConcurrentJobs = 3 + // PING Object type PING struct { logger log.Logger @@ -47,6 +49,7 @@ func NewPing(logger log.Logger, icmpID *common.IcmpID, startupDelay time.Duratio count: count, labels: labels, stop: make(chan struct{}), + result: &ping.PingResult{}, } t.wg.Add(1) go t.run(startupDelay) @@ -61,6 +64,7 @@ func (t *PING) run(startupDelay time.Duration) { } } + waitChan := make(chan struct{}, MaxConcurrentJobs) tick := time.NewTicker(t.interval) for { select { @@ -69,7 +73,11 @@ func (t *PING) run(startupDelay time.Duration) { t.wg.Done() return case <-tick.C: - go t.ping() + waitChan <- struct{}{} + go func() { + t.ping() + <-waitChan + }() } } } @@ -87,15 +95,18 @@ func (t *PING) ping() { level.Error(t.logger).Log("type", "ICMP", "func", "ping", "msg", fmt.Sprintf("%s", err)) } - bytes, err2 := json.Marshal(data) + t.Lock() + defer t.Unlock() + data.SntSummary += t.result.SntSummary + data.SntFailSummary += t.result.SntFailSummary + data.SntTimeSummary += t.result.SntTimeSummary + t.result = data + + bytes, err2 := json.Marshal(t.result) if err2 != nil { level.Error(t.logger).Log("type", "ICMP", "func", "ping", "msg", fmt.Sprintf("%s", err2)) } level.Debug(t.logger).Log("type", "ICMP", "func", "ping", "msg", bytes) - - t.Lock() - defer t.Unlock() - t.result = data } // Compute returns the results of the Ping metrics diff --git a/target/target_tcp.go b/target/target_tcp.go index 77312ed..2a97b3c 100644 --- a/target/target_tcp.go +++ b/target/target_tcp.go @@ -58,6 +58,7 @@ func (t *TCPPort) run(startupDelay time.Duration) { } } + waitChan := make(chan struct{}, MaxConcurrentJobs) tick := time.NewTicker(t.interval) for { select { @@ -66,7 +67,11 @@ func (t *TCPPort) run(startupDelay time.Duration) { t.wg.Done() return case <-tick.C: - go t.portCheck() + waitChan <- struct{}{} + go func() { + t.portCheck() + <-waitChan + }() } } } @@ -90,8 +95,8 @@ func (t *TCPPort) portCheck() { level.Debug(t.logger).Log("type", "TCP", "func", "port", "msg", bytes) t.Lock() + defer t.Unlock() t.result = data - t.Unlock() } // Compute returns the results of the TCP metrics