├── Gopkg.lock ├── Gopkg.toml ├── Procfile ├── README.md ├── app.json ├── logging.go ├── main.go ├── metrics.go ├── out.dot ├── out.dot.png ├── service.go ├── transport.go └── vendor └── github.com ├── VividCortex └── gohistogram │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── histogram.go │ ├── numerichistogram.go │ └── weightedhistogram.go ├── go-kit └── kit │ ├── LICENSE │ ├── endpoint │ ├── doc.go │ └── endpoint.go │ ├── log │ ├── README.md │ ├── doc.go │ ├── json_logger.go │ ├── log.go │ ├── logfmt_logger.go │ ├── nop_logger.go │ ├── stdlib.go │ ├── sync.go │ ├── term │ │ └── LICENSE │ └── value.go │ ├── metrics │ ├── README.md │ ├── debug.test │ ├── doc.go │ ├── expvar │ │ └── expvar.go │ ├── generic │ │ └── generic.go │ ├── internal │ │ └── lv │ │ │ ├── labelvalues.go │ │ │ └── space.go │ ├── metrics.go │ └── timer.go │ ├── ratelimit │ └── token_bucket.go │ └── transport │ └── http │ ├── client.go │ ├── doc.go │ ├── encode_decode.go │ ├── request_response_funcs.go │ └── server.go ├── go-logfmt └── logfmt │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── decode.go │ ├── doc.go │ ├── encode.go │ ├── fuzz.go │ └── jsonstring.go ├── go-stack └── stack │ ├── .travis.yml │ ├── LICENSE.md │ ├── README.md │ └── stack.go ├── juju └── ratelimit │ ├── LICENSE │ ├── README.md │ ├── ratelimit.go │ └── reader.go └── kr └── logfmt ├── .gitignore ├── Readme ├── decode.go ├── scanner.go └── unquote.go /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/VividCortex/gohistogram" 6 | packages = ["."] 7 | revision = "51564d9861991fb0ad0f531c99ef602d0f9866e6" 8 | version = "v1.0.0" 9 | 10 | [[projects]] 11 | name = "github.com/go-kit/kit" 12 | packages = [ 13 | "endpoint", 14 | "log", 15 | "metrics", 16 | "metrics/expvar", 17 | "metrics/generic", 18 | "metrics/internal/lv", 19 | "ratelimit", 20 | "transport/http" 21 | ] 22 | revision = "4dc7be5d2d12881735283bcab7352178e190fc71" 23 | version = "v0.6.0" 24 | 25 | [[projects]] 26 | name = "github.com/go-logfmt/logfmt" 27 | packages = ["."] 28 | revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" 29 | version = "v0.3.0" 30 | 31 | [[projects]] 32 | name = "github.com/go-stack/stack" 33 | packages = ["."] 34 | revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" 35 | version = "v1.7.0" 36 | 37 | [[projects]] 38 | name = "github.com/juju/ratelimit" 39 | packages = ["."] 40 | revision = "59fac5042749a5afb9af70e813da1dd5474f0167" 41 | version = "1.0.1" 42 | 43 | [[projects]] 44 | branch = "master" 45 | name = "github.com/kr/logfmt" 46 | packages = ["."] 47 | revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" 48 | 49 | [solve-meta] 50 | analyzer-name = "dep" 51 | analyzer-version = 1 52 | inputs-digest = "cc1f93023f91d5574ef1682ef9442ce7d5f85e5cd7a8d9d01df7321a2691fc7a" 53 | solver-name = "gps-cdcl" 54 | solver-version = 1 55 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [prune] 29 | go-tests = true 30 | unused-packages = true 31 | 32 | [[constraint]] 33 | name = "github.com/go-kit/kit" 34 | version = "0.6.0" 35 | 36 | [metadata.heroku] 37 | root-package = "github.com/heroku-examples/go-kit-ex1" -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: go-kit-ex1 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go-kit Example 1 2 | 3 | [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 4 | 5 | Very basic go-kit example app used in [this blog post](https://blog.heroku.com/microservices_in_go_using_go_kit). 6 | 7 | ## Install/Run Locally 8 | 9 | ```console 10 | $ go get -u github.com/heroku-examples/go-kit-ex1 11 | $ go-kit-ex1 12 | listening-on=8080 13 | ``` -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Go Kit Example1", 3 | "description": "go-kit basic example", 4 | "keywords": [ 5 | "go-kit", 6 | "go" 7 | ], 8 | "website": "http://github.com/heroku-examples/go-kit-ex1", 9 | "repository": "http://github.com/heroku-examples/go-kit-ex1" 10 | } 11 | -------------------------------------------------------------------------------- /logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/go-kit/kit/endpoint" 10 | "github.com/go-kit/kit/log" 11 | ) 12 | 13 | const ( 14 | requestIDKey = iota 15 | pathKey 16 | ) 17 | 18 | func beforeIDExtractor(ctx context.Context, r *http.Request) context.Context { 19 | return context.WithValue(ctx, requestIDKey, r.Header.Get("X-Request-Id")) 20 | } 21 | 22 | func beforePATHExtractor(ctx context.Context, r *http.Request) context.Context { 23 | return context.WithValue(ctx, pathKey, r.URL.EscapedPath()) 24 | } 25 | 26 | func loggingMiddlware(l log.Logger) endpoint.Middleware { 27 | return func(next endpoint.Endpoint) endpoint.Endpoint { 28 | return func(ctx context.Context, request interface{}) (result interface{}, err error) { 29 | var req, resp string 30 | 31 | defer func(b time.Time) { 32 | l.Log( 33 | "path", ctx.Value(pathKey), 34 | "request", req, 35 | "result", resp, 36 | "err", err, 37 | "request_id", ctx.Value(requestIDKey), 38 | "elapsed", time.Since(b), 39 | ) 40 | }(time.Now()) 41 | if r, ok := request.(fmt.Stringer); ok { 42 | req = r.String() 43 | } 44 | result, err = next(ctx, request) 45 | if r, ok := result.(fmt.Stringer); ok { 46 | resp = r.String() 47 | } 48 | return 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "expvar" 5 | "net/http" 6 | "os" 7 | "time" 8 | 9 | "github.com/go-kit/kit/log" 10 | "github.com/go-kit/kit/metrics/expvar" 11 | krl "github.com/go-kit/kit/ratelimit" 12 | kth "github.com/go-kit/kit/transport/http" 13 | "github.com/juju/ratelimit" 14 | ) 15 | 16 | func main() { 17 | logger := log.NewLogfmtLogger(os.Stdout) 18 | 19 | var c countService 20 | svc := makeAddEndpoint(&c) 21 | 22 | limit := ratelimit.NewBucket(2*time.Second, 1) 23 | svc = krl.NewTokenBucketLimiter(limit)(svc) 24 | 25 | requestCount := expvar.NewCounter("request.count") 26 | svc = metricsMiddleware(requestCount)(svc) 27 | svc = loggingMiddlware(logger)(svc) 28 | 29 | http.Handle("/add", 30 | kth.NewServer( 31 | svc, 32 | decodeAddRequest, 33 | encodeResponse, 34 | kth.ServerBefore(beforeIDExtractor, beforePATHExtractor), 35 | ), 36 | ) 37 | 38 | port := os.Getenv("PORT") 39 | if port == "" { 40 | port = "8080" 41 | } 42 | 43 | logger.Log("listening-on", port) 44 | if err := http.ListenAndServe(":"+port, nil); err != nil { 45 | logger.Log("listen.error", err) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /metrics.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/go-kit/kit/metrics" 8 | ) 9 | 10 | func metricsMiddleware(requestCount metrics.Counter) endpoint.Middleware { 11 | return func(next endpoint.Endpoint) endpoint.Endpoint { 12 | return func(ctx context.Context, request interface{}) (interface{}, error) { 13 | requestCount.Add(1) 14 | return next(ctx, request) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /out.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | node [shape=box]; 3 | 79706905 [label="github.com/heroku-examples/go-kit-ex1"]; 4 | 1542327640 [label="github.com/VividCortex/gohistogram\nv1.0.0"]; 5 | 1756365654 [label="github.com/go-kit/kit\nv0.6.0"]; 6 | 3962660754 [label="github.com/go-logfmt/logfmt\nv0.3.0"]; 7 | 1477303506 [label="github.com/go-stack/stack\nv1.7.0"]; 8 | 1499966208 [label="github.com/juju/ratelimit\n1.0.1"]; 9 | 1801388401 [label="github.com/kr/logfmt\nbranch master"]; 10 | 2759993708 [label="golang.org/x/net\nbranch master"]; 11 | 79706905 -> 1756365654; 12 | 79706905 -> 1499966208; 13 | 79706905 -> 2759993708; 14 | 1756365654 -> 1542327640; 15 | 1756365654 -> 3962660754; 16 | 1756365654 -> 1477303506; 17 | 1756365654 -> 1499966208; 18 | 1756365654 -> 2759993708; 19 | 3962660754 -> 1801388401; 20 | } 21 | -------------------------------------------------------------------------------- /out.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heroku-examples/go-kit-ex1/13853e32bec59afbb78f013d83607d7e6751bba6/out.dot.png -------------------------------------------------------------------------------- /service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | // Counter adds a value and returns a new value 6 | type Counter interface { 7 | Add(int) int 8 | } 9 | 10 | type countService struct { 11 | v int 12 | mu sync.Mutex 13 | } 14 | 15 | func (c *countService) Add(v int) int { 16 | c.mu.Lock() 17 | defer c.mu.Unlock() 18 | c.v += v 19 | return c.v 20 | } 21 | -------------------------------------------------------------------------------- /transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/go-kit/kit/endpoint" 10 | ) 11 | 12 | type addRequest struct { 13 | V int `json:"v"` 14 | } 15 | 16 | func (r addRequest) String() string { 17 | return fmt.Sprintf("%d", r.V) 18 | } 19 | 20 | type addResponse struct { 21 | V int `json:"v"` 22 | } 23 | 24 | func (r addResponse) String() string { 25 | return fmt.Sprintf("%d", r.V) 26 | } 27 | 28 | func makeAddEndpoint(svc Counter) endpoint.Endpoint { 29 | return func(ctx context.Context, request interface{}) (interface{}, error) { 30 | req := request.(addRequest) 31 | v := svc.Add(req.V) 32 | return addResponse{v}, nil 33 | } 34 | } 35 | 36 | func decodeAddRequest(ctx context.Context, r *http.Request) (interface{}, error) { 37 | var req addRequest 38 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 39 | return nil, err 40 | } 41 | return req, nil 42 | } 43 | 44 | func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 45 | return json.NewEncoder(w).Encode(response) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/VividCortex/gohistogram/.gitignore: -------------------------------------------------------------------------------- 1 | \#* 2 | .\#* -------------------------------------------------------------------------------- /vendor/github.com/VividCortex/gohistogram/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 VividCortex 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/VividCortex/gohistogram/README.md: -------------------------------------------------------------------------------- 1 | # gohistogram - Histograms in Go 2 | 3 | ![build status](https://circleci.com/gh/VividCortex/gohistogram.png?circle-token=d37ec652ea117165cd1b342400a801438f575209) 4 | 5 | This package provides [Streaming Approximate Histograms](https://vividcortex.com/blog/2013/07/08/streaming-approximate-histograms/) 6 | for efficient quantile approximations. 7 | 8 | The histograms in this package are based on the algorithms found in 9 | Ben-Haim & Yom-Tov's *A Streaming Parallel Decision Tree Algorithm* 10 | ([PDF](http://jmlr.org/papers/volume11/ben-haim10a/ben-haim10a.pdf)). 11 | Histogram bins do not have a preset size. As values stream into 12 | the histogram, bins are dynamically added and merged. 13 | 14 | Another implementation can be found in the Apache Hive project (see 15 | [NumericHistogram](http://hive.apache.org/docs/r0.11.0/api/org/apache/hadoop/hive/ql/udf/generic/NumericHistogram.html)). 16 | 17 | An example: 18 | 19 | ![histogram](http://i.imgur.com/5OplaRs.png) 20 | 21 | The accurate method of calculating quantiles (like percentiles) requires 22 | data to be sorted. Streaming histograms make it possible to approximate 23 | quantiles without sorting (or even individually storing) values. 24 | 25 | NumericHistogram is the more basic implementation of a streaming 26 | histogram. WeightedHistogram implements bin values as exponentially-weighted 27 | moving averages. 28 | 29 | A maximum bin size is passed as an argument to the constructor methods. A 30 | larger bin size yields more accurate approximations at the cost of increased 31 | memory utilization and performance. 32 | 33 | A picture of kittens: 34 | 35 | ![stack of kittens](http://i.imgur.com/QxRTWAE.jpg) 36 | 37 | ## Getting started 38 | 39 | ### Using in your own code 40 | 41 | $ go get github.com/VividCortex/gohistogram 42 | 43 | ```go 44 | import "github.com/VividCortex/gohistogram" 45 | ``` 46 | 47 | ### Running tests and making modifications 48 | 49 | Get the code into your workspace: 50 | 51 | $ cd $GOPATH 52 | $ git clone git@github.com:VividCortex/gohistogram.git ./src/github.com/VividCortex/gohistogram 53 | 54 | You can run the tests now: 55 | 56 | $ cd src/github.com/VividCortex/gohistogram 57 | $ go test . 58 | 59 | ## API Documentation 60 | 61 | Full source documentation can be found [here][godoc]. 62 | 63 | [godoc]: http://godoc.org/github.com/VividCortex/gohistogram 64 | 65 | ## Contributing 66 | 67 | We only accept pull requests for minor fixes or improvements. This includes: 68 | 69 | * Small bug fixes 70 | * Typos 71 | * Documentation or comments 72 | 73 | Please open issues to discuss new features. Pull requests for new features will be rejected, 74 | so we recommend forking the repository and making changes in your fork for your use case. 75 | 76 | ## License 77 | 78 | Copyright (c) 2013 VividCortex 79 | 80 | Released under MIT License. Check `LICENSE` file for details. 81 | -------------------------------------------------------------------------------- /vendor/github.com/VividCortex/gohistogram/histogram.go: -------------------------------------------------------------------------------- 1 | package gohistogram 2 | 3 | // Copyright (c) 2013 VividCortex, Inc. All rights reserved. 4 | // Please see the LICENSE file for applicable license terms. 5 | 6 | // Histogram is the interface that wraps the Add and Quantile methods. 7 | type Histogram interface { 8 | // Add adds a new value, n, to the histogram. Trimming is done 9 | // automatically. 10 | Add(n float64) 11 | 12 | // Quantile returns an approximation. 13 | Quantile(n float64) (q float64) 14 | 15 | // String returns a string reprentation of the histogram, 16 | // which is useful for printing to a terminal. 17 | String() (str string) 18 | } 19 | 20 | type bin struct { 21 | value float64 22 | count float64 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/VividCortex/gohistogram/numerichistogram.go: -------------------------------------------------------------------------------- 1 | package gohistogram 2 | 3 | // Copyright (c) 2013 VividCortex, Inc. All rights reserved. 4 | // Please see the LICENSE file for applicable license terms. 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type NumericHistogram struct { 11 | bins []bin 12 | maxbins int 13 | total uint64 14 | } 15 | 16 | // NewHistogram returns a new NumericHistogram with a maximum of n bins. 17 | // 18 | // There is no "optimal" bin count, but somewhere between 20 and 80 bins 19 | // should be sufficient. 20 | func NewHistogram(n int) *NumericHistogram { 21 | return &NumericHistogram{ 22 | bins: make([]bin, 0), 23 | maxbins: n, 24 | total: 0, 25 | } 26 | } 27 | 28 | func (h *NumericHistogram) Add(n float64) { 29 | defer h.trim() 30 | h.total++ 31 | for i := range h.bins { 32 | if h.bins[i].value == n { 33 | h.bins[i].count++ 34 | return 35 | } 36 | 37 | if h.bins[i].value > n { 38 | 39 | newbin := bin{value: n, count: 1} 40 | head := append(make([]bin, 0), h.bins[0:i]...) 41 | 42 | head = append(head, newbin) 43 | tail := h.bins[i:] 44 | h.bins = append(head, tail...) 45 | return 46 | } 47 | } 48 | 49 | h.bins = append(h.bins, bin{count: 1, value: n}) 50 | } 51 | 52 | func (h *NumericHistogram) Quantile(q float64) float64 { 53 | count := q * float64(h.total) 54 | for i := range h.bins { 55 | count -= float64(h.bins[i].count) 56 | 57 | if count <= 0 { 58 | return h.bins[i].value 59 | } 60 | } 61 | 62 | return -1 63 | } 64 | 65 | // CDF returns the value of the cumulative distribution function 66 | // at x 67 | func (h *NumericHistogram) CDF(x float64) float64 { 68 | count := 0.0 69 | for i := range h.bins { 70 | if h.bins[i].value <= x { 71 | count += float64(h.bins[i].count) 72 | } 73 | } 74 | 75 | return count / float64(h.total) 76 | } 77 | 78 | // Mean returns the sample mean of the distribution 79 | func (h *NumericHistogram) Mean() float64 { 80 | if h.total == 0 { 81 | return 0 82 | } 83 | 84 | sum := 0.0 85 | 86 | for i := range h.bins { 87 | sum += h.bins[i].value * h.bins[i].count 88 | } 89 | 90 | return sum / float64(h.total) 91 | } 92 | 93 | // Variance returns the variance of the distribution 94 | func (h *NumericHistogram) Variance() float64 { 95 | if h.total == 0 { 96 | return 0 97 | } 98 | 99 | sum := 0.0 100 | mean := h.Mean() 101 | 102 | for i := range h.bins { 103 | sum += (h.bins[i].count * (h.bins[i].value - mean) * (h.bins[i].value - mean)) 104 | } 105 | 106 | return sum / float64(h.total) 107 | } 108 | 109 | func (h *NumericHistogram) Count() float64 { 110 | return float64(h.total) 111 | } 112 | 113 | // trim merges adjacent bins to decrease the bin count to the maximum value 114 | func (h *NumericHistogram) trim() { 115 | for len(h.bins) > h.maxbins { 116 | // Find closest bins in terms of value 117 | minDelta := 1e99 118 | minDeltaIndex := 0 119 | for i := range h.bins { 120 | if i == 0 { 121 | continue 122 | } 123 | 124 | if delta := h.bins[i].value - h.bins[i-1].value; delta < minDelta { 125 | minDelta = delta 126 | minDeltaIndex = i 127 | } 128 | } 129 | 130 | // We need to merge bins minDeltaIndex-1 and minDeltaIndex 131 | totalCount := h.bins[minDeltaIndex-1].count + h.bins[minDeltaIndex].count 132 | mergedbin := bin{ 133 | value: (h.bins[minDeltaIndex-1].value* 134 | h.bins[minDeltaIndex-1].count + 135 | h.bins[minDeltaIndex].value* 136 | h.bins[minDeltaIndex].count) / 137 | totalCount, // weighted average 138 | count: totalCount, // summed heights 139 | } 140 | head := append(make([]bin, 0), h.bins[0:minDeltaIndex-1]...) 141 | tail := append([]bin{mergedbin}, h.bins[minDeltaIndex+1:]...) 142 | h.bins = append(head, tail...) 143 | } 144 | } 145 | 146 | // String returns a string reprentation of the histogram, 147 | // which is useful for printing to a terminal. 148 | func (h *NumericHistogram) String() (str string) { 149 | str += fmt.Sprintln("Total:", h.total) 150 | 151 | for i := range h.bins { 152 | var bar string 153 | for j := 0; j < int(float64(h.bins[i].count)/float64(h.total)*200); j++ { 154 | bar += "." 155 | } 156 | str += fmt.Sprintln(h.bins[i].value, "\t", bar) 157 | } 158 | 159 | return 160 | } 161 | -------------------------------------------------------------------------------- /vendor/github.com/VividCortex/gohistogram/weightedhistogram.go: -------------------------------------------------------------------------------- 1 | // Package gohistogram contains implementations of weighted and exponential histograms. 2 | package gohistogram 3 | 4 | // Copyright (c) 2013 VividCortex, Inc. All rights reserved. 5 | // Please see the LICENSE file for applicable license terms. 6 | 7 | import "fmt" 8 | 9 | // A WeightedHistogram implements Histogram. A WeightedHistogram has bins that have values 10 | // which are exponentially weighted moving averages. This allows you keep inserting large 11 | // amounts of data into the histogram and approximate quantiles with recency factored in. 12 | type WeightedHistogram struct { 13 | bins []bin 14 | maxbins int 15 | total float64 16 | alpha float64 17 | } 18 | 19 | // NewWeightedHistogram returns a new WeightedHistogram with a maximum of n bins with a decay factor 20 | // of alpha. 21 | // 22 | // There is no "optimal" bin count, but somewhere between 20 and 80 bins should be 23 | // sufficient. 24 | // 25 | // Alpha should be set to 2 / (N+1), where N represents the average age of the moving window. 26 | // For example, a 60-second window with an average age of 30 seconds would yield an 27 | // alpha of 0.064516129. 28 | func NewWeightedHistogram(n int, alpha float64) *WeightedHistogram { 29 | return &WeightedHistogram{ 30 | bins: make([]bin, 0), 31 | maxbins: n, 32 | total: 0, 33 | alpha: alpha, 34 | } 35 | } 36 | 37 | func ewma(existingVal float64, newVal float64, alpha float64) (result float64) { 38 | result = newVal*(1-alpha) + existingVal*alpha 39 | return 40 | } 41 | 42 | func (h *WeightedHistogram) scaleDown(except int) { 43 | for i := range h.bins { 44 | if i != except { 45 | h.bins[i].count = ewma(h.bins[i].count, 0, h.alpha) 46 | } 47 | } 48 | } 49 | 50 | func (h *WeightedHistogram) Add(n float64) { 51 | defer h.trim() 52 | for i := range h.bins { 53 | if h.bins[i].value == n { 54 | h.bins[i].count++ 55 | 56 | defer h.scaleDown(i) 57 | return 58 | } 59 | 60 | if h.bins[i].value > n { 61 | 62 | newbin := bin{value: n, count: 1} 63 | head := append(make([]bin, 0), h.bins[0:i]...) 64 | 65 | head = append(head, newbin) 66 | tail := h.bins[i:] 67 | h.bins = append(head, tail...) 68 | 69 | defer h.scaleDown(i) 70 | return 71 | } 72 | } 73 | 74 | h.bins = append(h.bins, bin{count: 1, value: n}) 75 | } 76 | 77 | func (h *WeightedHistogram) Quantile(q float64) float64 { 78 | count := q * h.total 79 | for i := range h.bins { 80 | count -= float64(h.bins[i].count) 81 | 82 | if count <= 0 { 83 | return h.bins[i].value 84 | } 85 | } 86 | 87 | return -1 88 | } 89 | 90 | // CDF returns the value of the cumulative distribution function 91 | // at x 92 | func (h *WeightedHistogram) CDF(x float64) float64 { 93 | count := 0.0 94 | for i := range h.bins { 95 | if h.bins[i].value <= x { 96 | count += float64(h.bins[i].count) 97 | } 98 | } 99 | 100 | return count / h.total 101 | } 102 | 103 | // Mean returns the sample mean of the distribution 104 | func (h *WeightedHistogram) Mean() float64 { 105 | if h.total == 0 { 106 | return 0 107 | } 108 | 109 | sum := 0.0 110 | 111 | for i := range h.bins { 112 | sum += h.bins[i].value * h.bins[i].count 113 | } 114 | 115 | return sum / h.total 116 | } 117 | 118 | // Variance returns the variance of the distribution 119 | func (h *WeightedHistogram) Variance() float64 { 120 | if h.total == 0 { 121 | return 0 122 | } 123 | 124 | sum := 0.0 125 | mean := h.Mean() 126 | 127 | for i := range h.bins { 128 | sum += (h.bins[i].count * (h.bins[i].value - mean) * (h.bins[i].value - mean)) 129 | } 130 | 131 | return sum / h.total 132 | } 133 | 134 | func (h *WeightedHistogram) Count() float64 { 135 | return h.total 136 | } 137 | 138 | func (h *WeightedHistogram) trim() { 139 | total := 0.0 140 | for i := range h.bins { 141 | total += h.bins[i].count 142 | } 143 | h.total = total 144 | for len(h.bins) > h.maxbins { 145 | 146 | // Find closest bins in terms of value 147 | minDelta := 1e99 148 | minDeltaIndex := 0 149 | for i := range h.bins { 150 | if i == 0 { 151 | continue 152 | } 153 | 154 | if delta := h.bins[i].value - h.bins[i-1].value; delta < minDelta { 155 | minDelta = delta 156 | minDeltaIndex = i 157 | } 158 | } 159 | 160 | // We need to merge bins minDeltaIndex-1 and minDeltaIndex 161 | totalCount := h.bins[minDeltaIndex-1].count + h.bins[minDeltaIndex].count 162 | mergedbin := bin{ 163 | value: (h.bins[minDeltaIndex-1].value* 164 | h.bins[minDeltaIndex-1].count + 165 | h.bins[minDeltaIndex].value* 166 | h.bins[minDeltaIndex].count) / 167 | totalCount, // weighted average 168 | count: totalCount, // summed heights 169 | } 170 | head := append(make([]bin, 0), h.bins[0:minDeltaIndex-1]...) 171 | tail := append([]bin{mergedbin}, h.bins[minDeltaIndex+1:]...) 172 | h.bins = append(head, tail...) 173 | } 174 | } 175 | 176 | // String returns a string reprentation of the histogram, 177 | // which is useful for printing to a terminal. 178 | func (h *WeightedHistogram) String() (str string) { 179 | str += fmt.Sprintln("Total:", h.total) 180 | 181 | for i := range h.bins { 182 | var bar string 183 | for j := 0; j < int(float64(h.bins[i].count)/float64(h.total)*200); j++ { 184 | bar += "." 185 | } 186 | str += fmt.Sprintln(h.bins[i].value, "\t", bar) 187 | } 188 | 189 | return 190 | } 191 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Bourgon 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 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/endpoint/doc.go: -------------------------------------------------------------------------------- 1 | // Package endpoint defines an abstraction for RPCs. 2 | // 3 | // Endpoints are a fundamental building block for many Go kit components. 4 | // Endpoints are implemented by servers, and called by clients. 5 | package endpoint 6 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/endpoint/endpoint.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // Endpoint is the fundamental building block of servers and clients. 8 | // It represents a single RPC method. 9 | type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error) 10 | 11 | // Nop is an endpoint that does nothing and returns a nil error. 12 | // Useful for tests. 13 | func Nop(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil } 14 | 15 | // Middleware is a chainable behavior modifier for endpoints. 16 | type Middleware func(Endpoint) Endpoint 17 | 18 | // Chain is a helper function for composing middlewares. Requests will 19 | // traverse them in the order they're declared. That is, the first middleware 20 | // is treated as the outermost middleware. 21 | func Chain(outer Middleware, others ...Middleware) Middleware { 22 | return func(next Endpoint) Endpoint { 23 | for i := len(others) - 1; i >= 0; i-- { // reverse 24 | next = others[i](next) 25 | } 26 | return outer(next) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/README.md: -------------------------------------------------------------------------------- 1 | # package log 2 | 3 | `package log` provides a minimal interface for structured logging in services. 4 | It may be wrapped to encode conventions, enforce type-safety, provide leveled 5 | logging, and so on. It can be used for both typical application log events, 6 | and log-structured data streams. 7 | 8 | ## Structured logging 9 | 10 | Structured logging is, basically, conceding to the reality that logs are 11 | _data_, and warrant some level of schematic rigor. Using a stricter, 12 | key/value-oriented message format for our logs, containing contextual and 13 | semantic information, makes it much easier to get insight into the 14 | operational activity of the systems we build. Consequently, `package log` is 15 | of the strong belief that "[the benefits of structured logging outweigh the 16 | minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)". 17 | 18 | Migrating from unstructured to structured logging is probably a lot easier 19 | than you'd expect. 20 | 21 | ```go 22 | // Unstructured 23 | log.Printf("HTTP server listening on %s", addr) 24 | 25 | // Structured 26 | logger.Log("transport", "HTTP", "addr", addr, "msg", "listening") 27 | ``` 28 | 29 | ## Usage 30 | 31 | ### Typical application logging 32 | 33 | ```go 34 | w := log.NewSyncWriter(os.Stderr) 35 | logger := log.NewLogfmtLogger(w) 36 | logger.Log("question", "what is the meaning of life?", "answer", 42) 37 | 38 | // Output: 39 | // question="what is the meaning of life?" answer=42 40 | ``` 41 | 42 | ### Contextual Loggers 43 | 44 | ```go 45 | func main() { 46 | var logger log.Logger 47 | logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) 48 | logger = log.With(logger, "instance_id", 123) 49 | 50 | logger.Log("msg", "starting") 51 | NewWorker(log.With(logger, "component", "worker")).Run() 52 | NewSlacker(log.With(logger, "component", "slacker")).Run() 53 | } 54 | 55 | // Output: 56 | // instance_id=123 msg=starting 57 | // instance_id=123 component=worker msg=running 58 | // instance_id=123 component=slacker msg=running 59 | ``` 60 | 61 | ### Interact with stdlib logger 62 | 63 | Redirect stdlib logger to Go kit logger. 64 | 65 | ```go 66 | import ( 67 | "os" 68 | stdlog "log" 69 | kitlog "github.com/go-kit/kit/log" 70 | ) 71 | 72 | func main() { 73 | logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout)) 74 | stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) 75 | stdlog.Print("I sure like pie") 76 | } 77 | 78 | // Output: 79 | // {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"} 80 | ``` 81 | 82 | Or, if, for legacy reasons, you need to pipe all of your logging through the 83 | stdlib log package, you can redirect Go kit logger to the stdlib logger. 84 | 85 | ```go 86 | logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{}) 87 | logger.Log("legacy", true, "msg", "at least it's something") 88 | 89 | // Output: 90 | // 2016/01/01 12:34:56 legacy=true msg="at least it's something" 91 | ``` 92 | 93 | ### Timestamps and callers 94 | 95 | ```go 96 | var logger log.Logger 97 | logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) 98 | logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) 99 | 100 | logger.Log("msg", "hello") 101 | 102 | // Output: 103 | // ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello 104 | ``` 105 | 106 | ## Supported output formats 107 | 108 | - [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write)) 109 | - JSON 110 | 111 | ## Enhancements 112 | 113 | `package log` is centered on the one-method Logger interface. 114 | 115 | ```go 116 | type Logger interface { 117 | Log(keyvals ...interface{}) error 118 | } 119 | ``` 120 | 121 | This interface, and its supporting code like is the product of much iteration 122 | and evaluation. For more details on the evolution of the Logger interface, 123 | see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1), 124 | a talk by [Chris Hines](https://github.com/ChrisHines). 125 | Also, please see 126 | [#63](https://github.com/go-kit/kit/issues/63), 127 | [#76](https://github.com/go-kit/kit/pull/76), 128 | [#131](https://github.com/go-kit/kit/issues/131), 129 | [#157](https://github.com/go-kit/kit/pull/157), 130 | [#164](https://github.com/go-kit/kit/issues/164), and 131 | [#252](https://github.com/go-kit/kit/pull/252) 132 | to review historical conversations about package log and the Logger interface. 133 | 134 | Value-add packages and suggestions, 135 | like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level), 136 | are of course welcome. Good proposals should 137 | 138 | - Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With), 139 | - Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and 140 | - Be friendly to packages that accept only an unadorned log.Logger. 141 | 142 | ## Benchmarks & comparisons 143 | 144 | There are a few Go logging benchmarks and comparisons that include Go kit's package log. 145 | 146 | - [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log 147 | - [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log 148 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/doc.go: -------------------------------------------------------------------------------- 1 | // Package log provides a structured logger. 2 | // 3 | // Structured logging produces logs easily consumed later by humans or 4 | // machines. Humans might be interested in debugging errors, or tracing 5 | // specific requests. Machines might be interested in counting interesting 6 | // events, or aggregating information for off-line processing. In both cases, 7 | // it is important that the log messages are structured and actionable. 8 | // Package log is designed to encourage both of these best practices. 9 | // 10 | // Basic Usage 11 | // 12 | // The fundamental interface is Logger. Loggers create log events from 13 | // key/value data. The Logger interface has a single method, Log, which 14 | // accepts a sequence of alternating key/value pairs, which this package names 15 | // keyvals. 16 | // 17 | // type Logger interface { 18 | // Log(keyvals ...interface{}) error 19 | // } 20 | // 21 | // Here is an example of a function using a Logger to create log events. 22 | // 23 | // func RunTask(task Task, logger log.Logger) string { 24 | // logger.Log("taskID", task.ID, "event", "starting task") 25 | // ... 26 | // logger.Log("taskID", task.ID, "event", "task complete") 27 | // } 28 | // 29 | // The keys in the above example are "taskID" and "event". The values are 30 | // task.ID, "starting task", and "task complete". Every key is followed 31 | // immediately by its value. 32 | // 33 | // Keys are usually plain strings. Values may be any type that has a sensible 34 | // encoding in the chosen log format. With structured logging it is a good 35 | // idea to log simple values without formatting them. This practice allows 36 | // the chosen logger to encode values in the most appropriate way. 37 | // 38 | // Contextual Loggers 39 | // 40 | // A contextual logger stores keyvals that it includes in all log events. 41 | // Building appropriate contextual loggers reduces repetition and aids 42 | // consistency in the resulting log output. With and WithPrefix add context to 43 | // a logger. We can use With to improve the RunTask example. 44 | // 45 | // func RunTask(task Task, logger log.Logger) string { 46 | // logger = log.With(logger, "taskID", task.ID) 47 | // logger.Log("event", "starting task") 48 | // ... 49 | // taskHelper(task.Cmd, logger) 50 | // ... 51 | // logger.Log("event", "task complete") 52 | // } 53 | // 54 | // The improved version emits the same log events as the original for the 55 | // first and last calls to Log. Passing the contextual logger to taskHelper 56 | // enables each log event created by taskHelper to include the task.ID even 57 | // though taskHelper does not have access to that value. Using contextual 58 | // loggers this way simplifies producing log output that enables tracing the 59 | // life cycle of individual tasks. (See the Contextual example for the full 60 | // code of the above snippet.) 61 | // 62 | // Dynamic Contextual Values 63 | // 64 | // A Valuer function stored in a contextual logger generates a new value each 65 | // time an event is logged. The Valuer example demonstrates how this feature 66 | // works. 67 | // 68 | // Valuers provide the basis for consistently logging timestamps and source 69 | // code location. The log package defines several valuers for that purpose. 70 | // See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and 71 | // DefaultCaller. A common logger initialization sequence that ensures all log 72 | // entries contain a timestamp and source location looks like this: 73 | // 74 | // logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) 75 | // logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) 76 | // 77 | // Concurrent Safety 78 | // 79 | // Applications with multiple goroutines want each log event written to the 80 | // same logger to remain separate from other log events. Package log provides 81 | // two simple solutions for concurrent safe logging. 82 | // 83 | // NewSyncWriter wraps an io.Writer and serializes each call to its Write 84 | // method. Using a SyncWriter has the benefit that the smallest practical 85 | // portion of the logging logic is performed within a mutex, but it requires 86 | // the formatting Logger to make only one call to Write per log event. 87 | // 88 | // NewSyncLogger wraps any Logger and serializes each call to its Log method. 89 | // Using a SyncLogger has the benefit that it guarantees each log event is 90 | // handled atomically within the wrapped logger, but it typically serializes 91 | // both the formatting and output logic. Use a SyncLogger if the formatting 92 | // logger may perform multiple writes per log event. 93 | // 94 | // Error Handling 95 | // 96 | // This package relies on the practice of wrapping or decorating loggers with 97 | // other loggers to provide composable pieces of functionality. It also means 98 | // that Logger.Log must return an error because some 99 | // implementations—especially those that output log data to an io.Writer—may 100 | // encounter errors that cannot be handled locally. This in turn means that 101 | // Loggers that wrap other loggers should return errors from the wrapped 102 | // logger up the stack. 103 | // 104 | // Fortunately, the decorator pattern also provides a way to avoid the 105 | // necessity to check for errors every time an application calls Logger.Log. 106 | // An application required to panic whenever its Logger encounters 107 | // an error could initialize its logger as follows. 108 | // 109 | // fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) 110 | // logger := log.LoggerFunc(func(keyvals ...interface{}) error { 111 | // if err := fmtlogger.Log(keyvals...); err != nil { 112 | // panic(err) 113 | // } 114 | // return nil 115 | // }) 116 | package log 117 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/json_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "encoding" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "reflect" 9 | ) 10 | 11 | type jsonLogger struct { 12 | io.Writer 13 | } 14 | 15 | // NewJSONLogger returns a Logger that encodes keyvals to the Writer as a 16 | // single JSON object. Each log event produces no more than one call to 17 | // w.Write. The passed Writer must be safe for concurrent use by multiple 18 | // goroutines if the returned Logger will be used concurrently. 19 | func NewJSONLogger(w io.Writer) Logger { 20 | return &jsonLogger{w} 21 | } 22 | 23 | func (l *jsonLogger) Log(keyvals ...interface{}) error { 24 | n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd 25 | m := make(map[string]interface{}, n) 26 | for i := 0; i < len(keyvals); i += 2 { 27 | k := keyvals[i] 28 | var v interface{} = ErrMissingValue 29 | if i+1 < len(keyvals) { 30 | v = keyvals[i+1] 31 | } 32 | merge(m, k, v) 33 | } 34 | return json.NewEncoder(l.Writer).Encode(m) 35 | } 36 | 37 | func merge(dst map[string]interface{}, k, v interface{}) { 38 | var key string 39 | switch x := k.(type) { 40 | case string: 41 | key = x 42 | case fmt.Stringer: 43 | key = safeString(x) 44 | default: 45 | key = fmt.Sprint(x) 46 | } 47 | 48 | // We want json.Marshaler and encoding.TextMarshaller to take priority over 49 | // err.Error() and v.String(). But json.Marshall (called later) does that by 50 | // default so we force a no-op if it's one of those 2 case. 51 | switch x := v.(type) { 52 | case json.Marshaler: 53 | case encoding.TextMarshaler: 54 | case error: 55 | v = safeError(x) 56 | case fmt.Stringer: 57 | v = safeString(x) 58 | } 59 | 60 | dst[key] = v 61 | } 62 | 63 | func safeString(str fmt.Stringer) (s string) { 64 | defer func() { 65 | if panicVal := recover(); panicVal != nil { 66 | if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { 67 | s = "NULL" 68 | } else { 69 | panic(panicVal) 70 | } 71 | } 72 | }() 73 | s = str.String() 74 | return 75 | } 76 | 77 | func safeError(err error) (s interface{}) { 78 | defer func() { 79 | if panicVal := recover(); panicVal != nil { 80 | if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { 81 | s = nil 82 | } else { 83 | panic(panicVal) 84 | } 85 | } 86 | }() 87 | s = err.Error() 88 | return 89 | } 90 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "errors" 4 | 5 | // Logger is the fundamental interface for all log operations. Log creates a 6 | // log event from keyvals, a variadic sequence of alternating keys and values. 7 | // Implementations must be safe for concurrent use by multiple goroutines. In 8 | // particular, any implementation of Logger that appends to keyvals or 9 | // modifies or retains any of its elements must make a copy first. 10 | type Logger interface { 11 | Log(keyvals ...interface{}) error 12 | } 13 | 14 | // ErrMissingValue is appended to keyvals slices with odd length to substitute 15 | // the missing value. 16 | var ErrMissingValue = errors.New("(MISSING)") 17 | 18 | // With returns a new contextual logger with keyvals prepended to those passed 19 | // to calls to Log. If logger is also a contextual logger created by With or 20 | // WithPrefix, keyvals is appended to the existing context. 21 | // 22 | // The returned Logger replaces all value elements (odd indexes) containing a 23 | // Valuer with their generated value for each call to its Log method. 24 | func With(logger Logger, keyvals ...interface{}) Logger { 25 | if len(keyvals) == 0 { 26 | return logger 27 | } 28 | l := newContext(logger) 29 | kvs := append(l.keyvals, keyvals...) 30 | if len(kvs)%2 != 0 { 31 | kvs = append(kvs, ErrMissingValue) 32 | } 33 | return &context{ 34 | logger: l.logger, 35 | // Limiting the capacity of the stored keyvals ensures that a new 36 | // backing array is created if the slice must grow in Log or With. 37 | // Using the extra capacity without copying risks a data race that 38 | // would violate the Logger interface contract. 39 | keyvals: kvs[:len(kvs):len(kvs)], 40 | hasValuer: l.hasValuer || containsValuer(keyvals), 41 | } 42 | } 43 | 44 | // WithPrefix returns a new contextual logger with keyvals prepended to those 45 | // passed to calls to Log. If logger is also a contextual logger created by 46 | // With or WithPrefix, keyvals is prepended to the existing context. 47 | // 48 | // The returned Logger replaces all value elements (odd indexes) containing a 49 | // Valuer with their generated value for each call to its Log method. 50 | func WithPrefix(logger Logger, keyvals ...interface{}) Logger { 51 | if len(keyvals) == 0 { 52 | return logger 53 | } 54 | l := newContext(logger) 55 | // Limiting the capacity of the stored keyvals ensures that a new 56 | // backing array is created if the slice must grow in Log or With. 57 | // Using the extra capacity without copying risks a data race that 58 | // would violate the Logger interface contract. 59 | n := len(l.keyvals) + len(keyvals) 60 | if len(keyvals)%2 != 0 { 61 | n++ 62 | } 63 | kvs := make([]interface{}, 0, n) 64 | kvs = append(kvs, keyvals...) 65 | if len(kvs)%2 != 0 { 66 | kvs = append(kvs, ErrMissingValue) 67 | } 68 | kvs = append(kvs, l.keyvals...) 69 | return &context{ 70 | logger: l.logger, 71 | keyvals: kvs, 72 | hasValuer: l.hasValuer || containsValuer(keyvals), 73 | } 74 | } 75 | 76 | // context is the Logger implementation returned by With and WithPrefix. It 77 | // wraps a Logger and holds keyvals that it includes in all log events. Its 78 | // Log method calls bindValues to generate values for each Valuer in the 79 | // context keyvals. 80 | // 81 | // A context must always have the same number of stack frames between calls to 82 | // its Log method and the eventual binding of Valuers to their value. This 83 | // requirement comes from the functional requirement to allow a context to 84 | // resolve application call site information for a Caller stored in the 85 | // context. To do this we must be able to predict the number of logging 86 | // functions on the stack when bindValues is called. 87 | // 88 | // Two implementation details provide the needed stack depth consistency. 89 | // 90 | // 1. newContext avoids introducing an additional layer when asked to 91 | // wrap another context. 92 | // 2. With and WithPrefix avoid introducing an additional layer by 93 | // returning a newly constructed context with a merged keyvals rather 94 | // than simply wrapping the existing context. 95 | type context struct { 96 | logger Logger 97 | keyvals []interface{} 98 | hasValuer bool 99 | } 100 | 101 | func newContext(logger Logger) *context { 102 | if c, ok := logger.(*context); ok { 103 | return c 104 | } 105 | return &context{logger: logger} 106 | } 107 | 108 | // Log replaces all value elements (odd indexes) containing a Valuer in the 109 | // stored context with their generated value, appends keyvals, and passes the 110 | // result to the wrapped Logger. 111 | func (l *context) Log(keyvals ...interface{}) error { 112 | kvs := append(l.keyvals, keyvals...) 113 | if len(kvs)%2 != 0 { 114 | kvs = append(kvs, ErrMissingValue) 115 | } 116 | if l.hasValuer { 117 | // If no keyvals were appended above then we must copy l.keyvals so 118 | // that future log events will reevaluate the stored Valuers. 119 | if len(keyvals) == 0 { 120 | kvs = append([]interface{}{}, l.keyvals...) 121 | } 122 | bindValues(kvs[:len(l.keyvals)]) 123 | } 124 | return l.logger.Log(kvs...) 125 | } 126 | 127 | // LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If 128 | // f is a function with the appropriate signature, LoggerFunc(f) is a Logger 129 | // object that calls f. 130 | type LoggerFunc func(...interface{}) error 131 | 132 | // Log implements Logger by calling f(keyvals...). 133 | func (f LoggerFunc) Log(keyvals ...interface{}) error { 134 | return f(keyvals...) 135 | } 136 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/logfmt_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "sync" 7 | 8 | "github.com/go-logfmt/logfmt" 9 | ) 10 | 11 | type logfmtEncoder struct { 12 | *logfmt.Encoder 13 | buf bytes.Buffer 14 | } 15 | 16 | func (l *logfmtEncoder) Reset() { 17 | l.Encoder.Reset() 18 | l.buf.Reset() 19 | } 20 | 21 | var logfmtEncoderPool = sync.Pool{ 22 | New: func() interface{} { 23 | var enc logfmtEncoder 24 | enc.Encoder = logfmt.NewEncoder(&enc.buf) 25 | return &enc 26 | }, 27 | } 28 | 29 | type logfmtLogger struct { 30 | w io.Writer 31 | } 32 | 33 | // NewLogfmtLogger returns a logger that encodes keyvals to the Writer in 34 | // logfmt format. Each log event produces no more than one call to w.Write. 35 | // The passed Writer must be safe for concurrent use by multiple goroutines if 36 | // the returned Logger will be used concurrently. 37 | func NewLogfmtLogger(w io.Writer) Logger { 38 | return &logfmtLogger{w} 39 | } 40 | 41 | func (l logfmtLogger) Log(keyvals ...interface{}) error { 42 | enc := logfmtEncoderPool.Get().(*logfmtEncoder) 43 | enc.Reset() 44 | defer logfmtEncoderPool.Put(enc) 45 | 46 | if err := enc.EncodeKeyvals(keyvals...); err != nil { 47 | return err 48 | } 49 | 50 | // Add newline to the end of the buffer 51 | if err := enc.EndRecord(); err != nil { 52 | return err 53 | } 54 | 55 | // The Logger interface requires implementations to be safe for concurrent 56 | // use by multiple goroutines. For this implementation that means making 57 | // only one call to l.w.Write() for each call to Log. 58 | if _, err := l.w.Write(enc.buf.Bytes()); err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/nop_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | type nopLogger struct{} 4 | 5 | // NewNopLogger returns a logger that doesn't do anything. 6 | func NewNopLogger() Logger { return nopLogger{} } 7 | 8 | func (nopLogger) Log(...interface{}) error { return nil } 9 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/stdlib.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | // StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's 11 | // designed to be passed to a Go kit logger as the writer, for cases where 12 | // it's necessary to redirect all Go kit log output to the stdlib logger. 13 | // 14 | // If you have any choice in the matter, you shouldn't use this. Prefer to 15 | // redirect the stdlib log to the Go kit logger via NewStdlibAdapter. 16 | type StdlibWriter struct{} 17 | 18 | // Write implements io.Writer. 19 | func (w StdlibWriter) Write(p []byte) (int, error) { 20 | log.Print(strings.TrimSpace(string(p))) 21 | return len(p), nil 22 | } 23 | 24 | // StdlibAdapter wraps a Logger and allows it to be passed to the stdlib 25 | // logger's SetOutput. It will extract date/timestamps, filenames, and 26 | // messages, and place them under relevant keys. 27 | type StdlibAdapter struct { 28 | Logger 29 | timestampKey string 30 | fileKey string 31 | messageKey string 32 | } 33 | 34 | // StdlibAdapterOption sets a parameter for the StdlibAdapter. 35 | type StdlibAdapterOption func(*StdlibAdapter) 36 | 37 | // TimestampKey sets the key for the timestamp field. By default, it's "ts". 38 | func TimestampKey(key string) StdlibAdapterOption { 39 | return func(a *StdlibAdapter) { a.timestampKey = key } 40 | } 41 | 42 | // FileKey sets the key for the file and line field. By default, it's "caller". 43 | func FileKey(key string) StdlibAdapterOption { 44 | return func(a *StdlibAdapter) { a.fileKey = key } 45 | } 46 | 47 | // MessageKey sets the key for the actual log message. By default, it's "msg". 48 | func MessageKey(key string) StdlibAdapterOption { 49 | return func(a *StdlibAdapter) { a.messageKey = key } 50 | } 51 | 52 | // NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed 53 | // logger. It's designed to be passed to log.SetOutput. 54 | func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { 55 | a := StdlibAdapter{ 56 | Logger: logger, 57 | timestampKey: "ts", 58 | fileKey: "caller", 59 | messageKey: "msg", 60 | } 61 | for _, option := range options { 62 | option(&a) 63 | } 64 | return a 65 | } 66 | 67 | func (a StdlibAdapter) Write(p []byte) (int, error) { 68 | result := subexps(p) 69 | keyvals := []interface{}{} 70 | var timestamp string 71 | if date, ok := result["date"]; ok && date != "" { 72 | timestamp = date 73 | } 74 | if time, ok := result["time"]; ok && time != "" { 75 | if timestamp != "" { 76 | timestamp += " " 77 | } 78 | timestamp += time 79 | } 80 | if timestamp != "" { 81 | keyvals = append(keyvals, a.timestampKey, timestamp) 82 | } 83 | if file, ok := result["file"]; ok && file != "" { 84 | keyvals = append(keyvals, a.fileKey, file) 85 | } 86 | if msg, ok := result["msg"]; ok { 87 | keyvals = append(keyvals, a.messageKey, msg) 88 | } 89 | if err := a.Logger.Log(keyvals...); err != nil { 90 | return 0, err 91 | } 92 | return len(p), nil 93 | } 94 | 95 | const ( 96 | logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` 97 | logRegexpTime = `(?P