├── .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 | [![Release](https://img.shields.io/github/release/alexdzyoba/smart_exporter.svg?style=flat-square)](https://github.com/alexdzyoba/smart_exporter/releases/latest) 4 | [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 5 | [![Build status](https://github.com/alexdzyoba/smart_exporter/workflows/Build/badge.svg)](https://github.com/alexdzyoba/smart_exporter/actions) 6 | [![Docker Cloud Automated build](https://img.shields.io/docker/cloud/automated/ontrif/smart_exporter)](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 | --------------------------------------------------------------------------------