├── .goreleaser.yml
├── go.mod
├── Dockerfile
├── .github
└── workflows
│ ├── build.yml
│ └── main.yml
├── LICENSE
├── main.go
├── README.md
├── metrics.go
├── updater.go
└── go.sum
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | ---
2 | builds:
3 | - env:
4 | - CGO_ENABLED=0
5 |
6 | archives:
7 | - format: binary
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/alexdzyoba/smart_exporter
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/alexdzyoba/sys v0.0.0-20200508200610-3d14fe44fd4c
7 | github.com/pkg/errors v0.9.1
8 | github.com/prometheus/client_golang v1.1.0
9 | github.com/rs/zerolog v1.18.0
10 | )
11 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.16 AS builder
2 | ENV CGO_ENABLED=0
3 | WORKDIR /app
4 | COPY . /app
5 | RUN go build -v -o smart_exporter
6 |
7 | FROM alpine:3.13
8 | RUN apk add --no-cache smartmontools
9 | COPY --from=builder /app/smart_exporter /
10 | CMD ["/smart_exporter"]
11 | EXPOSE 9649
12 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on: [push]
3 | jobs:
4 | build:
5 | name: Build code
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: Set up Go 1.12
9 | uses: actions/setup-go@v1
10 | with:
11 | go-version: 1.12
12 |
13 | - name: Check out code
14 | uses: actions/checkout@v1
15 |
16 | - name: Build
17 | run: go build -v
18 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - 'v*'
5 | name: Release
6 | jobs:
7 | release:
8 | name: Release with goreleaser
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Check out code
12 | uses: actions/checkout@master
13 | - name: goreleaser
14 | uses: docker://goreleaser/goreleaser
15 | env:
16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 | with:
18 | args: release
19 | if: success()
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Alex Dzyoba
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "net/http"
6 | "os"
7 | "time"
8 |
9 | "github.com/prometheus/client_golang/prometheus"
10 | "github.com/prometheus/client_golang/prometheus/promhttp"
11 | "github.com/rs/zerolog"
12 | "github.com/rs/zerolog/log"
13 | )
14 |
15 | func main() {
16 | listen := flag.String("listen-addr", ":9649", "Address to listen")
17 | interval := flag.Duration("update-interval", 10*time.Minute, "SMART metrics collect interval")
18 | verbose := flag.Bool("v", false, "Verbose output")
19 | flag.Parse()
20 |
21 | zerolog.SetGlobalLevel(zerolog.InfoLevel)
22 | if *verbose {
23 | zerolog.SetGlobalLevel(zerolog.DebugLevel)
24 | }
25 |
26 | log.Info().Msg("starting updater")
27 | updaterLogger := zerolog.New(os.Stderr).With().Str("component", "updater").Logger()
28 | u := NewSMARTUpdater(metrics, *interval, &updaterLogger)
29 | go u.Run()
30 |
31 | log.Info().Msg("registering metrics")
32 | for _, m := range metrics {
33 | prometheus.MustRegister(m)
34 | }
35 |
36 | http.Handle("/metrics", promhttp.Handler())
37 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
38 | _, _ = w.Write([]byte(`
39 |
SMART exporter
40 |
41 | SMART Exporter
42 | Metrics
43 |
44 |
45 | `))
46 | })
47 |
48 | log.Info().Str("listen", *listen).Msg("starting server")
49 | log.Fatal().Err(http.ListenAndServe(*listen, nil)).Send()
50 | }
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # smart_exporter
2 |
3 | [](https://github.com/alexdzyoba/smart_exporter/releases/latest)
4 | [](LICENSE)
5 | [](https://github.com/alexdzyoba/smart_exporter/actions)
6 | [](https://hub.docker.com/r/ontrif/smart_exporter)
7 |
8 | Prometheus exporter for critical S.M.A.R.T. metrics. It works by parsing
9 | smartctl output for every device. Parsing is performed independent of metrics
10 | HTTP handler (not on every scrape).
11 |
12 | Example scrape output with metrics:
13 |
14 | # HELP smart_read_uncorrected_errors_total Number of uncorrected read errors
15 | # TYPE smart_read_uncorrected_errors_total gauge
16 | smart_read_uncorrected_errors_total{device="/dev/sda"} 0
17 |
18 | # HELP smart_write_uncorrected_errors_total Number of uncorrected write errors
19 | # TYPE smart_write_uncorrected_errors_total gauge
20 | smart_write_uncorrected_errors_total{device="/dev/sda"} 0
21 |
22 | # HELP smart_grown_defect_list_total Number of elements in grown defect list
23 | # TYPE smart_grown_defect_list_total gauge
24 | smart_grown_defect_list_total{device="/dev/sda"} 0
25 |
26 | # HELP smart_reallocated_sectors_total Number of reallocated sectors
27 | # TYPE smart_reallocated_sectors_total gauge
28 | smart_reallocated_sectors_total{device="/dev/sda"} 0
29 |
30 | Docker image can be started with command:
31 | ```
32 | docker run --privileged -p 8000:8000 ontrif/smart_exporter
33 | ```
34 |
--------------------------------------------------------------------------------
/metrics.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "regexp"
5 | "sync"
6 |
7 | "github.com/prometheus/client_golang/prometheus"
8 | )
9 |
10 | // SMARTMetric wraps particular SMART metric and provides Prometheus integration
11 | type SMARTMetric struct {
12 | // Regexp holds regular expression used to parse metric value from smartctl
13 | // output.
14 | Regexp *regexp.Regexp
15 |
16 | // Desc provides Prometheus metric descriptor.
17 | Desc *prometheus.Desc
18 |
19 | // Vals contains metric values per label (that is device). Vals are updated
20 | // periodiclly in a separate goroutine.
21 | Vals map[string]float64
22 |
23 | // This lock guards access to Vals map
24 | sync.RWMutex
25 | }
26 |
27 | func (m *SMARTMetric) Collect(ch chan<- prometheus.Metric) {
28 | // Report metric with read lock because they're updated in a separate
29 | // goroutine
30 | m.RLock()
31 | for label, val := range m.Vals {
32 | ch <- prometheus.MustNewConstMetric(
33 | m.Desc,
34 | prometheus.GaugeValue,
35 | val,
36 | label,
37 | )
38 | }
39 | m.RUnlock()
40 | }
41 |
42 | func (m *SMARTMetric) Describe(ch chan<- *prometheus.Desc) {
43 | ch <- m.Desc
44 | }
45 |
46 | var metrics = []*SMARTMetric{
47 | &SMARTMetric{
48 | Regexp: regexp.MustCompile(`Reallocated_Sector_Ct`),
49 | Desc: prometheus.NewDesc(
50 | "smart_reallocated_sectors_total",
51 | "Number of reallocated sectors",
52 | []string{"device"},
53 | nil,
54 | ),
55 | Vals: make(map[string]float64),
56 | },
57 | &SMARTMetric{
58 | Regexp: regexp.MustCompile(`^Elements in grown defect list:`),
59 | Desc: prometheus.NewDesc(
60 | "smart_grown_defect_list_total",
61 | "Number of elements in grown defect list",
62 | []string{"device"},
63 | nil,
64 | ),
65 | Vals: make(map[string]float64),
66 | },
67 | &SMARTMetric{
68 | Regexp: regexp.MustCompile(`^read:`),
69 | Desc: prometheus.NewDesc(
70 | "smart_read_uncorrected_errors_total",
71 | "Number of uncorrected read errors",
72 | []string{"device"},
73 | nil,
74 | ),
75 | Vals: make(map[string]float64),
76 | },
77 | &SMARTMetric{
78 | Regexp: regexp.MustCompile(`^write:`),
79 | Desc: prometheus.NewDesc(
80 | "smart_write_uncorrected_errors_total",
81 | "Number of uncorrected write errors",
82 | []string{"device"},
83 | nil,
84 | ),
85 | Vals: make(map[string]float64),
86 | },
87 | }
88 |
--------------------------------------------------------------------------------
/updater.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os/exec"
5 | "strconv"
6 | "strings"
7 | "time"
8 |
9 | "github.com/alexdzyoba/sys/block"
10 | "github.com/pkg/errors"
11 | "github.com/rs/zerolog"
12 | "github.com/rs/zerolog/log"
13 | )
14 |
15 | // SMARTUpdater periodically updates metrics values
16 | type SMARTUpdater struct {
17 | metrics []*SMARTMetric
18 | interval time.Duration
19 | log *zerolog.Logger
20 | }
21 |
22 | // NewSMARTUpdater creates new SMARTUpdater
23 | func NewSMARTUpdater(metrics []*SMARTMetric, interval time.Duration, logger *zerolog.Logger) *SMARTUpdater {
24 | return &SMARTUpdater{
25 | interval: interval,
26 | metrics: metrics,
27 | log: logger,
28 | }
29 | }
30 |
31 | // Run periodically collects SMART data and updates metrics values.
32 | // The concurrency is up to the caller.
33 | func (u *SMARTUpdater) Run() {
34 | ticker := time.NewTicker(u.interval)
35 | u.Update()
36 | for range ticker.C {
37 | u.Update()
38 | }
39 | }
40 |
41 | // Update lists devices, gather SMART metrics for them and update metrics
42 | // values.
43 | func (u *SMARTUpdater) Update() {
44 | bds, err := block.ListDevices()
45 | if err != nil {
46 | u.log.Err(err).Msg("failed to list devices")
47 | return
48 | }
49 |
50 | for _, bd := range bds {
51 | if bd.Type == block.TypeDisk {
52 | u.UpdateDevice(&bd)
53 | }
54 | }
55 |
56 | u.removeEjectedDevices(bds)
57 | }
58 |
59 | // UpdateDevice gather SMART metrics for a given device and updates all metrics
60 | // values with them
61 | func (u *SMARTUpdater) UpdateDevice(bd *block.Device) {
62 | u.log.Debug().Str("device", bd.Name).Msg("gather metrics")
63 | cmd := exec.Command("smartctl", "-i", "-A", "-l", "error", "/dev/"+bd.Name)
64 | out, err := cmd.Output()
65 | if err != nil {
66 | // Ignore error to skip to the next device
67 | log.Err(err).Str("device", bd.Name).Msg("smartctl failed")
68 | return
69 | }
70 |
71 | lines := strings.Split(string(out), "\n")
72 | for _, line := range lines {
73 | for _, m := range u.metrics {
74 | // Lock metric to avoid incosistent view.
75 | m.Lock()
76 |
77 | if m.Regexp.MatchString(line) {
78 | val, err := lastField(line)
79 | if err != nil {
80 | u.log.Err(err).Str("device", bd.Name).Msg("failed to parse metrics")
81 | m.Unlock()
82 | continue
83 | }
84 |
85 | u.log.Debug().
86 | Str("device", bd.Name).
87 | Str("metric", m.Desc.String()).
88 | Float64("value", val).
89 | Msg("update metric value")
90 | m.Vals[bd.Name] = val
91 | }
92 | m.Unlock()
93 | }
94 | }
95 | }
96 |
97 | // removeEjectedDevices removes devices from metric labels that are not actually
98 | // present in the system
99 | func (u *SMARTUpdater) removeEjectedDevices(bds []block.Device) {
100 | deviceTable := make(map[string]struct{})
101 | for _, bd := range bds {
102 | deviceTable[bd.Name] = struct{}{}
103 | }
104 |
105 | for _, m := range u.metrics {
106 | m.Lock()
107 | for device := range m.Vals {
108 | if _, ok := deviceTable[device]; !ok {
109 | u.log.Debug().Str("device", device).Msg("removing metrics values")
110 | delete(m.Vals, device)
111 | }
112 | }
113 | m.Unlock()
114 | }
115 | }
116 |
117 | // lastField parses line last field as float64
118 | func lastField(line string) (float64, error) {
119 | fields := strings.Fields(line)
120 | lastField := fields[len(fields)-1]
121 |
122 | val, err := strconv.ParseFloat(lastField, 64)
123 | if err != nil {
124 | return 0, errors.Wrapf(err, "failed to parse value %v as float64", lastField)
125 | }
126 |
127 | return val, nil
128 | }
129 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
2 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
3 | github.com/alexdzyoba/sys v0.0.0-20200508200610-3d14fe44fd4c h1:hsd7lPV1eV7hZnGa7NDh92BqEw4UBQjX7x6LoejcJ9w=
4 | github.com/alexdzyoba/sys v0.0.0-20200508200610-3d14fe44fd4c/go.mod h1:tx0yA/p5KbSm7aJT+k2sNVeR11+OtbTJ1pWdasWgkl8=
5 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
6 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
7 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
8 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
9 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
13 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
14 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
15 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
16 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
17 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
18 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
19 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
20 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
21 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
22 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
23 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
24 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
25 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
26 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
27 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
28 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
29 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
30 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
32 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
33 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
34 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
35 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
36 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
37 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
38 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
39 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
40 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
41 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
42 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
43 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
44 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
45 | github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
46 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
47 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
48 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
49 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
50 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
51 | github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
52 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
53 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
54 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
55 | github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
56 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
57 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
58 | github.com/rs/zerolog v1.18.0 h1:CbAm3kP2Tptby1i9sYy2MGRg0uxIN9cyDb59Ys7W8z8=
59 | github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
60 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
61 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
62 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
63 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
64 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
65 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
66 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
67 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
68 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
69 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
70 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
71 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
72 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
73 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
74 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
75 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
76 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
77 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
78 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
79 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
80 | golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
81 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
82 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
84 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
85 |
--------------------------------------------------------------------------------