Browse Source

add prometheus stats

sugar 2 years ago
parent
commit
deb58152c1
100 changed files with 14305 additions and 169 deletions
  1. 1 0
      .gitignore
  2. 15 0
      cmd/main.go
  3. 43 18
      gateway/gateway.go
  4. 3 3
      gateway/http/context.go
  5. 2 2
      gateway/rpc/codec/gob_codec.go
  6. 1 1
      gateway/rpc/message.go
  7. 4 4
      gateway/rpc/response.go
  8. 4 1
      go.mod
  9. 137 0
      go.sum
  10. 7 19
      options.go
  11. 1 1
      registry/selector.go
  12. 11 48
      service.go
  13. 155 0
      stats/counter.go
  14. 73 0
      stats/counter_test.go
  15. 381 0
      stats/counters.go
  16. 138 0
      stats/duration.go
  17. 125 0
      stats/duration_test.go
  18. 328 0
      stats/export.go
  19. 159 0
      stats/export_test.go
  20. 175 0
      stats/histogram.go
  21. 89 0
      stats/histogram_test.go
  22. 34 0
      stats/hooks.go
  23. 63 0
      stats/kebab_case_converter.go
  24. 54 0
      stats/kebab_case_converter_test.go
  25. 54 0
      stats/multidimensional.go
  26. 46 0
      stats/multidimensional_test.go
  27. 351 0
      stats/opentsdb/opentsdb.go
  28. 400 0
      stats/opentsdb/opentsdb_test.go
  29. 378 0
      stats/prometheusbackend/collectors.go
  30. 38 0
      stats/prometheusbackend/collectors_test.go
  31. 111 0
      stats/prometheusbackend/prometheusbackend.go
  32. 354 0
      stats/prometheusbackend/prometheusbackend_test.go
  33. 213 0
      stats/rates.go
  34. 142 0
      stats/rates_test.go
  35. 45 0
      stats/ring.go
  36. 64 0
      stats/snake_case_converter.go
  37. 54 0
      stats/snake_case_converter_test.go
  38. 253 0
      stats/timings.go
  39. 29 0
      stats/variable_interface.go
  40. 212 0
      sync/atomic.go
  41. 89 0
      sync/batcher.go
  42. 6 0
      sync/norace.go
  43. 6 0
      sync/race.go
  44. 97 0
      sync/semaphore.go
  45. 0 72
      utils/metric/metric.go
  46. 20 0
      vendor/github.com/beorn7/perks/LICENSE
  47. 2388 0
      vendor/github.com/beorn7/perks/quantile/exampledata.txt
  48. 316 0
      vendor/github.com/beorn7/perks/quantile/stream.go
  49. 8 0
      vendor/github.com/cespare/xxhash/v2/.travis.yml
  50. 22 0
      vendor/github.com/cespare/xxhash/v2/LICENSE.txt
  51. 67 0
      vendor/github.com/cespare/xxhash/v2/README.md
  52. 3 0
      vendor/github.com/cespare/xxhash/v2/go.mod
  53. 0 0
      vendor/github.com/cespare/xxhash/v2/go.sum
  54. 236 0
      vendor/github.com/cespare/xxhash/v2/xxhash.go
  55. 13 0
      vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go
  56. 215 0
      vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
  57. 76 0
      vendor/github.com/cespare/xxhash/v2/xxhash_other.go
  58. 15 0
      vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
  59. 46 0
      vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
  60. 3 0
      vendor/github.com/golang/protobuf/AUTHORS
  61. 3 0
      vendor/github.com/golang/protobuf/CONTRIBUTORS
  62. 28 0
      vendor/github.com/golang/protobuf/LICENSE
  63. 324 0
      vendor/github.com/golang/protobuf/proto/buffer.go
  64. 63 0
      vendor/github.com/golang/protobuf/proto/defaults.go
  65. 113 0
      vendor/github.com/golang/protobuf/proto/deprecated.go
  66. 58 0
      vendor/github.com/golang/protobuf/proto/discard.go
  67. 356 0
      vendor/github.com/golang/protobuf/proto/extensions.go
  68. 306 0
      vendor/github.com/golang/protobuf/proto/properties.go
  69. 167 0
      vendor/github.com/golang/protobuf/proto/proto.go
  70. 323 0
      vendor/github.com/golang/protobuf/proto/registry.go
  71. 801 0
      vendor/github.com/golang/protobuf/proto/text_decode.go
  72. 560 0
      vendor/github.com/golang/protobuf/proto/text_encode.go
  73. 78 0
      vendor/github.com/golang/protobuf/proto/wire.go
  74. 34 0
      vendor/github.com/golang/protobuf/proto/wrappers.go
  75. 165 0
      vendor/github.com/golang/protobuf/ptypes/any.go
  76. 62 0
      vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
  77. 6 0
      vendor/github.com/golang/protobuf/ptypes/doc.go
  78. 72 0
      vendor/github.com/golang/protobuf/ptypes/duration.go
  79. 63 0
      vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
  80. 103 0
      vendor/github.com/golang/protobuf/ptypes/timestamp.go
  81. 64 0
      vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
  82. 201 0
      vendor/github.com/matttproud/golang_protobuf_extensions/LICENSE
  83. 1 0
      vendor/github.com/matttproud/golang_protobuf_extensions/NOTICE
  84. 1 0
      vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore
  85. 7 0
      vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/Makefile
  86. 75 0
      vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
  87. 16 0
      vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go
  88. 46 0
      vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
  89. 201 0
      vendor/github.com/prometheus/client_golang/LICENSE
  90. 23 0
      vendor/github.com/prometheus/client_golang/NOTICE
  91. 1 0
      vendor/github.com/prometheus/client_golang/prometheus/.gitignore
  92. 1 0
      vendor/github.com/prometheus/client_golang/prometheus/README.md
  93. 120 0
      vendor/github.com/prometheus/client_golang/prometheus/collector.go
  94. 321 0
      vendor/github.com/prometheus/client_golang/prometheus/counter.go
  95. 186 0
      vendor/github.com/prometheus/client_golang/prometheus/desc.go
  96. 199 0
      vendor/github.com/prometheus/client_golang/prometheus/doc.go
  97. 86 0
      vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
  98. 42 0
      vendor/github.com/prometheus/client_golang/prometheus/fnv.go
  99. 289 0
      vendor/github.com/prometheus/client_golang/prometheus/gauge.go
  100. 367 0
      vendor/github.com/prometheus/client_golang/prometheus/go_collector.go

+ 1 - 0
.gitignore

@@ -9,6 +9,7 @@ _site
 _posts
 *.dat
 *.db
+.DS_Store
 
 
 # Go.gitignore

+ 15 - 0
cmd/main.go

@@ -0,0 +1,15 @@
+package main
+
+import (
+	"git.nspix.com/golang/micro"
+)
+
+func main() {
+	svr := micro.New(
+		micro.WithName("git.nspix.com/test", "0.0.01"),
+		micro.WithoutRegister(),
+		micro.WithStats(),
+		micro.WithPort(6567),
+	)
+	svr.Run()
+}

+ 43 - 18
gateway/gateway.go

@@ -8,6 +8,8 @@ import (
 	"net"
 	"sync"
 	"time"
+
+	"git.nspix.com/golang/micro/stats"
 )
 
 const (
@@ -17,6 +19,9 @@ const (
 var (
 	ErrShortFeature  = errors.New("short feature")
 	ErrFeatureExists = errors.New("feature already exists")
+
+	requestCounter     = stats.NewCounter("GatewayRequestCount", "Number of gateway request")
+	requestDropCounter = stats.NewCountersWithSingleLabel("GatewayRequestDropedCount", "Number of dropped by unusual request", "Reason")
 )
 
 type (
@@ -69,32 +74,52 @@ func (g *Gateway) Detach(feature []byte) (err error) {
 	return
 }
 
-func (g *Gateway) loop(ctx context.Context) {
+func (g *Gateway) process(conn net.Conn) {
 	var (
 		n       int
-		ok      bool
 		err     error
-		conn    net.Conn
+		missing uint8
 		feature = make([]byte, MinFeatureLength)
 	)
+	requestCounter.Add(1)
+	//set deadline
+	if err = conn.SetReadDeadline(time.Now().Add(time.Millisecond * 800)); err != nil {
+		requestDropCounter.Add("ERROR", 1)
+		return
+	}
+	if n, err = io.ReadFull(conn, feature); err != nil {
+		requestDropCounter.Add("EOF", 1)
+		_ = conn.Close()
+		return
+	}
+	//reset deadline
+	if err = conn.SetReadDeadline(time.Time{}); err != nil {
+		requestDropCounter.Add("ERROR", 1)
+		return
+	}
+	missing = 1
+	for _, l := range g.listeners {
+		if bytes.Equal(feature[:n], l.feature[:n]) {
+			l.l.Receive(wrapConn(conn, feature[:n]))
+			missing = 0
+			break
+		}
+	}
+	if missing == 1 {
+		requestDropCounter.Add("MISSING", 1)
+	}
+}
+
+func (g *Gateway) wroker(ctx context.Context) {
+	var (
+		ok   bool
+		conn net.Conn
+	)
 	for {
 		select {
 		case conn, ok = <-g.ch:
 			if ok {
-				//set deadline
-				_ = conn.SetReadDeadline(time.Now().Add(time.Millisecond * 800))
-				if n, err = io.ReadFull(conn, feature); err != nil {
-					_ = conn.Close()
-					break
-				}
-				//reset deadline
-				_ = conn.SetReadDeadline(time.Time{})
-				for _, l := range g.listeners {
-					if bytes.Equal(feature[:n], l.feature[:n]) {
-						l.l.Receive(wrapConn(conn, feature[:n]))
-						break
-					}
-				}
+				g.process(conn)
 			}
 		case <-ctx.Done():
 			return
@@ -121,7 +146,7 @@ func (g *Gateway) Run(ctx context.Context) {
 	var wg sync.WaitGroup
 	wg.Add(2)
 	go func() {
-		g.loop(ctx)
+		g.wroker(ctx)
 		wg.Done()
 	}()
 	go func() {

+ 3 - 3
gateway/http/context.go

@@ -59,8 +59,8 @@ func (c *Context) Error(code int, message string) (err error) {
 	return
 }
 
-func (c *Context) SetParam(name string,value string)  {
-	if c.params == nil{
+func (c *Context) SetParam(name string, value string) {
+	if c.params == nil {
 		c.params = make(map[string]string)
 	}
 	c.params[name] = value
@@ -71,4 +71,4 @@ func (c *Context) ParamValue(name string) string {
 		return c.params[name]
 	}
 	return ""
-}
+}

+ 2 - 2
gateway/rpc/codec/gob_codec.go

@@ -6,7 +6,7 @@ import (
 	"io"
 )
 
-type GobCodec struct {}
+type GobCodec struct{}
 
 func (codec *GobCodec) EncodeTo(w io.Writer, i interface{}) (err error) {
 	return gob.NewEncoder(w).Encode(i)
@@ -26,4 +26,4 @@ func (codec *GobCodec) Encode(i interface{}) (b []byte, err error) {
 
 func (codec *GobCodec) Decode(b []byte, i interface{}) (err error) {
 	return gob.NewDecoder(bytes.NewReader(b)).Decode(i)
-}
+}

+ 1 - 1
gateway/rpc/message.go

@@ -14,7 +14,7 @@ const (
 )
 
 var (
-	feature           = []byte{82, 80, 67}				//RPC数据包
+	feature           = []byte{82, 80, 67} //RPC数据包
 	errInvalidPackage = errors.New("invalid package")
 )
 

+ 4 - 4
gateway/rpc/response.go

@@ -87,7 +87,7 @@ func (r *Response) WriteTo(w io.Writer) (n int64, err error) {
 
 func ReadResponse(b []byte) (resp *Response, err error) {
 	resp = &Response{
-		codec:  codec.DefaultCodec,
+		codec: codec.DefaultCodec,
 	}
 	if b == nil || len(b) <= 6 {
 		err = io.ErrShortBuffer
@@ -120,8 +120,8 @@ func ReadResponse(b []byte) (resp *Response, err error) {
 	return
 }
 
-func NewResponse() *Response  {
+func NewResponse() *Response {
 	return &Response{
-		codec:   codec.DefaultCodec,
+		codec: codec.DefaultCodec,
 	}
-}
+}

+ 4 - 1
go.mod

@@ -2,4 +2,7 @@ module git.nspix.com/golang/micro
 
 go 1.15
 
-require golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+require (
+	github.com/prometheus/client_golang v1.11.0
+	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+)

+ 137 - 0
go.sum

@@ -1,6 +1,143 @@
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 7 - 19
options.go

@@ -2,16 +2,12 @@ package micro
 
 import (
 	"context"
-	"os"
 	"strings"
 
 	"git.nspix.com/golang/micro/registry"
-	"git.nspix.com/golang/micro/utils/metric"
 )
 
 type (
-	ReadMetricFunc func() metric.Values
-
 	Options struct {
 		Zone                   string            //注册域
 		Name                   string            //名称
@@ -24,9 +20,8 @@ type (
 		Server                 Server            //加载的服务
 		Port                   int               //绑定端口
 		Address                string            //绑定地址
-		TSDBUrl                string            //TSDB数据上报URL
-		MetricCallback         ReadMetricFunc    //读取系统数据的回调
 		EnableHttpPProf        bool              //启用HTTP调试工具
+		EnableStats            bool              //启用数据统计
 		Context                context.Context
 		shortName              string
 	}
@@ -59,27 +54,21 @@ func WithPort(port int) Option {
 	}
 }
 
-func WithHttpDebug() Option {
+func WithStats() Option {
 	return func(o *Options) {
-		o.EnableHttpPProf = true
+		o.EnableStats = true
 	}
 }
 
-func WithRegistry(r registry.Registry) Option {
-	return func(o *Options) {
-		o.registry = r
-	}
-}
-
-func WithTSDBUrl(uri string) Option {
+func WithHttpDebug() Option {
 	return func(o *Options) {
-		o.TSDBUrl = uri
+		o.EnableHttpPProf = true
 	}
 }
 
-func WithMetricCallback(cb ReadMetricFunc) Option {
+func WithRegistry(r registry.Registry) Option {
 	return func(o *Options) {
-		o.MetricCallback = cb
+		o.registry = r
 	}
 }
 
@@ -117,6 +106,5 @@ func NewOptions() *Options {
 		Context:                context.Background(),
 		registry:               registry.DefaultRegistry,
 	}
-	opts.TSDBUrl = os.Getenv("TSDB_URL")
 	return opts
 }

+ 1 - 1
registry/selector.go

@@ -13,7 +13,7 @@ type (
 	}
 )
 
-func (selector *Selector) Select(ctx context.Context,name string) (node *ServiceNode, err error) {
+func (selector *Selector) Select(ctx context.Context, name string) (node *ServiceNode, err error) {
 	var (
 		nodes []*ServiceNode
 	)

+ 11 - 48
service.go

@@ -9,10 +9,8 @@ import (
 	"net/http/pprof"
 	"os"
 	"os/signal"
-	"runtime"
 	"strings"
 	"sync"
-	"sync/atomic"
 	"syscall"
 	"time"
 
@@ -21,14 +19,11 @@ import (
 	"git.nspix.com/golang/micro/gateway/rpc"
 	"git.nspix.com/golang/micro/log"
 	"git.nspix.com/golang/micro/registry"
+	"git.nspix.com/golang/micro/stats/prometheusbackend"
 	"git.nspix.com/golang/micro/utils/docker"
-	"git.nspix.com/golang/micro/utils/metric"
 	"git.nspix.com/golang/micro/utils/net/ip"
 	"git.nspix.com/golang/micro/utils/unsafestr"
-)
-
-var (
-	upMetricProcessFlag int32
+	"github.com/prometheus/client_golang/prometheus/promhttp"
 )
 
 type Service struct {
@@ -59,10 +54,8 @@ func (svr *Service) eventLoop() {
 	var (
 		err            error
 		registryTicker *time.Ticker
-		upMetricTicker *time.Ticker
 	)
 	registryTicker = time.NewTicker(time.Second * 20)
-	upMetricTicker = time.NewTicker(time.Second * 5)
 	defer func() {
 		registryTicker.Stop()
 	}()
@@ -74,46 +67,12 @@ func (svr *Service) eventLoop() {
 					log.Warnf("registry service %s error: %s", svr.opts.Name, err.Error())
 				}
 			}
-		case <-upMetricTicker.C:
-			if svr.opts.TSDBUrl != "" {
-				if atomic.CompareAndSwapInt32(&upMetricProcessFlag, 0, 1) {
-					go svr.upMetrics()
-				}
-			}
 		case <-svr.ctx.Done():
 			return
 		}
 	}
 }
 
-func (svr *Service) upMetrics() {
-	ms := make(metric.Values, 0)
-	tags := make(map[string]string)
-	tags["host"] = ip.InternalIP()
-	tags["name"] = svr.opts.ShortName()
-	tags["version"] = svr.opts.Version
-	memStats := &runtime.MemStats{}
-	runtime.ReadMemStats(memStats)
-	ms = append(ms, metric.New("sys.go.goroutine", float64(runtime.NumGoroutine())).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.ccall", float64(runtime.NumCgoCall())).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.cpus", float64(runtime.NumCPU())).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.gc", float64(memStats.NumGC)).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.next_gc", float64(memStats.NextGC)).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.heap_alloc", float64(memStats.HeapAlloc)).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.heap_inuse", float64(memStats.HeapInuse)).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.memory_allow", float64(memStats.Alloc)).SetTags(tags))
-	ms = append(ms, metric.New("sys.go.memory_frees", float64(memStats.Frees)).SetTags(tags))
-
-	if svr.opts.MetricCallback != nil {
-		vs := svr.opts.MetricCallback()
-		for _, v := range vs {
-			v.SetTags(tags)
-			ms = append(ms, v)
-		}
-	}
-	metric.Push(svr.ctx, svr.opts.TSDBUrl, ms)
-}
-
 func (svr *Service) Handle(method string, cb HandleFunc, opts ...HandleOption) {
 	opt := &HandleOptions{HttpMethod: "POST"}
 	for _, f := range opts {
@@ -218,7 +177,7 @@ func (svr *Service) instance() *registry.ServiceNode {
 	return node
 }
 
-func (svr *Service) startHttpServe() (err error) {
+func (svr *Service) startHTTPServe() (err error) {
 	l := gateway.NewListener(svr.listener.Addr())
 	if err = svr.gateway.Attaches([][]byte{[]byte("GET"), []byte("POST"), []byte("PUT"), []byte("DELETE"), []byte("OPTIONS")}, l); err == nil {
 		svr.wrapSync(func() {
@@ -244,6 +203,10 @@ func (svr *Service) startHttpServe() (err error) {
 			svr.httpSvr.Handler("GET", "/debug/pprof/symbol", hp.HandlerFunc(pprof.Symbol))
 			svr.httpSvr.Handler("GET", "/debug/pprof/trace", hp.HandlerFunc(pprof.Trace))
 		}
+		if svr.opts.EnableStats {
+			prometheusbackend.Init(svr.opts.shortName)
+			svr.httpSvr.Handler("GET", "/metrics", promhttp.Handler())
+		}
 		log.Infof("attach http server success")
 	} else {
 		log.Warnf("attach http listener error: %s", err.Error())
@@ -251,7 +214,7 @@ func (svr *Service) startHttpServe() (err error) {
 	return
 }
 
-func (svr *Service) startRpcServe() (err error) {
+func (svr *Service) startRPCServe() (err error) {
 	l := gateway.NewListener(svr.listener.Addr())
 	if err = svr.gateway.Attach([]byte("RPC"), l); err == nil {
 		svr.wrapSync(func() {
@@ -292,7 +255,7 @@ func (svr *Service) prepare() (err error) {
 		if svr.listener, err = net.ListenTCP("tcp", tcpAddr); err != nil {
 			return
 		}
-		log.Infof("listener on: %s", svr.listener.Addr())
+		log.Infof("listen on: %s", svr.listener.Addr())
 		svr.gateway = gateway.New(svr.listener)
 		svr.wrapSync(func() {
 			svr.gateway.Run(svr.ctx)
@@ -300,12 +263,12 @@ func (svr *Service) prepare() (err error) {
 
 		//start http serve
 		if svr.opts.EnableHttp {
-			err = svr.startHttpServe()
+			err = svr.startHTTPServe()
 		}
 
 		//start rpc serve
 		if svr.opts.EnableRPC {
-			err = svr.startRpcServe()
+			err = svr.startRPCServe()
 		}
 	}
 	svr.node = svr.instance()

+ 155 - 0
stats/counter.go

@@ -0,0 +1,155 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"strconv"
+
+	"git.nspix.com/golang/micro/sync"
+)
+
+// logCounterNegative is for throttling adding a negative value to a counter messages in logs
+
+// Counter tracks a cumulative count of a metric.
+// For a one-dimensional or multi-dimensional counter, please use
+// CountersWithSingleLabel or CountersWithMultiLabels instead.
+type Counter struct {
+	i    sync.AtomicInt64
+	help string
+}
+
+// NewCounter returns a new Counter.
+func NewCounter(name string, help string) *Counter {
+	v := &Counter{help: help}
+	if name != "" {
+		publish(name, v)
+	}
+	return v
+}
+
+// Add adds the provided value to the Counter.
+func (v *Counter) Add(delta int64) {
+	v.i.Add(delta)
+}
+
+// Reset resets the counter value to 0.
+func (v *Counter) Reset() {
+	v.i.Set(int64(0))
+}
+
+// Get returns the value.
+func (v *Counter) Get() int64 {
+	return v.i.Get()
+}
+
+// String implements the expvar.Var interface.
+func (v *Counter) String() string {
+	return strconv.FormatInt(v.i.Get(), 10)
+}
+
+// Help returns the help string.
+func (v *Counter) Help() string {
+	return v.help
+}
+
+// CounterFunc allows to provide the counter value via a custom function.
+// For implementations that differentiate between Counters/Gauges,
+// CounterFunc's values only go up (or are reset to 0).
+type CounterFunc struct {
+	F    func() int64
+	help string
+}
+
+// NewCounterFunc creates a new CounterFunc instance and publishes it if name is
+// set.
+func NewCounterFunc(name string, help string, f func() int64) *CounterFunc {
+	c := &CounterFunc{
+		F:    f,
+		help: help,
+	}
+
+	if name != "" {
+		publish(name, c)
+	}
+	return c
+}
+
+// Help returns the help string.
+func (cf CounterFunc) Help() string {
+	return cf.help
+}
+
+// Get returns the value.
+func (cf CounterFunc) Get() int64 {
+	return cf.F()
+}
+
+// String implements expvar.Var.
+func (cf CounterFunc) String() string {
+	return strconv.FormatInt(cf.F(), 10)
+}
+
+// Gauge tracks the current value of an integer metric.
+// The emphasis here is on *current* i.e. this is not a cumulative counter.
+// For a one-dimensional or multi-dimensional gauge, please use
+// GaugeWithSingleLabel or GaugesWithMultiLabels instead.
+type Gauge struct {
+	Counter
+}
+
+// NewGauge creates a new Gauge and publishes it if name is set.
+func NewGauge(name string, help string) *Gauge {
+	v := &Gauge{Counter: Counter{help: help}}
+
+	if name != "" {
+		publish(name, v)
+	}
+	return v
+}
+
+// Set overwrites the current value.
+func (v *Gauge) Set(value int64) {
+	v.Counter.i.Set(value)
+}
+
+// Add adds the provided value to the Gauge.
+func (v *Gauge) Add(delta int64) {
+	v.Counter.i.Add(delta)
+}
+
+// GaugeFunc is the same as CounterFunc but meant for gauges.
+// It's a wrapper around CounterFunc for values that go up/down for
+// implementations (like Prometheus) that need to differ between Counters and
+// Gauges.
+type GaugeFunc struct {
+	CounterFunc
+}
+
+// NewGaugeFunc creates a new GaugeFunc instance and publishes it if name is
+// set.
+func NewGaugeFunc(name string, help string, f func() int64) *GaugeFunc {
+	i := &GaugeFunc{
+		CounterFunc: CounterFunc{
+			F:    f,
+			help: help,
+		}}
+
+	if name != "" {
+		publish(name, i)
+	}
+	return i
+}

+ 73 - 0
stats/counter_test.go

@@ -0,0 +1,73 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"expvar"
+	"testing"
+)
+
+func TestCounter(t *testing.T) {
+	var gotname string
+	var gotv *Counter
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*Counter)
+	})
+	v := NewCounter("Int", "help")
+	if gotname != "Int" {
+		t.Errorf("want Int, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	v.Add(1)
+	if v.Get() != 1 {
+		t.Errorf("want 1, got %v", v.Get())
+	}
+	if v.String() != "1" {
+		t.Errorf("want 1, got %v", v.Get())
+	}
+	v.Reset()
+	if v.Get() != 0 {
+		t.Errorf("want 0, got %v", v.Get())
+	}
+}
+
+func TestGaugeFunc(t *testing.T) {
+	var gotname string
+	var gotv *GaugeFunc
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*GaugeFunc)
+	})
+
+	v := NewGaugeFunc("name", "help", func() int64 {
+		return 1
+	})
+	if gotname != "name" {
+		t.Errorf("want name, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	if v.String() != "1" {
+		t.Errorf("want 1, got %v", v.String())
+	}
+}

+ 381 - 0
stats/counters.go

@@ -0,0 +1,381 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"sync"
+)
+
+// counters is similar to expvar.Map, except that it doesn't allow floats.
+// It is used to build CountersWithSingleLabel and GaugesWithSingleLabel.
+type counters struct {
+	mu     sync.Mutex
+	counts map[string]int64
+
+	help string
+}
+
+func (c *counters) String() string {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	b := &strings.Builder{}
+	fmt.Fprintf(b, "{")
+	prefix := ""
+	for k, v := range c.counts {
+		fmt.Fprintf(b, "%s%q: %v", prefix, k, v)
+		prefix = ", "
+	}
+	fmt.Fprintf(b, "}")
+	return b.String()
+}
+
+func (c *counters) add(name string, value int64) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.counts[name] = c.counts[name] + value
+}
+
+func (c *counters) set(name string, value int64) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.counts[name] = value
+}
+
+func (c *counters) reset() {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.counts = make(map[string]int64)
+}
+
+// ZeroAll zeroes out all values
+func (c *counters) ZeroAll() {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	for k := range c.counts {
+		c.counts[k] = 0
+	}
+}
+
+// Counts returns a copy of the Counters' map.
+func (c *counters) Counts() map[string]int64 {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	counts := make(map[string]int64, len(c.counts))
+	for k, v := range c.counts {
+		counts[k] = v
+	}
+	return counts
+}
+
+// Help returns the help string.
+func (c *counters) Help() string {
+	return c.help
+}
+
+// CountersWithSingleLabel tracks multiple counter values for a single
+// dimension ("label").
+// It provides a Counts method which can be used for tracking rates.
+type CountersWithSingleLabel struct {
+	counters
+	label         string
+	labelCombined bool
+}
+
+// NewCountersWithSingleLabel create a new Counters instance.
+// If name is set, the variable gets published.
+// The function also accepts an optional list of tags that pre-creates them
+// initialized to 0.
+// label is a category name used to organize the tags. It is currently only
+// used by Prometheus, but not by the expvar package.
+func NewCountersWithSingleLabel(name, help, label string, tags ...string) *CountersWithSingleLabel {
+	c := &CountersWithSingleLabel{
+		counters: counters{
+			counts: make(map[string]int64),
+			help:   help,
+		},
+		label:         label,
+		labelCombined: IsDimensionCombined(label),
+	}
+
+	if c.labelCombined {
+		c.counts[StatsAllStr] = 0
+	} else {
+		for _, tag := range tags {
+			c.counts[tag] = 0
+		}
+	}
+	if name != "" {
+		publish(name, c)
+	}
+	return c
+}
+
+// Label returns the label name.
+func (c *CountersWithSingleLabel) Label() string {
+	return c.label
+}
+
+// Add adds a value to a named counter.
+func (c *CountersWithSingleLabel) Add(name string, value int64) {
+	if c.labelCombined {
+		name = StatsAllStr
+	}
+	c.counters.add(name, value)
+}
+
+// Reset resets the value for the name.
+func (c *CountersWithSingleLabel) Reset(name string) {
+	if c.labelCombined {
+		name = StatsAllStr
+	}
+	c.counters.set(name, 0)
+}
+
+// ResetAll clears the counters
+func (c *CountersWithSingleLabel) ResetAll() {
+	c.counters.reset()
+}
+
+// CountersWithMultiLabels is a multidimensional counters implementation.
+// Internally, each tuple of dimensions ("labels") is stored as a single
+// label value where all label values are joined with ".".
+type CountersWithMultiLabels struct {
+	counters
+	labels         []string
+	combinedLabels []bool
+}
+
+// NewCountersWithMultiLabels creates a new CountersWithMultiLabels
+// instance, and publishes it if name is set.
+func NewCountersWithMultiLabels(name, help string, labels []string) *CountersWithMultiLabels {
+	t := &CountersWithMultiLabels{
+		counters: counters{
+			counts: make(map[string]int64),
+			help:   help},
+		labels:         labels,
+		combinedLabels: make([]bool, len(labels)),
+	}
+	for i, label := range labels {
+		t.combinedLabels[i] = IsDimensionCombined(label)
+	}
+	if name != "" {
+		publish(name, t)
+	}
+
+	return t
+}
+
+// Labels returns the list of labels.
+func (mc *CountersWithMultiLabels) Labels() []string {
+	return mc.labels
+}
+
+// Add adds a value to a named counter.
+// len(names) must be equal to len(Labels)
+func (mc *CountersWithMultiLabels) Add(names []string, value int64) {
+	if len(names) != len(mc.labels) {
+		panic("CountersWithMultiLabels: wrong number of values in Add")
+	}
+	mc.counters.add(safeJoinLabels(names, mc.combinedLabels), value)
+}
+
+// Reset resets the value of a named counter back to 0.
+// len(names) must be equal to len(Labels).
+func (mc *CountersWithMultiLabels) Reset(names []string) {
+	if len(names) != len(mc.labels) {
+		panic("CountersWithMultiLabels: wrong number of values in Reset")
+	}
+
+	mc.counters.set(safeJoinLabels(names, mc.combinedLabels), 0)
+}
+
+// ResetAll clears the counters
+func (mc *CountersWithMultiLabels) ResetAll() {
+	mc.counters.reset()
+}
+
+// Counts returns a copy of the Counters' map.
+// The key is a single string where all labels are joined by a "." e.g.
+// "label1.label2".
+func (mc *CountersWithMultiLabels) Counts() map[string]int64 {
+	return mc.counters.Counts()
+}
+
+// CountersFuncWithMultiLabels is a multidimensional counters implementation
+// where names of categories are compound names made with joining
+// multiple strings with '.'.  Since the map is returned by the
+// function, we assume it's in the right format (meaning each key is
+// of the form 'aaa.bbb.ccc' with as many elements as there are in
+// Labels).
+//
+// Note that there is no CountersFuncWithSingleLabel object. That this
+// because such an object would be identical to this one because these
+// function-based counters have no Add() or Set() method which are different
+// for the single vs. multiple labels cases.
+// If you have only a single label, pass an array with a single element.
+type CountersFuncWithMultiLabels struct {
+	f      func() map[string]int64
+	help   string
+	labels []string
+}
+
+// Labels returns the list of labels.
+func (c CountersFuncWithMultiLabels) Labels() []string {
+	return c.labels
+}
+
+// Help returns the help string.
+func (c CountersFuncWithMultiLabels) Help() string {
+	return c.help
+}
+
+// NewCountersFuncWithMultiLabels creates a new CountersFuncWithMultiLabels
+// mapping to the provided function.
+func NewCountersFuncWithMultiLabels(name, help string, labels []string, f func() map[string]int64) *CountersFuncWithMultiLabels {
+	t := &CountersFuncWithMultiLabels{
+		f:      f,
+		help:   help,
+		labels: labels,
+	}
+	if name != "" {
+		publish(name, t)
+	}
+
+	return t
+}
+
+// Counts returns a copy of the counters' map.
+func (c CountersFuncWithMultiLabels) Counts() map[string]int64 {
+	return c.f()
+}
+
+// String implements the expvar.Var interface.
+func (c CountersFuncWithMultiLabels) String() string {
+	m := c.f()
+	if m == nil {
+		return "{}"
+	}
+	b := bytes.NewBuffer(make([]byte, 0, 4096))
+	fmt.Fprintf(b, "{")
+	firstValue := true
+	for k, v := range m {
+		if firstValue {
+			firstValue = false
+		} else {
+			fmt.Fprintf(b, ", ")
+		}
+		fmt.Fprintf(b, "%q: %v", k, v)
+	}
+	fmt.Fprintf(b, "}")
+	return b.String()
+}
+
+// GaugesWithSingleLabel is similar to CountersWithSingleLabel, except its
+// meant to track the current value and not a cumulative count.
+type GaugesWithSingleLabel struct {
+	CountersWithSingleLabel
+}
+
+// NewGaugesWithSingleLabel creates a new GaugesWithSingleLabel and
+// publishes it if the name is set.
+func NewGaugesWithSingleLabel(name, help, label string, tags ...string) *GaugesWithSingleLabel {
+	g := &GaugesWithSingleLabel{
+		CountersWithSingleLabel: CountersWithSingleLabel{
+			counters: counters{
+				counts: make(map[string]int64),
+				help:   help,
+			},
+			label: label,
+		},
+	}
+
+	for _, tag := range tags {
+		g.counts[tag] = 0
+	}
+	if name != "" {
+		publish(name, g)
+	}
+	return g
+}
+
+// Set sets the value of a named gauge.
+func (g *GaugesWithSingleLabel) Set(name string, value int64) {
+	g.counters.set(name, value)
+}
+
+// GaugesWithMultiLabels is a CountersWithMultiLabels implementation where
+// the values can go up and down.
+type GaugesWithMultiLabels struct {
+	CountersWithMultiLabels
+}
+
+// NewGaugesWithMultiLabels creates a new GaugesWithMultiLabels instance,
+// and publishes it if name is set.
+func NewGaugesWithMultiLabels(name, help string, labels []string) *GaugesWithMultiLabels {
+	t := &GaugesWithMultiLabels{
+		CountersWithMultiLabels: CountersWithMultiLabels{
+			counters: counters{
+				counts: make(map[string]int64),
+				help:   help,
+			},
+			labels: labels,
+		}}
+	if name != "" {
+		publish(name, t)
+	}
+
+	return t
+}
+
+// Set sets the value of a named counter.
+// len(names) must be equal to len(Labels).
+func (mg *GaugesWithMultiLabels) Set(names []string, value int64) {
+	if len(names) != len(mg.CountersWithMultiLabels.labels) {
+		panic("GaugesWithMultiLabels: wrong number of values in Set")
+	}
+	mg.counters.set(safeJoinLabels(names, nil), value)
+}
+
+// GaugesFuncWithMultiLabels is a wrapper around CountersFuncWithMultiLabels
+// for values that go up/down for implementations (like Prometheus) that
+// need to differ between Counters and Gauges.
+type GaugesFuncWithMultiLabels struct {
+	CountersFuncWithMultiLabels
+}
+
+// NewGaugesFuncWithMultiLabels creates a new GaugesFuncWithMultiLabels
+// mapping to the provided function.
+func NewGaugesFuncWithMultiLabels(name, help string, labels []string, f func() map[string]int64) *GaugesFuncWithMultiLabels {
+	t := &GaugesFuncWithMultiLabels{
+		CountersFuncWithMultiLabels: CountersFuncWithMultiLabels{
+			f:      f,
+			help:   help,
+			labels: labels,
+		}}
+
+	if name != "" {
+		publish(name, t)
+	}
+
+	return t
+}

+ 138 - 0
stats/duration.go

@@ -0,0 +1,138 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"strconv"
+	"time"
+
+	"git.nspix.com/golang/micro/sync"
+)
+
+// CounterDuration exports a time.Duration as counter.
+type CounterDuration struct {
+	i    sync.AtomicDuration
+	help string
+}
+
+// NewCounterDuration returns a new CounterDuration.
+func NewCounterDuration(name, help string) *CounterDuration {
+	cd := &CounterDuration{
+		help: help,
+	}
+	publish(name, cd)
+	return cd
+}
+
+// Help implements the Variable interface.
+func (cd CounterDuration) Help() string {
+	return cd.help
+}
+
+// String is the implementation of expvar.var.
+func (cd CounterDuration) String() string {
+	return strconv.FormatInt(int64(cd.i.Get()), 10)
+}
+
+// Add adds the provided value to the CounterDuration.
+func (cd *CounterDuration) Add(delta time.Duration) {
+	cd.i.Add(delta)
+}
+
+// Get returns the value.
+func (cd *CounterDuration) Get() time.Duration {
+	return cd.i.Get()
+}
+
+// GaugeDuration exports a time.Duration as gauge.
+// In addition to CounterDuration, it also has Set() which allows overriding
+// the current value.
+type GaugeDuration struct {
+	CounterDuration
+}
+
+// NewGaugeDuration returns a new GaugeDuration.
+func NewGaugeDuration(name, help string) *GaugeDuration {
+	gd := &GaugeDuration{
+		CounterDuration: CounterDuration{
+			help: help,
+		},
+	}
+	publish(name, gd)
+	return gd
+}
+
+// Set sets the value.
+func (gd *GaugeDuration) Set(value time.Duration) {
+	gd.i.Set(value)
+}
+
+// CounterDurationFunc allows to provide the value via a custom function.
+type CounterDurationFunc struct {
+	F    func() time.Duration
+	help string
+}
+
+// NewCounterDurationFunc creates a new CounterDurationFunc instance and
+// publishes it if name is set.
+func NewCounterDurationFunc(name string, help string, f func() time.Duration) *CounterDurationFunc {
+	cf := &CounterDurationFunc{
+		F:    f,
+		help: help,
+	}
+
+	if name != "" {
+		publish(name, cf)
+	}
+	return cf
+}
+
+// Help implements the Variable interface.
+func (cf CounterDurationFunc) Help() string {
+	return cf.help
+}
+
+// Get returns the value.
+func (cf CounterDurationFunc) Get() int64 {
+	return int64(cf.F())
+}
+
+// String is the implementation of expvar.var.
+func (cf CounterDurationFunc) String() string {
+	return strconv.FormatInt(int64(cf.F()), 10)
+}
+
+// GaugeDurationFunc allows to provide the value via a custom function.
+type GaugeDurationFunc struct {
+	CounterDurationFunc
+}
+
+// NewGaugeDurationFunc creates a new GaugeDurationFunc instance and
+// publishes it if name is set.
+func NewGaugeDurationFunc(name string, help string, f func() time.Duration) *GaugeDurationFunc {
+	gf := &GaugeDurationFunc{
+		CounterDurationFunc: CounterDurationFunc{
+			F:    f,
+			help: help,
+		},
+	}
+
+	if name != "" {
+		publish(name, gf)
+	}
+	return gf
+}

+ 125 - 0
stats/duration_test.go

@@ -0,0 +1,125 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"expvar"
+	"testing"
+	"time"
+)
+
+func TestCounterDuration(t *testing.T) {
+	var gotname string
+	var gotv *CounterDuration
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*CounterDuration)
+	})
+	v := NewCounterDuration("CounterDuration", "help")
+	if gotname != "CounterDuration" {
+		t.Errorf("want CounterDuration, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	if v.Get() != 0 {
+		t.Errorf("want 0, got %v", v.Get())
+	}
+	v.Add(time.Duration(1))
+	if v.Get() != 1 {
+		t.Errorf("want 1, got %v", v.Get())
+	}
+	if v.String() != "1" {
+		t.Errorf("want 1, got %v", v.Get())
+	}
+}
+
+func TestCounterDurationFunc(t *testing.T) {
+	var gotname string
+	var gotv *CounterDurationFunc
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*CounterDurationFunc)
+	})
+
+	v := NewCounterDurationFunc("CounterDurationFunc", "help", func() time.Duration {
+		return time.Duration(1)
+	})
+	if gotname != "CounterDurationFunc" {
+		t.Errorf("want CounterDurationFunc, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	if v.String() != "1" {
+		t.Errorf("want 1, got %v", v.String())
+	}
+}
+
+func TestGaugeDuration(t *testing.T) {
+	var gotname string
+	var gotv *GaugeDuration
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*GaugeDuration)
+	})
+	v := NewGaugeDuration("GaugeDuration", "help")
+	if gotname != "GaugeDuration" {
+		t.Errorf("want GaugeDuration, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	v.Set(time.Duration(5))
+	if v.Get() != 5 {
+		t.Errorf("want 5, got %v", v.Get())
+	}
+	v.Add(time.Duration(1))
+	if v.Get() != 6 {
+		t.Errorf("want 6, got %v", v.Get())
+	}
+	if v.String() != "6" {
+		t.Errorf("want 6, got %v", v.Get())
+	}
+}
+
+func TestGaugeDurationFunc(t *testing.T) {
+	var gotname string
+	var gotv *GaugeDurationFunc
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*GaugeDurationFunc)
+	})
+
+	v := NewGaugeDurationFunc("GaugeDurationFunc", "help", func() time.Duration {
+		return time.Duration(1)
+	})
+
+	if gotname != "GaugeDurationFunc" {
+		t.Errorf("want GaugeDurationFunc, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	if v.String() != "1" {
+		t.Errorf("want 1, got %v", v.String())
+	}
+}

+ 328 - 0
stats/export.go

@@ -0,0 +1,328 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package stats is a wrapper for expvar. It additionally
+// exports new types that can be used to track performance.
+// It also provides a callback hook that allows a program
+// to export the variables using methods other than /debug/vars.
+// All variables support a String function that
+// is expected to return a JSON representation
+// of the variable.
+// Any function named Add will add the specified
+// number to the variable.
+// Any function named Counts returns a map of counts
+// that can be used by Rates to track rates over time.
+package stats
+
+import (
+	"bytes"
+	"expvar"
+	"flag"
+	"fmt"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+var emitStats = flag.Bool("emit_stats", false, "If set, emit stats to push-based monitoring and stats backends")
+var statsEmitPeriod = flag.Duration("stats_emit_period", time.Duration(60*time.Second), "Interval between emitting stats to all registered backends")
+var statsBackend = flag.String("stats_backend", "", "The name of the registered push-based monitoring/stats backend to use")
+var combineDimensions = flag.String("stats_combine_dimensions", "", `List of dimensions to be combined into a single "all" value in exported stats vars`)
+var dropVariables = flag.String("stats_drop_variables", "", `Variables to be dropped from the list of exported variables.`)
+
+// CommonTags is a comma-separated list of common tags for stats backends
+var CommonTags = flag.String("stats_common_tags", "", `Comma-separated list of common tags for the stats backend. It provides both label and values. Example: label1:value1,label2:value2`)
+
+// StatsAllStr is the consolidated name if a dimension gets combined.
+const StatsAllStr = "all"
+
+// NewVarHook is the type of a hook to export variables in a different way
+type NewVarHook func(name string, v expvar.Var)
+
+type varGroup struct {
+	sync.Mutex
+	vars       map[string]expvar.Var
+	newVarHook NewVarHook
+}
+
+func (vg *varGroup) register(nvh NewVarHook) {
+	vg.Lock()
+	defer vg.Unlock()
+	if vg.newVarHook != nil {
+		panic("You've already registered a function")
+	}
+	if nvh == nil {
+		panic("nil not allowed")
+	}
+	vg.newVarHook = nvh
+	// Call hook on existing vars because some might have been
+	// created before the call to register
+	for k, v := range vg.vars {
+		nvh(k, v)
+	}
+	vg.vars = nil
+}
+
+func (vg *varGroup) publish(name string, v expvar.Var) {
+	if isVarDropped(name) {
+		return
+	}
+	vg.Lock()
+	defer vg.Unlock()
+
+	expvar.Publish(name, v)
+	if vg.newVarHook != nil {
+		vg.newVarHook(name, v)
+	} else {
+		vg.vars[name] = v
+	}
+}
+
+var defaultVarGroup = varGroup{vars: make(map[string]expvar.Var)}
+
+// Register allows you to register a callback function
+// that will be called whenever a new stats variable gets
+// created. This can be used to build alternate methods
+// of exporting stats variables.
+func Register(nvh NewVarHook) {
+	defaultVarGroup.register(nvh)
+}
+
+// Publish is expvar.Publish+hook
+func Publish(name string, v expvar.Var) {
+	publish(name, v)
+}
+
+func publish(name string, v expvar.Var) {
+	defaultVarGroup.publish(name, v)
+}
+
+// PushBackend is an interface for any stats/metrics backend that requires data
+// to be pushed to it. It's used to support push-based metrics backends, as expvar
+// by default only supports pull-based ones.
+type PushBackend interface {
+	// PushAll pushes all stats from expvar to the backend
+	PushAll() error
+}
+
+var pushBackends = make(map[string]PushBackend)
+var pushBackendsLock sync.Mutex
+var once sync.Once
+
+// RegisterPushBackend allows modules to register PushBackend implementations.
+// Should be called on init().
+func RegisterPushBackend(name string, backend PushBackend) {
+	pushBackendsLock.Lock()
+	defer pushBackendsLock.Unlock()
+	pushBackends[name] = backend
+	if *emitStats {
+		// Start a single goroutine to emit stats periodically
+		once.Do(func() {
+			go emitToBackend(statsEmitPeriod)
+		})
+	}
+}
+
+// emitToBackend does a periodic emit to the selected PushBackend. If a push fails,
+// it will be logged as a warning (but things will otherwise proceed as normal).
+func emitToBackend(emitPeriod *time.Duration) (err error) {
+	ticker := time.NewTicker(*emitPeriod)
+	defer ticker.Stop()
+	for range ticker.C {
+		backend, ok := pushBackends[*statsBackend]
+		if !ok {
+			return
+		}
+		err = backend.PushAll()
+	}
+	return
+}
+
+// FloatFunc converts a function that returns
+// a float64 as an expvar.
+type FloatFunc func() float64
+
+// Help returns the help string (undefined currently)
+func (f FloatFunc) Help() string {
+	return "help"
+}
+
+// String is the implementation of expvar.var
+func (f FloatFunc) String() string {
+	return strconv.FormatFloat(f(), 'g', -1, 64)
+}
+
+// String is expvar.String+Get+hook
+type String struct {
+	mu sync.Mutex
+	s  string
+}
+
+// NewString returns a new String
+func NewString(name string) *String {
+	v := new(String)
+	publish(name, v)
+	return v
+}
+
+// Set sets the value
+func (v *String) Set(value string) {
+	v.mu.Lock()
+	v.s = value
+	v.mu.Unlock()
+}
+
+// Get returns the value
+func (v *String) Get() string {
+	v.mu.Lock()
+	s := v.s
+	v.mu.Unlock()
+	return s
+}
+
+// String is the implementation of expvar.var
+func (v *String) String() string {
+	return strconv.Quote(v.Get())
+}
+
+// StringFunc converts a function that returns
+// an string as an expvar.
+type StringFunc func() string
+
+// String is the implementation of expvar.var
+func (f StringFunc) String() string {
+	return strconv.Quote(f())
+}
+
+// JSONFunc is the public type for a single function that returns json directly.
+type JSONFunc func() string
+
+// String is the implementation of expvar.var
+func (f JSONFunc) String() string {
+	return f()
+}
+
+// PublishJSONFunc publishes any function that returns
+// a JSON string as a variable. The string is sent to
+// expvar as is.
+func PublishJSONFunc(name string, f func() string) {
+	publish(name, JSONFunc(f))
+}
+
+// StringMapFunc is the function equivalent of StringMap
+type StringMapFunc func() map[string]string
+
+// String is used by expvar.
+func (f StringMapFunc) String() string {
+	m := f()
+	if m == nil {
+		return "{}"
+	}
+	return stringMapToString(m)
+}
+
+func stringMapToString(m map[string]string) string {
+	b := bytes.NewBuffer(make([]byte, 0, 4096))
+	fmt.Fprintf(b, "{")
+	firstValue := true
+	for k, v := range m {
+		if firstValue {
+			firstValue = false
+		} else {
+			fmt.Fprintf(b, ", ")
+		}
+		fmt.Fprintf(b, "\"%v\": %v", k, strconv.Quote(v))
+	}
+	fmt.Fprintf(b, "}")
+	return b.String()
+}
+
+var (
+	varsMu             sync.Mutex
+	combinedDimensions map[string]bool
+	droppedVars        map[string]bool
+)
+
+// IsDimensionCombined returns true if the specified dimension should be combined.
+func IsDimensionCombined(name string) bool {
+	varsMu.Lock()
+	defer varsMu.Unlock()
+
+	if combinedDimensions == nil {
+		dims := strings.Split(*combineDimensions, ",")
+		combinedDimensions = make(map[string]bool, len(dims))
+		for _, dim := range dims {
+			if dim == "" {
+				continue
+			}
+			combinedDimensions[dim] = true
+		}
+	}
+	return combinedDimensions[name]
+}
+
+// safeJoinLabels joins the label values with ".", but first replaces any existing
+// "." characters in the labels with the proper replacement, to avoid issues parsing
+// them apart later. The function also replaces specific label values with "all"
+// if a dimenstion is marked as true in combinedLabels.
+func safeJoinLabels(labels []string, combinedLabels []bool) string {
+	sanitizedLabels := make([]string, len(labels))
+	for idx, label := range labels {
+		if combinedLabels != nil && combinedLabels[idx] {
+			sanitizedLabels[idx] = StatsAllStr
+		} else {
+			sanitizedLabels[idx] = safeLabel(label)
+		}
+	}
+	return strings.Join(sanitizedLabels, ".")
+}
+
+func safeLabel(label string) string {
+	return strings.Replace(label, ".", "_", -1)
+}
+
+func isVarDropped(name string) bool {
+	varsMu.Lock()
+	defer varsMu.Unlock()
+
+	if droppedVars == nil {
+		dims := strings.Split(*dropVariables, ",")
+		droppedVars = make(map[string]bool, len(dims))
+		for _, dim := range dims {
+			if dim == "" {
+				continue
+			}
+			droppedVars[dim] = true
+		}
+	}
+	return droppedVars[name]
+}
+
+// ParseCommonTags parses a comma-separated string into map of tags
+// If you want to global service values like host, service name, git revision, etc,
+// this is the place to do it.
+func ParseCommonTags(s string) map[string]string {
+	inputs := strings.Split(s, ",")
+	tags := make(map[string]string)
+	for _, input := range inputs {
+		if strings.Contains(input, ":") {
+			tag := strings.Split(input, ":")
+			tags[strings.TrimSpace(tag[0])] = strings.TrimSpace(tag[1])
+		}
+	}
+	return tags
+}

+ 159 - 0
stats/export_test.go

@@ -0,0 +1,159 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"expvar"
+	"reflect"
+	"testing"
+)
+
+func clear() {
+	defaultVarGroup.vars = make(map[string]expvar.Var)
+	defaultVarGroup.newVarHook = nil
+	*combineDimensions = ""
+	*dropVariables = ""
+	combinedDimensions = nil
+	droppedVars = nil
+}
+
+func TestNoHook(t *testing.T) {
+	clear()
+	v := NewCounter("plainint", "help")
+	v.Add(1)
+	if v.String() != "1" {
+		t.Errorf("want 1, got %s", v.String())
+	}
+}
+
+func TestString(t *testing.T) {
+	var gotname string
+	var gotv *String
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*String)
+	})
+	v := NewString("String")
+	if gotname != "String" {
+		t.Errorf("want String, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+	v.Set("a\"b")
+	if v.Get() != "a\"b" {
+		t.Errorf("want \"a\"b\", got %#v", gotv)
+	}
+	if v.String() != "\"a\\\"b\"" {
+		t.Errorf("want \"\"a\\\"b\"\", got %#v", gotv)
+	}
+
+	f := StringFunc(func() string {
+		return "a"
+	})
+	if f.String() != "\"a\"" {
+		t.Errorf("want \"a\", got %v", f.String())
+	}
+}
+
+type Mystr string
+
+func (m *Mystr) String() string {
+	return string(*m)
+}
+
+func TestPublish(t *testing.T) {
+	var gotname string
+	var gotv expvar.Var
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*Mystr)
+	})
+	v := Mystr("abcd")
+	Publish("Mystr", &v)
+	if gotname != "Mystr" {
+		t.Errorf("want Mystr, got %s", gotname)
+	}
+	if gotv != &v {
+		t.Errorf("want %#v, got %#v", &v, gotv)
+	}
+}
+
+func f() string {
+	return "abcd"
+}
+
+type expvarFunc func() string
+
+func (f expvarFunc) String() string {
+	return f()
+}
+
+func TestPublishFunc(t *testing.T) {
+	var gotname string
+	var gotv expvarFunc
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(expvarFunc)
+	})
+	publish("Myfunc", expvarFunc(f))
+	if gotname != "Myfunc" {
+		t.Errorf("want Myfunc, got %s", gotname)
+	}
+	if gotv.String() != f() {
+		t.Errorf("want %v, got %#v", f(), gotv())
+	}
+}
+
+func TestDropVariable(t *testing.T) {
+	clear()
+	*dropVariables = "dropTest"
+
+	// This should not panic.
+	_ = NewGaugesWithSingleLabel("dropTest", "help", "label")
+	_ = NewGaugesWithSingleLabel("dropTest", "help", "label")
+}
+
+func TestStringMapToString(t *testing.T) {
+	expected1 := "{\"aaa\": \"111\", \"bbb\": \"222\"}"
+	expected2 := "{\"bbb\": \"222\", \"aaa\": \"111\"}"
+	got := stringMapToString(map[string]string{"aaa": "111", "bbb": "222"})
+
+	if got != expected1 && got != expected2 {
+		t.Errorf("expected %v or %v, got  %v", expected1, expected2, got)
+	}
+}
+
+func TestParseCommonTags(t *testing.T) {
+	res := ParseCommonTags("")
+	if len(res) != 0 {
+		t.Errorf("expected empty result, got %v", res)
+	}
+	res = ParseCommonTags("s,a:b ")
+	expected1 := map[string]string{"a": "b"}
+	if !reflect.DeepEqual(expected1, res) {
+		t.Errorf("expected %v, got %v", expected1, res)
+	}
+	res = ParseCommonTags("a:b,  c:d")
+	expected2 := map[string]string{"a": "b", "c": "d"}
+	if !reflect.DeepEqual(expected2, res) {
+		t.Errorf("expected %v, got %v", expected2, res)
+	}
+}

+ 175 - 0
stats/histogram.go

@@ -0,0 +1,175 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"bytes"
+	"fmt"
+
+	"git.nspix.com/golang/micro/sync"
+)
+
+// Histogram tracks counts and totals while
+// splitting the counts under different buckets
+// using specified cutoffs.
+type Histogram struct {
+	name       string
+	help       string
+	cutoffs    []int64
+	labels     []string
+	countLabel string
+	totalLabel string
+	hook       func(int64)
+
+	buckets []sync.AtomicInt64
+	total   sync.AtomicInt64
+}
+
+// NewHistogram creates a histogram with auto-generated labels
+// based on the cutoffs. The buckets are categorized using the
+// following criterion: cutoff[i-1] < value <= cutoff[i]. Anything
+// higher than the highest cutoff is labeled as "inf".
+func NewHistogram(name, help string, cutoffs []int64) *Histogram {
+	labels := make([]string, len(cutoffs)+1)
+	for i, v := range cutoffs {
+		labels[i] = fmt.Sprintf("%d", v)
+	}
+	labels[len(labels)-1] = "inf"
+	return NewGenericHistogram(name, help, cutoffs, labels, "Count", "Total")
+}
+
+// NewGenericHistogram creates a histogram where all the labels are
+// supplied by the caller. The number of labels has to be one more than
+// the number of cutoffs because the last label captures everything that
+// exceeds the highest cutoff.
+func NewGenericHistogram(name, help string, cutoffs []int64, labels []string, countLabel, totalLabel string) *Histogram {
+	if len(cutoffs) != len(labels)-1 {
+		panic("mismatched cutoff and label lengths")
+	}
+	h := &Histogram{
+		name:       name,
+		help:       help,
+		cutoffs:    cutoffs,
+		labels:     labels,
+		countLabel: countLabel,
+		totalLabel: totalLabel,
+		buckets:    make([]sync.AtomicInt64, len(labels)),
+	}
+	if name != "" {
+		publish(name, h)
+	}
+	return h
+}
+
+// Add adds a new measurement to the Histogram.
+func (h *Histogram) Add(value int64) {
+	for i := range h.labels {
+		if i == len(h.labels)-1 || value <= h.cutoffs[i] {
+			h.buckets[i].Add(1)
+			h.total.Add(value)
+			break
+		}
+	}
+	if h.hook != nil {
+		h.hook(value)
+	}
+	if defaultStatsdHook.histogramHook != nil && h.name != "" {
+		defaultStatsdHook.histogramHook(h.name, value)
+	}
+}
+
+// String returns a string representation of the Histogram.
+// Note that sum of all buckets may not be equal to the total temporarily,
+// because Add() increments bucket and total with two atomic operations.
+func (h *Histogram) String() string {
+	b, _ := h.MarshalJSON()
+	return string(b)
+}
+
+// MarshalJSON returns a JSON representation of the Histogram.
+// Note that sum of all buckets may not be equal to the total temporarily,
+// because Add() increments bucket and total with two atomic operations.
+func (h *Histogram) MarshalJSON() ([]byte, error) {
+	b := bytes.NewBuffer(make([]byte, 0, 4096))
+	fmt.Fprintf(b, "{")
+	totalCount := int64(0)
+	for i, label := range h.labels {
+		count := h.buckets[i].Get()
+		totalCount += count
+		fmt.Fprintf(b, "\"%v\": %v, ", label, count)
+	}
+	fmt.Fprintf(b, "\"%s\": %v, ", h.countLabel, totalCount)
+	fmt.Fprintf(b, "\"%s\": %v", h.totalLabel, h.total.Get())
+	fmt.Fprintf(b, "}")
+	return b.Bytes(), nil
+}
+
+// Counts returns a map from labels to the current count in the Histogram for that label.
+func (h *Histogram) Counts() map[string]int64 {
+	counts := make(map[string]int64, len(h.labels))
+	for i, label := range h.labels {
+		counts[label] = h.buckets[i].Get()
+	}
+	return counts
+}
+
+// CountLabel returns the count label that was set when this Histogram was created.
+func (h *Histogram) CountLabel() string {
+	return h.countLabel
+}
+
+// Count returns the number of times Add has been called.
+func (h *Histogram) Count() (count int64) {
+	for i := range h.buckets {
+		count += h.buckets[i].Get()
+	}
+	return
+}
+
+// TotalLabel returns the total label that was set when this Histogram was created.
+func (h *Histogram) TotalLabel() string {
+	return h.totalLabel
+}
+
+// Total returns the sum of all values that have been added to this Histogram.
+func (h *Histogram) Total() (total int64) {
+	return h.total.Get()
+}
+
+// Labels returns the labels that were set when this Histogram was created.
+func (h *Histogram) Labels() []string {
+	return h.labels
+}
+
+// Cutoffs returns the cutoffs that were set when this Histogram was created.
+func (h *Histogram) Cutoffs() []int64 {
+	return h.cutoffs
+}
+
+// Buckets returns a snapshot of the current values in all buckets.
+func (h *Histogram) Buckets() []int64 {
+	buckets := make([]int64, len(h.buckets))
+	for i := range h.buckets {
+		buckets[i] = h.buckets[i].Get()
+	}
+	return buckets
+}
+
+// Help returns the help string.
+func (h *Histogram) Help() string {
+	return h.help
+}

+ 89 - 0
stats/histogram_test.go

@@ -0,0 +1,89 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"expvar"
+	"testing"
+)
+
+func TestHistogram(t *testing.T) {
+	clear()
+	h := NewHistogram("hist1", "help", []int64{1, 5})
+	for i := 0; i < 10; i++ {
+		h.Add(int64(i))
+	}
+	want := `{"1": 2, "5": 4, "inf": 4, "Count": 10, "Total": 45}`
+	if h.String() != want {
+		t.Errorf("got %v, want %v", h.String(), want)
+	}
+	counts := h.Counts()
+	counts["Count"] = h.Count()
+	counts["Total"] = h.Total()
+	for k, want := range map[string]int64{
+		"1":     2,
+		"5":     4,
+		"inf":   4,
+		"Count": 10,
+		"Total": 45,
+	} {
+		if got := counts[k]; got != want {
+			t.Errorf("histogram counts [%v]: got %d, want %d", k, got, want)
+		}
+	}
+	if got, want := h.CountLabel(), "Count"; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	if got, want := h.TotalLabel(), "Total"; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
+func TestGenericHistogram(t *testing.T) {
+	clear()
+	h := NewGenericHistogram(
+		"histgen",
+		"help",
+		[]int64{1, 5},
+		[]string{"one", "five", "max"},
+		"count",
+		"total",
+	)
+	want := `{"one": 0, "five": 0, "max": 0, "count": 0, "total": 0}`
+	if got := h.String(); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
+func TestHistogramHook(t *testing.T) {
+	var gotname string
+	var gotv *Histogram
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*Histogram)
+	})
+
+	name := "hist2"
+	v := NewHistogram(name, "help", []int64{1})
+	if gotname != name {
+		t.Errorf("got %v; want %v", gotname, name)
+	}
+	if gotv != v {
+		t.Errorf("got %#v, want %#v", gotv, v)
+	}
+}

+ 34 - 0
stats/hooks.go

@@ -0,0 +1,34 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+type statsdHook struct {
+	timerHook     func(string, string, int64, *Timings)
+	histogramHook func(string, int64)
+}
+
+var defaultStatsdHook = statsdHook{}
+
+// RegisterTimerHook registers timer hook
+func RegisterTimerHook(hook func(string, string, int64, *Timings)) {
+	defaultStatsdHook.timerHook = hook
+}
+
+// RegisterHistogramHook registers timer hook
+func RegisterHistogramHook(hook func(string, int64)) {
+	defaultStatsdHook.histogramHook = hook
+}

+ 63 - 0
stats/kebab_case_converter.go

@@ -0,0 +1,63 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"regexp"
+	"strings"
+	"sync"
+)
+
+// toKebabCase produces a monitoring compliant name from the
+// original. It converts CamelCase to camel-case,
+// and CAMEL_CASE to camel-case. For numbers, it
+// converts 0.5 to v0_5.
+func toKebabCase(name string) (hyphenated string) {
+	memoizer.Lock()
+	defer memoizer.Unlock()
+	if hyphenated = memoizer.memo[name]; hyphenated != "" {
+		return hyphenated
+	}
+	hyphenated = name
+	for _, converter := range kebabConverters {
+		hyphenated = converter.re.ReplaceAllString(hyphenated, converter.repl)
+	}
+	hyphenated = strings.ToLower(hyphenated)
+	memoizer.memo[name] = hyphenated
+	return
+}
+
+var kebabConverters = []struct {
+	re   *regexp.Regexp
+	repl string
+}{
+	// example: LC -> L-C (e.g. CamelCase -> Camel-Case).
+	{regexp.MustCompile("([a-z])([A-Z])"), "$1-$2"},
+	// example: CCa -> C-Ca (e.g. CCamel -> C-Camel).
+	{regexp.MustCompile("([A-Z])([A-Z][a-z])"), "$1-$2"},
+	{regexp.MustCompile("_"), "-"},
+	{regexp.MustCompile(`\.`), "_"},
+}
+
+var memoizer = memoizerType{
+	memo: make(map[string]string),
+}
+
+type memoizerType struct {
+	sync.Mutex
+	memo map[string]string
+}

+ 54 - 0
stats/kebab_case_converter_test.go

@@ -0,0 +1,54 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"testing"
+)
+
+func TestToKebabCase(t *testing.T) {
+	var kebabCaseTest = []struct{ input, output string }{
+		{"Camel", "camel"},
+		{"Camel", "camel"},
+		{"CamelCase", "camel-case"},
+		{"CamelCaseAgain", "camel-case-again"},
+		{"CCamel", "c-camel"},
+		{"CCCamel", "cc-camel"},
+		{"CAMEL_CASE", "camel-case"},
+		{"camel-case", "camel-case"},
+		{"0", "0"},
+		{"0.0", "0_0"},
+		{"JSON", "json"},
+	}
+
+	for _, tt := range kebabCaseTest {
+		if got, want := toKebabCase(tt.input), tt.output; got != want {
+			t.Errorf("want '%s', got '%s'", want, got)
+		}
+	}
+}
+
+func TestMemoize(t *testing.T) {
+	key := "Test"
+	if memoizer.memo[key] != "" {
+		t.Errorf("want '', got '%s'", memoizer.memo[key])
+	}
+	toKebabCase(key)
+	if memoizer.memo[key] != "test" {
+		t.Errorf("want 'test', got '%s'", memoizer.memo[key])
+	}
+}

+ 54 - 0
stats/multidimensional.go

@@ -0,0 +1,54 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"fmt"
+	"strings"
+)
+
+// MultiTracker is a CountTracker that tracks counts grouping them by
+// more than one dimension.
+type MultiTracker interface {
+	CountTracker
+	Labels() []string
+}
+
+// CounterForDimension returns a CountTracker for the provided
+// dimension. It will panic if the dimension isn't a legal label for
+// mt.
+func CounterForDimension(mt MultiTracker, dimension string) CountTracker {
+	for i, lab := range mt.Labels() {
+		if lab == dimension {
+			return wrappedCountTracker{
+				f: func() map[string]int64 {
+					result := make(map[string]int64)
+					for k, v := range mt.Counts() {
+						if k == "All" {
+							result[k] = v
+							continue
+						}
+						result[strings.Split(k, ".")[i]] += v
+					}
+					return result
+				},
+			}
+		}
+	}
+
+	panic(fmt.Sprintf("label %v is not one of %v", dimension, mt.Labels()))
+}

+ 46 - 0
stats/multidimensional_test.go

@@ -0,0 +1,46 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestMultiTimingsCounterFor(t *testing.T) {
+	clear()
+	mtm := NewMultiTimings("multitimings3", "help", []string{"dim1", "dim2"})
+
+	mtm.Add([]string{"tag1a", "tag1b"}, 500*time.Microsecond)
+	mtm.Add([]string{"tag1a", "tag2b"}, 500*time.Millisecond)
+	mtm.Add([]string{"tag2a", "tag2b"}, 500*time.Millisecond)
+
+	cases := []struct {
+		dim  string
+		want map[string]int64
+	}{
+		{"dim1", map[string]int64{"tag1a": 2, "tag2a": 1, "All": 3}},
+		{"dim2", map[string]int64{"tag1b": 1, "tag2b": 2, "All": 3}},
+	}
+	for _, c := range cases {
+		counts := CounterForDimension(mtm, c.dim).Counts()
+		if !reflect.DeepEqual(c.want, counts) {
+			t.Errorf("mtm.CounterFor(%q).Counts()=%v, want %v", c.dim, counts, c.want)
+		}
+	}
+}

+ 351 - 0
stats/opentsdb/opentsdb.go

@@ -0,0 +1,351 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package opentsdb adds support for pushing stats to opentsdb.
+package opentsdb
+
+import (
+	"bytes"
+	"encoding/json"
+	"expvar"
+	"flag"
+	"net/http"
+	"sort"
+	"strings"
+	"time"
+	"unicode"
+
+	"git.nspix.com/golang/micro/stats"
+)
+
+var (
+	openTsdbURI = flag.String("opentsdb_uri", "", "URI of opentsdb /api/put method")
+)
+
+// dataPoint represents a single OpenTSDB data point.
+type dataPoint struct {
+	// Example: sys.cpu.nice
+	Metric string `json:"metric"`
+	// Seconds or milliseconds since unix epoch.
+	Timestamp float64           `json:"timestamp"`
+	Value     float64           `json:"value"`
+	Tags      map[string]string `json:"tags"`
+}
+
+// sendDataPoints pushes a list of data points to openTSDB.
+// All other code in this file is just to support getting this function called
+// with all stats represented as data points.
+func sendDataPoints(data []dataPoint) error {
+	json, err := json.Marshal(data)
+	if err != nil {
+		return err
+	}
+
+	resp, err := http.Post(*openTsdbURI, "application/json", bytes.NewReader(json))
+	if err != nil {
+		return err
+	}
+	resp.Body.Close()
+	return nil
+}
+
+// openTSDBBackend implements stats.PushBackend
+type openTSDBBackend struct {
+	// The prefix is the name of the binary (vtgate, vttablet, etc.) and will be
+	// prepended to all the stats reported.
+	prefix string
+	// Tags that should be included with every data point. If there's a tag name
+	// collision between the common tags and a single data point's tags, the data
+	// point tag will override the common tag.
+	commonTags map[string]string
+}
+
+// dataCollector tracks state for a single pass of stats reporting / data collection.
+type dataCollector struct {
+	settings   *openTSDBBackend
+	timestamp  int64
+	dataPoints []dataPoint
+}
+
+// InitWithoutServenv initializes the opentsdb without servenv
+func InitWithoutServenv(prefix string) {
+	if *openTsdbURI == "" {
+		return
+	}
+
+	backend := &openTSDBBackend{
+		prefix:     prefix,
+		commonTags: stats.ParseCommonTags(*stats.CommonTags),
+	}
+
+	stats.RegisterPushBackend("opentsdb", backend)
+
+	http.HandleFunc("/debug/opentsdb", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json; charset=utf-8")
+		dataPoints := (*backend).getDataPoints()
+		sort.Sort(byMetric(dataPoints))
+
+		if b, err := json.MarshalIndent(dataPoints, "", "  "); err != nil {
+			w.Write([]byte(err.Error()))
+		} else {
+			w.Write(b)
+		}
+	})
+}
+
+// PushAll pushes all stats to OpenTSDB
+func (backend *openTSDBBackend) PushAll() error {
+	return sendDataPoints(backend.getDataPoints())
+}
+
+// getDataPoints fetches all stats in an opentsdb-compatible format.
+// This is separated from PushAll() so it can be reused for the /debug/opentsdb handler.
+func (backend *openTSDBBackend) getDataPoints() []dataPoint {
+	dataCollector := &dataCollector{
+		settings:  backend,
+		timestamp: time.Now().Unix(),
+	}
+
+	expvar.Do(func(kv expvar.KeyValue) {
+		dataCollector.addExpVar(kv)
+	})
+
+	return dataCollector.dataPoints
+}
+
+// combineMetricName joins parts of a hierarchical name with a "."
+func combineMetricName(parts ...string) string {
+	return strings.Join(parts, ".")
+}
+
+func (dc *dataCollector) addInt(metric string, val int64, tags map[string]string) {
+	dc.addFloat(metric, float64(val), tags)
+}
+
+func (dc *dataCollector) addFloat(metric string, val float64, tags map[string]string) {
+	var fullMetric string
+	if len(dc.settings.prefix) > 0 {
+		fullMetric = combineMetricName(dc.settings.prefix, metric)
+	} else {
+		fullMetric = metric
+	}
+
+	// Restrict metric and tag name/values to legal characters:
+	// http://opentsdb.net/docs/build/html/user_guide/writing.html#metrics-and-tags
+	//
+	// Also make everything lowercase, since opentsdb is case sensitive and lowercase
+	// simplifies the convention.
+	sanitize := func(text string) string {
+		var b bytes.Buffer
+		for _, r := range text {
+			if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '-' || r == '_' || r == '/' || r == '.' {
+				b.WriteRune(r)
+			} else {
+				// For characters that would cause errors, write underscore instead
+				b.WriteRune('_')
+			}
+		}
+		return strings.ToLower(b.String())
+	}
+
+	fullTags := make(map[string]string)
+	for k, v := range dc.settings.commonTags {
+		fullTags[sanitize(k)] = sanitize(v)
+	}
+	for k, v := range tags {
+		fullTags[sanitize(k)] = sanitize(v)
+	}
+
+	dp := dataPoint{
+		Metric:    sanitize(fullMetric),
+		Value:     val,
+		Timestamp: float64(dc.timestamp),
+		Tags:      fullTags,
+	}
+	dc.dataPoints = append(dc.dataPoints, dp)
+}
+
+// addExpVar adds all the data points associated with a particular expvar to the list of
+// opentsdb data points. How an expvar is translated depends on its type.
+//
+// Well-known metric types like histograms and integers are directly converted (saving labels
+// as tags).
+//
+// Generic unrecognized expvars are serialized to json and their int/float values are exported.
+// Strings and lists in expvars are not exported.
+func (dc *dataCollector) addExpVar(kv expvar.KeyValue) {
+	k := kv.Key
+	switch v := kv.Value.(type) {
+	case stats.FloatFunc:
+		dc.addFloat(k, v(), nil)
+	case *stats.Counter:
+		dc.addInt(k, v.Get(), nil)
+	case *stats.CounterFunc:
+		dc.addInt(k, v.F(), nil)
+	case *stats.Gauge:
+		dc.addInt(k, v.Get(), nil)
+	case *stats.GaugeFunc:
+		dc.addInt(k, v.F(), nil)
+	case *stats.CounterDuration:
+		dc.addInt(k, int64(v.Get()), nil)
+	case *stats.CounterDurationFunc:
+		dc.addInt(k, int64(v.F()), nil)
+	case *stats.MultiTimings:
+		dc.addTimings(v.Labels(), &v.Timings, k)
+	case *stats.Timings:
+		dc.addTimings([]string{v.Label()}, v, k)
+	case *stats.Histogram:
+		dc.addHistogram(v, 1, k, make(map[string]string))
+	case *stats.CountersWithSingleLabel:
+		for labelVal, val := range v.Counts() {
+			dc.addInt(k, val, makeLabel(v.Label(), labelVal))
+		}
+	case *stats.CountersWithMultiLabels:
+		for labelVals, val := range v.Counts() {
+			dc.addInt(k, val, makeLabels(v.Labels(), labelVals))
+		}
+	case *stats.CountersFuncWithMultiLabels:
+		for labelVals, val := range v.Counts() {
+			dc.addInt(k, val, makeLabels(v.Labels(), labelVals))
+		}
+	case *stats.GaugesWithMultiLabels:
+		for labelVals, val := range v.Counts() {
+			dc.addInt(k, val, makeLabels(v.Labels(), labelVals))
+		}
+	case *stats.GaugesFuncWithMultiLabels:
+		for labelVals, val := range v.Counts() {
+			dc.addInt(k, val, makeLabels(v.Labels(), labelVals))
+		}
+	case *stats.GaugesWithSingleLabel:
+		for labelVal, val := range v.Counts() {
+			dc.addInt(k, val, makeLabel(v.Label(), labelVal))
+		}
+	default:
+		// Deal with generic expvars by converting them to JSON and pulling out
+		// all the floats. Strings and lists will not be exported to opentsdb.
+		var obj map[string]interface{}
+		if err := json.Unmarshal([]byte(v.String()), &obj); err != nil {
+			return
+		}
+
+		// Recursive helper function.
+		dc.addUnrecognizedExpvars(combineMetricName("expvar", k), obj)
+	}
+}
+
+// makeLabel builds a tag list with a single label + value.
+func makeLabel(labelName string, labelVal string) map[string]string {
+	return map[string]string{labelName: labelVal}
+}
+
+// makeLabels takes the vitess stat representation of label values ("."-separated list) and breaks it
+// apart into a map of label name -> label value.
+func makeLabels(labelNames []string, labelValsCombined string) map[string]string {
+	tags := make(map[string]string)
+	labelVals := strings.Split(labelValsCombined, ".")
+	for i, v := range labelVals {
+		tags[labelNames[i]] = v
+	}
+	return tags
+}
+
+// addUnrecognizedExpvars recurses into a json object to pull out float64 variables to report.
+func (dc *dataCollector) addUnrecognizedExpvars(prefix string, obj map[string]interface{}) {
+	for k, v := range obj {
+		prefix := combineMetricName(prefix, k)
+		switch v := v.(type) {
+		case map[string]interface{}:
+			dc.addUnrecognizedExpvars(prefix, v)
+		case float64:
+			dc.addFloat(prefix, v, nil)
+		}
+	}
+}
+
+// addTimings converts a vitess Timings stat to something opentsdb can deal with.
+func (dc *dataCollector) addTimings(labels []string, timings *stats.Timings, prefix string) {
+	histograms := timings.Histograms()
+	for labelValsCombined, histogram := range histograms {
+		// If you prefer millisecond timings over nanoseconds you can pass 1000000 here instead of 1.
+		dc.addHistogram(histogram, 1, prefix, makeLabels(labels, labelValsCombined))
+	}
+}
+
+func (dc *dataCollector) addHistogram(histogram *stats.Histogram, divideBy int64, prefix string, tags map[string]string) {
+	// TODO: OpenTSDB 2.3 doesn't have histogram support, although it's forthcoming.
+	// For simplicity we report each bucket as a different metric.
+	//
+	// An alternative approach if you don't mind changing the code is to add a hook to Histogram creation that
+	// associates each histogram with a shadow type that can track percentiles (like Timer from rcrowley/go-metrics).
+
+	labels := histogram.Labels()
+	buckets := histogram.Buckets()
+	for i := range labels {
+		dc.addInt(
+			combineMetricName(prefix, labels[i]),
+			buckets[i],
+			tags,
+		)
+	}
+
+	dc.addInt(
+		combineMetricName(prefix, histogram.CountLabel()),
+		(*histogram).Count(),
+		tags,
+	)
+	dc.addInt(
+		combineMetricName(prefix, histogram.TotalLabel()),
+		(*histogram).Total()/divideBy,
+		tags,
+	)
+}
+
+// byMetric implements sort.Interface for []dataPoint based on the metric key
+// and then tag values (prioritized in tag name order). Having a consistent sort order
+// is convenient when refreshing /debug/opentsdb or for encoding and comparing JSON directly
+// in the tests.
+type byMetric []dataPoint
+
+func (m byMetric) Len() int      { return len(m) }
+func (m byMetric) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
+func (m byMetric) Less(i, j int) bool {
+	if m[i].Metric < m[j].Metric {
+		return true
+	}
+
+	if m[i].Metric > m[j].Metric {
+		return false
+	}
+
+	// Metric names are the same. We can use tag values to figure out the sort order.
+	// The deciding tag will be the lexicographically earliest tag name where tag values differ.
+	decidingTagName := ""
+	result := false
+	for tagName, iVal := range m[i].Tags {
+		jVal, ok := m[j].Tags[tagName]
+		if !ok {
+			// We'll arbitrarily declare that if i has any tag name that j doesn't then it sorts earlier.
+			// This shouldn't happen in practice, though, if metric code is correct...
+			return true
+		}
+
+		if iVal != jVal && (tagName < decidingTagName || decidingTagName == "") {
+			decidingTagName = tagName
+			result = iVal < jVal
+		}
+	}
+	return result
+}

+ 400 - 0
stats/opentsdb/opentsdb_test.go

@@ -0,0 +1,400 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package opentsdb
+
+import (
+	"encoding/json"
+	"expvar"
+	"reflect"
+	"sort"
+	"testing"
+	"time"
+
+	"git.nspix.com/golang/micro/stats"
+)
+
+func TestOpenTsdbCounter(t *testing.T) {
+	name := "counter_name"
+	c := stats.NewCounter(name, "counter description")
+	c.Add(1)
+
+	checkOutput(t, name, `
+		[
+		  {
+		    "metric": "vtgate.counter_name",
+		    "timestamp": 1234,
+		    "value": 1,
+		    "tags": {
+		      "host": "localhost"
+		    }
+		  }
+		]`)
+}
+
+func TestOpenTsdbCounterFunc(t *testing.T) {
+	name := "counter_fn_name"
+	stats.NewCounterFunc(name, "help", func() int64 {
+		return 2
+	})
+	checkOutput(t, name, `
+		[
+		  {
+		    "metric": "vtgate.counter_fn_name",
+		    "timestamp": 1234,
+		    "value": 2,
+		    "tags": {
+		      "host": "localhost"
+		    }
+		  }
+		]`)
+}
+
+func TestGaugesWithMultiLabels(t *testing.T) {
+	name := "gauges_with_multi_labels_name"
+	gauges := stats.NewGaugesWithMultiLabels(name, "help", []string{"flavor", "texture"})
+	gauges.Add([]string{"sour", "brittle"}, 3)
+
+	checkOutput(t, name, `
+		[
+			{
+		    "metric": "vtgate.gauges_with_multi_labels_name",
+		    "timestamp": 1234,
+		    "value": 3,
+		    "tags": {
+		      "flavor": "sour",
+		      "host": "localhost",
+		      "texture": "brittle"
+		    }
+		  }
+		]`)
+}
+
+type myVar bool
+
+func (mv *myVar) String() string {
+	return `{"myKey": 1.2}`
+}
+
+func TestExpvar(t *testing.T) {
+	name := "blah_expvar"
+	expvar.Publish(name, new(myVar))
+	checkOutput(t, name, `
+		[
+		  {
+		    "metric": "vtgate.expvar.blah_expvar.mykey",
+		    "timestamp": 1234,
+		    "value": 1.2,
+		    "tags": {
+		      "host": "localhost"
+		    }
+		  }
+		]`)
+}
+
+func TestOpenTsdbTimings(t *testing.T) {
+	name := "blah_timings"
+	cats := []string{"cat1", "cat2"}
+	timing := stats.NewTimings(name, "help", "category", cats...)
+	timing.Add("cat1", time.Duration(1000000000))
+	timing.Add("cat1", time.Duration(1))
+
+	checkOutput(t, name, `
+		[
+		  {
+		    "metric": "vtgate.blah_timings.1000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.1000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.10000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.10000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.100000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.100000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.1000000000",
+		    "timestamp": 1234,
+		    "value": 1,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.1000000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.10000000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.10000000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.500000",
+		    "timestamp": 1234,
+		    "value": 1,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.500000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.5000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.5000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.50000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.50000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.500000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.500000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.5000000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.5000000000",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.count",
+		    "timestamp": 1234,
+		    "value": 2,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.count",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.inf",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.inf",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.time",
+		    "timestamp": 1234,
+		    "value": 1000000001,
+		    "tags": {
+		      "category": "cat1",
+		      "host": "localhost"
+		    }
+		  },
+		  {
+		    "metric": "vtgate.blah_timings.time",
+		    "timestamp": 1234,
+		    "value": 0,
+		    "tags": {
+		      "category": "cat2",
+		      "host": "localhost"
+		    }
+		  }
+		]`)
+}
+
+func checkOutput(t *testing.T, statName string, wantJSON string) {
+	backend := &openTSDBBackend{
+		prefix:     "vtgate",
+		commonTags: map[string]string{"host": "localhost"},
+	}
+	timestamp := int64(1234)
+
+	dc := &dataCollector{
+		settings:  backend,
+		timestamp: timestamp,
+	}
+	found := false
+	expvar.Do(func(kv expvar.KeyValue) {
+		if kv.Key == statName {
+			found = true
+
+			dc.addExpVar(kv)
+			sort.Sort(byMetric(dc.dataPoints))
+
+			gotBytes, err := json.MarshalIndent(dc.dataPoints, "", "  ")
+			if err != nil {
+				t.Errorf("Failed to marshal json: %v", err)
+				return
+			}
+			var got interface{}
+			err = json.Unmarshal(gotBytes, &got)
+			if err != nil {
+				t.Errorf("Failed to marshal json: %v", err)
+				return
+			}
+
+			var want interface{}
+			err = json.Unmarshal([]byte(wantJSON), &want)
+			if err != nil {
+				t.Errorf("Failed to marshal json: %v", err)
+				return
+			}
+
+			if !reflect.DeepEqual(got, want) {
+				t.Errorf("addExpVar(%#v) = %s, want %s", kv, string(gotBytes), wantJSON)
+			}
+		}
+	})
+	if !found {
+		t.Errorf("Stat %s not found?...", statName)
+	}
+}

+ 378 - 0
stats/prometheusbackend/collectors.go

@@ -0,0 +1,378 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package prometheusbackend
+
+import (
+	"strings"
+
+	"github.com/prometheus/client_golang/prometheus"
+
+	"git.nspix.com/golang/micro/stats"
+)
+
+type metricFuncCollector struct {
+	// f returns the floating point value of the metric.
+	f    func() float64
+	desc *prometheus.Desc
+	vt   prometheus.ValueType
+}
+
+func newMetricFuncCollector(v stats.Variable, name string, vt prometheus.ValueType, f func() float64) {
+	collector := &metricFuncCollector{
+		f: f,
+		desc: prometheus.NewDesc(
+			name,
+			v.Help(),
+			nil,
+			nil),
+		vt: vt}
+
+	// Will panic if it fails
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (mc *metricFuncCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- mc.desc
+}
+
+// Collect implements Collector.
+func (mc *metricFuncCollector) Collect(ch chan<- prometheus.Metric) {
+	metric, err := prometheus.NewConstMetric(mc.desc, mc.vt, float64(mc.f()))
+	if err == nil {
+		ch <- metric
+	}
+}
+
+// countersWithSingleLabelCollector collects stats.CountersWithSingleLabel.
+type countersWithSingleLabelCollector struct {
+	counters *stats.CountersWithSingleLabel
+	desc     *prometheus.Desc
+	vt       prometheus.ValueType
+}
+
+func newCountersWithSingleLabelCollector(c *stats.CountersWithSingleLabel, name string, labelName string, vt prometheus.ValueType) {
+	collector := &countersWithSingleLabelCollector{
+		counters: c,
+		desc: prometheus.NewDesc(
+			name,
+			c.Help(),
+			[]string{labelName},
+			nil),
+		vt: vt}
+
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (c *countersWithSingleLabelCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *countersWithSingleLabelCollector) Collect(ch chan<- prometheus.Metric) {
+	for tag, val := range c.counters.Counts() {
+		metric, err := prometheus.NewConstMetric(c.desc, c.vt, float64(val), tag)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+// gaugesWithSingleLabelCollector collects stats.GaugesWithSingleLabel.
+type gaugesWithSingleLabelCollector struct {
+	gauges *stats.GaugesWithSingleLabel
+	desc   *prometheus.Desc
+	vt     prometheus.ValueType
+}
+
+func newGaugesWithSingleLabelCollector(g *stats.GaugesWithSingleLabel, name string, labelName string, vt prometheus.ValueType) {
+	collector := &gaugesWithSingleLabelCollector{
+		gauges: g,
+		desc: prometheus.NewDesc(
+			name,
+			g.Help(),
+			[]string{labelName},
+			nil),
+		vt: vt}
+
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (g *gaugesWithSingleLabelCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- g.desc
+}
+
+// Collect implements Collector.
+func (g *gaugesWithSingleLabelCollector) Collect(ch chan<- prometheus.Metric) {
+	for tag, val := range g.gauges.Counts() {
+		metric, err := prometheus.NewConstMetric(g.desc, g.vt, float64(val), tag)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+type metricWithMultiLabelsCollector struct {
+	cml  *stats.CountersWithMultiLabels
+	desc *prometheus.Desc
+}
+
+func newMetricWithMultiLabelsCollector(cml *stats.CountersWithMultiLabels, name string) {
+	c := &metricWithMultiLabelsCollector{
+		cml: cml,
+		desc: prometheus.NewDesc(
+			name,
+			cml.Help(),
+			labelsToSnake(cml.Labels()),
+			nil),
+	}
+
+	prometheus.MustRegister(c)
+}
+
+// Describe implements Collector.
+func (c *metricWithMultiLabelsCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *metricWithMultiLabelsCollector) Collect(ch chan<- prometheus.Metric) {
+	for lvs, val := range c.cml.Counts() {
+		labelValues := strings.Split(lvs, ".")
+		value := float64(val)
+		metric, err := prometheus.NewConstMetric(c.desc, prometheus.CounterValue, value, labelValues...)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+type gaugesWithMultiLabelsCollector struct {
+	gml  *stats.GaugesWithMultiLabels
+	desc *prometheus.Desc
+}
+
+func newGaugesWithMultiLabelsCollector(gml *stats.GaugesWithMultiLabels, name string) {
+	c := &gaugesWithMultiLabelsCollector{
+		gml: gml,
+		desc: prometheus.NewDesc(
+			name,
+			gml.Help(),
+			labelsToSnake(gml.Labels()),
+			nil),
+	}
+
+	prometheus.MustRegister(c)
+}
+
+// Describe implements Collector.
+func (c *gaugesWithMultiLabelsCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *gaugesWithMultiLabelsCollector) Collect(ch chan<- prometheus.Metric) {
+	for lvs, val := range c.gml.Counts() {
+		labelValues := strings.Split(lvs, ".")
+		value := float64(val)
+		metric, err := prometheus.NewConstMetric(c.desc, prometheus.GaugeValue, value, labelValues...)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+type metricsFuncWithMultiLabelsCollector struct {
+	cfml *stats.CountersFuncWithMultiLabels
+	desc *prometheus.Desc
+	vt   prometheus.ValueType
+}
+
+func newMetricsFuncWithMultiLabelsCollector(cfml *stats.CountersFuncWithMultiLabels, name string, vt prometheus.ValueType) {
+	collector := &metricsFuncWithMultiLabelsCollector{
+		cfml: cfml,
+		desc: prometheus.NewDesc(
+			name,
+			cfml.Help(),
+			labelsToSnake(cfml.Labels()),
+			nil),
+		vt: vt,
+	}
+
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (c *metricsFuncWithMultiLabelsCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *metricsFuncWithMultiLabelsCollector) Collect(ch chan<- prometheus.Metric) {
+	for lvs, val := range c.cfml.Counts() {
+		labelValues := strings.Split(lvs, ".")
+		value := float64(val)
+		metric, err := prometheus.NewConstMetric(c.desc, c.vt, value, labelValues...)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+type timingsCollector struct {
+	t       *stats.Timings
+	cutoffs []float64
+	desc    *prometheus.Desc
+}
+
+func newTimingsCollector(t *stats.Timings, name string) {
+	cutoffs := make([]float64, len(t.Cutoffs()))
+	for i, val := range t.Cutoffs() {
+		cutoffs[i] = float64(val) / 1000000000
+	}
+
+	collector := &timingsCollector{
+		t:       t,
+		cutoffs: cutoffs,
+		desc: prometheus.NewDesc(
+			name,
+			t.Help(),
+			[]string{t.Label()},
+			nil),
+	}
+
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (c *timingsCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *timingsCollector) Collect(ch chan<- prometheus.Metric) {
+	for cat, his := range c.t.Histograms() {
+		metric, err := prometheus.NewConstHistogram(c.desc,
+			uint64(his.Count()),
+			float64(his.Total())/1000000000,
+			makeCumulativeBuckets(c.cutoffs,
+				his.Buckets()), cat)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+func makeCumulativeBuckets(cutoffs []float64, buckets []int64) map[float64]uint64 {
+	output := make(map[float64]uint64)
+	last := uint64(0)
+	for i, key := range cutoffs {
+		//TODO(zmagg): int64 => uint64 conversion. error if it overflows?
+		output[key] = uint64(buckets[i]) + last
+		last = output[key]
+	}
+	return output
+}
+
+type multiTimingsCollector struct {
+	mt      *stats.MultiTimings
+	cutoffs []float64
+	desc    *prometheus.Desc
+}
+
+func newMultiTimingsCollector(mt *stats.MultiTimings, name string) {
+	cutoffs := make([]float64, len(mt.Cutoffs()))
+	for i, val := range mt.Cutoffs() {
+		cutoffs[i] = float64(val) / 1000000000
+	}
+
+	collector := &multiTimingsCollector{
+		mt:      mt,
+		cutoffs: cutoffs,
+		desc: prometheus.NewDesc(
+			name,
+			mt.Help(),
+			labelsToSnake(mt.Labels()),
+			nil),
+	}
+
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (c *multiTimingsCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *multiTimingsCollector) Collect(ch chan<- prometheus.Metric) {
+	for cat, his := range c.mt.Timings.Histograms() {
+		labelValues := strings.Split(cat, ".")
+		metric, err := prometheus.NewConstHistogram(
+			c.desc,
+			uint64(his.Count()),
+			float64(his.Total())/1000000000,
+			makeCumulativeBuckets(c.cutoffs, his.Buckets()),
+			labelValues...)
+		if err == nil {
+			ch <- metric
+		}
+	}
+}
+
+type histogramCollector struct {
+	h       *stats.Histogram
+	cutoffs []float64
+	desc    *prometheus.Desc
+}
+
+func newHistogramCollector(h *stats.Histogram, name string) {
+	cutoffs := make([]float64, len(h.Cutoffs()))
+	for i, val := range h.Cutoffs() {
+		cutoffs[i] = float64(val)
+	}
+
+	collector := &histogramCollector{
+		h:       h,
+		cutoffs: cutoffs,
+		desc: prometheus.NewDesc(
+			name,
+			h.Help(),
+			[]string{},
+			nil),
+	}
+
+	prometheus.MustRegister(collector)
+}
+
+// Describe implements Collector.
+func (c *histogramCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- c.desc
+}
+
+// Collect implements Collector.
+func (c *histogramCollector) Collect(ch chan<- prometheus.Metric) {
+	metric, err := prometheus.NewConstHistogram(c.desc,
+		uint64(c.h.Count()),
+		float64(c.h.Total()),
+		makeCumulativeBuckets(c.cutoffs, c.h.Buckets()))
+	if err == nil {
+		ch <- metric
+	}
+}

+ 38 - 0
stats/prometheusbackend/collectors_test.go

@@ -0,0 +1,38 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package prometheusbackend
+
+import (
+	"testing"
+
+	"git.nspix.com/golang/micro/stats"
+)
+
+func getStats() map[string]int64 {
+	stats := make(map[string]int64)
+	stats["table1.plan"] = 1
+	stats["table2.plan"] = 2
+	// Errors are expected to be logged from collectors.go when
+	// adding this value (issue #5599).  Test will succeed, as intended.
+	stats["table3.dot.plan"] = 3
+	return stats
+}
+
+func TestCollector(t *testing.T) {
+	c := stats.NewCountersFuncWithMultiLabels("Name", "description", []string{"Table", "Plan"}, getStats)
+	c.Counts()
+}

+ 111 - 0
stats/prometheusbackend/prometheusbackend.go

@@ -0,0 +1,111 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package prometheusbackend
+
+import (
+	"expvar"
+	"strings"
+
+	"github.com/prometheus/client_golang/prometheus"
+
+	"git.nspix.com/golang/micro/stats"
+)
+
+// PromBackend implements PullBackend using Prometheus as the backing metrics storage.
+type PromBackend struct {
+	namespace string
+}
+
+var (
+	be PromBackend
+)
+
+// Init initializes the Prometheus be with the given namespace.
+func Init(namespace string) {
+	be.namespace = namespace
+	stats.Register(be.publishPrometheusMetric)
+}
+
+// PublishPromMetric is used to publish the metric to Prometheus.
+func (be PromBackend) publishPrometheusMetric(name string, v expvar.Var) {
+	switch st := v.(type) {
+	case *stats.Counter:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return float64(st.Get()) })
+	case *stats.CounterFunc:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return float64(st.F()) })
+	case *stats.Gauge:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return float64(st.Get()) })
+	case *stats.GaugeFunc:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return float64(st.F()) })
+	case stats.FloatFunc:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return (st)() })
+	case *stats.CountersWithSingleLabel:
+		newCountersWithSingleLabelCollector(st, be.buildPromName(name), st.Label(), prometheus.CounterValue)
+	case *stats.CountersWithMultiLabels:
+		newMetricWithMultiLabelsCollector(st, be.buildPromName(name))
+	case *stats.CountersFuncWithMultiLabels:
+		newMetricsFuncWithMultiLabelsCollector(st, be.buildPromName(name), prometheus.CounterValue)
+	case *stats.GaugesFuncWithMultiLabels:
+		newMetricsFuncWithMultiLabelsCollector(&st.CountersFuncWithMultiLabels, be.buildPromName(name), prometheus.GaugeValue)
+	case *stats.GaugesWithSingleLabel:
+		newGaugesWithSingleLabelCollector(st, be.buildPromName(name), st.Label(), prometheus.GaugeValue)
+	case *stats.GaugesWithMultiLabels:
+		newGaugesWithMultiLabelsCollector(st, be.buildPromName(name))
+	case *stats.CounterDuration:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return st.Get().Seconds() })
+	case *stats.CounterDurationFunc:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.CounterValue, func() float64 { return st.F().Seconds() })
+	case *stats.GaugeDuration:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return st.Get().Seconds() })
+	case *stats.GaugeDurationFunc:
+		newMetricFuncCollector(st, be.buildPromName(name), prometheus.GaugeValue, func() float64 { return st.F().Seconds() })
+	case *stats.Timings:
+		newTimingsCollector(st, be.buildPromName(name))
+	case *stats.MultiTimings:
+		newMultiTimingsCollector(st, be.buildPromName(name))
+	case *stats.Histogram:
+		newHistogramCollector(st, be.buildPromName(name))
+	case *stats.String, stats.StringFunc, stats.StringMapFunc, *stats.Rates, *stats.RatesFunc:
+		// Silently ignore these types since they don't make sense to
+		// export to Prometheus' data model.
+	default:
+	}
+}
+
+// buildPromName specifies the namespace as a prefix to the metric name
+func (be PromBackend) buildPromName(name string) string {
+	s := strings.TrimPrefix(normalizeMetric(name), be.namespace+"_")
+	return prometheus.BuildFQName("", be.namespace, s)
+}
+
+func labelsToSnake(labels []string) []string {
+	output := make([]string, len(labels))
+	for i, l := range labels {
+		output[i] = normalizeMetric(l)
+	}
+	return output
+}
+
+// normalizeMetricForPrometheus produces a compliant name by applying
+// special case conversions and then applying a camel case to snake case converter.
+func normalizeMetric(name string) string {
+	// Special cases
+	r := strings.NewReplacer("VSchema", "vschema", "VtGate", "vtgate")
+	name = r.Replace(name)
+
+	return stats.GetSnakeName(name)
+}

+ 354 - 0
stats/prometheusbackend/prometheusbackend_test.go

@@ -0,0 +1,354 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package prometheusbackend
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"strings"
+	"testing"
+	"time"
+
+	"git.nspix.com/golang/micro/stats"
+
+	"github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+const namespace = "namespace"
+
+func TestPrometheusCounter(t *testing.T) {
+	name := "blah"
+	c := stats.NewCounter(name, "blah")
+	c.Add(1)
+	checkHandlerForMetrics(t, name, 1)
+	//TODO: ban this? And for other counter types too?
+	// c.Add(-1)
+	c.Reset()
+	checkHandlerForMetrics(t, name, 0)
+}
+
+func TestPrometheusGauge(t *testing.T) {
+	name := "blah_gauge"
+	c := stats.NewGauge(name, "help")
+	c.Add(1)
+	checkHandlerForMetrics(t, name, 1)
+	c.Add(-1)
+	checkHandlerForMetrics(t, name, 0)
+	c.Set(-5)
+	checkHandlerForMetrics(t, name, -5)
+	c.Reset()
+	checkHandlerForMetrics(t, name, 0)
+}
+
+func TestPrometheusCounterFunc(t *testing.T) {
+	name := "blah_counterfunc"
+	stats.NewCounterFunc(name, "help", func() int64 {
+		return 2
+	})
+
+	checkHandlerForMetrics(t, name, 2)
+}
+
+func TestPrometheusGaugeFunc(t *testing.T) {
+	name := "blah_gaugefunc"
+
+	stats.NewGaugeFunc(name, "help", func() int64 {
+		return -3
+	})
+
+	checkHandlerForMetrics(t, name, -3)
+}
+
+func TestPrometheusFloatFunc(t *testing.T) {
+	name := "blah_floatfunc"
+
+	stats.Publish(name, stats.FloatFunc(func() float64 { return -4 }))
+	checkHandlerForMetrics(t, name, -4)
+}
+
+func TestPrometheusCounterDuration(t *testing.T) {
+	name := "blah_counterduration"
+
+	d := stats.NewCounterDuration(name, "help")
+	d.Add(1 * time.Second)
+
+	checkHandlerForMetrics(t, name, 1)
+}
+
+func TestPrometheusCounterDurationFunc(t *testing.T) {
+	name := "blah_counterdurationfunc"
+
+	stats.NewCounterDurationFunc(name, "help", func() time.Duration { return 1 * time.Second })
+
+	checkHandlerForMetrics(t, name, 1)
+}
+
+func TestPrometheusGaugeDuration(t *testing.T) {
+	name := "blah_gaugeduration"
+
+	d := stats.NewGaugeDuration(name, "help")
+	d.Set(1 * time.Second)
+
+	checkHandlerForMetrics(t, name, 1)
+}
+
+func TestPrometheusGaugeDurationFunc(t *testing.T) {
+	name := "blah_gaugedurationfunc"
+
+	stats.NewGaugeDurationFunc(name, "help", func() time.Duration { return 1 * time.Second })
+
+	checkHandlerForMetrics(t, name, 1)
+}
+
+func checkHandlerForMetrics(t *testing.T, metric string, value int) {
+	response := testMetricsHandler(t)
+
+	expected := fmt.Sprintf("%s_%s %d", namespace, metric, value)
+
+	if !strings.Contains(response.Body.String(), expected) {
+		t.Fatalf("Expected %s got %s", expected, response.Body.String())
+	}
+}
+
+func TestPrometheusCountersWithSingleLabel(t *testing.T) {
+	name := "blah_counterswithsinglelabel"
+	c := stats.NewCountersWithSingleLabel(name, "help", "label", "tag1", "tag2")
+	c.Add("tag1", 1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
+	c.Add("tag2", 41)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 41)
+	c.Reset("tag2")
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
+}
+
+func TestPrometheusGaugesWithSingleLabel(t *testing.T) {
+	name := "blah_gaugeswithsinglelabel"
+	c := stats.NewGaugesWithSingleLabel(name, "help", "label", "tag1", "tag2")
+	c.Add("tag1", 1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
+
+	c.Add("tag2", 1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 1)
+
+	c.Set("tag1", -1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", -1)
+
+	c.Reset("tag2")
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", -1)
+	checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
+}
+
+func checkHandlerForMetricWithSingleLabel(t *testing.T, metric, label, tag string, value int) {
+	response := testMetricsHandler(t)
+
+	expected := fmt.Sprintf("%s_%s{%s=\"%s\"} %d", namespace, metric, label, tag, value)
+
+	if !strings.Contains(response.Body.String(), expected) {
+		t.Fatalf("Expected %s got %s", expected, response.Body.String())
+	}
+}
+
+func TestPrometheusCountersWithMultiLabels(t *testing.T) {
+	name := "blah_counterswithmultilabels"
+	labels := []string{"label1", "label2"}
+	labelValues := []string{"foo", "bar"}
+	c := stats.NewCountersWithMultiLabels(name, "help", labels)
+	c.Add(labelValues, 1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
+	labelValues2 := []string{"baz", "bazbar"}
+	c.Add(labelValues2, 1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
+	c.Reset(labelValues)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 0)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
+}
+
+func TestPrometheusGaugesWithMultiLabels(t *testing.T) {
+	name := "blah_gaugeswithmultilabels"
+	labels := []string{"label1", "label2"}
+	labelValues := []string{"foo", "bar"}
+	c := stats.NewGaugesWithMultiLabels(name, "help", labels)
+	c.Add(labelValues, 1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
+
+	c.Set(labelValues, -1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, -1)
+
+	labelValues2 := []string{"baz", "bazbar"}
+	c.Add(labelValues2, 1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, -1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
+
+	c.Reset(labelValues)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 0)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
+}
+
+func TestPrometheusCountersWithMultiLabels_AddPanic(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("The code did not panic when adding to inequal label lengths")
+		}
+	}()
+
+	name := "blah_counterswithmultilabels_inequallength"
+	c := stats.NewCountersWithMultiLabels(name, "help", []string{"label1", "label2"})
+	c.Add([]string{"label1"}, 1)
+}
+
+func TestPrometheusCountersFuncWithMultiLabels(t *testing.T) {
+	name := "blah_countersfuncwithmultilabels"
+	labels := []string{"label1", "label2"}
+
+	stats.NewCountersFuncWithMultiLabels(name, "help", labels, func() map[string]int64 {
+		m := make(map[string]int64)
+		m["foo.bar"] = 1
+		m["bar.baz"] = 1
+		return m
+	})
+
+	checkHandlerForMetricWithMultiLabels(t, name, labels, []string{"foo", "bar"}, 1)
+	checkHandlerForMetricWithMultiLabels(t, name, labels, []string{"bar", "baz"}, 1)
+}
+
+func checkHandlerForMetricWithMultiLabels(t *testing.T, metric string, labels []string, labelValues []string, value int64) {
+	response := testMetricsHandler(t)
+
+	expected := fmt.Sprintf("%s_%s{%s=\"%s\",%s=\"%s\"} %d", namespace, metric, labels[0], labelValues[0], labels[1], labelValues[1], value)
+
+	if !strings.Contains(response.Body.String(), expected) {
+		t.Fatalf("Expected %s got %s", expected, response.Body.String())
+	}
+}
+
+func TestPrometheusTimings(t *testing.T) {
+	name := "blah_timings"
+	cats := []string{"cat1", "cat2"}
+	timing := stats.NewTimings(name, "help", "category", cats...)
+	timing.Add("cat1", time.Duration(30*time.Millisecond))
+	timing.Add("cat1", time.Duration(200*time.Millisecond))
+	timing.Add("cat1", time.Duration(1*time.Second))
+
+	response := testMetricsHandler(t)
+	var s []string
+
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.0005\"} %d", namespace, name, cats[0], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.001\"} %d", namespace, name, cats[0], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.005\"} %d", namespace, name, cats[0], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.01\"} %d", namespace, name, cats[0], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.05\"} %d", namespace, name, cats[0], 1))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.1\"} %d", namespace, name, cats[0], 1))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.5\"} %d", namespace, name, cats[0], 2))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"1\"} %d", namespace, name, cats[0], 3))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"5\"} %d", namespace, name, cats[0], 3))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"10\"} %d", namespace, name, cats[0], 3))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"+Inf\"} %d", namespace, name, cats[0], 3))
+	s = append(s, fmt.Sprintf("%s_%s_sum{category=\"%s\"} %s", namespace, name, cats[0], "1.23"))
+	s = append(s, fmt.Sprintf("%s_%s_count{category=\"%s\"} %d", namespace, name, cats[0], 3))
+
+	for _, line := range s {
+		if !strings.Contains(response.Body.String(), line) {
+			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
+		}
+	}
+}
+
+func TestPrometheusMultiTimings(t *testing.T) {
+	name := "blah_multitimings"
+	cats := []string{"cat1", "cat2"}
+	catLabels := []string{"foo", "bar"}
+	timing := stats.NewMultiTimings(name, "help", cats)
+	timing.Add(catLabels, time.Duration(30*time.Millisecond))
+	timing.Add(catLabels, time.Duration(200*time.Millisecond))
+	timing.Add(catLabels, time.Duration(1*time.Second))
+
+	response := testMetricsHandler(t)
+	var s []string
+
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.0005\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.001\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.005\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.01\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.05\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 1))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.1\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 1))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.5\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 2))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"1\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"5\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"10\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"+Inf\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
+	s = append(s, fmt.Sprintf("%s_%s_sum{%s=\"%s\",%s=\"%s\"} %s", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], "1.23"))
+	s = append(s, fmt.Sprintf("%s_%s_count{%s=\"%s\",%s=\"%s\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
+
+	for _, line := range s {
+		if !strings.Contains(response.Body.String(), line) {
+			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
+		}
+	}
+}
+
+func TestPrometheusMultiTimings_PanicWrongLength(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("The code did not panic when adding to inequal label lengths")
+		}
+	}()
+
+	c := stats.NewMultiTimings("name", "help", []string{"label1", "label2"})
+	c.Add([]string{"label1"}, time.Duration(100000000))
+}
+
+func TestPrometheusHistogram(t *testing.T) {
+	name := "blah_hist"
+	hist := stats.NewHistogram(name, "help", []int64{1, 5, 10})
+	hist.Add(2)
+	hist.Add(3)
+	hist.Add(6)
+
+	response := testMetricsHandler(t)
+	var s []string
+
+	s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"1\"} %d", namespace, name, 0))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"5\"} %d", namespace, name, 2))
+	s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"10\"} %d", namespace, name, 3))
+	s = append(s, fmt.Sprintf("%s_%s_sum %d", namespace, name, 1))
+	s = append(s, fmt.Sprintf("%s_%s_count %d", namespace, name, 3))
+
+	for _, line := range s {
+		if !strings.Contains(response.Body.String(), line) {
+			t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
+		}
+	}
+}
+
+func testMetricsHandler(t *testing.T) *httptest.ResponseRecorder {
+	req, _ := http.NewRequest("GET", "/metrics", nil)
+	response := httptest.NewRecorder()
+
+	promhttp.Handler().ServeHTTP(response, req)
+	return response
+}
+
+func TestMain(m *testing.M) {
+	Init(namespace)
+	os.Exit(m.Run())
+}

+ 213 - 0
stats/rates.go

@@ -0,0 +1,213 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"encoding/json"
+	"math"
+	"sync"
+	"time"
+)
+
+var timeNow = time.Now
+
+// CountTracker defines the interface that needs to
+// be supported by a variable for being tracked by
+// Rates.
+type CountTracker interface {
+	// Counts returns a map which maps each category to a count.
+	// Subsequent calls must return a monotonously increasing count for the same
+	// category.
+	// Optionally, an implementation may include the "All" category which has
+	// the total count across all categories (e.g. timing.go does this).
+	Counts() map[string]int64
+}
+
+// wrappedCountTracker implements the CountTracker interface.
+// It is used in multidimensional.go to publish specific, one-dimensional
+// counters.
+type wrappedCountTracker struct {
+	f func() map[string]int64
+}
+
+func (t wrappedCountTracker) Counts() map[string]int64 { return t.f() }
+
+// Rates is capable of reporting the rate (typically QPS)
+// for any variable that satisfies the CountTracker interface.
+type Rates struct {
+	// mu guards all fields.
+	mu           sync.Mutex
+	timeStamps   *RingInt64
+	counts       map[string]*RingInt64
+	countTracker CountTracker
+	samples      int
+	interval     time.Duration
+	// previousTotalCount is the total number of counts (across all categories)
+	// seen in the last sampling interval.
+	// It's used to calculate the latest total rate.
+	previousTotalCount int64
+	// timestampLastSampling is the time the periodic sampling was run last.
+	timestampLastSampling time.Time
+	// totalRate is the rate of total counts per second seen in the latest
+	// sampling interval e.g. 100 queries / 5 seconds sampling interval = 20 QPS.
+	totalRate float64
+}
+
+// NewRates reports rolling rate information for countTracker. samples specifies
+// the number of samples to report, and interval specifies the time interval
+// between samples. The minimum interval is 1 second.
+// If passing the special value of -1s as interval, we don't snapshot.
+// (use this for tests).
+func NewRates(name string, countTracker CountTracker, samples int, interval time.Duration) *Rates {
+	if interval < 1*time.Second && interval != -1*time.Second {
+		panic("interval too small")
+	}
+	rt := &Rates{
+		timeStamps:            NewRingInt64(samples + 1),
+		counts:                make(map[string]*RingInt64),
+		countTracker:          countTracker,
+		samples:               samples + 1,
+		interval:              interval,
+		timestampLastSampling: timeNow(),
+	}
+	if name != "" {
+		publish(name, rt)
+	}
+	if interval > 0 {
+		go rt.track()
+	}
+	return rt
+}
+
+func (rt *Rates) track() {
+	for {
+		rt.snapshot()
+		<-time.After(rt.interval)
+	}
+}
+
+func (rt *Rates) snapshot() {
+	rt.mu.Lock()
+	defer rt.mu.Unlock()
+
+	now := timeNow()
+	rt.timeStamps.Add(now.UnixNano())
+
+	// Record current count for each category.
+	var totalCount int64
+	for k, v := range rt.countTracker.Counts() {
+		if k != "All" {
+			// Include call categories except "All" (which is returned by the
+			// "Timer.Counts()" implementation) to avoid double counting.
+			totalCount += v
+		}
+		if values, ok := rt.counts[k]; ok {
+			values.Add(v)
+		} else {
+			rt.counts[k] = NewRingInt64(rt.samples)
+			rt.counts[k].Add(0)
+			rt.counts[k].Add(v)
+		}
+	}
+
+	// Calculate current total rate.
+	// NOTE: We assume that every category with a non-zero value, which was
+	// tracked in "rt.previousTotalCount" in a previous sampling interval, is
+	// tracked in the current sampling interval in "totalCount" as well.
+	// (I.e. categories and their count must not "disappear" in
+	//  "rt.countTracker.Counts()".)
+	durationSeconds := now.Sub(rt.timestampLastSampling).Seconds()
+	rate := float64(totalCount-rt.previousTotalCount) / durationSeconds
+	// Round rate with a precision of 0.1.
+	rt.totalRate = math.Floor(rate*10+0.5) / 10
+	rt.previousTotalCount = totalCount
+	rt.timestampLastSampling = now
+}
+
+// Get returns for each category (string) its latest rates (up to X values
+// where X is the configured number of samples of the Rates struct).
+// Rates are ordered from least recent (index 0) to most recent (end of slice).
+func (rt *Rates) Get() (rateMap map[string][]float64) {
+	rt.mu.Lock()
+	defer rt.mu.Unlock()
+
+	rateMap = make(map[string][]float64)
+	timeStamps := rt.timeStamps.Values()
+	if len(timeStamps) <= 1 {
+		return
+	}
+	for k, v := range rt.counts {
+		rateMap[k] = make([]float64, len(timeStamps)-1)
+		values := v.Values()
+		valueIndex := len(values) - 1
+		for i := len(timeStamps) - 1; i > 0; i-- {
+			if valueIndex <= 0 {
+				rateMap[k][i-1] = 0
+				continue
+			}
+			elapsed := float64((timeStamps[i] - timeStamps[i-1]) / 1e9)
+			rateMap[k][i-1] = float64(values[valueIndex]-values[valueIndex-1]) / elapsed
+			valueIndex--
+		}
+	}
+	return
+}
+
+// TotalRate returns the current total rate (counted across categories).
+func (rt *Rates) TotalRate() float64 {
+	rt.mu.Lock()
+	defer rt.mu.Unlock()
+
+	return rt.totalRate
+}
+
+func (rt *Rates) String() string {
+	data, err := json.Marshal(rt.Get())
+	if err != nil {
+		data, _ = json.Marshal(err.Error())
+	}
+	return string(data)
+}
+
+type RatesFunc struct {
+	F    func() map[string][]float64
+	help string
+}
+
+func NewRateFunc(name string, help string, f func() map[string][]float64) *RatesFunc {
+	c := &RatesFunc{
+		F:    f,
+		help: help,
+	}
+
+	if name != "" {
+		publish(name, c)
+	}
+	return c
+}
+
+func (rf *RatesFunc) Help() string {
+	return rf.help
+}
+
+func (rf *RatesFunc) String() string {
+	data, err := json.Marshal(rf.F())
+	if err != nil {
+		data, _ = json.Marshal(err.Error())
+	}
+	return string(data)
+}

+ 142 - 0
stats/rates_test.go

@@ -0,0 +1,142 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"expvar"
+	"testing"
+	"time"
+)
+
+// For tests, we want to control exactly the time used by Rates.
+// The way Rates works is:
+// - at creation, do a snapshot.
+// - every interval, do a snapshot.
+// So in these tests, we make sure to always call snapshot() every interval.
+// We do other actions after epsilon, but then wait for intervalMinusEpsilon
+// and call snapshot().
+const (
+	interval             = 1 * time.Second
+	epsilon              = 50 * time.Millisecond
+	intervalMinusEpsilon = interval - epsilon
+)
+
+func TestRates(t *testing.T) {
+	now := time.Now()
+	timeNow = func() time.Time {
+		return now
+	}
+
+	clear()
+	c := NewCountersWithSingleLabel("rcounter1", "rcounter help", "label")
+	r := NewRates("rates1", c, 3, -1*time.Second)
+	r.snapshot()
+	now = now.Add(epsilon)
+	c.Add("tag1", 0)
+	c.Add("tag2", 0)
+	now = now.Add(intervalMinusEpsilon)
+	r.snapshot()
+	now = now.Add(epsilon)
+	checkRates(t, r, "after 1s", 0.0, `{"tag1":[0],"tag2":[0]}`)
+
+	c.Add("tag1", 10)
+	c.Add("tag2", 20)
+	now = now.Add(intervalMinusEpsilon)
+	r.snapshot()
+	now = now.Add(epsilon)
+	checkRates(t, r, "after 2s", 30.0, `{"tag1":[0,10],"tag2":[0,20]}`)
+
+	now = now.Add(intervalMinusEpsilon)
+	r.snapshot()
+	now = now.Add(epsilon)
+	checkRates(t, r, "after 3s", 0.0, `{"tag1":[0,10,0],"tag2":[0,20,0]}`)
+
+	now = now.Add(intervalMinusEpsilon)
+	r.snapshot()
+	now = now.Add(epsilon)
+	checkRates(t, r, "after 4s", 0.0, `{"tag1":[10,0,0],"tag2":[20,0,0]}`)
+}
+
+func checkRates(t *testing.T, r *Rates, desc string, wantRate float64, wantRateMap string) {
+	if got := r.String(); got != wantRateMap {
+		t.Errorf("%v: want %s, got %s", desc, wantRateMap, got)
+	}
+	if got := r.TotalRate(); got != wantRate {
+		t.Errorf("%v: want rate %v, got rate %v", desc, wantRate, got)
+	}
+}
+
+func TestRatesConsistency(t *testing.T) {
+	now := time.Now()
+	timeNow = func() time.Time {
+		return now
+	}
+
+	// This tests the following invariant: in the time window
+	// covered by rates, the sum of the rates reported must be
+	// equal to the count reported by the counter.
+	clear()
+	c := NewCountersWithSingleLabel("rcounter4", "rcounter4 help", "label")
+	r := NewRates("rates4", c, 100, -1*time.Second)
+	r.snapshot()
+
+	now = now.Add(epsilon)
+	c.Add("a", 1000)
+	now = now.Add(intervalMinusEpsilon)
+	r.snapshot()
+	now = now.Add(epsilon)
+	c.Add("a", 1)
+	now = now.Add(intervalMinusEpsilon)
+	r.snapshot()
+	now = now.Add(epsilon)
+
+	result := r.Get()
+	counts := c.Counts()
+	t.Logf("r.Get(): %v", result)
+	t.Logf("c.Counts(): %v", counts)
+
+	rate, count := result["a"], counts["a"]
+
+	var sum float64
+	for _, v := range rate {
+		sum += v
+	}
+	if sum != float64(counts["a"]) {
+		t.Errorf("rate inconsistent with count: sum of %v != %v", rate, count)
+	}
+
+}
+
+func TestRatesHook(t *testing.T) {
+	clear()
+	c := NewCountersWithSingleLabel("rcounter2", "rcounter2 help", "label")
+	var gotname string
+	var gotv *Rates
+	clear()
+	Register(func(name string, v expvar.Var) {
+		gotname = name
+		gotv = v.(*Rates)
+	})
+
+	v := NewRates("rates2", c, 2, 10*time.Second)
+	if gotname != "rates2" {
+		t.Errorf("want rates2, got %s", gotname)
+	}
+	if gotv != v {
+		t.Errorf("want %#v, got %#v", v, gotv)
+	}
+}

+ 45 - 0
stats/ring.go

@@ -0,0 +1,45 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+// Ring of int64 values
+// Not thread safe
+type RingInt64 struct {
+	position int
+	values   []int64
+}
+
+func NewRingInt64(capacity int) *RingInt64 {
+	return &RingInt64{values: make([]int64, 0, capacity)}
+}
+
+func (ri *RingInt64) Add(val int64) {
+	if len(ri.values) == cap(ri.values) {
+		ri.values[ri.position] = val
+		ri.position = (ri.position + 1) % cap(ri.values)
+	} else {
+		ri.values = append(ri.values, val)
+	}
+}
+
+func (ri *RingInt64) Values() (values []int64) {
+	values = make([]int64, len(ri.values))
+	for i := 0; i < len(ri.values); i++ {
+		values[i] = ri.values[(ri.position+i)%cap(ri.values)]
+	}
+	return values
+}

+ 64 - 0
stats/snake_case_converter.go

@@ -0,0 +1,64 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"regexp"
+	"strings"
+)
+
+// GetSnakeName calls toSnakeName on the passed in string. It produces
+// a snake-cased name from the provided camel-cased name.
+// It memoizes the transformation and returns the stored result if available.
+func GetSnakeName(name string) string {
+	return toSnakeCase(name)
+}
+
+// toSnakeCase produces a monitoring compliant name from the original.
+// For systems (like Prometheus) that ask for snake-case names.
+// It converts CamelCase to camel_case, and CAMEL_CASE to camel_case.
+// For numbers, it converts 0.5 to v0_5.
+func toSnakeCase(name string) (hyphenated string) {
+	snakeMemoizer.Lock()
+	defer snakeMemoizer.Unlock()
+	if hyphenated = snakeMemoizer.memo[name]; hyphenated != "" {
+		return hyphenated
+	}
+	hyphenated = name
+	for _, converter := range snakeConverters {
+		hyphenated = converter.re.ReplaceAllString(hyphenated, converter.repl)
+	}
+	hyphenated = strings.ToLower(hyphenated)
+	snakeMemoizer.memo[name] = hyphenated
+	return
+}
+
+var snakeConverters = []struct {
+	re   *regexp.Regexp
+	repl string
+}{
+	// example: LC -> L_C (e.g. CamelCase -> Camel_Case).
+	{regexp.MustCompile("([a-z])([A-Z])"), "${1}_${2}"},
+	// example: CCa -> C_Ca (e.g. CCamel -> C_Camel).
+	{regexp.MustCompile("([A-Z])([A-Z][a-z])"), "${1}_${2}"},
+	{regexp.MustCompile(`\.`), "_"},
+	{regexp.MustCompile("-"), "_"},
+}
+
+var snakeMemoizer = memoizerType{
+	memo: make(map[string]string),
+}

+ 54 - 0
stats/snake_case_converter_test.go

@@ -0,0 +1,54 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"testing"
+)
+
+func TestToSnakeCase(t *testing.T) {
+	var snakeCaseTest = []struct{ input, output string }{
+		{"Camel", "camel"},
+		{"Camel", "camel"},
+		{"CamelCase", "camel_case"},
+		{"CamelCaseAgain", "camel_case_again"},
+		{"CCamel", "c_camel"},
+		{"CCCamel", "cc_camel"},
+		{"CAMEL_CASE", "camel_case"},
+		{"camel-case", "camel_case"},
+		{"0", "0"},
+		{"0.0", "0_0"},
+		{"JSON", "json"},
+	}
+
+	for _, tt := range snakeCaseTest {
+		if got, want := toSnakeCase(tt.input), tt.output; got != want {
+			t.Errorf("want '%s', got '%s'", want, got)
+		}
+	}
+}
+
+func TestSnakeMemoize(t *testing.T) {
+	key := "Test"
+	if snakeMemoizer.memo[key] != "" {
+		t.Errorf("want '', got '%s'", snakeMemoizer.memo[key])
+	}
+	toSnakeCase(key)
+	if snakeMemoizer.memo[key] != "test" {
+		t.Errorf("want 'test', got '%s'", snakeMemoizer.memo[key])
+	}
+}

+ 253 - 0
stats/timings.go

@@ -0,0 +1,253 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+import (
+	"encoding/json"
+	"fmt"
+	"sync"
+	"time"
+
+	syncX "git.nspix.com/golang/micro/sync"
+)
+
+// Timings is meant to tracks timing data
+// by named categories as well as histograms.
+type Timings struct {
+	totalCount syncX.AtomicInt64
+	totalTime  syncX.AtomicInt64
+
+	mu         sync.RWMutex
+	histograms map[string]*Histogram
+
+	name          string
+	help          string
+	label         string
+	labelCombined bool
+}
+
+// NewTimings creates a new Timings object, and publishes it if name is set.
+// categories is an optional list of categories to initialize to 0.
+// Categories that aren't initialized will be missing from the map until the
+// first time they are updated.
+func NewTimings(name, help, label string, categories ...string) *Timings {
+	t := &Timings{
+		histograms:    make(map[string]*Histogram),
+		name:          name,
+		help:          help,
+		label:         label,
+		labelCombined: IsDimensionCombined(label),
+	}
+	for _, cat := range categories {
+		t.histograms[cat] = NewGenericHistogram("", "", bucketCutoffs, bucketLabels, "Count", "Time")
+	}
+	if name != "" {
+		publish(name, t)
+	}
+
+	return t
+}
+
+// Reset will clear histograms: used during testing
+func (t *Timings) Reset() {
+	t.mu.RLock()
+	t.histograms = make(map[string]*Histogram)
+	t.mu.RUnlock()
+}
+
+// Add will add a new value to the named histogram.
+func (t *Timings) Add(name string, elapsed time.Duration) {
+	if t.labelCombined {
+		name = StatsAllStr
+	}
+	// Get existing Histogram.
+	t.mu.RLock()
+	hist, ok := t.histograms[name]
+	t.mu.RUnlock()
+
+	// Create Histogram if it does not exist.
+	if !ok {
+		t.mu.Lock()
+		hist, ok = t.histograms[name]
+		if !ok {
+			hist = NewGenericHistogram("", "", bucketCutoffs, bucketLabels, "Count", "Time")
+			t.histograms[name] = hist
+		}
+		t.mu.Unlock()
+	}
+	if defaultStatsdHook.timerHook != nil && t.name != "" {
+		defaultStatsdHook.timerHook(t.name, name, elapsed.Milliseconds(), t)
+	}
+
+	elapsedNs := int64(elapsed)
+	hist.Add(elapsedNs)
+	t.totalCount.Add(1)
+	t.totalTime.Add(elapsedNs)
+}
+
+// Record is a convenience function that records completion
+// timing data based on the provided start time of an event.
+func (t *Timings) Record(name string, startTime time.Time) {
+	if t.labelCombined {
+		name = StatsAllStr
+	}
+	t.Add(name, time.Since(startTime))
+}
+
+// String is for expvar.
+func (t *Timings) String() string {
+	t.mu.RLock()
+	defer t.mu.RUnlock()
+
+	tm := struct {
+		TotalCount int64
+		TotalTime  int64
+		Histograms map[string]*Histogram
+	}{
+		t.totalCount.Get(),
+		t.totalTime.Get(),
+		t.histograms,
+	}
+
+	data, err := json.Marshal(tm)
+	if err != nil {
+		data, _ = json.Marshal(err.Error())
+	}
+	return string(data)
+}
+
+// Histograms returns a map pointing at the histograms.
+func (t *Timings) Histograms() (h map[string]*Histogram) {
+	t.mu.RLock()
+	defer t.mu.RUnlock()
+	h = make(map[string]*Histogram, len(t.histograms))
+	for k, v := range t.histograms {
+		h[k] = v
+	}
+	return
+}
+
+// Count returns the total count for all values.
+func (t *Timings) Count() int64 {
+	return t.totalCount.Get()
+}
+
+// Time returns the total time elapsed for all values.
+func (t *Timings) Time() int64 {
+	return t.totalTime.Get()
+}
+
+// Counts returns the total count for each value.
+func (t *Timings) Counts() map[string]int64 {
+	t.mu.RLock()
+	defer t.mu.RUnlock()
+
+	counts := make(map[string]int64, len(t.histograms)+1)
+	for k, v := range t.histograms {
+		counts[k] = v.Count()
+	}
+	counts["All"] = t.totalCount.Get()
+	return counts
+}
+
+// Cutoffs returns the cutoffs used in the component histograms.
+// Do not change the returned slice.
+func (t *Timings) Cutoffs() []int64 {
+	return bucketCutoffs
+}
+
+// Help returns the help string.
+func (t *Timings) Help() string {
+	return t.help
+}
+
+// Label returns the label name.
+func (t *Timings) Label() string {
+	return t.label
+}
+
+var bucketCutoffs = []int64{5e5, 1e6, 5e6, 1e7, 5e7, 1e8, 5e8, 1e9, 5e9, 1e10}
+
+var bucketLabels []string
+
+func init() {
+	bucketLabels = make([]string, len(bucketCutoffs)+1)
+	for i, v := range bucketCutoffs {
+		bucketLabels[i] = fmt.Sprintf("%d", v)
+	}
+	bucketLabels[len(bucketLabels)-1] = "inf"
+}
+
+// MultiTimings is meant to tracks timing data by categories as well
+// as histograms. The names of the categories are compound names made
+// with joining multiple strings with '.'.
+type MultiTimings struct {
+	Timings
+	labels         []string
+	combinedLabels []bool
+}
+
+// NewMultiTimings creates a new MultiTimings object.
+func NewMultiTimings(name string, help string, labels []string) *MultiTimings {
+	combinedLabels := make([]bool, len(labels))
+	for i, label := range labels {
+		combinedLabels[i] = IsDimensionCombined(label)
+	}
+	t := &MultiTimings{
+		Timings: Timings{
+			histograms: make(map[string]*Histogram),
+			name:       name,
+			help:       help,
+			label:      safeJoinLabels(labels, combinedLabels),
+		},
+		labels:         labels,
+		combinedLabels: combinedLabels,
+	}
+	if name != "" {
+		publish(name, t)
+	}
+
+	return t
+}
+
+// Labels returns descriptions of the parts of each compound category name.
+func (mt *MultiTimings) Labels() []string {
+	return mt.labels
+}
+
+// Add will add a new value to the named histogram.
+func (mt *MultiTimings) Add(names []string, elapsed time.Duration) {
+	if len(names) != len(mt.labels) {
+		panic("MultiTimings: wrong number of values in Add")
+	}
+	mt.Timings.Add(safeJoinLabels(names, mt.combinedLabels), elapsed)
+}
+
+// Record is a convenience function that records completion
+// timing data based on the provided start time of an event.
+func (mt *MultiTimings) Record(names []string, startTime time.Time) {
+	if len(names) != len(mt.labels) {
+		panic("MultiTimings: wrong number of values in Record")
+	}
+	mt.Timings.Record(safeJoinLabels(names, mt.combinedLabels), startTime)
+}
+
+// Cutoffs returns the cutoffs used in the component histograms.
+// Do not change the returned slice.
+func (mt *MultiTimings) Cutoffs() []int64 {
+	return bucketCutoffs
+}

+ 29 - 0
stats/variable_interface.go

@@ -0,0 +1,29 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package stats
+
+// Variable is the minimal interface which each type in this "stats" package
+// must implement.
+// When integrating the Vitess stats types ("variables") with the different
+// monitoring systems, you can rely on this interface.
+type Variable interface {
+	// Help returns the description of the variable.
+	Help() string
+
+	// String must implement String() from the expvar.Var interface.
+	String() string
+}

+ 212 - 0
sync/atomic.go

@@ -0,0 +1,212 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sync
+
+import (
+	"math"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+// AtomicInt32 is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Int32 functions.
+type AtomicInt32 struct {
+	int32
+}
+
+// NewAtomicInt32 initializes a new AtomicInt32 with a given value.
+func NewAtomicInt32(n int32) AtomicInt32 {
+	return AtomicInt32{n}
+}
+
+// Add atomically adds n to the value.
+func (i *AtomicInt32) Add(n int32) int32 {
+	return atomic.AddInt32(&i.int32, n)
+}
+
+// Set atomically sets n as new value.
+func (i *AtomicInt32) Set(n int32) {
+	atomic.StoreInt32(&i.int32, n)
+}
+
+// Get atomically returns the current value.
+func (i *AtomicInt32) Get() int32 {
+	return atomic.LoadInt32(&i.int32)
+}
+
+// CompareAndSwap automatically swaps the old with the new value.
+func (i *AtomicInt32) CompareAndSwap(oldval, newval int32) (swapped bool) {
+	return atomic.CompareAndSwapInt32(&i.int32, oldval, newval)
+}
+
+// AtomicInt64 is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Int64 functions.
+type AtomicInt64 struct {
+	int64
+}
+
+// NewAtomicInt64 initializes a new AtomicInt64 with a given value.
+func NewAtomicInt64(n int64) AtomicInt64 {
+	return AtomicInt64{n}
+}
+
+// Add atomically adds n to the value.
+func (i *AtomicInt64) Add(n int64) int64 {
+	return atomic.AddInt64(&i.int64, n)
+}
+
+// Set atomically sets n as new value.
+func (i *AtomicInt64) Set(n int64) {
+	atomic.StoreInt64(&i.int64, n)
+}
+
+// Get atomically returns the current value.
+func (i *AtomicInt64) Get() int64 {
+	return atomic.LoadInt64(&i.int64)
+}
+
+// CompareAndSwap automatically swaps the old with the new value.
+func (i *AtomicInt64) CompareAndSwap(oldval, newval int64) (swapped bool) {
+	return atomic.CompareAndSwapInt64(&i.int64, oldval, newval)
+}
+
+// AtomicFloat64 is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Flat64 functions.
+type AtomicFloat64 struct {
+	uint64
+}
+
+// NewAtomicFloat64 initializes a new AtomicFloat64 with a given value.
+func NewAtomicFloat64(n float64) AtomicFloat64 {
+	return AtomicFloat64{math.Float64bits(n)}
+}
+
+// Set atomically sets n as new value.
+func (f *AtomicFloat64) Set(n float64) {
+	atomic.StoreUint64(&f.uint64, math.Float64bits(n))
+}
+
+// Get atomically returns the current value.
+func (f *AtomicFloat64) Get() float64 {
+	return math.Float64frombits(atomic.LoadUint64(&f.uint64))
+}
+
+// CompareAndSwap automatically swaps the old with the new value.
+func (f *AtomicFloat64) CompareAndSwap(oldval, newval float64) (swapped bool) {
+	return atomic.CompareAndSwapUint64(&f.uint64, math.Float64bits(oldval), math.Float64bits(newval))
+}
+
+// AtomicDuration is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Int64 functions.
+type AtomicDuration struct {
+	int64
+}
+
+// NewAtomicDuration initializes a new AtomicDuration with a given value.
+func NewAtomicDuration(duration time.Duration) AtomicDuration {
+	return AtomicDuration{int64(duration)}
+}
+
+// Add atomically adds duration to the value.
+func (d *AtomicDuration) Add(duration time.Duration) time.Duration {
+	return time.Duration(atomic.AddInt64(&d.int64, int64(duration)))
+}
+
+// Set atomically sets duration as new value.
+func (d *AtomicDuration) Set(duration time.Duration) {
+	atomic.StoreInt64(&d.int64, int64(duration))
+}
+
+// Get atomically returns the current value.
+func (d *AtomicDuration) Get() time.Duration {
+	return time.Duration(atomic.LoadInt64(&d.int64))
+}
+
+// CompareAndSwap automatically swaps the old with the new value.
+func (d *AtomicDuration) CompareAndSwap(oldval, newval time.Duration) (swapped bool) {
+	return atomic.CompareAndSwapInt64(&d.int64, int64(oldval), int64(newval))
+}
+
+// AtomicBool gives an atomic boolean variable.
+type AtomicBool struct {
+	int32
+}
+
+// NewAtomicBool initializes a new AtomicBool with a given value.
+func NewAtomicBool(n bool) AtomicBool {
+	if n {
+		return AtomicBool{1}
+	}
+	return AtomicBool{0}
+}
+
+// Set atomically sets n as new value.
+func (i *AtomicBool) Set(n bool) {
+	if n {
+		atomic.StoreInt32(&i.int32, 1)
+	} else {
+		atomic.StoreInt32(&i.int32, 0)
+	}
+}
+
+// Get atomically returns the current value.
+func (i *AtomicBool) Get() bool {
+	return atomic.LoadInt32(&i.int32) != 0
+}
+
+// CompareAndSwap automatically swaps the old with the new value.
+func (i *AtomicBool) CompareAndSwap(o, n bool) bool {
+	var old, new int32
+	if o {
+		old = 1
+	}
+	if n {
+		new = 1
+	}
+	return atomic.CompareAndSwapInt32(&i.int32, old, new)
+}
+
+// AtomicString gives you atomic-style APIs for string, but
+// it's only a convenience wrapper that uses a mutex. So, it's
+// not as efficient as the rest of the atomic types.
+type AtomicString struct {
+	mu  sync.Mutex
+	str string
+}
+
+// Set atomically sets str as new value.
+func (s *AtomicString) Set(str string) {
+	s.mu.Lock()
+	s.str = str
+	s.mu.Unlock()
+}
+
+// Get atomically returns the current value.
+func (s *AtomicString) Get() string {
+	s.mu.Lock()
+	str := s.str
+	s.mu.Unlock()
+	return str
+}
+
+// CompareAndSwap automatically swaps the old with the new value.
+func (s *AtomicString) CompareAndSwap(oldval, newval string) (swqpped bool) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.str == oldval {
+		s.str = newval
+		return true
+	}
+	return false
+}

+ 89 - 0
sync/batcher.go

@@ -0,0 +1,89 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sync
+
+import (
+	"time"
+)
+
+// Batcher delays concurrent operations for a configurable interval in order to
+// batch them up or otherwise clock their operation to run concurrently.
+//
+// It is implemented as a channel of int32s. Each waiter blocks on the channel
+// from which it gets a sequentially increasing batch ID when the timer elapses.
+//
+// Hence a waiter is delayed for at most the batch interval.
+type Batcher struct {
+	interval time.Duration
+	queue    chan int
+	waiters  AtomicInt32
+	nextID   AtomicInt32
+	after    func(time.Duration) <-chan time.Time
+}
+
+// NewBatcher returns a new Batcher
+func NewBatcher(interval time.Duration) *Batcher {
+	return &Batcher{
+		interval: interval,
+		queue:    make(chan int),
+		waiters:  NewAtomicInt32(0),
+		nextID:   NewAtomicInt32(0),
+		after:    time.After,
+	}
+}
+
+// newBatcherForTest returns a Batcher for testing where time.After can
+// be replaced by a fake alternative.
+func newBatcherForTest(interval time.Duration, after func(time.Duration) <-chan time.Time) *Batcher {
+	return &Batcher{
+		interval: interval,
+		queue:    make(chan int),
+		waiters:  NewAtomicInt32(0),
+		nextID:   NewAtomicInt32(0),
+		after:    after,
+	}
+}
+
+// Wait adds a new waiter to the queue and blocks until the next batch
+func (b *Batcher) Wait() int {
+	numWaiters := b.waiters.Add(1)
+	if numWaiters == 1 {
+		b.newBatch()
+	}
+	return <-b.queue
+}
+
+// newBatch starts a new batch
+func (b *Batcher) newBatch() {
+	go func() {
+		<-b.after(b.interval)
+
+		id := b.nextID.Add(1)
+
+		// Make sure to atomically reset the number of waiters to make
+		// sure that all incoming requests either make it into the
+		// current batch or the next one.
+		waiters := b.waiters.Get()
+		for !b.waiters.CompareAndSwap(waiters, 0) {
+			waiters = b.waiters.Get()
+		}
+
+		for i := int32(0); i < waiters; i++ {
+			b.queue <- int(id)
+		}
+	}()
+}

+ 6 - 0
sync/norace.go

@@ -0,0 +1,6 @@
+// +build !race
+
+package sync
+
+// Race reports if the race detector is enabled.
+const Race = false

+ 6 - 0
sync/race.go

@@ -0,0 +1,6 @@
+// +build race
+
+package sync
+
+// Race reports if the race detector is enabled.
+const Race = true

+ 97 - 0
sync/semaphore.go

@@ -0,0 +1,97 @@
+/*
+Copyright 2019 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sync
+
+// What's in a name? Channels have all you need to emulate a counting
+// semaphore with a boatload of extra functionality. However, in some
+// cases, you just want a familiar API.
+
+import (
+	"context"
+	"time"
+)
+
+// Semaphore is a counting semaphore with the option to
+// specify a timeout.
+type Semaphore struct {
+	slots   chan struct{}
+	timeout time.Duration
+}
+
+// NewSemaphore creates a Semaphore. The count parameter must be a positive
+// number. A timeout of zero means that there is no timeout.
+func NewSemaphore(count int, timeout time.Duration) *Semaphore {
+	sem := &Semaphore{
+		slots:   make(chan struct{}, count),
+		timeout: timeout,
+	}
+	for i := 0; i < count; i++ {
+		sem.slots <- struct{}{}
+	}
+	return sem
+}
+
+// Acquire returns true on successful acquisition, and
+// false on a timeout.
+func (sem *Semaphore) Acquire() bool {
+	if sem.timeout == 0 {
+		<-sem.slots
+		return true
+	}
+	tm := time.NewTimer(sem.timeout)
+	defer tm.Stop()
+	select {
+	case <-sem.slots:
+		return true
+	case <-tm.C:
+		return false
+	}
+}
+
+// AcquireContext returns true on successful acquisition, and
+// false on context expiry. Timeout is ignored.
+func (sem *Semaphore) AcquireContext(ctx context.Context) bool {
+	select {
+	case <-sem.slots:
+		return true
+	case <-ctx.Done():
+		return false
+	}
+}
+
+// TryAcquire acquires a semaphore if it's immediately available.
+// It returns false otherwise.
+func (sem *Semaphore) TryAcquire() bool {
+	select {
+	case <-sem.slots:
+		return true
+	default:
+		return false
+	}
+}
+
+// Release releases the acquired semaphore. You must
+// not release more than the number of semaphores you've
+// acquired.
+func (sem *Semaphore) Release() {
+	sem.slots <- struct{}{}
+}
+
+// Size returns the current number of available slots.
+func (sem *Semaphore) Size() int {
+	return len(sem.slots)
+}

+ 0 - 72
utils/metric/metric.go

@@ -1,72 +0,0 @@
-package metric
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"errors"
-	"net/http"
-	"time"
-)
-
-type (
-	Value struct {
-		Metric string            `json:"metric"`
-		Value  float64           `json:"value"`
-		Tags   map[string]string `json:"tags"`
-	}
-
-	Values []*Value
-)
-
-func (m *Value) SetTag(key string, value string) *Value {
-	if m.Tags == nil {
-		m.Tags = make(map[string]string)
-	}
-	m.Tags[key] = value
-	return m
-}
-
-func (m *Value) SetTags(tags map[string]string) *Value {
-	for k, v := range tags {
-		m.Tags[k] = v
-	}
-	return m
-}
-
-func (m *Value) Bytes() []byte {
-	buf, _ := json.Marshal(m)
-	return buf
-}
-
-func (m Values) Bytes() []byte {
-	buf, _ := json.Marshal(m)
-	return buf
-}
-
-func New(metric string, value float64) *Value {
-	m := &Value{
-		Metric: metric,
-		Value:  value,
-	}
-	m.Tags = make(map[string]string)
-	return m
-}
-
-func Push(ctx context.Context, uri string, ms Values) (err error) {
-	var (
-		req *http.Request
-		res *http.Response
-	)
-	if req, err = http.NewRequest("POST", uri, bytes.NewReader(ms.Bytes())); err != nil {
-		return
-	}
-	cc, _ := context.WithTimeout(ctx, time.Second*5)
-	if res, err = http.DefaultClient.Do(req.WithContext(cc)); err == nil {
-		if !(res.StatusCode == 200 || res.StatusCode == 204) {
-			err = errors.New("read response error:" + res.Status)
-		}
-		_ = res.Body.Close()
-	}
-	return
-}

+ 20 - 0
vendor/github.com/beorn7/perks/LICENSE

@@ -0,0 +1,20 @@
+Copyright (C) 2013 Blake Mizerany
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 2388 - 0
vendor/github.com/beorn7/perks/quantile/exampledata.txt

@@ -0,0 +1,2388 @@
+8
+5
+26
+12
+5
+235
+13
+6
+28
+30
+3
+3
+3
+3
+5
+2
+33
+7
+2
+4
+7
+12
+14
+5
+8
+3
+10
+4
+5
+3
+6
+6
+209
+20
+3
+10
+14
+3
+4
+6
+8
+5
+11
+7
+3
+2
+3
+3
+212
+5
+222
+4
+10
+10
+5
+6
+3
+8
+3
+10
+254
+220
+2
+3
+5
+24
+5
+4
+222
+7
+3
+3
+223
+8
+15
+12
+14
+14
+3
+2
+2
+3
+13
+3
+11
+4
+4
+6
+5
+7
+13
+5
+3
+5
+2
+5
+3
+5
+2
+7
+15
+17
+14
+3
+6
+6
+3
+17
+5
+4
+7
+6
+4
+4
+8
+6
+8
+3
+9
+3
+6
+3
+4
+5
+3
+3
+660
+4
+6
+10
+3
+6
+3
+2
+5
+13
+2
+4
+4
+10
+4
+8
+4
+3
+7
+9
+9
+3
+10
+37
+3
+13
+4
+12
+3
+6
+10
+8
+5
+21
+2
+3
+8
+3
+2
+3
+3
+4
+12
+2
+4
+8
+8
+4
+3
+2
+20
+1
+6
+32
+2
+11
+6
+18
+3
+8
+11
+3
+212
+3
+4
+2
+6
+7
+12
+11
+3
+2
+16
+10
+6
+4
+6
+3
+2
+7
+3
+2
+2
+2
+2
+5
+6
+4
+3
+10
+3
+4
+6
+5
+3
+4
+4
+5
+6
+4
+3
+4
+4
+5
+7
+5
+5
+3
+2
+7
+2
+4
+12
+4
+5
+6
+2
+4
+4
+8
+4
+15
+13
+7
+16
+5
+3
+23
+5
+5
+7
+3
+2
+9
+8
+7
+5
+8
+11
+4
+10
+76
+4
+47
+4
+3
+2
+7
+4
+2
+3
+37
+10
+4
+2
+20
+5
+4
+4
+10
+10
+4
+3
+7
+23
+240
+7
+13
+5
+5
+3
+3
+2
+5
+4
+2
+8
+7
+19
+2
+23
+8
+7
+2
+5
+3
+8
+3
+8
+13
+5
+5
+5
+2
+3
+23
+4
+9
+8
+4
+3
+3
+5
+220
+2
+3
+4
+6
+14
+3
+53
+6
+2
+5
+18
+6
+3
+219
+6
+5
+2
+5
+3
+6
+5
+15
+4
+3
+17
+3
+2
+4
+7
+2
+3
+3
+4
+4
+3
+2
+664
+6
+3
+23
+5
+5
+16
+5
+8
+2
+4
+2
+24
+12
+3
+2
+3
+5
+8
+3
+5
+4
+3
+14
+3
+5
+8
+2
+3
+7
+9
+4
+2
+3
+6
+8
+4
+3
+4
+6
+5
+3
+3
+6
+3
+19
+4
+4
+6
+3
+6
+3
+5
+22
+5
+4
+4
+3
+8
+11
+4
+9
+7
+6
+13
+4
+4
+4
+6
+17
+9
+3
+3
+3
+4
+3
+221
+5
+11
+3
+4
+2
+12
+6
+3
+5
+7
+5
+7
+4
+9
+7
+14
+37
+19
+217
+16
+3
+5
+2
+2
+7
+19
+7
+6
+7
+4
+24
+5
+11
+4
+7
+7
+9
+13
+3
+4
+3
+6
+28
+4
+4
+5
+5
+2
+5
+6
+4
+4
+6
+10
+5
+4
+3
+2
+3
+3
+6
+5
+5
+4
+3
+2
+3
+7
+4
+6
+18
+16
+8
+16
+4
+5
+8
+6
+9
+13
+1545
+6
+215
+6
+5
+6
+3
+45
+31
+5
+2
+2
+4
+3
+3
+2
+5
+4
+3
+5
+7
+7
+4
+5
+8
+5
+4
+749
+2
+31
+9
+11
+2
+11
+5
+4
+4
+7
+9
+11
+4
+5
+4
+7
+3
+4
+6
+2
+15
+3
+4
+3
+4
+3
+5
+2
+13
+5
+5
+3
+3
+23
+4
+4
+5
+7
+4
+13
+2
+4
+3
+4
+2
+6
+2
+7
+3
+5
+5
+3
+29
+5
+4
+4
+3
+10
+2
+3
+79
+16
+6
+6
+7
+7
+3
+5
+5
+7
+4
+3
+7
+9
+5
+6
+5
+9
+6
+3
+6
+4
+17
+2
+10
+9
+3
+6
+2
+3
+21
+22
+5
+11
+4
+2
+17
+2
+224
+2
+14
+3
+4
+4
+2
+4
+4
+4
+4
+5
+3
+4
+4
+10
+2
+6
+3
+3
+5
+7
+2
+7
+5
+6
+3
+218
+2
+2
+5
+2
+6
+3
+5
+222
+14
+6
+33
+3
+2
+5
+3
+3
+3
+9
+5
+3
+3
+2
+7
+4
+3
+4
+3
+5
+6
+5
+26
+4
+13
+9
+7
+3
+221
+3
+3
+4
+4
+4
+4
+2
+18
+5
+3
+7
+9
+6
+8
+3
+10
+3
+11
+9
+5
+4
+17
+5
+5
+6
+6
+3
+2
+4
+12
+17
+6
+7
+218
+4
+2
+4
+10
+3
+5
+15
+3
+9
+4
+3
+3
+6
+29
+3
+3
+4
+5
+5
+3
+8
+5
+6
+6
+7
+5
+3
+5
+3
+29
+2
+31
+5
+15
+24
+16
+5
+207
+4
+3
+3
+2
+15
+4
+4
+13
+5
+5
+4
+6
+10
+2
+7
+8
+4
+6
+20
+5
+3
+4
+3
+12
+12
+5
+17
+7
+3
+3
+3
+6
+10
+3
+5
+25
+80
+4
+9
+3
+2
+11
+3
+3
+2
+3
+8
+7
+5
+5
+19
+5
+3
+3
+12
+11
+2
+6
+5
+5
+5
+3
+3
+3
+4
+209
+14
+3
+2
+5
+19
+4
+4
+3
+4
+14
+5
+6
+4
+13
+9
+7
+4
+7
+10
+2
+9
+5
+7
+2
+8
+4
+6
+5
+5
+222
+8
+7
+12
+5
+216
+3
+4
+4
+6
+3
+14
+8
+7
+13
+4
+3
+3
+3
+3
+17
+5
+4
+3
+33
+6
+6
+33
+7
+5
+3
+8
+7
+5
+2
+9
+4
+2
+233
+24
+7
+4
+8
+10
+3
+4
+15
+2
+16
+3
+3
+13
+12
+7
+5
+4
+207
+4
+2
+4
+27
+15
+2
+5
+2
+25
+6
+5
+5
+6
+13
+6
+18
+6
+4
+12
+225
+10
+7
+5
+2
+2
+11
+4
+14
+21
+8
+10
+3
+5
+4
+232
+2
+5
+5
+3
+7
+17
+11
+6
+6
+23
+4
+6
+3
+5
+4
+2
+17
+3
+6
+5
+8
+3
+2
+2
+14
+9
+4
+4
+2
+5
+5
+3
+7
+6
+12
+6
+10
+3
+6
+2
+2
+19
+5
+4
+4
+9
+2
+4
+13
+3
+5
+6
+3
+6
+5
+4
+9
+6
+3
+5
+7
+3
+6
+6
+4
+3
+10
+6
+3
+221
+3
+5
+3
+6
+4
+8
+5
+3
+6
+4
+4
+2
+54
+5
+6
+11
+3
+3
+4
+4
+4
+3
+7
+3
+11
+11
+7
+10
+6
+13
+223
+213
+15
+231
+7
+3
+7
+228
+2
+3
+4
+4
+5
+6
+7
+4
+13
+3
+4
+5
+3
+6
+4
+6
+7
+2
+4
+3
+4
+3
+3
+6
+3
+7
+3
+5
+18
+5
+6
+8
+10
+3
+3
+3
+2
+4
+2
+4
+4
+5
+6
+6
+4
+10
+13
+3
+12
+5
+12
+16
+8
+4
+19
+11
+2
+4
+5
+6
+8
+5
+6
+4
+18
+10
+4
+2
+216
+6
+6
+6
+2
+4
+12
+8
+3
+11
+5
+6
+14
+5
+3
+13
+4
+5
+4
+5
+3
+28
+6
+3
+7
+219
+3
+9
+7
+3
+10
+6
+3
+4
+19
+5
+7
+11
+6
+15
+19
+4
+13
+11
+3
+7
+5
+10
+2
+8
+11
+2
+6
+4
+6
+24
+6
+3
+3
+3
+3
+6
+18
+4
+11
+4
+2
+5
+10
+8
+3
+9
+5
+3
+4
+5
+6
+2
+5
+7
+4
+4
+14
+6
+4
+4
+5
+5
+7
+2
+4
+3
+7
+3
+3
+6
+4
+5
+4
+4
+4
+3
+3
+3
+3
+8
+14
+2
+3
+5
+3
+2
+4
+5
+3
+7
+3
+3
+18
+3
+4
+4
+5
+7
+3
+3
+3
+13
+5
+4
+8
+211
+5
+5
+3
+5
+2
+5
+4
+2
+655
+6
+3
+5
+11
+2
+5
+3
+12
+9
+15
+11
+5
+12
+217
+2
+6
+17
+3
+3
+207
+5
+5
+4
+5
+9
+3
+2
+8
+5
+4
+3
+2
+5
+12
+4
+14
+5
+4
+2
+13
+5
+8
+4
+225
+4
+3
+4
+5
+4
+3
+3
+6
+23
+9
+2
+6
+7
+233
+4
+4
+6
+18
+3
+4
+6
+3
+4
+4
+2
+3
+7
+4
+13
+227
+4
+3
+5
+4
+2
+12
+9
+17
+3
+7
+14
+6
+4
+5
+21
+4
+8
+9
+2
+9
+25
+16
+3
+6
+4
+7
+8
+5
+2
+3
+5
+4
+3
+3
+5
+3
+3
+3
+2
+3
+19
+2
+4
+3
+4
+2
+3
+4
+4
+2
+4
+3
+3
+3
+2
+6
+3
+17
+5
+6
+4
+3
+13
+5
+3
+3
+3
+4
+9
+4
+2
+14
+12
+4
+5
+24
+4
+3
+37
+12
+11
+21
+3
+4
+3
+13
+4
+2
+3
+15
+4
+11
+4
+4
+3
+8
+3
+4
+4
+12
+8
+5
+3
+3
+4
+2
+220
+3
+5
+223
+3
+3
+3
+10
+3
+15
+4
+241
+9
+7
+3
+6
+6
+23
+4
+13
+7
+3
+4
+7
+4
+9
+3
+3
+4
+10
+5
+5
+1
+5
+24
+2
+4
+5
+5
+6
+14
+3
+8
+2
+3
+5
+13
+13
+3
+5
+2
+3
+15
+3
+4
+2
+10
+4
+4
+4
+5
+5
+3
+5
+3
+4
+7
+4
+27
+3
+6
+4
+15
+3
+5
+6
+6
+5
+4
+8
+3
+9
+2
+6
+3
+4
+3
+7
+4
+18
+3
+11
+3
+3
+8
+9
+7
+24
+3
+219
+7
+10
+4
+5
+9
+12
+2
+5
+4
+4
+4
+3
+3
+19
+5
+8
+16
+8
+6
+22
+3
+23
+3
+242
+9
+4
+3
+3
+5
+7
+3
+3
+5
+8
+3
+7
+5
+14
+8
+10
+3
+4
+3
+7
+4
+6
+7
+4
+10
+4
+3
+11
+3
+7
+10
+3
+13
+6
+8
+12
+10
+5
+7
+9
+3
+4
+7
+7
+10
+8
+30
+9
+19
+4
+3
+19
+15
+4
+13
+3
+215
+223
+4
+7
+4
+8
+17
+16
+3
+7
+6
+5
+5
+4
+12
+3
+7
+4
+4
+13
+4
+5
+2
+5
+6
+5
+6
+6
+7
+10
+18
+23
+9
+3
+3
+6
+5
+2
+4
+2
+7
+3
+3
+2
+5
+5
+14
+10
+224
+6
+3
+4
+3
+7
+5
+9
+3
+6
+4
+2
+5
+11
+4
+3
+3
+2
+8
+4
+7
+4
+10
+7
+3
+3
+18
+18
+17
+3
+3
+3
+4
+5
+3
+3
+4
+12
+7
+3
+11
+13
+5
+4
+7
+13
+5
+4
+11
+3
+12
+3
+6
+4
+4
+21
+4
+6
+9
+5
+3
+10
+8
+4
+6
+4
+4
+6
+5
+4
+8
+6
+4
+6
+4
+4
+5
+9
+6
+3
+4
+2
+9
+3
+18
+2
+4
+3
+13
+3
+6
+6
+8
+7
+9
+3
+2
+16
+3
+4
+6
+3
+2
+33
+22
+14
+4
+9
+12
+4
+5
+6
+3
+23
+9
+4
+3
+5
+5
+3
+4
+5
+3
+5
+3
+10
+4
+5
+5
+8
+4
+4
+6
+8
+5
+4
+3
+4
+6
+3
+3
+3
+5
+9
+12
+6
+5
+9
+3
+5
+3
+2
+2
+2
+18
+3
+2
+21
+2
+5
+4
+6
+4
+5
+10
+3
+9
+3
+2
+10
+7
+3
+6
+6
+4
+4
+8
+12
+7
+3
+7
+3
+3
+9
+3
+4
+5
+4
+4
+5
+5
+10
+15
+4
+4
+14
+6
+227
+3
+14
+5
+216
+22
+5
+4
+2
+2
+6
+3
+4
+2
+9
+9
+4
+3
+28
+13
+11
+4
+5
+3
+3
+2
+3
+3
+5
+3
+4
+3
+5
+23
+26
+3
+4
+5
+6
+4
+6
+3
+5
+5
+3
+4
+3
+2
+2
+2
+7
+14
+3
+6
+7
+17
+2
+2
+15
+14
+16
+4
+6
+7
+13
+6
+4
+5
+6
+16
+3
+3
+28
+3
+6
+15
+3
+9
+2
+4
+6
+3
+3
+22
+4
+12
+6
+7
+2
+5
+4
+10
+3
+16
+6
+9
+2
+5
+12
+7
+5
+5
+5
+5
+2
+11
+9
+17
+4
+3
+11
+7
+3
+5
+15
+4
+3
+4
+211
+8
+7
+5
+4
+7
+6
+7
+6
+3
+6
+5
+6
+5
+3
+4
+4
+26
+4
+6
+10
+4
+4
+3
+2
+3
+3
+4
+5
+9
+3
+9
+4
+4
+5
+5
+8
+2
+4
+2
+3
+8
+4
+11
+19
+5
+8
+6
+3
+5
+6
+12
+3
+2
+4
+16
+12
+3
+4
+4
+8
+6
+5
+6
+6
+219
+8
+222
+6
+16
+3
+13
+19
+5
+4
+3
+11
+6
+10
+4
+7
+7
+12
+5
+3
+3
+5
+6
+10
+3
+8
+2
+5
+4
+7
+2
+4
+4
+2
+12
+9
+6
+4
+2
+40
+2
+4
+10
+4
+223
+4
+2
+20
+6
+7
+24
+5
+4
+5
+2
+20
+16
+6
+5
+13
+2
+3
+3
+19
+3
+2
+4
+5
+6
+7
+11
+12
+5
+6
+7
+7
+3
+5
+3
+5
+3
+14
+3
+4
+4
+2
+11
+1
+7
+3
+9
+6
+11
+12
+5
+8
+6
+221
+4
+2
+12
+4
+3
+15
+4
+5
+226
+7
+218
+7
+5
+4
+5
+18
+4
+5
+9
+4
+4
+2
+9
+18
+18
+9
+5
+6
+6
+3
+3
+7
+3
+5
+4
+4
+4
+12
+3
+6
+31
+5
+4
+7
+3
+6
+5
+6
+5
+11
+2
+2
+11
+11
+6
+7
+5
+8
+7
+10
+5
+23
+7
+4
+3
+5
+34
+2
+5
+23
+7
+3
+6
+8
+4
+4
+4
+2
+5
+3
+8
+5
+4
+8
+25
+2
+3
+17
+8
+3
+4
+8
+7
+3
+15
+6
+5
+7
+21
+9
+5
+6
+6
+5
+3
+2
+3
+10
+3
+6
+3
+14
+7
+4
+4
+8
+7
+8
+2
+6
+12
+4
+213
+6
+5
+21
+8
+2
+5
+23
+3
+11
+2
+3
+6
+25
+2
+3
+6
+7
+6
+6
+4
+4
+6
+3
+17
+9
+7
+6
+4
+3
+10
+7
+2
+3
+3
+3
+11
+8
+3
+7
+6
+4
+14
+36
+3
+4
+3
+3
+22
+13
+21
+4
+2
+7
+4
+4
+17
+15
+3
+7
+11
+2
+4
+7
+6
+209
+6
+3
+2
+2
+24
+4
+9
+4
+3
+3
+3
+29
+2
+2
+4
+3
+3
+5
+4
+6
+3
+3
+2
+4

+ 316 - 0
vendor/github.com/beorn7/perks/quantile/stream.go

@@ -0,0 +1,316 @@
+// Package quantile computes approximate quantiles over an unbounded data
+// stream within low memory and CPU bounds.
+//
+// A small amount of accuracy is traded to achieve the above properties.
+//
+// Multiple streams can be merged before calling Query to generate a single set
+// of results. This is meaningful when the streams represent the same type of
+// data. See Merge and Samples.
+//
+// For more detailed information about the algorithm used, see:
+//
+// Effective Computation of Biased Quantiles over Data Streams
+//
+// http://www.cs.rutgers.edu/~muthu/bquant.pdf
+package quantile
+
+import (
+	"math"
+	"sort"
+)
+
+// Sample holds an observed value and meta information for compression. JSON
+// tags have been added for convenience.
+type Sample struct {
+	Value float64 `json:",string"`
+	Width float64 `json:",string"`
+	Delta float64 `json:",string"`
+}
+
+// Samples represents a slice of samples. It implements sort.Interface.
+type Samples []Sample
+
+func (a Samples) Len() int           { return len(a) }
+func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
+func (a Samples) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+
+type invariant func(s *stream, r float64) float64
+
+// NewLowBiased returns an initialized Stream for low-biased quantiles
+// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
+// error guarantees can still be given even for the lower ranks of the data
+// distribution.
+//
+// The provided epsilon is a relative error, i.e. the true quantile of a value
+// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
+//
+// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
+// properties.
+func NewLowBiased(epsilon float64) *Stream {
+	ƒ := func(s *stream, r float64) float64 {
+		return 2 * epsilon * r
+	}
+	return newStream(ƒ)
+}
+
+// NewHighBiased returns an initialized Stream for high-biased quantiles
+// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
+// error guarantees can still be given even for the higher ranks of the data
+// distribution.
+//
+// The provided epsilon is a relative error, i.e. the true quantile of a value
+// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
+//
+// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
+// properties.
+func NewHighBiased(epsilon float64) *Stream {
+	ƒ := func(s *stream, r float64) float64 {
+		return 2 * epsilon * (s.n - r)
+	}
+	return newStream(ƒ)
+}
+
+// NewTargeted returns an initialized Stream concerned with a particular set of
+// quantile values that are supplied a priori. Knowing these a priori reduces
+// space and computation time. The targets map maps the desired quantiles to
+// their absolute errors, i.e. the true quantile of a value returned by a query
+// is guaranteed to be within (Quantile±Epsilon).
+//
+// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
+func NewTargeted(targetMap map[float64]float64) *Stream {
+	// Convert map to slice to avoid slow iterations on a map.
+	// ƒ is called on the hot path, so converting the map to a slice
+	// beforehand results in significant CPU savings.
+	targets := targetMapToSlice(targetMap)
+
+	ƒ := func(s *stream, r float64) float64 {
+		var m = math.MaxFloat64
+		var f float64
+		for _, t := range targets {
+			if t.quantile*s.n <= r {
+				f = (2 * t.epsilon * r) / t.quantile
+			} else {
+				f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
+			}
+			if f < m {
+				m = f
+			}
+		}
+		return m
+	}
+	return newStream(ƒ)
+}
+
+type target struct {
+	quantile float64
+	epsilon  float64
+}
+
+func targetMapToSlice(targetMap map[float64]float64) []target {
+	targets := make([]target, 0, len(targetMap))
+
+	for quantile, epsilon := range targetMap {
+		t := target{
+			quantile: quantile,
+			epsilon:  epsilon,
+		}
+		targets = append(targets, t)
+	}
+
+	return targets
+}
+
+// Stream computes quantiles for a stream of float64s. It is not thread-safe by
+// design. Take care when using across multiple goroutines.
+type Stream struct {
+	*stream
+	b      Samples
+	sorted bool
+}
+
+func newStream(ƒ invariant) *Stream {
+	x := &stream{ƒ: ƒ}
+	return &Stream{x, make(Samples, 0, 500), true}
+}
+
+// Insert inserts v into the stream.
+func (s *Stream) Insert(v float64) {
+	s.insert(Sample{Value: v, Width: 1})
+}
+
+func (s *Stream) insert(sample Sample) {
+	s.b = append(s.b, sample)
+	s.sorted = false
+	if len(s.b) == cap(s.b) {
+		s.flush()
+	}
+}
+
+// Query returns the computed qth percentiles value. If s was created with
+// NewTargeted, and q is not in the set of quantiles provided a priori, Query
+// will return an unspecified result.
+func (s *Stream) Query(q float64) float64 {
+	if !s.flushed() {
+		// Fast path when there hasn't been enough data for a flush;
+		// this also yields better accuracy for small sets of data.
+		l := len(s.b)
+		if l == 0 {
+			return 0
+		}
+		i := int(math.Ceil(float64(l) * q))
+		if i > 0 {
+			i -= 1
+		}
+		s.maybeSort()
+		return s.b[i].Value
+	}
+	s.flush()
+	return s.stream.query(q)
+}
+
+// Merge merges samples into the underlying streams samples. This is handy when
+// merging multiple streams from separate threads, database shards, etc.
+//
+// ATTENTION: This method is broken and does not yield correct results. The
+// underlying algorithm is not capable of merging streams correctly.
+func (s *Stream) Merge(samples Samples) {
+	sort.Sort(samples)
+	s.stream.merge(samples)
+}
+
+// Reset reinitializes and clears the list reusing the samples buffer memory.
+func (s *Stream) Reset() {
+	s.stream.reset()
+	s.b = s.b[:0]
+}
+
+// Samples returns stream samples held by s.
+func (s *Stream) Samples() Samples {
+	if !s.flushed() {
+		return s.b
+	}
+	s.flush()
+	return s.stream.samples()
+}
+
+// Count returns the total number of samples observed in the stream
+// since initialization.
+func (s *Stream) Count() int {
+	return len(s.b) + s.stream.count()
+}
+
+func (s *Stream) flush() {
+	s.maybeSort()
+	s.stream.merge(s.b)
+	s.b = s.b[:0]
+}
+
+func (s *Stream) maybeSort() {
+	if !s.sorted {
+		s.sorted = true
+		sort.Sort(s.b)
+	}
+}
+
+func (s *Stream) flushed() bool {
+	return len(s.stream.l) > 0
+}
+
+type stream struct {
+	n float64
+	l []Sample
+	ƒ invariant
+}
+
+func (s *stream) reset() {
+	s.l = s.l[:0]
+	s.n = 0
+}
+
+func (s *stream) insert(v float64) {
+	s.merge(Samples{{v, 1, 0}})
+}
+
+func (s *stream) merge(samples Samples) {
+	// TODO(beorn7): This tries to merge not only individual samples, but
+	// whole summaries. The paper doesn't mention merging summaries at
+	// all. Unittests show that the merging is inaccurate. Find out how to
+	// do merges properly.
+	var r float64
+	i := 0
+	for _, sample := range samples {
+		for ; i < len(s.l); i++ {
+			c := s.l[i]
+			if c.Value > sample.Value {
+				// Insert at position i.
+				s.l = append(s.l, Sample{})
+				copy(s.l[i+1:], s.l[i:])
+				s.l[i] = Sample{
+					sample.Value,
+					sample.Width,
+					math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
+					// TODO(beorn7): How to calculate delta correctly?
+				}
+				i++
+				goto inserted
+			}
+			r += c.Width
+		}
+		s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
+		i++
+	inserted:
+		s.n += sample.Width
+		r += sample.Width
+	}
+	s.compress()
+}
+
+func (s *stream) count() int {
+	return int(s.n)
+}
+
+func (s *stream) query(q float64) float64 {
+	t := math.Ceil(q * s.n)
+	t += math.Ceil(s.ƒ(s, t) / 2)
+	p := s.l[0]
+	var r float64
+	for _, c := range s.l[1:] {
+		r += p.Width
+		if r+c.Width+c.Delta > t {
+			return p.Value
+		}
+		p = c
+	}
+	return p.Value
+}
+
+func (s *stream) compress() {
+	if len(s.l) < 2 {
+		return
+	}
+	x := s.l[len(s.l)-1]
+	xi := len(s.l) - 1
+	r := s.n - 1 - x.Width
+
+	for i := len(s.l) - 2; i >= 0; i-- {
+		c := s.l[i]
+		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
+			x.Width += c.Width
+			s.l[xi] = x
+			// Remove element at i.
+			copy(s.l[i:], s.l[i+1:])
+			s.l = s.l[:len(s.l)-1]
+			xi -= 1
+		} else {
+			x = c
+			xi = i
+		}
+		r -= c.Width
+	}
+}
+
+func (s *stream) samples() Samples {
+	samples := make(Samples, len(s.l))
+	copy(samples, s.l)
+	return samples
+}

+ 8 - 0
vendor/github.com/cespare/xxhash/v2/.travis.yml

@@ -0,0 +1,8 @@
+language: go
+go:
+  - "1.x"
+  - master
+env:
+  - TAGS=""
+  - TAGS="-tags purego"
+script: go test $TAGS -v ./...

+ 22 - 0
vendor/github.com/cespare/xxhash/v2/LICENSE.txt

@@ -0,0 +1,22 @@
+Copyright (c) 2016 Caleb Spare
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 67 - 0
vendor/github.com/cespare/xxhash/v2/README.md

@@ -0,0 +1,67 @@
+# xxhash
+
+[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash)
+[![Build Status](https://travis-ci.org/cespare/xxhash.svg?branch=master)](https://travis-ci.org/cespare/xxhash)
+
+xxhash is a Go implementation of the 64-bit
+[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
+high-quality hashing algorithm that is much faster than anything in the Go
+standard library.
+
+This package provides a straightforward API:
+
+```
+func Sum64(b []byte) uint64
+func Sum64String(s string) uint64
+type Digest struct{ ... }
+    func New() *Digest
+```
+
+The `Digest` type implements hash.Hash64. Its key methods are:
+
+```
+func (*Digest) Write([]byte) (int, error)
+func (*Digest) WriteString(string) (int, error)
+func (*Digest) Sum64() uint64
+```
+
+This implementation provides a fast pure-Go implementation and an even faster
+assembly implementation for amd64.
+
+## Compatibility
+
+This package is in a module and the latest code is in version 2 of the module.
+You need a version of Go with at least "minimal module compatibility" to use
+github.com/cespare/xxhash/v2:
+
+* 1.9.7+ for Go 1.9
+* 1.10.3+ for Go 1.10
+* Go 1.11 or later
+
+I recommend using the latest release of Go.
+
+## Benchmarks
+
+Here are some quick benchmarks comparing the pure-Go and assembly
+implementations of Sum64.
+
+| input size | purego | asm |
+| --- | --- | --- |
+| 5 B   |  979.66 MB/s |  1291.17 MB/s  |
+| 100 B | 7475.26 MB/s | 7973.40 MB/s  |
+| 4 KB  | 17573.46 MB/s | 17602.65 MB/s |
+| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
+
+These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
+the following commands under Go 1.11.2:
+
+```
+$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
+$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
+```
+
+## Projects using this package
+
+- [InfluxDB](https://github.com/influxdata/influxdb)
+- [Prometheus](https://github.com/prometheus/prometheus)
+- [FreeCache](https://github.com/coocood/freecache)

+ 3 - 0
vendor/github.com/cespare/xxhash/v2/go.mod

@@ -0,0 +1,3 @@
+module github.com/cespare/xxhash/v2
+
+go 1.11

+ 0 - 0
vendor/github.com/cespare/xxhash/v2/go.sum


+ 236 - 0
vendor/github.com/cespare/xxhash/v2/xxhash.go

@@ -0,0 +1,236 @@
+// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described
+// at http://cyan4973.github.io/xxHash/.
+package xxhash
+
+import (
+	"encoding/binary"
+	"errors"
+	"math/bits"
+)
+
+const (
+	prime1 uint64 = 11400714785074694791
+	prime2 uint64 = 14029467366897019727
+	prime3 uint64 = 1609587929392839161
+	prime4 uint64 = 9650029242287828579
+	prime5 uint64 = 2870177450012600261
+)
+
+// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
+// possible in the Go code is worth a small (but measurable) performance boost
+// by avoiding some MOVQs. Vars are needed for the asm and also are useful for
+// convenience in the Go code in a few places where we need to intentionally
+// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
+// result overflows a uint64).
+var (
+	prime1v = prime1
+	prime2v = prime2
+	prime3v = prime3
+	prime4v = prime4
+	prime5v = prime5
+)
+
+// Digest implements hash.Hash64.
+type Digest struct {
+	v1    uint64
+	v2    uint64
+	v3    uint64
+	v4    uint64
+	total uint64
+	mem   [32]byte
+	n     int // how much of mem is used
+}
+
+// New creates a new Digest that computes the 64-bit xxHash algorithm.
+func New() *Digest {
+	var d Digest
+	d.Reset()
+	return &d
+}
+
+// Reset clears the Digest's state so that it can be reused.
+func (d *Digest) Reset() {
+	d.v1 = prime1v + prime2
+	d.v2 = prime2
+	d.v3 = 0
+	d.v4 = -prime1v
+	d.total = 0
+	d.n = 0
+}
+
+// Size always returns 8 bytes.
+func (d *Digest) Size() int { return 8 }
+
+// BlockSize always returns 32 bytes.
+func (d *Digest) BlockSize() int { return 32 }
+
+// Write adds more data to d. It always returns len(b), nil.
+func (d *Digest) Write(b []byte) (n int, err error) {
+	n = len(b)
+	d.total += uint64(n)
+
+	if d.n+n < 32 {
+		// This new data doesn't even fill the current block.
+		copy(d.mem[d.n:], b)
+		d.n += n
+		return
+	}
+
+	if d.n > 0 {
+		// Finish off the partial block.
+		copy(d.mem[d.n:], b)
+		d.v1 = round(d.v1, u64(d.mem[0:8]))
+		d.v2 = round(d.v2, u64(d.mem[8:16]))
+		d.v3 = round(d.v3, u64(d.mem[16:24]))
+		d.v4 = round(d.v4, u64(d.mem[24:32]))
+		b = b[32-d.n:]
+		d.n = 0
+	}
+
+	if len(b) >= 32 {
+		// One or more full blocks left.
+		nw := writeBlocks(d, b)
+		b = b[nw:]
+	}
+
+	// Store any remaining partial block.
+	copy(d.mem[:], b)
+	d.n = len(b)
+
+	return
+}
+
+// Sum appends the current hash to b and returns the resulting slice.
+func (d *Digest) Sum(b []byte) []byte {
+	s := d.Sum64()
+	return append(
+		b,
+		byte(s>>56),
+		byte(s>>48),
+		byte(s>>40),
+		byte(s>>32),
+		byte(s>>24),
+		byte(s>>16),
+		byte(s>>8),
+		byte(s),
+	)
+}
+
+// Sum64 returns the current hash.
+func (d *Digest) Sum64() uint64 {
+	var h uint64
+
+	if d.total >= 32 {
+		v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
+		h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
+		h = mergeRound(h, v1)
+		h = mergeRound(h, v2)
+		h = mergeRound(h, v3)
+		h = mergeRound(h, v4)
+	} else {
+		h = d.v3 + prime5
+	}
+
+	h += d.total
+
+	i, end := 0, d.n
+	for ; i+8 <= end; i += 8 {
+		k1 := round(0, u64(d.mem[i:i+8]))
+		h ^= k1
+		h = rol27(h)*prime1 + prime4
+	}
+	if i+4 <= end {
+		h ^= uint64(u32(d.mem[i:i+4])) * prime1
+		h = rol23(h)*prime2 + prime3
+		i += 4
+	}
+	for i < end {
+		h ^= uint64(d.mem[i]) * prime5
+		h = rol11(h) * prime1
+		i++
+	}
+
+	h ^= h >> 33
+	h *= prime2
+	h ^= h >> 29
+	h *= prime3
+	h ^= h >> 32
+
+	return h
+}
+
+const (
+	magic         = "xxh\x06"
+	marshaledSize = len(magic) + 8*5 + 32
+)
+
+// MarshalBinary implements the encoding.BinaryMarshaler interface.
+func (d *Digest) MarshalBinary() ([]byte, error) {
+	b := make([]byte, 0, marshaledSize)
+	b = append(b, magic...)
+	b = appendUint64(b, d.v1)
+	b = appendUint64(b, d.v2)
+	b = appendUint64(b, d.v3)
+	b = appendUint64(b, d.v4)
+	b = appendUint64(b, d.total)
+	b = append(b, d.mem[:d.n]...)
+	b = b[:len(b)+len(d.mem)-d.n]
+	return b, nil
+}
+
+// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
+func (d *Digest) UnmarshalBinary(b []byte) error {
+	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
+		return errors.New("xxhash: invalid hash state identifier")
+	}
+	if len(b) != marshaledSize {
+		return errors.New("xxhash: invalid hash state size")
+	}
+	b = b[len(magic):]
+	b, d.v1 = consumeUint64(b)
+	b, d.v2 = consumeUint64(b)
+	b, d.v3 = consumeUint64(b)
+	b, d.v4 = consumeUint64(b)
+	b, d.total = consumeUint64(b)
+	copy(d.mem[:], b)
+	b = b[len(d.mem):]
+	d.n = int(d.total % uint64(len(d.mem)))
+	return nil
+}
+
+func appendUint64(b []byte, x uint64) []byte {
+	var a [8]byte
+	binary.LittleEndian.PutUint64(a[:], x)
+	return append(b, a[:]...)
+}
+
+func consumeUint64(b []byte) ([]byte, uint64) {
+	x := u64(b)
+	return b[8:], x
+}
+
+func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }
+func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
+
+func round(acc, input uint64) uint64 {
+	acc += input * prime2
+	acc = rol31(acc)
+	acc *= prime1
+	return acc
+}
+
+func mergeRound(acc, val uint64) uint64 {
+	val = round(0, val)
+	acc ^= val
+	acc = acc*prime1 + prime4
+	return acc
+}
+
+func rol1(x uint64) uint64  { return bits.RotateLeft64(x, 1) }
+func rol7(x uint64) uint64  { return bits.RotateLeft64(x, 7) }
+func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) }
+func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) }
+func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) }
+func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) }
+func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) }
+func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) }

+ 13 - 0
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go

@@ -0,0 +1,13 @@
+// +build !appengine
+// +build gc
+// +build !purego
+
+package xxhash
+
+// Sum64 computes the 64-bit xxHash digest of b.
+//
+//go:noescape
+func Sum64(b []byte) uint64
+
+//go:noescape
+func writeBlocks(d *Digest, b []byte) int

+ 215 - 0
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s

@@ -0,0 +1,215 @@
+// +build !appengine
+// +build gc
+// +build !purego
+
+#include "textflag.h"
+
+// Register allocation:
+// AX	h
+// CX	pointer to advance through b
+// DX	n
+// BX	loop end
+// R8	v1, k1
+// R9	v2
+// R10	v3
+// R11	v4
+// R12	tmp
+// R13	prime1v
+// R14	prime2v
+// R15	prime4v
+
+// round reads from and advances the buffer pointer in CX.
+// It assumes that R13 has prime1v and R14 has prime2v.
+#define round(r) \
+	MOVQ  (CX), R12 \
+	ADDQ  $8, CX    \
+	IMULQ R14, R12  \
+	ADDQ  R12, r    \
+	ROLQ  $31, r    \
+	IMULQ R13, r
+
+// mergeRound applies a merge round on the two registers acc and val.
+// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v.
+#define mergeRound(acc, val) \
+	IMULQ R14, val \
+	ROLQ  $31, val \
+	IMULQ R13, val \
+	XORQ  val, acc \
+	IMULQ R13, acc \
+	ADDQ  R15, acc
+
+// func Sum64(b []byte) uint64
+TEXT ·Sum64(SB), NOSPLIT, $0-32
+	// Load fixed primes.
+	MOVQ ·prime1v(SB), R13
+	MOVQ ·prime2v(SB), R14
+	MOVQ ·prime4v(SB), R15
+
+	// Load slice.
+	MOVQ b_base+0(FP), CX
+	MOVQ b_len+8(FP), DX
+	LEAQ (CX)(DX*1), BX
+
+	// The first loop limit will be len(b)-32.
+	SUBQ $32, BX
+
+	// Check whether we have at least one block.
+	CMPQ DX, $32
+	JLT  noBlocks
+
+	// Set up initial state (v1, v2, v3, v4).
+	MOVQ R13, R8
+	ADDQ R14, R8
+	MOVQ R14, R9
+	XORQ R10, R10
+	XORQ R11, R11
+	SUBQ R13, R11
+
+	// Loop until CX > BX.
+blockLoop:
+	round(R8)
+	round(R9)
+	round(R10)
+	round(R11)
+
+	CMPQ CX, BX
+	JLE  blockLoop
+
+	MOVQ R8, AX
+	ROLQ $1, AX
+	MOVQ R9, R12
+	ROLQ $7, R12
+	ADDQ R12, AX
+	MOVQ R10, R12
+	ROLQ $12, R12
+	ADDQ R12, AX
+	MOVQ R11, R12
+	ROLQ $18, R12
+	ADDQ R12, AX
+
+	mergeRound(AX, R8)
+	mergeRound(AX, R9)
+	mergeRound(AX, R10)
+	mergeRound(AX, R11)
+
+	JMP afterBlocks
+
+noBlocks:
+	MOVQ ·prime5v(SB), AX
+
+afterBlocks:
+	ADDQ DX, AX
+
+	// Right now BX has len(b)-32, and we want to loop until CX > len(b)-8.
+	ADDQ $24, BX
+
+	CMPQ CX, BX
+	JG   fourByte
+
+wordLoop:
+	// Calculate k1.
+	MOVQ  (CX), R8
+	ADDQ  $8, CX
+	IMULQ R14, R8
+	ROLQ  $31, R8
+	IMULQ R13, R8
+
+	XORQ  R8, AX
+	ROLQ  $27, AX
+	IMULQ R13, AX
+	ADDQ  R15, AX
+
+	CMPQ CX, BX
+	JLE  wordLoop
+
+fourByte:
+	ADDQ $4, BX
+	CMPQ CX, BX
+	JG   singles
+
+	MOVL  (CX), R8
+	ADDQ  $4, CX
+	IMULQ R13, R8
+	XORQ  R8, AX
+
+	ROLQ  $23, AX
+	IMULQ R14, AX
+	ADDQ  ·prime3v(SB), AX
+
+singles:
+	ADDQ $4, BX
+	CMPQ CX, BX
+	JGE  finalize
+
+singlesLoop:
+	MOVBQZX (CX), R12
+	ADDQ    $1, CX
+	IMULQ   ·prime5v(SB), R12
+	XORQ    R12, AX
+
+	ROLQ  $11, AX
+	IMULQ R13, AX
+
+	CMPQ CX, BX
+	JL   singlesLoop
+
+finalize:
+	MOVQ  AX, R12
+	SHRQ  $33, R12
+	XORQ  R12, AX
+	IMULQ R14, AX
+	MOVQ  AX, R12
+	SHRQ  $29, R12
+	XORQ  R12, AX
+	IMULQ ·prime3v(SB), AX
+	MOVQ  AX, R12
+	SHRQ  $32, R12
+	XORQ  R12, AX
+
+	MOVQ AX, ret+24(FP)
+	RET
+
+// writeBlocks uses the same registers as above except that it uses AX to store
+// the d pointer.
+
+// func writeBlocks(d *Digest, b []byte) int
+TEXT ·writeBlocks(SB), NOSPLIT, $0-40
+	// Load fixed primes needed for round.
+	MOVQ ·prime1v(SB), R13
+	MOVQ ·prime2v(SB), R14
+
+	// Load slice.
+	MOVQ b_base+8(FP), CX
+	MOVQ b_len+16(FP), DX
+	LEAQ (CX)(DX*1), BX
+	SUBQ $32, BX
+
+	// Load vN from d.
+	MOVQ d+0(FP), AX
+	MOVQ 0(AX), R8   // v1
+	MOVQ 8(AX), R9   // v2
+	MOVQ 16(AX), R10 // v3
+	MOVQ 24(AX), R11 // v4
+
+	// We don't need to check the loop condition here; this function is
+	// always called with at least one block of data to process.
+blockLoop:
+	round(R8)
+	round(R9)
+	round(R10)
+	round(R11)
+
+	CMPQ CX, BX
+	JLE  blockLoop
+
+	// Copy vN back to d.
+	MOVQ R8, 0(AX)
+	MOVQ R9, 8(AX)
+	MOVQ R10, 16(AX)
+	MOVQ R11, 24(AX)
+
+	// The number of bytes written is CX minus the old base pointer.
+	SUBQ b_base+8(FP), CX
+	MOVQ CX, ret+32(FP)
+
+	RET

+ 76 - 0
vendor/github.com/cespare/xxhash/v2/xxhash_other.go

@@ -0,0 +1,76 @@
+// +build !amd64 appengine !gc purego
+
+package xxhash
+
+// Sum64 computes the 64-bit xxHash digest of b.
+func Sum64(b []byte) uint64 {
+	// A simpler version would be
+	//   d := New()
+	//   d.Write(b)
+	//   return d.Sum64()
+	// but this is faster, particularly for small inputs.
+
+	n := len(b)
+	var h uint64
+
+	if n >= 32 {
+		v1 := prime1v + prime2
+		v2 := prime2
+		v3 := uint64(0)
+		v4 := -prime1v
+		for len(b) >= 32 {
+			v1 = round(v1, u64(b[0:8:len(b)]))
+			v2 = round(v2, u64(b[8:16:len(b)]))
+			v3 = round(v3, u64(b[16:24:len(b)]))
+			v4 = round(v4, u64(b[24:32:len(b)]))
+			b = b[32:len(b):len(b)]
+		}
+		h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
+		h = mergeRound(h, v1)
+		h = mergeRound(h, v2)
+		h = mergeRound(h, v3)
+		h = mergeRound(h, v4)
+	} else {
+		h = prime5
+	}
+
+	h += uint64(n)
+
+	i, end := 0, len(b)
+	for ; i+8 <= end; i += 8 {
+		k1 := round(0, u64(b[i:i+8:len(b)]))
+		h ^= k1
+		h = rol27(h)*prime1 + prime4
+	}
+	if i+4 <= end {
+		h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
+		h = rol23(h)*prime2 + prime3
+		i += 4
+	}
+	for ; i < end; i++ {
+		h ^= uint64(b[i]) * prime5
+		h = rol11(h) * prime1
+	}
+
+	h ^= h >> 33
+	h *= prime2
+	h ^= h >> 29
+	h *= prime3
+	h ^= h >> 32
+
+	return h
+}
+
+func writeBlocks(d *Digest, b []byte) int {
+	v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
+	n := len(b)
+	for len(b) >= 32 {
+		v1 = round(v1, u64(b[0:8:len(b)]))
+		v2 = round(v2, u64(b[8:16:len(b)]))
+		v3 = round(v3, u64(b[16:24:len(b)]))
+		v4 = round(v4, u64(b[24:32:len(b)]))
+		b = b[32:len(b):len(b)]
+	}
+	d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4
+	return n - len(b)
+}

+ 15 - 0
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go

@@ -0,0 +1,15 @@
+// +build appengine
+
+// This file contains the safe implementations of otherwise unsafe-using code.
+
+package xxhash
+
+// Sum64String computes the 64-bit xxHash digest of s.
+func Sum64String(s string) uint64 {
+	return Sum64([]byte(s))
+}
+
+// WriteString adds more data to d. It always returns len(s), nil.
+func (d *Digest) WriteString(s string) (n int, err error) {
+	return d.Write([]byte(s))
+}

+ 46 - 0
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go

@@ -0,0 +1,46 @@
+// +build !appengine
+
+// This file encapsulates usage of unsafe.
+// xxhash_safe.go contains the safe implementations.
+
+package xxhash
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+// Notes:
+//
+// See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ
+// for some discussion about these unsafe conversions.
+//
+// In the future it's possible that compiler optimizations will make these
+// unsafe operations unnecessary: https://golang.org/issue/2205.
+//
+// Both of these wrapper functions still incur function call overhead since they
+// will not be inlined. We could write Go/asm copies of Sum64 and Digest.Write
+// for strings to squeeze out a bit more speed. Mid-stack inlining should
+// eventually fix this.
+
+// Sum64String computes the 64-bit xxHash digest of s.
+// It may be faster than Sum64([]byte(s)) by avoiding a copy.
+func Sum64String(s string) uint64 {
+	var b []byte
+	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+	bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
+	bh.Len = len(s)
+	bh.Cap = len(s)
+	return Sum64(b)
+}
+
+// WriteString adds more data to d. It always returns len(s), nil.
+// It may be faster than Write([]byte(s)) by avoiding a copy.
+func (d *Digest) WriteString(s string) (n int, err error) {
+	var b []byte
+	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+	bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
+	bh.Len = len(s)
+	bh.Cap = len(s)
+	return d.Write(b)
+}

+ 3 - 0
vendor/github.com/golang/protobuf/AUTHORS

@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.

+ 3 - 0
vendor/github.com/golang/protobuf/CONTRIBUTORS

@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.

+ 28 - 0
vendor/github.com/golang/protobuf/LICENSE

@@ -0,0 +1,28 @@
+Copyright 2010 The Go Authors.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

+ 324 - 0
vendor/github.com/golang/protobuf/proto/buffer.go

@@ -0,0 +1,324 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"errors"
+	"fmt"
+
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/runtime/protoimpl"
+)
+
+const (
+	WireVarint     = 0
+	WireFixed32    = 5
+	WireFixed64    = 1
+	WireBytes      = 2
+	WireStartGroup = 3
+	WireEndGroup   = 4
+)
+
+// EncodeVarint returns the varint encoded bytes of v.
+func EncodeVarint(v uint64) []byte {
+	return protowire.AppendVarint(nil, v)
+}
+
+// SizeVarint returns the length of the varint encoded bytes of v.
+// This is equal to len(EncodeVarint(v)).
+func SizeVarint(v uint64) int {
+	return protowire.SizeVarint(v)
+}
+
+// DecodeVarint parses a varint encoded integer from b,
+// returning the integer value and the length of the varint.
+// It returns (0, 0) if there is a parse error.
+func DecodeVarint(b []byte) (uint64, int) {
+	v, n := protowire.ConsumeVarint(b)
+	if n < 0 {
+		return 0, 0
+	}
+	return v, n
+}
+
+// Buffer is a buffer for encoding and decoding the protobuf wire format.
+// It may be reused between invocations to reduce memory usage.
+type Buffer struct {
+	buf           []byte
+	idx           int
+	deterministic bool
+}
+
+// NewBuffer allocates a new Buffer initialized with buf,
+// where the contents of buf are considered the unread portion of the buffer.
+func NewBuffer(buf []byte) *Buffer {
+	return &Buffer{buf: buf}
+}
+
+// SetDeterministic specifies whether to use deterministic serialization.
+//
+// Deterministic serialization guarantees that for a given binary, equal
+// messages will always be serialized to the same bytes. This implies:
+//
+//   - Repeated serialization of a message will return the same bytes.
+//   - Different processes of the same binary (which may be executing on
+//     different machines) will serialize equal messages to the same bytes.
+//
+// Note that the deterministic serialization is NOT canonical across
+// languages. It is not guaranteed to remain stable over time. It is unstable
+// across different builds with schema changes due to unknown fields.
+// Users who need canonical serialization (e.g., persistent storage in a
+// canonical form, fingerprinting, etc.) should define their own
+// canonicalization specification and implement their own serializer rather
+// than relying on this API.
+//
+// If deterministic serialization is requested, map entries will be sorted
+// by keys in lexographical order. This is an implementation detail and
+// subject to change.
+func (b *Buffer) SetDeterministic(deterministic bool) {
+	b.deterministic = deterministic
+}
+
+// SetBuf sets buf as the internal buffer,
+// where the contents of buf are considered the unread portion of the buffer.
+func (b *Buffer) SetBuf(buf []byte) {
+	b.buf = buf
+	b.idx = 0
+}
+
+// Reset clears the internal buffer of all written and unread data.
+func (b *Buffer) Reset() {
+	b.buf = b.buf[:0]
+	b.idx = 0
+}
+
+// Bytes returns the internal buffer.
+func (b *Buffer) Bytes() []byte {
+	return b.buf
+}
+
+// Unread returns the unread portion of the buffer.
+func (b *Buffer) Unread() []byte {
+	return b.buf[b.idx:]
+}
+
+// Marshal appends the wire-format encoding of m to the buffer.
+func (b *Buffer) Marshal(m Message) error {
+	var err error
+	b.buf, err = marshalAppend(b.buf, m, b.deterministic)
+	return err
+}
+
+// Unmarshal parses the wire-format message in the buffer and
+// places the decoded results in m.
+// It does not reset m before unmarshaling.
+func (b *Buffer) Unmarshal(m Message) error {
+	err := UnmarshalMerge(b.Unread(), m)
+	b.idx = len(b.buf)
+	return err
+}
+
+type unknownFields struct{ XXX_unrecognized protoimpl.UnknownFields }
+
+func (m *unknownFields) String() string { panic("not implemented") }
+func (m *unknownFields) Reset()         { panic("not implemented") }
+func (m *unknownFields) ProtoMessage()  { panic("not implemented") }
+
+// DebugPrint dumps the encoded bytes of b with a header and footer including s
+// to stdout. This is only intended for debugging.
+func (*Buffer) DebugPrint(s string, b []byte) {
+	m := MessageReflect(new(unknownFields))
+	m.SetUnknown(b)
+	b, _ = prototext.MarshalOptions{AllowPartial: true, Indent: "\t"}.Marshal(m.Interface())
+	fmt.Printf("==== %s ====\n%s==== %s ====\n", s, b, s)
+}
+
+// EncodeVarint appends an unsigned varint encoding to the buffer.
+func (b *Buffer) EncodeVarint(v uint64) error {
+	b.buf = protowire.AppendVarint(b.buf, v)
+	return nil
+}
+
+// EncodeZigzag32 appends a 32-bit zig-zag varint encoding to the buffer.
+func (b *Buffer) EncodeZigzag32(v uint64) error {
+	return b.EncodeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31))))
+}
+
+// EncodeZigzag64 appends a 64-bit zig-zag varint encoding to the buffer.
+func (b *Buffer) EncodeZigzag64(v uint64) error {
+	return b.EncodeVarint(uint64((uint64(v) << 1) ^ uint64((int64(v) >> 63))))
+}
+
+// EncodeFixed32 appends a 32-bit little-endian integer to the buffer.
+func (b *Buffer) EncodeFixed32(v uint64) error {
+	b.buf = protowire.AppendFixed32(b.buf, uint32(v))
+	return nil
+}
+
+// EncodeFixed64 appends a 64-bit little-endian integer to the buffer.
+func (b *Buffer) EncodeFixed64(v uint64) error {
+	b.buf = protowire.AppendFixed64(b.buf, uint64(v))
+	return nil
+}
+
+// EncodeRawBytes appends a length-prefixed raw bytes to the buffer.
+func (b *Buffer) EncodeRawBytes(v []byte) error {
+	b.buf = protowire.AppendBytes(b.buf, v)
+	return nil
+}
+
+// EncodeStringBytes appends a length-prefixed raw bytes to the buffer.
+// It does not validate whether v contains valid UTF-8.
+func (b *Buffer) EncodeStringBytes(v string) error {
+	b.buf = protowire.AppendString(b.buf, v)
+	return nil
+}
+
+// EncodeMessage appends a length-prefixed encoded message to the buffer.
+func (b *Buffer) EncodeMessage(m Message) error {
+	var err error
+	b.buf = protowire.AppendVarint(b.buf, uint64(Size(m)))
+	b.buf, err = marshalAppend(b.buf, m, b.deterministic)
+	return err
+}
+
+// DecodeVarint consumes an encoded unsigned varint from the buffer.
+func (b *Buffer) DecodeVarint() (uint64, error) {
+	v, n := protowire.ConsumeVarint(b.buf[b.idx:])
+	if n < 0 {
+		return 0, protowire.ParseError(n)
+	}
+	b.idx += n
+	return uint64(v), nil
+}
+
+// DecodeZigzag32 consumes an encoded 32-bit zig-zag varint from the buffer.
+func (b *Buffer) DecodeZigzag32() (uint64, error) {
+	v, err := b.DecodeVarint()
+	if err != nil {
+		return 0, err
+	}
+	return uint64((uint32(v) >> 1) ^ uint32((int32(v&1)<<31)>>31)), nil
+}
+
+// DecodeZigzag64 consumes an encoded 64-bit zig-zag varint from the buffer.
+func (b *Buffer) DecodeZigzag64() (uint64, error) {
+	v, err := b.DecodeVarint()
+	if err != nil {
+		return 0, err
+	}
+	return uint64((uint64(v) >> 1) ^ uint64((int64(v&1)<<63)>>63)), nil
+}
+
+// DecodeFixed32 consumes a 32-bit little-endian integer from the buffer.
+func (b *Buffer) DecodeFixed32() (uint64, error) {
+	v, n := protowire.ConsumeFixed32(b.buf[b.idx:])
+	if n < 0 {
+		return 0, protowire.ParseError(n)
+	}
+	b.idx += n
+	return uint64(v), nil
+}
+
+// DecodeFixed64 consumes a 64-bit little-endian integer from the buffer.
+func (b *Buffer) DecodeFixed64() (uint64, error) {
+	v, n := protowire.ConsumeFixed64(b.buf[b.idx:])
+	if n < 0 {
+		return 0, protowire.ParseError(n)
+	}
+	b.idx += n
+	return uint64(v), nil
+}
+
+// DecodeRawBytes consumes a length-prefixed raw bytes from the buffer.
+// If alloc is specified, it returns a copy the raw bytes
+// rather than a sub-slice of the buffer.
+func (b *Buffer) DecodeRawBytes(alloc bool) ([]byte, error) {
+	v, n := protowire.ConsumeBytes(b.buf[b.idx:])
+	if n < 0 {
+		return nil, protowire.ParseError(n)
+	}
+	b.idx += n
+	if alloc {
+		v = append([]byte(nil), v...)
+	}
+	return v, nil
+}
+
+// DecodeStringBytes consumes a length-prefixed raw bytes from the buffer.
+// It does not validate whether the raw bytes contain valid UTF-8.
+func (b *Buffer) DecodeStringBytes() (string, error) {
+	v, n := protowire.ConsumeString(b.buf[b.idx:])
+	if n < 0 {
+		return "", protowire.ParseError(n)
+	}
+	b.idx += n
+	return v, nil
+}
+
+// DecodeMessage consumes a length-prefixed message from the buffer.
+// It does not reset m before unmarshaling.
+func (b *Buffer) DecodeMessage(m Message) error {
+	v, err := b.DecodeRawBytes(false)
+	if err != nil {
+		return err
+	}
+	return UnmarshalMerge(v, m)
+}
+
+// DecodeGroup consumes a message group from the buffer.
+// It assumes that the start group marker has already been consumed and
+// consumes all bytes until (and including the end group marker).
+// It does not reset m before unmarshaling.
+func (b *Buffer) DecodeGroup(m Message) error {
+	v, n, err := consumeGroup(b.buf[b.idx:])
+	if err != nil {
+		return err
+	}
+	b.idx += n
+	return UnmarshalMerge(v, m)
+}
+
+// consumeGroup parses b until it finds an end group marker, returning
+// the raw bytes of the message (excluding the end group marker) and the
+// the total length of the message (including the end group marker).
+func consumeGroup(b []byte) ([]byte, int, error) {
+	b0 := b
+	depth := 1 // assume this follows a start group marker
+	for {
+		_, wtyp, tagLen := protowire.ConsumeTag(b)
+		if tagLen < 0 {
+			return nil, 0, protowire.ParseError(tagLen)
+		}
+		b = b[tagLen:]
+
+		var valLen int
+		switch wtyp {
+		case protowire.VarintType:
+			_, valLen = protowire.ConsumeVarint(b)
+		case protowire.Fixed32Type:
+			_, valLen = protowire.ConsumeFixed32(b)
+		case protowire.Fixed64Type:
+			_, valLen = protowire.ConsumeFixed64(b)
+		case protowire.BytesType:
+			_, valLen = protowire.ConsumeBytes(b)
+		case protowire.StartGroupType:
+			depth++
+		case protowire.EndGroupType:
+			depth--
+		default:
+			return nil, 0, errors.New("proto: cannot parse reserved wire type")
+		}
+		if valLen < 0 {
+			return nil, 0, protowire.ParseError(valLen)
+		}
+		b = b[valLen:]
+
+		if depth == 0 {
+			return b0[:len(b0)-len(b)-tagLen], len(b0) - len(b), nil
+		}
+	}
+}

+ 63 - 0
vendor/github.com/golang/protobuf/proto/defaults.go

@@ -0,0 +1,63 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// SetDefaults sets unpopulated scalar fields to their default values.
+// Fields within a oneof are not set even if they have a default value.
+// SetDefaults is recursively called upon any populated message fields.
+func SetDefaults(m Message) {
+	if m != nil {
+		setDefaults(MessageReflect(m))
+	}
+}
+
+func setDefaults(m protoreflect.Message) {
+	fds := m.Descriptor().Fields()
+	for i := 0; i < fds.Len(); i++ {
+		fd := fds.Get(i)
+		if !m.Has(fd) {
+			if fd.HasDefault() && fd.ContainingOneof() == nil {
+				v := fd.Default()
+				if fd.Kind() == protoreflect.BytesKind {
+					v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...)) // copy the default bytes
+				}
+				m.Set(fd, v)
+			}
+			continue
+		}
+	}
+
+	m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
+		switch {
+		// Handle singular message.
+		case fd.Cardinality() != protoreflect.Repeated:
+			if fd.Message() != nil {
+				setDefaults(m.Get(fd).Message())
+			}
+		// Handle list of messages.
+		case fd.IsList():
+			if fd.Message() != nil {
+				ls := m.Get(fd).List()
+				for i := 0; i < ls.Len(); i++ {
+					setDefaults(ls.Get(i).Message())
+				}
+			}
+		// Handle map of messages.
+		case fd.IsMap():
+			if fd.MapValue().Message() != nil {
+				ms := m.Get(fd).Map()
+				ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
+					setDefaults(v.Message())
+					return true
+				})
+			}
+		}
+		return true
+	})
+}

+ 113 - 0
vendor/github.com/golang/protobuf/proto/deprecated.go

@@ -0,0 +1,113 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strconv"
+
+	protoV2 "google.golang.org/protobuf/proto"
+)
+
+var (
+	// Deprecated: No longer returned.
+	ErrNil = errors.New("proto: Marshal called with nil")
+
+	// Deprecated: No longer returned.
+	ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
+
+	// Deprecated: No longer returned.
+	ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
+)
+
+// Deprecated: Do not use.
+type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 }
+
+// Deprecated: Do not use.
+func GetStats() Stats { return Stats{} }
+
+// Deprecated: Do not use.
+func MarshalMessageSet(interface{}) ([]byte, error) {
+	return nil, errors.New("proto: not implemented")
+}
+
+// Deprecated: Do not use.
+func UnmarshalMessageSet([]byte, interface{}) error {
+	return errors.New("proto: not implemented")
+}
+
+// Deprecated: Do not use.
+func MarshalMessageSetJSON(interface{}) ([]byte, error) {
+	return nil, errors.New("proto: not implemented")
+}
+
+// Deprecated: Do not use.
+func UnmarshalMessageSetJSON([]byte, interface{}) error {
+	return errors.New("proto: not implemented")
+}
+
+// Deprecated: Do not use.
+func RegisterMessageSetType(Message, int32, string) {}
+
+// Deprecated: Do not use.
+func EnumName(m map[int32]string, v int32) string {
+	s, ok := m[v]
+	if ok {
+		return s
+	}
+	return strconv.Itoa(int(v))
+}
+
+// Deprecated: Do not use.
+func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
+	if data[0] == '"' {
+		// New style: enums are strings.
+		var repr string
+		if err := json.Unmarshal(data, &repr); err != nil {
+			return -1, err
+		}
+		val, ok := m[repr]
+		if !ok {
+			return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
+		}
+		return val, nil
+	}
+	// Old style: enums are ints.
+	var val int32
+	if err := json.Unmarshal(data, &val); err != nil {
+		return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
+	}
+	return val, nil
+}
+
+// Deprecated: Do not use; this type existed for intenal-use only.
+type InternalMessageInfo struct{}
+
+// Deprecated: Do not use; this method existed for intenal-use only.
+func (*InternalMessageInfo) DiscardUnknown(m Message) {
+	DiscardUnknown(m)
+}
+
+// Deprecated: Do not use; this method existed for intenal-use only.
+func (*InternalMessageInfo) Marshal(b []byte, m Message, deterministic bool) ([]byte, error) {
+	return protoV2.MarshalOptions{Deterministic: deterministic}.MarshalAppend(b, MessageV2(m))
+}
+
+// Deprecated: Do not use; this method existed for intenal-use only.
+func (*InternalMessageInfo) Merge(dst, src Message) {
+	protoV2.Merge(MessageV2(dst), MessageV2(src))
+}
+
+// Deprecated: Do not use; this method existed for intenal-use only.
+func (*InternalMessageInfo) Size(m Message) int {
+	return protoV2.Size(MessageV2(m))
+}
+
+// Deprecated: Do not use; this method existed for intenal-use only.
+func (*InternalMessageInfo) Unmarshal(m Message, b []byte) error {
+	return protoV2.UnmarshalOptions{Merge: true}.Unmarshal(b, MessageV2(m))
+}

+ 58 - 0
vendor/github.com/golang/protobuf/proto/discard.go

@@ -0,0 +1,58 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// DiscardUnknown recursively discards all unknown fields from this message
+// and all embedded messages.
+//
+// When unmarshaling a message with unrecognized fields, the tags and values
+// of such fields are preserved in the Message. This allows a later call to
+// marshal to be able to produce a message that continues to have those
+// unrecognized fields. To avoid this, DiscardUnknown is used to
+// explicitly clear the unknown fields after unmarshaling.
+func DiscardUnknown(m Message) {
+	if m != nil {
+		discardUnknown(MessageReflect(m))
+	}
+}
+
+func discardUnknown(m protoreflect.Message) {
+	m.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool {
+		switch {
+		// Handle singular message.
+		case fd.Cardinality() != protoreflect.Repeated:
+			if fd.Message() != nil {
+				discardUnknown(m.Get(fd).Message())
+			}
+		// Handle list of messages.
+		case fd.IsList():
+			if fd.Message() != nil {
+				ls := m.Get(fd).List()
+				for i := 0; i < ls.Len(); i++ {
+					discardUnknown(ls.Get(i).Message())
+				}
+			}
+		// Handle map of messages.
+		case fd.IsMap():
+			if fd.MapValue().Message() != nil {
+				ms := m.Get(fd).Map()
+				ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
+					discardUnknown(v.Message())
+					return true
+				})
+			}
+		}
+		return true
+	})
+
+	// Discard unknown fields.
+	if len(m.GetUnknown()) > 0 {
+		m.SetUnknown(nil)
+	}
+}

+ 356 - 0
vendor/github.com/golang/protobuf/proto/extensions.go

@@ -0,0 +1,356 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
+	"google.golang.org/protobuf/runtime/protoiface"
+	"google.golang.org/protobuf/runtime/protoimpl"
+)
+
+type (
+	// ExtensionDesc represents an extension descriptor and
+	// is used to interact with an extension field in a message.
+	//
+	// Variables of this type are generated in code by protoc-gen-go.
+	ExtensionDesc = protoimpl.ExtensionInfo
+
+	// ExtensionRange represents a range of message extensions.
+	// Used in code generated by protoc-gen-go.
+	ExtensionRange = protoiface.ExtensionRangeV1
+
+	// Deprecated: Do not use; this is an internal type.
+	Extension = protoimpl.ExtensionFieldV1
+
+	// Deprecated: Do not use; this is an internal type.
+	XXX_InternalExtensions = protoimpl.ExtensionFields
+)
+
+// ErrMissingExtension reports whether the extension was not present.
+var ErrMissingExtension = errors.New("proto: missing extension")
+
+var errNotExtendable = errors.New("proto: not an extendable proto.Message")
+
+// HasExtension reports whether the extension field is present in m
+// either as an explicitly populated field or as an unknown field.
+func HasExtension(m Message, xt *ExtensionDesc) (has bool) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() {
+		return false
+	}
+
+	// Check whether any populated known field matches the field number.
+	xtd := xt.TypeDescriptor()
+	if isValidExtension(mr.Descriptor(), xtd) {
+		has = mr.Has(xtd)
+	} else {
+		mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
+			has = int32(fd.Number()) == xt.Field
+			return !has
+		})
+	}
+
+	// Check whether any unknown field matches the field number.
+	for b := mr.GetUnknown(); !has && len(b) > 0; {
+		num, _, n := protowire.ConsumeField(b)
+		has = int32(num) == xt.Field
+		b = b[n:]
+	}
+	return has
+}
+
+// ClearExtension removes the extension field from m
+// either as an explicitly populated field or as an unknown field.
+func ClearExtension(m Message, xt *ExtensionDesc) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() {
+		return
+	}
+
+	xtd := xt.TypeDescriptor()
+	if isValidExtension(mr.Descriptor(), xtd) {
+		mr.Clear(xtd)
+	} else {
+		mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
+			if int32(fd.Number()) == xt.Field {
+				mr.Clear(fd)
+				return false
+			}
+			return true
+		})
+	}
+	clearUnknown(mr, fieldNum(xt.Field))
+}
+
+// ClearAllExtensions clears all extensions from m.
+// This includes populated fields and unknown fields in the extension range.
+func ClearAllExtensions(m Message) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() {
+		return
+	}
+
+	mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
+		if fd.IsExtension() {
+			mr.Clear(fd)
+		}
+		return true
+	})
+	clearUnknown(mr, mr.Descriptor().ExtensionRanges())
+}
+
+// GetExtension retrieves a proto2 extended field from m.
+//
+// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil),
+// then GetExtension parses the encoded field and returns a Go value of the specified type.
+// If the field is not present, then the default value is returned (if one is specified),
+// otherwise ErrMissingExtension is reported.
+//
+// If the descriptor is type incomplete (i.e., ExtensionDesc.ExtensionType is nil),
+// then GetExtension returns the raw encoded bytes for the extension field.
+func GetExtension(m Message, xt *ExtensionDesc) (interface{}, error) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
+		return nil, errNotExtendable
+	}
+
+	// Retrieve the unknown fields for this extension field.
+	var bo protoreflect.RawFields
+	for bi := mr.GetUnknown(); len(bi) > 0; {
+		num, _, n := protowire.ConsumeField(bi)
+		if int32(num) == xt.Field {
+			bo = append(bo, bi[:n]...)
+		}
+		bi = bi[n:]
+	}
+
+	// For type incomplete descriptors, only retrieve the unknown fields.
+	if xt.ExtensionType == nil {
+		return []byte(bo), nil
+	}
+
+	// If the extension field only exists as unknown fields, unmarshal it.
+	// This is rarely done since proto.Unmarshal eagerly unmarshals extensions.
+	xtd := xt.TypeDescriptor()
+	if !isValidExtension(mr.Descriptor(), xtd) {
+		return nil, fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
+	}
+	if !mr.Has(xtd) && len(bo) > 0 {
+		m2 := mr.New()
+		if err := (proto.UnmarshalOptions{
+			Resolver: extensionResolver{xt},
+		}.Unmarshal(bo, m2.Interface())); err != nil {
+			return nil, err
+		}
+		if m2.Has(xtd) {
+			mr.Set(xtd, m2.Get(xtd))
+			clearUnknown(mr, fieldNum(xt.Field))
+		}
+	}
+
+	// Check whether the message has the extension field set or a default.
+	var pv protoreflect.Value
+	switch {
+	case mr.Has(xtd):
+		pv = mr.Get(xtd)
+	case xtd.HasDefault():
+		pv = xtd.Default()
+	default:
+		return nil, ErrMissingExtension
+	}
+
+	v := xt.InterfaceOf(pv)
+	rv := reflect.ValueOf(v)
+	if isScalarKind(rv.Kind()) {
+		rv2 := reflect.New(rv.Type())
+		rv2.Elem().Set(rv)
+		v = rv2.Interface()
+	}
+	return v, nil
+}
+
+// extensionResolver is a custom extension resolver that stores a single
+// extension type that takes precedence over the global registry.
+type extensionResolver struct{ xt protoreflect.ExtensionType }
+
+func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
+	if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field {
+		return r.xt, nil
+	}
+	return protoregistry.GlobalTypes.FindExtensionByName(field)
+}
+
+func (r extensionResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
+	if xtd := r.xt.TypeDescriptor(); xtd.ContainingMessage().FullName() == message && xtd.Number() == field {
+		return r.xt, nil
+	}
+	return protoregistry.GlobalTypes.FindExtensionByNumber(message, field)
+}
+
+// GetExtensions returns a list of the extensions values present in m,
+// corresponding with the provided list of extension descriptors, xts.
+// If an extension is missing in m, the corresponding value is nil.
+func GetExtensions(m Message, xts []*ExtensionDesc) ([]interface{}, error) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() {
+		return nil, errNotExtendable
+	}
+
+	vs := make([]interface{}, len(xts))
+	for i, xt := range xts {
+		v, err := GetExtension(m, xt)
+		if err != nil {
+			if err == ErrMissingExtension {
+				continue
+			}
+			return vs, err
+		}
+		vs[i] = v
+	}
+	return vs, nil
+}
+
+// SetExtension sets an extension field in m to the provided value.
+func SetExtension(m Message, xt *ExtensionDesc, v interface{}) error {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
+		return errNotExtendable
+	}
+
+	rv := reflect.ValueOf(v)
+	if reflect.TypeOf(v) != reflect.TypeOf(xt.ExtensionType) {
+		return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", v, xt.ExtensionType)
+	}
+	if rv.Kind() == reflect.Ptr {
+		if rv.IsNil() {
+			return fmt.Errorf("proto: SetExtension called with nil value of type %T", v)
+		}
+		if isScalarKind(rv.Elem().Kind()) {
+			v = rv.Elem().Interface()
+		}
+	}
+
+	xtd := xt.TypeDescriptor()
+	if !isValidExtension(mr.Descriptor(), xtd) {
+		return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
+	}
+	mr.Set(xtd, xt.ValueOf(v))
+	clearUnknown(mr, fieldNum(xt.Field))
+	return nil
+}
+
+// SetRawExtension inserts b into the unknown fields of m.
+//
+// Deprecated: Use Message.ProtoReflect.SetUnknown instead.
+func SetRawExtension(m Message, fnum int32, b []byte) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() {
+		return
+	}
+
+	// Verify that the raw field is valid.
+	for b0 := b; len(b0) > 0; {
+		num, _, n := protowire.ConsumeField(b0)
+		if int32(num) != fnum {
+			panic(fmt.Sprintf("mismatching field number: got %d, want %d", num, fnum))
+		}
+		b0 = b0[n:]
+	}
+
+	ClearExtension(m, &ExtensionDesc{Field: fnum})
+	mr.SetUnknown(append(mr.GetUnknown(), b...))
+}
+
+// ExtensionDescs returns a list of extension descriptors found in m,
+// containing descriptors for both populated extension fields in m and
+// also unknown fields of m that are in the extension range.
+// For the later case, an type incomplete descriptor is provided where only
+// the ExtensionDesc.Field field is populated.
+// The order of the extension descriptors is undefined.
+func ExtensionDescs(m Message) ([]*ExtensionDesc, error) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
+		return nil, errNotExtendable
+	}
+
+	// Collect a set of known extension descriptors.
+	extDescs := make(map[protoreflect.FieldNumber]*ExtensionDesc)
+	mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
+		if fd.IsExtension() {
+			xt := fd.(protoreflect.ExtensionTypeDescriptor)
+			if xd, ok := xt.Type().(*ExtensionDesc); ok {
+				extDescs[fd.Number()] = xd
+			}
+		}
+		return true
+	})
+
+	// Collect a set of unknown extension descriptors.
+	extRanges := mr.Descriptor().ExtensionRanges()
+	for b := mr.GetUnknown(); len(b) > 0; {
+		num, _, n := protowire.ConsumeField(b)
+		if extRanges.Has(num) && extDescs[num] == nil {
+			extDescs[num] = nil
+		}
+		b = b[n:]
+	}
+
+	// Transpose the set of descriptors into a list.
+	var xts []*ExtensionDesc
+	for num, xt := range extDescs {
+		if xt == nil {
+			xt = &ExtensionDesc{Field: int32(num)}
+		}
+		xts = append(xts, xt)
+	}
+	return xts, nil
+}
+
+// isValidExtension reports whether xtd is a valid extension descriptor for md.
+func isValidExtension(md protoreflect.MessageDescriptor, xtd protoreflect.ExtensionTypeDescriptor) bool {
+	return xtd.ContainingMessage() == md && md.ExtensionRanges().Has(xtd.Number())
+}
+
+// isScalarKind reports whether k is a protobuf scalar kind (except bytes).
+// This function exists for historical reasons since the representation of
+// scalars differs between v1 and v2, where v1 uses *T and v2 uses T.
+func isScalarKind(k reflect.Kind) bool {
+	switch k {
+	case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
+		return true
+	default:
+		return false
+	}
+}
+
+// clearUnknown removes unknown fields from m where remover.Has reports true.
+func clearUnknown(m protoreflect.Message, remover interface {
+	Has(protoreflect.FieldNumber) bool
+}) {
+	var bo protoreflect.RawFields
+	for bi := m.GetUnknown(); len(bi) > 0; {
+		num, _, n := protowire.ConsumeField(bi)
+		if !remover.Has(num) {
+			bo = append(bo, bi[:n]...)
+		}
+		bi = bi[n:]
+	}
+	if bi := m.GetUnknown(); len(bi) != len(bo) {
+		m.SetUnknown(bo)
+	}
+}
+
+type fieldNum protoreflect.FieldNumber
+
+func (n1 fieldNum) Has(n2 protoreflect.FieldNumber) bool {
+	return protoreflect.FieldNumber(n1) == n2
+}

+ 306 - 0
vendor/github.com/golang/protobuf/proto/properties.go

@@ -0,0 +1,306 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"sync"
+
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/runtime/protoimpl"
+)
+
+// StructProperties represents protocol buffer type information for a
+// generated protobuf message in the open-struct API.
+//
+// Deprecated: Do not use.
+type StructProperties struct {
+	// Prop are the properties for each field.
+	//
+	// Fields belonging to a oneof are stored in OneofTypes instead, with a
+	// single Properties representing the parent oneof held here.
+	//
+	// The order of Prop matches the order of fields in the Go struct.
+	// Struct fields that are not related to protobufs have a "XXX_" prefix
+	// in the Properties.Name and must be ignored by the user.
+	Prop []*Properties
+
+	// OneofTypes contains information about the oneof fields in this message.
+	// It is keyed by the protobuf field name.
+	OneofTypes map[string]*OneofProperties
+}
+
+// Properties represents the type information for a protobuf message field.
+//
+// Deprecated: Do not use.
+type Properties struct {
+	// Name is a placeholder name with little meaningful semantic value.
+	// If the name has an "XXX_" prefix, the entire Properties must be ignored.
+	Name string
+	// OrigName is the protobuf field name or oneof name.
+	OrigName string
+	// JSONName is the JSON name for the protobuf field.
+	JSONName string
+	// Enum is a placeholder name for enums.
+	// For historical reasons, this is neither the Go name for the enum,
+	// nor the protobuf name for the enum.
+	Enum string // Deprecated: Do not use.
+	// Weak contains the full name of the weakly referenced message.
+	Weak string
+	// Wire is a string representation of the wire type.
+	Wire string
+	// WireType is the protobuf wire type for the field.
+	WireType int
+	// Tag is the protobuf field number.
+	Tag int
+	// Required reports whether this is a required field.
+	Required bool
+	// Optional reports whether this is a optional field.
+	Optional bool
+	// Repeated reports whether this is a repeated field.
+	Repeated bool
+	// Packed reports whether this is a packed repeated field of scalars.
+	Packed bool
+	// Proto3 reports whether this field operates under the proto3 syntax.
+	Proto3 bool
+	// Oneof reports whether this field belongs within a oneof.
+	Oneof bool
+
+	// Default is the default value in string form.
+	Default string
+	// HasDefault reports whether the field has a default value.
+	HasDefault bool
+
+	// MapKeyProp is the properties for the key field for a map field.
+	MapKeyProp *Properties
+	// MapValProp is the properties for the value field for a map field.
+	MapValProp *Properties
+}
+
+// OneofProperties represents the type information for a protobuf oneof.
+//
+// Deprecated: Do not use.
+type OneofProperties struct {
+	// Type is a pointer to the generated wrapper type for the field value.
+	// This is nil for messages that are not in the open-struct API.
+	Type reflect.Type
+	// Field is the index into StructProperties.Prop for the containing oneof.
+	Field int
+	// Prop is the properties for the field.
+	Prop *Properties
+}
+
+// String formats the properties in the protobuf struct field tag style.
+func (p *Properties) String() string {
+	s := p.Wire
+	s += "," + strconv.Itoa(p.Tag)
+	if p.Required {
+		s += ",req"
+	}
+	if p.Optional {
+		s += ",opt"
+	}
+	if p.Repeated {
+		s += ",rep"
+	}
+	if p.Packed {
+		s += ",packed"
+	}
+	s += ",name=" + p.OrigName
+	if p.JSONName != "" {
+		s += ",json=" + p.JSONName
+	}
+	if len(p.Enum) > 0 {
+		s += ",enum=" + p.Enum
+	}
+	if len(p.Weak) > 0 {
+		s += ",weak=" + p.Weak
+	}
+	if p.Proto3 {
+		s += ",proto3"
+	}
+	if p.Oneof {
+		s += ",oneof"
+	}
+	if p.HasDefault {
+		s += ",def=" + p.Default
+	}
+	return s
+}
+
+// Parse populates p by parsing a string in the protobuf struct field tag style.
+func (p *Properties) Parse(tag string) {
+	// For example: "bytes,49,opt,name=foo,def=hello!"
+	for len(tag) > 0 {
+		i := strings.IndexByte(tag, ',')
+		if i < 0 {
+			i = len(tag)
+		}
+		switch s := tag[:i]; {
+		case strings.HasPrefix(s, "name="):
+			p.OrigName = s[len("name="):]
+		case strings.HasPrefix(s, "json="):
+			p.JSONName = s[len("json="):]
+		case strings.HasPrefix(s, "enum="):
+			p.Enum = s[len("enum="):]
+		case strings.HasPrefix(s, "weak="):
+			p.Weak = s[len("weak="):]
+		case strings.Trim(s, "0123456789") == "":
+			n, _ := strconv.ParseUint(s, 10, 32)
+			p.Tag = int(n)
+		case s == "opt":
+			p.Optional = true
+		case s == "req":
+			p.Required = true
+		case s == "rep":
+			p.Repeated = true
+		case s == "varint" || s == "zigzag32" || s == "zigzag64":
+			p.Wire = s
+			p.WireType = WireVarint
+		case s == "fixed32":
+			p.Wire = s
+			p.WireType = WireFixed32
+		case s == "fixed64":
+			p.Wire = s
+			p.WireType = WireFixed64
+		case s == "bytes":
+			p.Wire = s
+			p.WireType = WireBytes
+		case s == "group":
+			p.Wire = s
+			p.WireType = WireStartGroup
+		case s == "packed":
+			p.Packed = true
+		case s == "proto3":
+			p.Proto3 = true
+		case s == "oneof":
+			p.Oneof = true
+		case strings.HasPrefix(s, "def="):
+			// The default tag is special in that everything afterwards is the
+			// default regardless of the presence of commas.
+			p.HasDefault = true
+			p.Default, i = tag[len("def="):], len(tag)
+		}
+		tag = strings.TrimPrefix(tag[i:], ",")
+	}
+}
+
+// Init populates the properties from a protocol buffer struct tag.
+//
+// Deprecated: Do not use.
+func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
+	p.Name = name
+	p.OrigName = name
+	if tag == "" {
+		return
+	}
+	p.Parse(tag)
+
+	if typ != nil && typ.Kind() == reflect.Map {
+		p.MapKeyProp = new(Properties)
+		p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
+		p.MapValProp = new(Properties)
+		p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
+	}
+}
+
+var propertiesCache sync.Map // map[reflect.Type]*StructProperties
+
+// GetProperties returns the list of properties for the type represented by t,
+// which must be a generated protocol buffer message in the open-struct API,
+// where protobuf message fields are represented by exported Go struct fields.
+//
+// Deprecated: Use protobuf reflection instead.
+func GetProperties(t reflect.Type) *StructProperties {
+	if p, ok := propertiesCache.Load(t); ok {
+		return p.(*StructProperties)
+	}
+	p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
+	return p.(*StructProperties)
+}
+
+func newProperties(t reflect.Type) *StructProperties {
+	if t.Kind() != reflect.Struct {
+		panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
+	}
+
+	var hasOneof bool
+	prop := new(StructProperties)
+
+	// Construct a list of properties for each field in the struct.
+	for i := 0; i < t.NumField(); i++ {
+		p := new(Properties)
+		f := t.Field(i)
+		tagField := f.Tag.Get("protobuf")
+		p.Init(f.Type, f.Name, tagField, &f)
+
+		tagOneof := f.Tag.Get("protobuf_oneof")
+		if tagOneof != "" {
+			hasOneof = true
+			p.OrigName = tagOneof
+		}
+
+		// Rename unrelated struct fields with the "XXX_" prefix since so much
+		// user code simply checks for this to exclude special fields.
+		if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
+			p.Name = "XXX_" + p.Name
+			p.OrigName = "XXX_" + p.OrigName
+		} else if p.Weak != "" {
+			p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
+		}
+
+		prop.Prop = append(prop.Prop, p)
+	}
+
+	// Construct a mapping of oneof field names to properties.
+	if hasOneof {
+		var oneofWrappers []interface{}
+		if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
+			oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
+		}
+		if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
+			oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
+		}
+		if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
+			if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
+				oneofWrappers = m.ProtoMessageInfo().OneofWrappers
+			}
+		}
+
+		prop.OneofTypes = make(map[string]*OneofProperties)
+		for _, wrapper := range oneofWrappers {
+			p := &OneofProperties{
+				Type: reflect.ValueOf(wrapper).Type(), // *T
+				Prop: new(Properties),
+			}
+			f := p.Type.Elem().Field(0)
+			p.Prop.Name = f.Name
+			p.Prop.Parse(f.Tag.Get("protobuf"))
+
+			// Determine the struct field that contains this oneof.
+			// Each wrapper is assignable to exactly one parent field.
+			var foundOneof bool
+			for i := 0; i < t.NumField() && !foundOneof; i++ {
+				if p.Type.AssignableTo(t.Field(i).Type) {
+					p.Field = i
+					foundOneof = true
+				}
+			}
+			if !foundOneof {
+				panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
+			}
+			prop.OneofTypes[p.Prop.OrigName] = p
+		}
+	}
+
+	return prop
+}
+
+func (sp *StructProperties) Len() int           { return len(sp.Prop) }
+func (sp *StructProperties) Less(i, j int) bool { return false }
+func (sp *StructProperties) Swap(i, j int)      { return }

+ 167 - 0
vendor/github.com/golang/protobuf/proto/proto.go

@@ -0,0 +1,167 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package proto provides functionality for handling protocol buffer messages.
+// In particular, it provides marshaling and unmarshaling between a protobuf
+// message and the binary wire format.
+//
+// See https://developers.google.com/protocol-buffers/docs/gotutorial for
+// more information.
+//
+// Deprecated: Use the "google.golang.org/protobuf/proto" package instead.
+package proto
+
+import (
+	protoV2 "google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/runtime/protoiface"
+	"google.golang.org/protobuf/runtime/protoimpl"
+)
+
+const (
+	ProtoPackageIsVersion1 = true
+	ProtoPackageIsVersion2 = true
+	ProtoPackageIsVersion3 = true
+	ProtoPackageIsVersion4 = true
+)
+
+// GeneratedEnum is any enum type generated by protoc-gen-go
+// which is a named int32 kind.
+// This type exists for documentation purposes.
+type GeneratedEnum interface{}
+
+// GeneratedMessage is any message type generated by protoc-gen-go
+// which is a pointer to a named struct kind.
+// This type exists for documentation purposes.
+type GeneratedMessage interface{}
+
+// Message is a protocol buffer message.
+//
+// This is the v1 version of the message interface and is marginally better
+// than an empty interface as it lacks any method to programatically interact
+// with the contents of the message.
+//
+// A v2 message is declared in "google.golang.org/protobuf/proto".Message and
+// exposes protobuf reflection as a first-class feature of the interface.
+//
+// To convert a v1 message to a v2 message, use the MessageV2 function.
+// To convert a v2 message to a v1 message, use the MessageV1 function.
+type Message = protoiface.MessageV1
+
+// MessageV1 converts either a v1 or v2 message to a v1 message.
+// It returns nil if m is nil.
+func MessageV1(m GeneratedMessage) protoiface.MessageV1 {
+	return protoimpl.X.ProtoMessageV1Of(m)
+}
+
+// MessageV2 converts either a v1 or v2 message to a v2 message.
+// It returns nil if m is nil.
+func MessageV2(m GeneratedMessage) protoV2.Message {
+	return protoimpl.X.ProtoMessageV2Of(m)
+}
+
+// MessageReflect returns a reflective view for a message.
+// It returns nil if m is nil.
+func MessageReflect(m Message) protoreflect.Message {
+	return protoimpl.X.MessageOf(m)
+}
+
+// Marshaler is implemented by messages that can marshal themselves.
+// This interface is used by the following functions: Size, Marshal,
+// Buffer.Marshal, and Buffer.EncodeMessage.
+//
+// Deprecated: Do not implement.
+type Marshaler interface {
+	// Marshal formats the encoded bytes of the message.
+	// It should be deterministic and emit valid protobuf wire data.
+	// The caller takes ownership of the returned buffer.
+	Marshal() ([]byte, error)
+}
+
+// Unmarshaler is implemented by messages that can unmarshal themselves.
+// This interface is used by the following functions: Unmarshal, UnmarshalMerge,
+// Buffer.Unmarshal, Buffer.DecodeMessage, and Buffer.DecodeGroup.
+//
+// Deprecated: Do not implement.
+type Unmarshaler interface {
+	// Unmarshal parses the encoded bytes of the protobuf wire input.
+	// The provided buffer is only valid for during method call.
+	// It should not reset the receiver message.
+	Unmarshal([]byte) error
+}
+
+// Merger is implemented by messages that can merge themselves.
+// This interface is used by the following functions: Clone and Merge.
+//
+// Deprecated: Do not implement.
+type Merger interface {
+	// Merge merges the contents of src into the receiver message.
+	// It clones all data structures in src such that it aliases no mutable
+	// memory referenced by src.
+	Merge(src Message)
+}
+
+// RequiredNotSetError is an error type returned when
+// marshaling or unmarshaling a message with missing required fields.
+type RequiredNotSetError struct {
+	err error
+}
+
+func (e *RequiredNotSetError) Error() string {
+	if e.err != nil {
+		return e.err.Error()
+	}
+	return "proto: required field not set"
+}
+func (e *RequiredNotSetError) RequiredNotSet() bool {
+	return true
+}
+
+func checkRequiredNotSet(m protoV2.Message) error {
+	if err := protoV2.CheckInitialized(m); err != nil {
+		return &RequiredNotSetError{err: err}
+	}
+	return nil
+}
+
+// Clone returns a deep copy of src.
+func Clone(src Message) Message {
+	return MessageV1(protoV2.Clone(MessageV2(src)))
+}
+
+// Merge merges src into dst, which must be messages of the same type.
+//
+// Populated scalar fields in src are copied to dst, while populated
+// singular messages in src are merged into dst by recursively calling Merge.
+// The elements of every list field in src is appended to the corresponded
+// list fields in dst. The entries of every map field in src is copied into
+// the corresponding map field in dst, possibly replacing existing entries.
+// The unknown fields of src are appended to the unknown fields of dst.
+func Merge(dst, src Message) {
+	protoV2.Merge(MessageV2(dst), MessageV2(src))
+}
+
+// Equal reports whether two messages are equal.
+// If two messages marshal to the same bytes under deterministic serialization,
+// then Equal is guaranteed to report true.
+//
+// Two messages are equal if they are the same protobuf message type,
+// have the same set of populated known and extension field values,
+// and the same set of unknown fields values.
+//
+// Scalar values are compared with the equivalent of the == operator in Go,
+// except bytes values which are compared using bytes.Equal and
+// floating point values which specially treat NaNs as equal.
+// Message values are compared by recursively calling Equal.
+// Lists are equal if each element value is also equal.
+// Maps are equal if they have the same set of keys, where the pair of values
+// for each key is also equal.
+func Equal(x, y Message) bool {
+	return protoV2.Equal(MessageV2(x), MessageV2(y))
+}
+
+func isMessageSet(md protoreflect.MessageDescriptor) bool {
+	ms, ok := md.(interface{ IsMessageSet() bool })
+	return ok && ms.IsMessageSet()
+}

+ 323 - 0
vendor/github.com/golang/protobuf/proto/registry.go

@@ -0,0 +1,323 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"io/ioutil"
+	"reflect"
+	"strings"
+	"sync"
+
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
+	"google.golang.org/protobuf/runtime/protoimpl"
+)
+
+// filePath is the path to the proto source file.
+type filePath = string // e.g., "google/protobuf/descriptor.proto"
+
+// fileDescGZIP is the compressed contents of the encoded FileDescriptorProto.
+type fileDescGZIP = []byte
+
+var fileCache sync.Map // map[filePath]fileDescGZIP
+
+// RegisterFile is called from generated code to register the compressed
+// FileDescriptorProto with the file path for a proto source file.
+//
+// Deprecated: Use protoregistry.GlobalFiles.RegisterFile instead.
+func RegisterFile(s filePath, d fileDescGZIP) {
+	// Decompress the descriptor.
+	zr, err := gzip.NewReader(bytes.NewReader(d))
+	if err != nil {
+		panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
+	}
+	b, err := ioutil.ReadAll(zr)
+	if err != nil {
+		panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
+	}
+
+	// Construct a protoreflect.FileDescriptor from the raw descriptor.
+	// Note that DescBuilder.Build automatically registers the constructed
+	// file descriptor with the v2 registry.
+	protoimpl.DescBuilder{RawDescriptor: b}.Build()
+
+	// Locally cache the raw descriptor form for the file.
+	fileCache.Store(s, d)
+}
+
+// FileDescriptor returns the compressed FileDescriptorProto given the file path
+// for a proto source file. It returns nil if not found.
+//
+// Deprecated: Use protoregistry.GlobalFiles.FindFileByPath instead.
+func FileDescriptor(s filePath) fileDescGZIP {
+	if v, ok := fileCache.Load(s); ok {
+		return v.(fileDescGZIP)
+	}
+
+	// Find the descriptor in the v2 registry.
+	var b []byte
+	if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
+		if fd, ok := fd.(interface{ ProtoLegacyRawDesc() []byte }); ok {
+			b = fd.ProtoLegacyRawDesc()
+		} else {
+			// TODO: Use protodesc.ToFileDescriptorProto to construct
+			// a descriptorpb.FileDescriptorProto and marshal it.
+			// However, doing so causes the proto package to have a dependency
+			// on descriptorpb, leading to cyclic dependency issues.
+		}
+	}
+
+	// Locally cache the raw descriptor form for the file.
+	if len(b) > 0 {
+		v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b))
+		return v.(fileDescGZIP)
+	}
+	return nil
+}
+
+// enumName is the name of an enum. For historical reasons, the enum name is
+// neither the full Go name nor the full protobuf name of the enum.
+// The name is the dot-separated combination of just the proto package that the
+// enum is declared within followed by the Go type name of the generated enum.
+type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum"
+
+// enumsByName maps enum values by name to their numeric counterpart.
+type enumsByName = map[string]int32
+
+// enumsByNumber maps enum values by number to their name counterpart.
+type enumsByNumber = map[int32]string
+
+var enumCache sync.Map     // map[enumName]enumsByName
+var numFilesCache sync.Map // map[protoreflect.FullName]int
+
+// RegisterEnum is called from the generated code to register the mapping of
+// enum value names to enum numbers for the enum identified by s.
+//
+// Deprecated: Use protoregistry.GlobalTypes.RegisterEnum instead.
+func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) {
+	if _, ok := enumCache.Load(s); ok {
+		panic("proto: duplicate enum registered: " + s)
+	}
+	enumCache.Store(s, m)
+
+	// This does not forward registration to the v2 registry since this API
+	// lacks sufficient information to construct a complete v2 enum descriptor.
+}
+
+// EnumValueMap returns the mapping from enum value names to enum numbers for
+// the enum of the given name. It returns nil if not found.
+//
+// Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead.
+func EnumValueMap(s enumName) enumsByName {
+	if v, ok := enumCache.Load(s); ok {
+		return v.(enumsByName)
+	}
+
+	// Check whether the cache is stale. If the number of files in the current
+	// package differs, then it means that some enums may have been recently
+	// registered upstream that we do not know about.
+	var protoPkg protoreflect.FullName
+	if i := strings.LastIndexByte(s, '.'); i >= 0 {
+		protoPkg = protoreflect.FullName(s[:i])
+	}
+	v, _ := numFilesCache.Load(protoPkg)
+	numFiles, _ := v.(int)
+	if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles {
+		return nil // cache is up-to-date; was not found earlier
+	}
+
+	// Update the enum cache for all enums declared in the given proto package.
+	numFiles = 0
+	protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool {
+		walkEnums(fd, func(ed protoreflect.EnumDescriptor) {
+			name := protoimpl.X.LegacyEnumName(ed)
+			if _, ok := enumCache.Load(name); !ok {
+				m := make(enumsByName)
+				evs := ed.Values()
+				for i := evs.Len() - 1; i >= 0; i-- {
+					ev := evs.Get(i)
+					m[string(ev.Name())] = int32(ev.Number())
+				}
+				enumCache.LoadOrStore(name, m)
+			}
+		})
+		numFiles++
+		return true
+	})
+	numFilesCache.Store(protoPkg, numFiles)
+
+	// Check cache again for enum map.
+	if v, ok := enumCache.Load(s); ok {
+		return v.(enumsByName)
+	}
+	return nil
+}
+
+// walkEnums recursively walks all enums declared in d.
+func walkEnums(d interface {
+	Enums() protoreflect.EnumDescriptors
+	Messages() protoreflect.MessageDescriptors
+}, f func(protoreflect.EnumDescriptor)) {
+	eds := d.Enums()
+	for i := eds.Len() - 1; i >= 0; i-- {
+		f(eds.Get(i))
+	}
+	mds := d.Messages()
+	for i := mds.Len() - 1; i >= 0; i-- {
+		walkEnums(mds.Get(i), f)
+	}
+}
+
+// messageName is the full name of protobuf message.
+type messageName = string
+
+var messageTypeCache sync.Map // map[messageName]reflect.Type
+
+// RegisterType is called from generated code to register the message Go type
+// for a message of the given name.
+//
+// Deprecated: Use protoregistry.GlobalTypes.RegisterMessage instead.
+func RegisterType(m Message, s messageName) {
+	mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s))
+	if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil {
+		panic(err)
+	}
+	messageTypeCache.Store(s, reflect.TypeOf(m))
+}
+
+// RegisterMapType is called from generated code to register the Go map type
+// for a protobuf message representing a map entry.
+//
+// Deprecated: Do not use.
+func RegisterMapType(m interface{}, s messageName) {
+	t := reflect.TypeOf(m)
+	if t.Kind() != reflect.Map {
+		panic(fmt.Sprintf("invalid map kind: %v", t))
+	}
+	if _, ok := messageTypeCache.Load(s); ok {
+		panic(fmt.Errorf("proto: duplicate proto message registered: %s", s))
+	}
+	messageTypeCache.Store(s, t)
+}
+
+// MessageType returns the message type for a named message.
+// It returns nil if not found.
+//
+// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead.
+func MessageType(s messageName) reflect.Type {
+	if v, ok := messageTypeCache.Load(s); ok {
+		return v.(reflect.Type)
+	}
+
+	// Derive the message type from the v2 registry.
+	var t reflect.Type
+	if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil {
+		t = messageGoType(mt)
+	}
+
+	// If we could not get a concrete type, it is possible that it is a
+	// pseudo-message for a map entry.
+	if t == nil {
+		d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s))
+		if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() {
+			kt := goTypeForField(md.Fields().ByNumber(1))
+			vt := goTypeForField(md.Fields().ByNumber(2))
+			t = reflect.MapOf(kt, vt)
+		}
+	}
+
+	// Locally cache the message type for the given name.
+	if t != nil {
+		v, _ := messageTypeCache.LoadOrStore(s, t)
+		return v.(reflect.Type)
+	}
+	return nil
+}
+
+func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type {
+	switch k := fd.Kind(); k {
+	case protoreflect.EnumKind:
+		if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil {
+			return enumGoType(et)
+		}
+		return reflect.TypeOf(protoreflect.EnumNumber(0))
+	case protoreflect.MessageKind, protoreflect.GroupKind:
+		if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil {
+			return messageGoType(mt)
+		}
+		return reflect.TypeOf((*protoreflect.Message)(nil)).Elem()
+	default:
+		return reflect.TypeOf(fd.Default().Interface())
+	}
+}
+
+func enumGoType(et protoreflect.EnumType) reflect.Type {
+	return reflect.TypeOf(et.New(0))
+}
+
+func messageGoType(mt protoreflect.MessageType) reflect.Type {
+	return reflect.TypeOf(MessageV1(mt.Zero().Interface()))
+}
+
+// MessageName returns the full protobuf name for the given message type.
+//
+// Deprecated: Use protoreflect.MessageDescriptor.FullName instead.
+func MessageName(m Message) messageName {
+	if m == nil {
+		return ""
+	}
+	if m, ok := m.(interface{ XXX_MessageName() messageName }); ok {
+		return m.XXX_MessageName()
+	}
+	return messageName(protoimpl.X.MessageDescriptorOf(m).FullName())
+}
+
+// RegisterExtension is called from the generated code to register
+// the extension descriptor.
+//
+// Deprecated: Use protoregistry.GlobalTypes.RegisterExtension instead.
+func RegisterExtension(d *ExtensionDesc) {
+	if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil {
+		panic(err)
+	}
+}
+
+type extensionsByNumber = map[int32]*ExtensionDesc
+
+var extensionCache sync.Map // map[messageName]extensionsByNumber
+
+// RegisteredExtensions returns a map of the registered extensions for the
+// provided protobuf message, indexed by the extension field number.
+//
+// Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead.
+func RegisteredExtensions(m Message) extensionsByNumber {
+	// Check whether the cache is stale. If the number of extensions for
+	// the given message differs, then it means that some extensions were
+	// recently registered upstream that we do not know about.
+	s := MessageName(m)
+	v, _ := extensionCache.Load(s)
+	xs, _ := v.(extensionsByNumber)
+	if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) {
+		return xs // cache is up-to-date
+	}
+
+	// Cache is stale, re-compute the extensions map.
+	xs = make(extensionsByNumber)
+	protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool {
+		if xd, ok := xt.(*ExtensionDesc); ok {
+			xs[int32(xt.TypeDescriptor().Number())] = xd
+		} else {
+			// TODO: This implies that the protoreflect.ExtensionType is a
+			// custom type not generated by protoc-gen-go. We could try and
+			// convert the type to an ExtensionDesc.
+		}
+		return true
+	})
+	extensionCache.Store(s, xs)
+	return xs
+}

+ 801 - 0
vendor/github.com/golang/protobuf/proto/text_decode.go

@@ -0,0 +1,801 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"encoding"
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+
+	"google.golang.org/protobuf/encoding/prototext"
+	protoV2 "google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
+)
+
+const wrapTextUnmarshalV2 = false
+
+// ParseError is returned by UnmarshalText.
+type ParseError struct {
+	Message string
+
+	// Deprecated: Do not use.
+	Line, Offset int
+}
+
+func (e *ParseError) Error() string {
+	if wrapTextUnmarshalV2 {
+		return e.Message
+	}
+	if e.Line == 1 {
+		return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message)
+	}
+	return fmt.Sprintf("line %d: %v", e.Line, e.Message)
+}
+
+// UnmarshalText parses a proto text formatted string into m.
+func UnmarshalText(s string, m Message) error {
+	if u, ok := m.(encoding.TextUnmarshaler); ok {
+		return u.UnmarshalText([]byte(s))
+	}
+
+	m.Reset()
+	mi := MessageV2(m)
+
+	if wrapTextUnmarshalV2 {
+		err := prototext.UnmarshalOptions{
+			AllowPartial: true,
+		}.Unmarshal([]byte(s), mi)
+		if err != nil {
+			return &ParseError{Message: err.Error()}
+		}
+		return checkRequiredNotSet(mi)
+	} else {
+		if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil {
+			return err
+		}
+		return checkRequiredNotSet(mi)
+	}
+}
+
+type textParser struct {
+	s            string // remaining input
+	done         bool   // whether the parsing is finished (success or error)
+	backed       bool   // whether back() was called
+	offset, line int
+	cur          token
+}
+
+type token struct {
+	value    string
+	err      *ParseError
+	line     int    // line number
+	offset   int    // byte number from start of input, not start of line
+	unquoted string // the unquoted version of value, if it was a quoted string
+}
+
+func newTextParser(s string) *textParser {
+	p := new(textParser)
+	p.s = s
+	p.line = 1
+	p.cur.line = 1
+	return p
+}
+
+func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) {
+	md := m.Descriptor()
+	fds := md.Fields()
+
+	// A struct is a sequence of "name: value", terminated by one of
+	// '>' or '}', or the end of the input.  A name may also be
+	// "[extension]" or "[type/url]".
+	//
+	// The whole struct can also be an expanded Any message, like:
+	// [type/url] < ... struct contents ... >
+	seen := make(map[protoreflect.FieldNumber]bool)
+	for {
+		tok := p.next()
+		if tok.err != nil {
+			return tok.err
+		}
+		if tok.value == terminator {
+			break
+		}
+		if tok.value == "[" {
+			if err := p.unmarshalExtensionOrAny(m, seen); err != nil {
+				return err
+			}
+			continue
+		}
+
+		// This is a normal, non-extension field.
+		name := protoreflect.Name(tok.value)
+		fd := fds.ByName(name)
+		switch {
+		case fd == nil:
+			gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name))))
+			if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name {
+				fd = gd
+			}
+		case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name:
+			fd = nil
+		case fd.IsWeak() && fd.Message().IsPlaceholder():
+			fd = nil
+		}
+		if fd == nil {
+			typeName := string(md.FullName())
+			if m, ok := m.Interface().(Message); ok {
+				t := reflect.TypeOf(m)
+				if t.Kind() == reflect.Ptr {
+					typeName = t.Elem().String()
+				}
+			}
+			return p.errorf("unknown field name %q in %v", name, typeName)
+		}
+		if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil {
+			return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name())
+		}
+		if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] {
+			return p.errorf("non-repeated field %q was repeated", fd.Name())
+		}
+		seen[fd.Number()] = true
+
+		// Consume any colon.
+		if err := p.checkForColon(fd); err != nil {
+			return err
+		}
+
+		// Parse into the field.
+		v := m.Get(fd)
+		if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
+			v = m.Mutable(fd)
+		}
+		if v, err = p.unmarshalValue(v, fd); err != nil {
+			return err
+		}
+		m.Set(fd, v)
+
+		if err := p.consumeOptionalSeparator(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error {
+	name, err := p.consumeExtensionOrAnyName()
+	if err != nil {
+		return err
+	}
+
+	// If it contains a slash, it's an Any type URL.
+	if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 {
+		tok := p.next()
+		if tok.err != nil {
+			return tok.err
+		}
+		// consume an optional colon
+		if tok.value == ":" {
+			tok = p.next()
+			if tok.err != nil {
+				return tok.err
+			}
+		}
+
+		var terminator string
+		switch tok.value {
+		case "<":
+			terminator = ">"
+		case "{":
+			terminator = "}"
+		default:
+			return p.errorf("expected '{' or '<', found %q", tok.value)
+		}
+
+		mt, err := protoregistry.GlobalTypes.FindMessageByURL(name)
+		if err != nil {
+			return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):])
+		}
+		m2 := mt.New()
+		if err := p.unmarshalMessage(m2, terminator); err != nil {
+			return err
+		}
+		b, err := protoV2.Marshal(m2.Interface())
+		if err != nil {
+			return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err)
+		}
+
+		urlFD := m.Descriptor().Fields().ByName("type_url")
+		valFD := m.Descriptor().Fields().ByName("value")
+		if seen[urlFD.Number()] {
+			return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name())
+		}
+		if seen[valFD.Number()] {
+			return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name())
+		}
+		m.Set(urlFD, protoreflect.ValueOfString(name))
+		m.Set(valFD, protoreflect.ValueOfBytes(b))
+		seen[urlFD.Number()] = true
+		seen[valFD.Number()] = true
+		return nil
+	}
+
+	xname := protoreflect.FullName(name)
+	xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname)
+	if xt == nil && isMessageSet(m.Descriptor()) {
+		xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension"))
+	}
+	if xt == nil {
+		return p.errorf("unrecognized extension %q", name)
+	}
+	fd := xt.TypeDescriptor()
+	if fd.ContainingMessage().FullName() != m.Descriptor().FullName() {
+		return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName())
+	}
+
+	if err := p.checkForColon(fd); err != nil {
+		return err
+	}
+
+	v := m.Get(fd)
+	if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
+		v = m.Mutable(fd)
+	}
+	v, err = p.unmarshalValue(v, fd)
+	if err != nil {
+		return err
+	}
+	m.Set(fd, v)
+	return p.consumeOptionalSeparator()
+}
+
+func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
+	tok := p.next()
+	if tok.err != nil {
+		return v, tok.err
+	}
+	if tok.value == "" {
+		return v, p.errorf("unexpected EOF")
+	}
+
+	switch {
+	case fd.IsList():
+		lv := v.List()
+		var err error
+		if tok.value == "[" {
+			// Repeated field with list notation, like [1,2,3].
+			for {
+				vv := lv.NewElement()
+				vv, err = p.unmarshalSingularValue(vv, fd)
+				if err != nil {
+					return v, err
+				}
+				lv.Append(vv)
+
+				tok := p.next()
+				if tok.err != nil {
+					return v, tok.err
+				}
+				if tok.value == "]" {
+					break
+				}
+				if tok.value != "," {
+					return v, p.errorf("Expected ']' or ',' found %q", tok.value)
+				}
+			}
+			return v, nil
+		}
+
+		// One value of the repeated field.
+		p.back()
+		vv := lv.NewElement()
+		vv, err = p.unmarshalSingularValue(vv, fd)
+		if err != nil {
+			return v, err
+		}
+		lv.Append(vv)
+		return v, nil
+	case fd.IsMap():
+		// The map entry should be this sequence of tokens:
+		//	< key : KEY value : VALUE >
+		// However, implementations may omit key or value, and technically
+		// we should support them in any order.
+		var terminator string
+		switch tok.value {
+		case "<":
+			terminator = ">"
+		case "{":
+			terminator = "}"
+		default:
+			return v, p.errorf("expected '{' or '<', found %q", tok.value)
+		}
+
+		keyFD := fd.MapKey()
+		valFD := fd.MapValue()
+
+		mv := v.Map()
+		kv := keyFD.Default()
+		vv := mv.NewValue()
+		for {
+			tok := p.next()
+			if tok.err != nil {
+				return v, tok.err
+			}
+			if tok.value == terminator {
+				break
+			}
+			var err error
+			switch tok.value {
+			case "key":
+				if err := p.consumeToken(":"); err != nil {
+					return v, err
+				}
+				if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil {
+					return v, err
+				}
+				if err := p.consumeOptionalSeparator(); err != nil {
+					return v, err
+				}
+			case "value":
+				if err := p.checkForColon(valFD); err != nil {
+					return v, err
+				}
+				if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil {
+					return v, err
+				}
+				if err := p.consumeOptionalSeparator(); err != nil {
+					return v, err
+				}
+			default:
+				p.back()
+				return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
+			}
+		}
+		mv.Set(kv.MapKey(), vv)
+		return v, nil
+	default:
+		p.back()
+		return p.unmarshalSingularValue(v, fd)
+	}
+}
+
+func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
+	tok := p.next()
+	if tok.err != nil {
+		return v, tok.err
+	}
+	if tok.value == "" {
+		return v, p.errorf("unexpected EOF")
+	}
+
+	switch fd.Kind() {
+	case protoreflect.BoolKind:
+		switch tok.value {
+		case "true", "1", "t", "True":
+			return protoreflect.ValueOfBool(true), nil
+		case "false", "0", "f", "False":
+			return protoreflect.ValueOfBool(false), nil
+		}
+	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
+		if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
+			return protoreflect.ValueOfInt32(int32(x)), nil
+		}
+
+		// The C++ parser accepts large positive hex numbers that uses
+		// two's complement arithmetic to represent negative numbers.
+		// This feature is here for backwards compatibility with C++.
+		if strings.HasPrefix(tok.value, "0x") {
+			if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
+				return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil
+			}
+		}
+	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
+		if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
+			return protoreflect.ValueOfInt64(int64(x)), nil
+		}
+
+		// The C++ parser accepts large positive hex numbers that uses
+		// two's complement arithmetic to represent negative numbers.
+		// This feature is here for backwards compatibility with C++.
+		if strings.HasPrefix(tok.value, "0x") {
+			if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
+				return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil
+			}
+		}
+	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
+		if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
+			return protoreflect.ValueOfUint32(uint32(x)), nil
+		}
+	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
+		if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
+			return protoreflect.ValueOfUint64(uint64(x)), nil
+		}
+	case protoreflect.FloatKind:
+		// Ignore 'f' for compatibility with output generated by C++,
+		// but don't remove 'f' when the value is "-inf" or "inf".
+		v := tok.value
+		if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
+			v = v[:len(v)-len("f")]
+		}
+		if x, err := strconv.ParseFloat(v, 32); err == nil {
+			return protoreflect.ValueOfFloat32(float32(x)), nil
+		}
+	case protoreflect.DoubleKind:
+		// Ignore 'f' for compatibility with output generated by C++,
+		// but don't remove 'f' when the value is "-inf" or "inf".
+		v := tok.value
+		if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
+			v = v[:len(v)-len("f")]
+		}
+		if x, err := strconv.ParseFloat(v, 64); err == nil {
+			return protoreflect.ValueOfFloat64(float64(x)), nil
+		}
+	case protoreflect.StringKind:
+		if isQuote(tok.value[0]) {
+			return protoreflect.ValueOfString(tok.unquoted), nil
+		}
+	case protoreflect.BytesKind:
+		if isQuote(tok.value[0]) {
+			return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil
+		}
+	case protoreflect.EnumKind:
+		if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
+			return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil
+		}
+		vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value))
+		if vd != nil {
+			return protoreflect.ValueOfEnum(vd.Number()), nil
+		}
+	case protoreflect.MessageKind, protoreflect.GroupKind:
+		var terminator string
+		switch tok.value {
+		case "{":
+			terminator = "}"
+		case "<":
+			terminator = ">"
+		default:
+			return v, p.errorf("expected '{' or '<', found %q", tok.value)
+		}
+		err := p.unmarshalMessage(v.Message(), terminator)
+		return v, err
+	default:
+		panic(fmt.Sprintf("invalid kind %v", fd.Kind()))
+	}
+	return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value)
+}
+
+// Consume a ':' from the input stream (if the next token is a colon),
+// returning an error if a colon is needed but not present.
+func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value != ":" {
+		if fd.Message() == nil {
+			return p.errorf("expected ':', found %q", tok.value)
+		}
+		p.back()
+	}
+	return nil
+}
+
+// consumeExtensionOrAnyName consumes an extension name or an Any type URL and
+// the following ']'. It returns the name or URL consumed.
+func (p *textParser) consumeExtensionOrAnyName() (string, error) {
+	tok := p.next()
+	if tok.err != nil {
+		return "", tok.err
+	}
+
+	// If extension name or type url is quoted, it's a single token.
+	if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
+		name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
+		if err != nil {
+			return "", err
+		}
+		return name, p.consumeToken("]")
+	}
+
+	// Consume everything up to "]"
+	var parts []string
+	for tok.value != "]" {
+		parts = append(parts, tok.value)
+		tok = p.next()
+		if tok.err != nil {
+			return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
+		}
+		if p.done && tok.value != "]" {
+			return "", p.errorf("unclosed type_url or extension name")
+		}
+	}
+	return strings.Join(parts, ""), nil
+}
+
+// consumeOptionalSeparator consumes an optional semicolon or comma.
+// It is used in unmarshalMessage to provide backward compatibility.
+func (p *textParser) consumeOptionalSeparator() error {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value != ";" && tok.value != "," {
+		p.back()
+	}
+	return nil
+}
+
+func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
+	pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
+	p.cur.err = pe
+	p.done = true
+	return pe
+}
+
+func (p *textParser) skipWhitespace() {
+	i := 0
+	for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
+		if p.s[i] == '#' {
+			// comment; skip to end of line or input
+			for i < len(p.s) && p.s[i] != '\n' {
+				i++
+			}
+			if i == len(p.s) {
+				break
+			}
+		}
+		if p.s[i] == '\n' {
+			p.line++
+		}
+		i++
+	}
+	p.offset += i
+	p.s = p.s[i:len(p.s)]
+	if len(p.s) == 0 {
+		p.done = true
+	}
+}
+
+func (p *textParser) advance() {
+	// Skip whitespace
+	p.skipWhitespace()
+	if p.done {
+		return
+	}
+
+	// Start of non-whitespace
+	p.cur.err = nil
+	p.cur.offset, p.cur.line = p.offset, p.line
+	p.cur.unquoted = ""
+	switch p.s[0] {
+	case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
+		// Single symbol
+		p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
+	case '"', '\'':
+		// Quoted string
+		i := 1
+		for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
+			if p.s[i] == '\\' && i+1 < len(p.s) {
+				// skip escaped char
+				i++
+			}
+			i++
+		}
+		if i >= len(p.s) || p.s[i] != p.s[0] {
+			p.errorf("unmatched quote")
+			return
+		}
+		unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
+		if err != nil {
+			p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
+			return
+		}
+		p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
+		p.cur.unquoted = unq
+	default:
+		i := 0
+		for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
+			i++
+		}
+		if i == 0 {
+			p.errorf("unexpected byte %#x", p.s[0])
+			return
+		}
+		p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
+	}
+	p.offset += len(p.cur.value)
+}
+
+// Back off the parser by one token. Can only be done between calls to next().
+// It makes the next advance() a no-op.
+func (p *textParser) back() { p.backed = true }
+
+// Advances the parser and returns the new current token.
+func (p *textParser) next() *token {
+	if p.backed || p.done {
+		p.backed = false
+		return &p.cur
+	}
+	p.advance()
+	if p.done {
+		p.cur.value = ""
+	} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
+		// Look for multiple quoted strings separated by whitespace,
+		// and concatenate them.
+		cat := p.cur
+		for {
+			p.skipWhitespace()
+			if p.done || !isQuote(p.s[0]) {
+				break
+			}
+			p.advance()
+			if p.cur.err != nil {
+				return &p.cur
+			}
+			cat.value += " " + p.cur.value
+			cat.unquoted += p.cur.unquoted
+		}
+		p.done = false // parser may have seen EOF, but we want to return cat
+		p.cur = cat
+	}
+	return &p.cur
+}
+
+func (p *textParser) consumeToken(s string) error {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value != s {
+		p.back()
+		return p.errorf("expected %q, found %q", s, tok.value)
+	}
+	return nil
+}
+
+var errBadUTF8 = errors.New("proto: bad UTF-8")
+
+func unquoteC(s string, quote rune) (string, error) {
+	// This is based on C++'s tokenizer.cc.
+	// Despite its name, this is *not* parsing C syntax.
+	// For instance, "\0" is an invalid quoted string.
+
+	// Avoid allocation in trivial cases.
+	simple := true
+	for _, r := range s {
+		if r == '\\' || r == quote {
+			simple = false
+			break
+		}
+	}
+	if simple {
+		return s, nil
+	}
+
+	buf := make([]byte, 0, 3*len(s)/2)
+	for len(s) > 0 {
+		r, n := utf8.DecodeRuneInString(s)
+		if r == utf8.RuneError && n == 1 {
+			return "", errBadUTF8
+		}
+		s = s[n:]
+		if r != '\\' {
+			if r < utf8.RuneSelf {
+				buf = append(buf, byte(r))
+			} else {
+				buf = append(buf, string(r)...)
+			}
+			continue
+		}
+
+		ch, tail, err := unescape(s)
+		if err != nil {
+			return "", err
+		}
+		buf = append(buf, ch...)
+		s = tail
+	}
+	return string(buf), nil
+}
+
+func unescape(s string) (ch string, tail string, err error) {
+	r, n := utf8.DecodeRuneInString(s)
+	if r == utf8.RuneError && n == 1 {
+		return "", "", errBadUTF8
+	}
+	s = s[n:]
+	switch r {
+	case 'a':
+		return "\a", s, nil
+	case 'b':
+		return "\b", s, nil
+	case 'f':
+		return "\f", s, nil
+	case 'n':
+		return "\n", s, nil
+	case 'r':
+		return "\r", s, nil
+	case 't':
+		return "\t", s, nil
+	case 'v':
+		return "\v", s, nil
+	case '?':
+		return "?", s, nil // trigraph workaround
+	case '\'', '"', '\\':
+		return string(r), s, nil
+	case '0', '1', '2', '3', '4', '5', '6', '7':
+		if len(s) < 2 {
+			return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
+		}
+		ss := string(r) + s[:2]
+		s = s[2:]
+		i, err := strconv.ParseUint(ss, 8, 8)
+		if err != nil {
+			return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
+		}
+		return string([]byte{byte(i)}), s, nil
+	case 'x', 'X', 'u', 'U':
+		var n int
+		switch r {
+		case 'x', 'X':
+			n = 2
+		case 'u':
+			n = 4
+		case 'U':
+			n = 8
+		}
+		if len(s) < n {
+			return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
+		}
+		ss := s[:n]
+		s = s[n:]
+		i, err := strconv.ParseUint(ss, 16, 64)
+		if err != nil {
+			return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
+		}
+		if r == 'x' || r == 'X' {
+			return string([]byte{byte(i)}), s, nil
+		}
+		if i > utf8.MaxRune {
+			return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
+		}
+		return string(rune(i)), s, nil
+	}
+	return "", "", fmt.Errorf(`unknown escape \%c`, r)
+}
+
+func isIdentOrNumberChar(c byte) bool {
+	switch {
+	case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
+		return true
+	case '0' <= c && c <= '9':
+		return true
+	}
+	switch c {
+	case '-', '+', '.', '_':
+		return true
+	}
+	return false
+}
+
+func isWhitespace(c byte) bool {
+	switch c {
+	case ' ', '\t', '\n', '\r':
+		return true
+	}
+	return false
+}
+
+func isQuote(c byte) bool {
+	switch c {
+	case '"', '\'':
+		return true
+	}
+	return false
+}

+ 560 - 0
vendor/github.com/golang/protobuf/proto/text_encode.go

@@ -0,0 +1,560 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	"bytes"
+	"encoding"
+	"fmt"
+	"io"
+	"math"
+	"sort"
+	"strings"
+
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
+)
+
+const wrapTextMarshalV2 = false
+
+// TextMarshaler is a configurable text format marshaler.
+type TextMarshaler struct {
+	Compact   bool // use compact text format (one line)
+	ExpandAny bool // expand google.protobuf.Any messages of known types
+}
+
+// Marshal writes the proto text format of m to w.
+func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error {
+	b, err := tm.marshal(m)
+	if len(b) > 0 {
+		if _, err := w.Write(b); err != nil {
+			return err
+		}
+	}
+	return err
+}
+
+// Text returns a proto text formatted string of m.
+func (tm *TextMarshaler) Text(m Message) string {
+	b, _ := tm.marshal(m)
+	return string(b)
+}
+
+func (tm *TextMarshaler) marshal(m Message) ([]byte, error) {
+	mr := MessageReflect(m)
+	if mr == nil || !mr.IsValid() {
+		return []byte("<nil>"), nil
+	}
+
+	if wrapTextMarshalV2 {
+		if m, ok := m.(encoding.TextMarshaler); ok {
+			return m.MarshalText()
+		}
+
+		opts := prototext.MarshalOptions{
+			AllowPartial: true,
+			EmitUnknown:  true,
+		}
+		if !tm.Compact {
+			opts.Indent = "  "
+		}
+		if !tm.ExpandAny {
+			opts.Resolver = (*protoregistry.Types)(nil)
+		}
+		return opts.Marshal(mr.Interface())
+	} else {
+		w := &textWriter{
+			compact:   tm.Compact,
+			expandAny: tm.ExpandAny,
+			complete:  true,
+		}
+
+		if m, ok := m.(encoding.TextMarshaler); ok {
+			b, err := m.MarshalText()
+			if err != nil {
+				return nil, err
+			}
+			w.Write(b)
+			return w.buf, nil
+		}
+
+		err := w.writeMessage(mr)
+		return w.buf, err
+	}
+}
+
+var (
+	defaultTextMarshaler = TextMarshaler{}
+	compactTextMarshaler = TextMarshaler{Compact: true}
+)
+
+// MarshalText writes the proto text format of m to w.
+func MarshalText(w io.Writer, m Message) error { return defaultTextMarshaler.Marshal(w, m) }
+
+// MarshalTextString returns a proto text formatted string of m.
+func MarshalTextString(m Message) string { return defaultTextMarshaler.Text(m) }
+
+// CompactText writes the compact proto text format of m to w.
+func CompactText(w io.Writer, m Message) error { return compactTextMarshaler.Marshal(w, m) }
+
+// CompactTextString returns a compact proto text formatted string of m.
+func CompactTextString(m Message) string { return compactTextMarshaler.Text(m) }
+
+var (
+	newline         = []byte("\n")
+	endBraceNewline = []byte("}\n")
+	posInf          = []byte("inf")
+	negInf          = []byte("-inf")
+	nan             = []byte("nan")
+)
+
+// textWriter is an io.Writer that tracks its indentation level.
+type textWriter struct {
+	compact   bool // same as TextMarshaler.Compact
+	expandAny bool // same as TextMarshaler.ExpandAny
+	complete  bool // whether the current position is a complete line
+	indent    int  // indentation level; never negative
+	buf       []byte
+}
+
+func (w *textWriter) Write(p []byte) (n int, _ error) {
+	newlines := bytes.Count(p, newline)
+	if newlines == 0 {
+		if !w.compact && w.complete {
+			w.writeIndent()
+		}
+		w.buf = append(w.buf, p...)
+		w.complete = false
+		return len(p), nil
+	}
+
+	frags := bytes.SplitN(p, newline, newlines+1)
+	if w.compact {
+		for i, frag := range frags {
+			if i > 0 {
+				w.buf = append(w.buf, ' ')
+				n++
+			}
+			w.buf = append(w.buf, frag...)
+			n += len(frag)
+		}
+		return n, nil
+	}
+
+	for i, frag := range frags {
+		if w.complete {
+			w.writeIndent()
+		}
+		w.buf = append(w.buf, frag...)
+		n += len(frag)
+		if i+1 < len(frags) {
+			w.buf = append(w.buf, '\n')
+			n++
+		}
+	}
+	w.complete = len(frags[len(frags)-1]) == 0
+	return n, nil
+}
+
+func (w *textWriter) WriteByte(c byte) error {
+	if w.compact && c == '\n' {
+		c = ' '
+	}
+	if !w.compact && w.complete {
+		w.writeIndent()
+	}
+	w.buf = append(w.buf, c)
+	w.complete = c == '\n'
+	return nil
+}
+
+func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) {
+	if !w.compact && w.complete {
+		w.writeIndent()
+	}
+	w.complete = false
+
+	if fd.Kind() != protoreflect.GroupKind {
+		w.buf = append(w.buf, fd.Name()...)
+		w.WriteByte(':')
+	} else {
+		// Use message type name for group field name.
+		w.buf = append(w.buf, fd.Message().Name()...)
+	}
+
+	if !w.compact {
+		w.WriteByte(' ')
+	}
+}
+
+func requiresQuotes(u string) bool {
+	// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
+	for _, ch := range u {
+		switch {
+		case ch == '.' || ch == '/' || ch == '_':
+			continue
+		case '0' <= ch && ch <= '9':
+			continue
+		case 'A' <= ch && ch <= 'Z':
+			continue
+		case 'a' <= ch && ch <= 'z':
+			continue
+		default:
+			return true
+		}
+	}
+	return false
+}
+
+// writeProto3Any writes an expanded google.protobuf.Any message.
+//
+// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
+// required messages are not linked in).
+//
+// It returns (true, error) when sv was written in expanded format or an error
+// was encountered.
+func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) {
+	md := m.Descriptor()
+	fdURL := md.Fields().ByName("type_url")
+	fdVal := md.Fields().ByName("value")
+
+	url := m.Get(fdURL).String()
+	mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
+	if err != nil {
+		return false, nil
+	}
+
+	b := m.Get(fdVal).Bytes()
+	m2 := mt.New()
+	if err := proto.Unmarshal(b, m2.Interface()); err != nil {
+		return false, nil
+	}
+	w.Write([]byte("["))
+	if requiresQuotes(url) {
+		w.writeQuotedString(url)
+	} else {
+		w.Write([]byte(url))
+	}
+	if w.compact {
+		w.Write([]byte("]:<"))
+	} else {
+		w.Write([]byte("]: <\n"))
+		w.indent++
+	}
+	if err := w.writeMessage(m2); err != nil {
+		return true, err
+	}
+	if w.compact {
+		w.Write([]byte("> "))
+	} else {
+		w.indent--
+		w.Write([]byte(">\n"))
+	}
+	return true, nil
+}
+
+func (w *textWriter) writeMessage(m protoreflect.Message) error {
+	md := m.Descriptor()
+	if w.expandAny && md.FullName() == "google.protobuf.Any" {
+		if canExpand, err := w.writeProto3Any(m); canExpand {
+			return err
+		}
+	}
+
+	fds := md.Fields()
+	for i := 0; i < fds.Len(); {
+		fd := fds.Get(i)
+		if od := fd.ContainingOneof(); od != nil {
+			fd = m.WhichOneof(od)
+			i += od.Fields().Len()
+		} else {
+			i++
+		}
+		if fd == nil || !m.Has(fd) {
+			continue
+		}
+
+		switch {
+		case fd.IsList():
+			lv := m.Get(fd).List()
+			for j := 0; j < lv.Len(); j++ {
+				w.writeName(fd)
+				v := lv.Get(j)
+				if err := w.writeSingularValue(v, fd); err != nil {
+					return err
+				}
+				w.WriteByte('\n')
+			}
+		case fd.IsMap():
+			kfd := fd.MapKey()
+			vfd := fd.MapValue()
+			mv := m.Get(fd).Map()
+
+			type entry struct{ key, val protoreflect.Value }
+			var entries []entry
+			mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
+				entries = append(entries, entry{k.Value(), v})
+				return true
+			})
+			sort.Slice(entries, func(i, j int) bool {
+				switch kfd.Kind() {
+				case protoreflect.BoolKind:
+					return !entries[i].key.Bool() && entries[j].key.Bool()
+				case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
+					return entries[i].key.Int() < entries[j].key.Int()
+				case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
+					return entries[i].key.Uint() < entries[j].key.Uint()
+				case protoreflect.StringKind:
+					return entries[i].key.String() < entries[j].key.String()
+				default:
+					panic("invalid kind")
+				}
+			})
+			for _, entry := range entries {
+				w.writeName(fd)
+				w.WriteByte('<')
+				if !w.compact {
+					w.WriteByte('\n')
+				}
+				w.indent++
+				w.writeName(kfd)
+				if err := w.writeSingularValue(entry.key, kfd); err != nil {
+					return err
+				}
+				w.WriteByte('\n')
+				w.writeName(vfd)
+				if err := w.writeSingularValue(entry.val, vfd); err != nil {
+					return err
+				}
+				w.WriteByte('\n')
+				w.indent--
+				w.WriteByte('>')
+				w.WriteByte('\n')
+			}
+		default:
+			w.writeName(fd)
+			if err := w.writeSingularValue(m.Get(fd), fd); err != nil {
+				return err
+			}
+			w.WriteByte('\n')
+		}
+	}
+
+	if b := m.GetUnknown(); len(b) > 0 {
+		w.writeUnknownFields(b)
+	}
+	return w.writeExtensions(m)
+}
+
+func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
+	switch fd.Kind() {
+	case protoreflect.FloatKind, protoreflect.DoubleKind:
+		switch vf := v.Float(); {
+		case math.IsInf(vf, +1):
+			w.Write(posInf)
+		case math.IsInf(vf, -1):
+			w.Write(negInf)
+		case math.IsNaN(vf):
+			w.Write(nan)
+		default:
+			fmt.Fprint(w, v.Interface())
+		}
+	case protoreflect.StringKind:
+		// NOTE: This does not validate UTF-8 for historical reasons.
+		w.writeQuotedString(string(v.String()))
+	case protoreflect.BytesKind:
+		w.writeQuotedString(string(v.Bytes()))
+	case protoreflect.MessageKind, protoreflect.GroupKind:
+		var bra, ket byte = '<', '>'
+		if fd.Kind() == protoreflect.GroupKind {
+			bra, ket = '{', '}'
+		}
+		w.WriteByte(bra)
+		if !w.compact {
+			w.WriteByte('\n')
+		}
+		w.indent++
+		m := v.Message()
+		if m2, ok := m.Interface().(encoding.TextMarshaler); ok {
+			b, err := m2.MarshalText()
+			if err != nil {
+				return err
+			}
+			w.Write(b)
+		} else {
+			w.writeMessage(m)
+		}
+		w.indent--
+		w.WriteByte(ket)
+	case protoreflect.EnumKind:
+		if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil {
+			fmt.Fprint(w, ev.Name())
+		} else {
+			fmt.Fprint(w, v.Enum())
+		}
+	default:
+		fmt.Fprint(w, v.Interface())
+	}
+	return nil
+}
+
+// writeQuotedString writes a quoted string in the protocol buffer text format.
+func (w *textWriter) writeQuotedString(s string) {
+	w.WriteByte('"')
+	for i := 0; i < len(s); i++ {
+		switch c := s[i]; c {
+		case '\n':
+			w.buf = append(w.buf, `\n`...)
+		case '\r':
+			w.buf = append(w.buf, `\r`...)
+		case '\t':
+			w.buf = append(w.buf, `\t`...)
+		case '"':
+			w.buf = append(w.buf, `\"`...)
+		case '\\':
+			w.buf = append(w.buf, `\\`...)
+		default:
+			if isPrint := c >= 0x20 && c < 0x7f; isPrint {
+				w.buf = append(w.buf, c)
+			} else {
+				w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...)
+			}
+		}
+	}
+	w.WriteByte('"')
+}
+
+func (w *textWriter) writeUnknownFields(b []byte) {
+	if !w.compact {
+		fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b))
+	}
+
+	for len(b) > 0 {
+		num, wtyp, n := protowire.ConsumeTag(b)
+		if n < 0 {
+			return
+		}
+		b = b[n:]
+
+		if wtyp == protowire.EndGroupType {
+			w.indent--
+			w.Write(endBraceNewline)
+			continue
+		}
+		fmt.Fprint(w, num)
+		if wtyp != protowire.StartGroupType {
+			w.WriteByte(':')
+		}
+		if !w.compact || wtyp == protowire.StartGroupType {
+			w.WriteByte(' ')
+		}
+		switch wtyp {
+		case protowire.VarintType:
+			v, n := protowire.ConsumeVarint(b)
+			if n < 0 {
+				return
+			}
+			b = b[n:]
+			fmt.Fprint(w, v)
+		case protowire.Fixed32Type:
+			v, n := protowire.ConsumeFixed32(b)
+			if n < 0 {
+				return
+			}
+			b = b[n:]
+			fmt.Fprint(w, v)
+		case protowire.Fixed64Type:
+			v, n := protowire.ConsumeFixed64(b)
+			if n < 0 {
+				return
+			}
+			b = b[n:]
+			fmt.Fprint(w, v)
+		case protowire.BytesType:
+			v, n := protowire.ConsumeBytes(b)
+			if n < 0 {
+				return
+			}
+			b = b[n:]
+			fmt.Fprintf(w, "%q", v)
+		case protowire.StartGroupType:
+			w.WriteByte('{')
+			w.indent++
+		default:
+			fmt.Fprintf(w, "/* unknown wire type %d */", wtyp)
+		}
+		w.WriteByte('\n')
+	}
+}
+
+// writeExtensions writes all the extensions in m.
+func (w *textWriter) writeExtensions(m protoreflect.Message) error {
+	md := m.Descriptor()
+	if md.ExtensionRanges().Len() == 0 {
+		return nil
+	}
+
+	type ext struct {
+		desc protoreflect.FieldDescriptor
+		val  protoreflect.Value
+	}
+	var exts []ext
+	m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
+		if fd.IsExtension() {
+			exts = append(exts, ext{fd, v})
+		}
+		return true
+	})
+	sort.Slice(exts, func(i, j int) bool {
+		return exts[i].desc.Number() < exts[j].desc.Number()
+	})
+
+	for _, ext := range exts {
+		// For message set, use the name of the message as the extension name.
+		name := string(ext.desc.FullName())
+		if isMessageSet(ext.desc.ContainingMessage()) {
+			name = strings.TrimSuffix(name, ".message_set_extension")
+		}
+
+		if !ext.desc.IsList() {
+			if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil {
+				return err
+			}
+		} else {
+			lv := ext.val.List()
+			for i := 0; i < lv.Len(); i++ {
+				if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
+	fmt.Fprintf(w, "[%s]:", name)
+	if !w.compact {
+		w.WriteByte(' ')
+	}
+	if err := w.writeSingularValue(v, fd); err != nil {
+		return err
+	}
+	w.WriteByte('\n')
+	return nil
+}
+
+func (w *textWriter) writeIndent() {
+	if !w.complete {
+		return
+	}
+	for i := 0; i < w.indent*2; i++ {
+		w.buf = append(w.buf, ' ')
+	}
+	w.complete = false
+}

+ 78 - 0
vendor/github.com/golang/protobuf/proto/wire.go

@@ -0,0 +1,78 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+import (
+	protoV2 "google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/runtime/protoiface"
+)
+
+// Size returns the size in bytes of the wire-format encoding of m.
+func Size(m Message) int {
+	if m == nil {
+		return 0
+	}
+	mi := MessageV2(m)
+	return protoV2.Size(mi)
+}
+
+// Marshal returns the wire-format encoding of m.
+func Marshal(m Message) ([]byte, error) {
+	b, err := marshalAppend(nil, m, false)
+	if b == nil {
+		b = zeroBytes
+	}
+	return b, err
+}
+
+var zeroBytes = make([]byte, 0, 0)
+
+func marshalAppend(buf []byte, m Message, deterministic bool) ([]byte, error) {
+	if m == nil {
+		return nil, ErrNil
+	}
+	mi := MessageV2(m)
+	nbuf, err := protoV2.MarshalOptions{
+		Deterministic: deterministic,
+		AllowPartial:  true,
+	}.MarshalAppend(buf, mi)
+	if err != nil {
+		return buf, err
+	}
+	if len(buf) == len(nbuf) {
+		if !mi.ProtoReflect().IsValid() {
+			return buf, ErrNil
+		}
+	}
+	return nbuf, checkRequiredNotSet(mi)
+}
+
+// Unmarshal parses a wire-format message in b and places the decoded results in m.
+//
+// Unmarshal resets m before starting to unmarshal, so any existing data in m is always
+// removed. Use UnmarshalMerge to preserve and append to existing data.
+func Unmarshal(b []byte, m Message) error {
+	m.Reset()
+	return UnmarshalMerge(b, m)
+}
+
+// UnmarshalMerge parses a wire-format message in b and places the decoded results in m.
+func UnmarshalMerge(b []byte, m Message) error {
+	mi := MessageV2(m)
+	out, err := protoV2.UnmarshalOptions{
+		AllowPartial: true,
+		Merge:        true,
+	}.UnmarshalState(protoiface.UnmarshalInput{
+		Buf:     b,
+		Message: mi.ProtoReflect(),
+	})
+	if err != nil {
+		return err
+	}
+	if out.Flags&protoiface.UnmarshalInitialized > 0 {
+		return nil
+	}
+	return checkRequiredNotSet(mi)
+}

+ 34 - 0
vendor/github.com/golang/protobuf/proto/wrappers.go

@@ -0,0 +1,34 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package proto
+
+// Bool stores v in a new bool value and returns a pointer to it.
+func Bool(v bool) *bool { return &v }
+
+// Int stores v in a new int32 value and returns a pointer to it.
+//
+// Deprecated: Use Int32 instead.
+func Int(v int) *int32 { return Int32(int32(v)) }
+
+// Int32 stores v in a new int32 value and returns a pointer to it.
+func Int32(v int32) *int32 { return &v }
+
+// Int64 stores v in a new int64 value and returns a pointer to it.
+func Int64(v int64) *int64 { return &v }
+
+// Uint32 stores v in a new uint32 value and returns a pointer to it.
+func Uint32(v uint32) *uint32 { return &v }
+
+// Uint64 stores v in a new uint64 value and returns a pointer to it.
+func Uint64(v uint64) *uint64 { return &v }
+
+// Float32 stores v in a new float32 value and returns a pointer to it.
+func Float32(v float32) *float32 { return &v }
+
+// Float64 stores v in a new float64 value and returns a pointer to it.
+func Float64(v float64) *float64 { return &v }
+
+// String stores v in a new string value and returns a pointer to it.
+func String(v string) *string { return &v }

+ 165 - 0
vendor/github.com/golang/protobuf/ptypes/any.go

@@ -0,0 +1,165 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ptypes
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
+
+	anypb "github.com/golang/protobuf/ptypes/any"
+)
+
+const urlPrefix = "type.googleapis.com/"
+
+// AnyMessageName returns the message name contained in an anypb.Any message.
+// Most type assertions should use the Is function instead.
+func AnyMessageName(any *anypb.Any) (string, error) {
+	name, err := anyMessageName(any)
+	return string(name), err
+}
+func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) {
+	if any == nil {
+		return "", fmt.Errorf("message is nil")
+	}
+	name := protoreflect.FullName(any.TypeUrl)
+	if i := strings.LastIndex(any.TypeUrl, "/"); i >= 0 {
+		name = name[i+len("/"):]
+	}
+	if !name.IsValid() {
+		return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl)
+	}
+	return name, nil
+}
+
+// MarshalAny marshals the given message m into an anypb.Any message.
+func MarshalAny(m proto.Message) (*anypb.Any, error) {
+	switch dm := m.(type) {
+	case DynamicAny:
+		m = dm.Message
+	case *DynamicAny:
+		if dm == nil {
+			return nil, proto.ErrNil
+		}
+		m = dm.Message
+	}
+	b, err := proto.Marshal(m)
+	if err != nil {
+		return nil, err
+	}
+	return &anypb.Any{TypeUrl: urlPrefix + proto.MessageName(m), Value: b}, nil
+}
+
+// Empty returns a new message of the type specified in an anypb.Any message.
+// It returns protoregistry.NotFound if the corresponding message type could not
+// be resolved in the global registry.
+func Empty(any *anypb.Any) (proto.Message, error) {
+	name, err := anyMessageName(any)
+	if err != nil {
+		return nil, err
+	}
+	mt, err := protoregistry.GlobalTypes.FindMessageByName(name)
+	if err != nil {
+		return nil, err
+	}
+	return proto.MessageV1(mt.New().Interface()), nil
+}
+
+// UnmarshalAny unmarshals the encoded value contained in the anypb.Any message
+// into the provided message m. It returns an error if the target message
+// does not match the type in the Any message or if an unmarshal error occurs.
+//
+// The target message m may be a *DynamicAny message. If the underlying message
+// type could not be resolved, then this returns protoregistry.NotFound.
+func UnmarshalAny(any *anypb.Any, m proto.Message) error {
+	if dm, ok := m.(*DynamicAny); ok {
+		if dm.Message == nil {
+			var err error
+			dm.Message, err = Empty(any)
+			if err != nil {
+				return err
+			}
+		}
+		m = dm.Message
+	}
+
+	anyName, err := AnyMessageName(any)
+	if err != nil {
+		return err
+	}
+	msgName := proto.MessageName(m)
+	if anyName != msgName {
+		return fmt.Errorf("mismatched message type: got %q want %q", anyName, msgName)
+	}
+	return proto.Unmarshal(any.Value, m)
+}
+
+// Is reports whether the Any message contains a message of the specified type.
+func Is(any *anypb.Any, m proto.Message) bool {
+	if any == nil || m == nil {
+		return false
+	}
+	name := proto.MessageName(m)
+	if !strings.HasSuffix(any.TypeUrl, name) {
+		return false
+	}
+	return len(any.TypeUrl) == len(name) || any.TypeUrl[len(any.TypeUrl)-len(name)-1] == '/'
+}
+
+// DynamicAny is a value that can be passed to UnmarshalAny to automatically
+// allocate a proto.Message for the type specified in an anypb.Any message.
+// The allocated message is stored in the embedded proto.Message.
+//
+// Example:
+//   var x ptypes.DynamicAny
+//   if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
+//   fmt.Printf("unmarshaled message: %v", x.Message)
+type DynamicAny struct{ proto.Message }
+
+func (m DynamicAny) String() string {
+	if m.Message == nil {
+		return "<nil>"
+	}
+	return m.Message.String()
+}
+func (m DynamicAny) Reset() {
+	if m.Message == nil {
+		return
+	}
+	m.Message.Reset()
+}
+func (m DynamicAny) ProtoMessage() {
+	return
+}
+func (m DynamicAny) ProtoReflect() protoreflect.Message {
+	if m.Message == nil {
+		return nil
+	}
+	return dynamicAny{proto.MessageReflect(m.Message)}
+}
+
+type dynamicAny struct{ protoreflect.Message }
+
+func (m dynamicAny) Type() protoreflect.MessageType {
+	return dynamicAnyType{m.Message.Type()}
+}
+func (m dynamicAny) New() protoreflect.Message {
+	return dynamicAnyType{m.Message.Type()}.New()
+}
+func (m dynamicAny) Interface() protoreflect.ProtoMessage {
+	return DynamicAny{proto.MessageV1(m.Message.Interface())}
+}
+
+type dynamicAnyType struct{ protoreflect.MessageType }
+
+func (t dynamicAnyType) New() protoreflect.Message {
+	return dynamicAny{t.MessageType.New()}
+}
+func (t dynamicAnyType) Zero() protoreflect.Message {
+	return dynamicAny{t.MessageType.Zero()}
+}

+ 62 - 0
vendor/github.com/golang/protobuf/ptypes/any/any.pb.go

@@ -0,0 +1,62 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: github.com/golang/protobuf/ptypes/any/any.proto
+
+package any
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	anypb "google.golang.org/protobuf/types/known/anypb"
+	reflect "reflect"
+)
+
+// Symbols defined in public import of google/protobuf/any.proto.
+
+type Any = anypb.Any
+
+var File_github_com_golang_protobuf_ptypes_any_any_proto protoreflect.FileDescriptor
+
+var file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc = []byte{
+	0x0a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
+	0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
+	0x70, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x2b, 0x5a, 0x29,
+	0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e,
+	0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65,
+	0x73, 0x2f, 0x61, 0x6e, 0x79, 0x3b, 0x61, 0x6e, 0x79, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x33,
+}
+
+var file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes = []interface{}{}
+var file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_github_com_golang_protobuf_ptypes_any_any_proto_init() }
+func file_github_com_golang_protobuf_ptypes_any_any_proto_init() {
+	if File_github_com_golang_protobuf_ptypes_any_any_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes,
+		DependencyIndexes: file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs,
+	}.Build()
+	File_github_com_golang_protobuf_ptypes_any_any_proto = out.File
+	file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc = nil
+	file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes = nil
+	file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs = nil
+}

+ 6 - 0
vendor/github.com/golang/protobuf/ptypes/doc.go

@@ -0,0 +1,6 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package ptypes provides functionality for interacting with well-known types.
+package ptypes

+ 72 - 0
vendor/github.com/golang/protobuf/ptypes/duration.go

@@ -0,0 +1,72 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ptypes
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	durationpb "github.com/golang/protobuf/ptypes/duration"
+)
+
+// Range of google.protobuf.Duration as specified in duration.proto.
+// This is about 10,000 years in seconds.
+const (
+	maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60)
+	minSeconds = -maxSeconds
+)
+
+// Duration converts a durationpb.Duration to a time.Duration.
+// Duration returns an error if dur is invalid or overflows a time.Duration.
+func Duration(dur *durationpb.Duration) (time.Duration, error) {
+	if err := validateDuration(dur); err != nil {
+		return 0, err
+	}
+	d := time.Duration(dur.Seconds) * time.Second
+	if int64(d/time.Second) != dur.Seconds {
+		return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
+	}
+	if dur.Nanos != 0 {
+		d += time.Duration(dur.Nanos) * time.Nanosecond
+		if (d < 0) != (dur.Nanos < 0) {
+			return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
+		}
+	}
+	return d, nil
+}
+
+// DurationProto converts a time.Duration to a durationpb.Duration.
+func DurationProto(d time.Duration) *durationpb.Duration {
+	nanos := d.Nanoseconds()
+	secs := nanos / 1e9
+	nanos -= secs * 1e9
+	return &durationpb.Duration{
+		Seconds: int64(secs),
+		Nanos:   int32(nanos),
+	}
+}
+
+// validateDuration determines whether the durationpb.Duration is valid
+// according to the definition in google/protobuf/duration.proto.
+// A valid durpb.Duration may still be too large to fit into a time.Duration
+// Note that the range of durationpb.Duration is about 10,000 years,
+// while the range of time.Duration is about 290 years.
+func validateDuration(dur *durationpb.Duration) error {
+	if dur == nil {
+		return errors.New("duration: nil Duration")
+	}
+	if dur.Seconds < minSeconds || dur.Seconds > maxSeconds {
+		return fmt.Errorf("duration: %v: seconds out of range", dur)
+	}
+	if dur.Nanos <= -1e9 || dur.Nanos >= 1e9 {
+		return fmt.Errorf("duration: %v: nanos out of range", dur)
+	}
+	// Seconds and Nanos must have the same sign, unless d.Nanos is zero.
+	if (dur.Seconds < 0 && dur.Nanos > 0) || (dur.Seconds > 0 && dur.Nanos < 0) {
+		return fmt.Errorf("duration: %v: seconds and nanos have different signs", dur)
+	}
+	return nil
+}

+ 63 - 0
vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go

@@ -0,0 +1,63 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: github.com/golang/protobuf/ptypes/duration/duration.proto
+
+package duration
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	durationpb "google.golang.org/protobuf/types/known/durationpb"
+	reflect "reflect"
+)
+
+// Symbols defined in public import of google/protobuf/duration.proto.
+
+type Duration = durationpb.Duration
+
+var File_github_com_golang_protobuf_ptypes_duration_duration_proto protoreflect.FileDescriptor
+
+var file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = []byte{
+	0x0a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
+	0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
+	0x70, 0x65, 0x73, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x64, 0x75, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x35, 0x5a, 0x33, 0x67,
+	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67,
+	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73,
+	0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = []interface{}{}
+var file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() }
+func file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() {
+	if File_github_com_golang_protobuf_ptypes_duration_duration_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes,
+		DependencyIndexes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs,
+	}.Build()
+	File_github_com_golang_protobuf_ptypes_duration_duration_proto = out.File
+	file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = nil
+	file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = nil
+	file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = nil
+}

+ 103 - 0
vendor/github.com/golang/protobuf/ptypes/timestamp.go

@@ -0,0 +1,103 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ptypes
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	timestamppb "github.com/golang/protobuf/ptypes/timestamp"
+)
+
+// Range of google.protobuf.Duration as specified in timestamp.proto.
+const (
+	// Seconds field of the earliest valid Timestamp.
+	// This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
+	minValidSeconds = -62135596800
+	// Seconds field just after the latest valid Timestamp.
+	// This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
+	maxValidSeconds = 253402300800
+)
+
+// Timestamp converts a timestamppb.Timestamp to a time.Time.
+// It returns an error if the argument is invalid.
+//
+// Unlike most Go functions, if Timestamp returns an error, the first return
+// value is not the zero time.Time. Instead, it is the value obtained from the
+// time.Unix function when passed the contents of the Timestamp, in the UTC
+// locale. This may or may not be a meaningful time; many invalid Timestamps
+// do map to valid time.Times.
+//
+// A nil Timestamp returns an error. The first return value in that case is
+// undefined.
+func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
+	// Don't return the zero value on error, because corresponds to a valid
+	// timestamp. Instead return whatever time.Unix gives us.
+	var t time.Time
+	if ts == nil {
+		t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
+	} else {
+		t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
+	}
+	return t, validateTimestamp(ts)
+}
+
+// TimestampNow returns a google.protobuf.Timestamp for the current time.
+func TimestampNow() *timestamppb.Timestamp {
+	ts, err := TimestampProto(time.Now())
+	if err != nil {
+		panic("ptypes: time.Now() out of Timestamp range")
+	}
+	return ts
+}
+
+// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
+// It returns an error if the resulting Timestamp is invalid.
+func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
+	ts := &timestamppb.Timestamp{
+		Seconds: t.Unix(),
+		Nanos:   int32(t.Nanosecond()),
+	}
+	if err := validateTimestamp(ts); err != nil {
+		return nil, err
+	}
+	return ts, nil
+}
+
+// TimestampString returns the RFC 3339 string for valid Timestamps.
+// For invalid Timestamps, it returns an error message in parentheses.
+func TimestampString(ts *timestamppb.Timestamp) string {
+	t, err := Timestamp(ts)
+	if err != nil {
+		return fmt.Sprintf("(%v)", err)
+	}
+	return t.Format(time.RFC3339Nano)
+}
+
+// validateTimestamp determines whether a Timestamp is valid.
+// A valid timestamp represents a time in the range [0001-01-01, 10000-01-01)
+// and has a Nanos field in the range [0, 1e9).
+//
+// If the Timestamp is valid, validateTimestamp returns nil.
+// Otherwise, it returns an error that describes the problem.
+//
+// Every valid Timestamp can be represented by a time.Time,
+// but the converse is not true.
+func validateTimestamp(ts *timestamppb.Timestamp) error {
+	if ts == nil {
+		return errors.New("timestamp: nil Timestamp")
+	}
+	if ts.Seconds < minValidSeconds {
+		return fmt.Errorf("timestamp: %v before 0001-01-01", ts)
+	}
+	if ts.Seconds >= maxValidSeconds {
+		return fmt.Errorf("timestamp: %v after 10000-01-01", ts)
+	}
+	if ts.Nanos < 0 || ts.Nanos >= 1e9 {
+		return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts)
+	}
+	return nil
+}

+ 64 - 0
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go

@@ -0,0 +1,64 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
+
+package timestamp
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
+	reflect "reflect"
+)
+
+// Symbols defined in public import of google/protobuf/timestamp.proto.
+
+type Timestamp = timestamppb.Timestamp
+
+var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor
+
+var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{
+	0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
+	0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
+	0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
+	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37,
+	0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
+	0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
+	0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x33,
+}
+
+var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{}
+var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() }
+func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() {
+	if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes,
+		DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs,
+	}.Build()
+	File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File
+	file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil
+	file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil
+	file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil
+}

+ 201 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 1 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/NOTICE

@@ -0,0 +1 @@
+Copyright 2012 Matt T. Proud (matt.proud@gmail.com)

+ 1 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore

@@ -0,0 +1 @@
+cover.dat

+ 7 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/Makefile

@@ -0,0 +1,7 @@
+all:
+
+cover:
+	go test -cover -v -coverprofile=cover.dat ./...
+	go tool cover -func cover.dat
+
+.PHONY: cover

+ 75 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go

@@ -0,0 +1,75 @@
+// Copyright 2013 Matt T. Proud
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package pbutil
+
+import (
+	"encoding/binary"
+	"errors"
+	"io"
+
+	"github.com/golang/protobuf/proto"
+)
+
+var errInvalidVarint = errors.New("invalid varint32 encountered")
+
+// ReadDelimited decodes a message from the provided length-delimited stream,
+// where the length is encoded as 32-bit varint prefix to the message body.
+// It returns the total number of bytes read and any applicable error.  This is
+// roughly equivalent to the companion Java API's
+// MessageLite#parseDelimitedFrom.  As per the reader contract, this function
+// calls r.Read repeatedly as required until exactly one message including its
+// prefix is read and decoded (or an error has occurred).  The function never
+// reads more bytes from the stream than required.  The function never returns
+// an error if a message has been read and decoded correctly, even if the end
+// of the stream has been reached in doing so.  In that case, any subsequent
+// calls return (0, io.EOF).
+func ReadDelimited(r io.Reader, m proto.Message) (n int, err error) {
+	// Per AbstractParser#parsePartialDelimitedFrom with
+	// CodedInputStream#readRawVarint32.
+	var headerBuf [binary.MaxVarintLen32]byte
+	var bytesRead, varIntBytes int
+	var messageLength uint64
+	for varIntBytes == 0 { // i.e. no varint has been decoded yet.
+		if bytesRead >= len(headerBuf) {
+			return bytesRead, errInvalidVarint
+		}
+		// We have to read byte by byte here to avoid reading more bytes
+		// than required. Each read byte is appended to what we have
+		// read before.
+		newBytesRead, err := r.Read(headerBuf[bytesRead : bytesRead+1])
+		if newBytesRead == 0 {
+			if err != nil {
+				return bytesRead, err
+			}
+			// A Reader should not return (0, nil), but if it does,
+			// it should be treated as no-op (according to the
+			// Reader contract). So let's go on...
+			continue
+		}
+		bytesRead += newBytesRead
+		// Now present everything read so far to the varint decoder and
+		// see if a varint can be decoded already.
+		messageLength, varIntBytes = proto.DecodeVarint(headerBuf[:bytesRead])
+	}
+
+	messageBuf := make([]byte, messageLength)
+	newBytesRead, err := io.ReadFull(r, messageBuf)
+	bytesRead += newBytesRead
+	if err != nil {
+		return bytesRead, err
+	}
+
+	return bytesRead, proto.Unmarshal(messageBuf, m)
+}

+ 16 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go

@@ -0,0 +1,16 @@
+// Copyright 2013 Matt T. Proud
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package pbutil provides record length-delimited Protocol Buffer streaming.
+package pbutil

+ 46 - 0
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go

@@ -0,0 +1,46 @@
+// Copyright 2013 Matt T. Proud
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package pbutil
+
+import (
+	"encoding/binary"
+	"io"
+
+	"github.com/golang/protobuf/proto"
+)
+
+// WriteDelimited encodes and dumps a message to the provided writer prefixed
+// with a 32-bit varint indicating the length of the encoded message, producing
+// a length-delimited record stream, which can be used to chain together
+// encoded messages of the same type together in a file.  It returns the total
+// number of bytes written and any applicable error.  This is roughly
+// equivalent to the companion Java API's MessageLite#writeDelimitedTo.
+func WriteDelimited(w io.Writer, m proto.Message) (n int, err error) {
+	buffer, err := proto.Marshal(m)
+	if err != nil {
+		return 0, err
+	}
+
+	var buf [binary.MaxVarintLen32]byte
+	encodedLength := binary.PutUvarint(buf[:], uint64(len(buffer)))
+
+	sync, err := w.Write(buf[:encodedLength])
+	if err != nil {
+		return sync, err
+	}
+
+	n, err = w.Write(buffer)
+	return n + sync, err
+}

+ 201 - 0
vendor/github.com/prometheus/client_golang/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 23 - 0
vendor/github.com/prometheus/client_golang/NOTICE

@@ -0,0 +1,23 @@
+Prometheus instrumentation library for Go applications
+Copyright 2012-2015 The Prometheus Authors
+
+This product includes software developed at
+SoundCloud Ltd. (http://soundcloud.com/).
+
+
+The following components are included in this product:
+
+perks - a fork of https://github.com/bmizerany/perks
+https://github.com/beorn7/perks
+Copyright 2013-2015 Blake Mizerany, Björn Rabenstein
+See https://github.com/beorn7/perks/blob/master/README.md for license details.
+
+Go support for Protocol Buffers - Google's data interchange format
+http://github.com/golang/protobuf/
+Copyright 2010 The Go Authors
+See source code for license details.
+
+Support for streaming Protocol Buffer messages for the Go language (golang).
+https://github.com/matttproud/golang_protobuf_extensions
+Copyright 2013 Matt T. Proud
+Licensed under the Apache License, Version 2.0

+ 1 - 0
vendor/github.com/prometheus/client_golang/prometheus/.gitignore

@@ -0,0 +1 @@
+command-line-arguments.test

+ 1 - 0
vendor/github.com/prometheus/client_golang/prometheus/README.md

@@ -0,0 +1 @@
+See [![go-doc](https://godoc.org/github.com/prometheus/client_golang/prometheus?status.svg)](https://godoc.org/github.com/prometheus/client_golang/prometheus).

+ 120 - 0
vendor/github.com/prometheus/client_golang/prometheus/collector.go

@@ -0,0 +1,120 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+// Collector is the interface implemented by anything that can be used by
+// Prometheus to collect metrics. A Collector has to be registered for
+// collection. See Registerer.Register.
+//
+// The stock metrics provided by this package (Gauge, Counter, Summary,
+// Histogram, Untyped) are also Collectors (which only ever collect one metric,
+// namely itself). An implementer of Collector may, however, collect multiple
+// metrics in a coordinated fashion and/or create metrics on the fly. Examples
+// for collectors already implemented in this library are the metric vectors
+// (i.e. collection of multiple instances of the same Metric but with different
+// label values) like GaugeVec or SummaryVec, and the ExpvarCollector.
+type Collector interface {
+	// Describe sends the super-set of all possible descriptors of metrics
+	// collected by this Collector to the provided channel and returns once
+	// the last descriptor has been sent. The sent descriptors fulfill the
+	// consistency and uniqueness requirements described in the Desc
+	// documentation.
+	//
+	// It is valid if one and the same Collector sends duplicate
+	// descriptors. Those duplicates are simply ignored. However, two
+	// different Collectors must not send duplicate descriptors.
+	//
+	// Sending no descriptor at all marks the Collector as “unchecked”,
+	// i.e. no checks will be performed at registration time, and the
+	// Collector may yield any Metric it sees fit in its Collect method.
+	//
+	// This method idempotently sends the same descriptors throughout the
+	// lifetime of the Collector. It may be called concurrently and
+	// therefore must be implemented in a concurrency safe way.
+	//
+	// If a Collector encounters an error while executing this method, it
+	// must send an invalid descriptor (created with NewInvalidDesc) to
+	// signal the error to the registry.
+	Describe(chan<- *Desc)
+	// Collect is called by the Prometheus registry when collecting
+	// metrics. The implementation sends each collected metric via the
+	// provided channel and returns once the last metric has been sent. The
+	// descriptor of each sent metric is one of those returned by Describe
+	// (unless the Collector is unchecked, see above). Returned metrics that
+	// share the same descriptor must differ in their variable label
+	// values.
+	//
+	// This method may be called concurrently and must therefore be
+	// implemented in a concurrency safe way. Blocking occurs at the expense
+	// of total performance of rendering all registered metrics. Ideally,
+	// Collector implementations support concurrent readers.
+	Collect(chan<- Metric)
+}
+
+// DescribeByCollect is a helper to implement the Describe method of a custom
+// Collector. It collects the metrics from the provided Collector and sends
+// their descriptors to the provided channel.
+//
+// If a Collector collects the same metrics throughout its lifetime, its
+// Describe method can simply be implemented as:
+//
+//   func (c customCollector) Describe(ch chan<- *Desc) {
+//   	DescribeByCollect(c, ch)
+//   }
+//
+// However, this will not work if the metrics collected change dynamically over
+// the lifetime of the Collector in a way that their combined set of descriptors
+// changes as well. The shortcut implementation will then violate the contract
+// of the Describe method. If a Collector sometimes collects no metrics at all
+// (for example vectors like CounterVec, GaugeVec, etc., which only collect
+// metrics after a metric with a fully specified label set has been accessed),
+// it might even get registered as an unchecked Collector (cf. the Register
+// method of the Registerer interface). Hence, only use this shortcut
+// implementation of Describe if you are certain to fulfill the contract.
+//
+// The Collector example demonstrates a use of DescribeByCollect.
+func DescribeByCollect(c Collector, descs chan<- *Desc) {
+	metrics := make(chan Metric)
+	go func() {
+		c.Collect(metrics)
+		close(metrics)
+	}()
+	for m := range metrics {
+		descs <- m.Desc()
+	}
+}
+
+// selfCollector implements Collector for a single Metric so that the Metric
+// collects itself. Add it as an anonymous field to a struct that implements
+// Metric, and call init with the Metric itself as an argument.
+type selfCollector struct {
+	self Metric
+}
+
+// init provides the selfCollector with a reference to the metric it is supposed
+// to collect. It is usually called within the factory function to create a
+// metric. See example.
+func (c *selfCollector) init(self Metric) {
+	c.self = self
+}
+
+// Describe implements Collector.
+func (c *selfCollector) Describe(ch chan<- *Desc) {
+	ch <- c.self.Desc()
+}
+
+// Collect implements Collector.
+func (c *selfCollector) Collect(ch chan<- Metric) {
+	ch <- c.self
+}

+ 321 - 0
vendor/github.com/prometheus/client_golang/prometheus/counter.go

@@ -0,0 +1,321 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+	"errors"
+	"math"
+	"sync/atomic"
+	"time"
+
+	dto "github.com/prometheus/client_model/go"
+)
+
+// Counter is a Metric that represents a single numerical value that only ever
+// goes up. That implies that it cannot be used to count items whose number can
+// also go down, e.g. the number of currently running goroutines. Those
+// "counters" are represented by Gauges.
+//
+// A Counter is typically used to count requests served, tasks completed, errors
+// occurred, etc.
+//
+// To create Counter instances, use NewCounter.
+type Counter interface {
+	Metric
+	Collector
+
+	// Inc increments the counter by 1. Use Add to increment it by arbitrary
+	// non-negative values.
+	Inc()
+	// Add adds the given value to the counter. It panics if the value is <
+	// 0.
+	Add(float64)
+}
+
+// ExemplarAdder is implemented by Counters that offer the option of adding a
+// value to the Counter together with an exemplar. Its AddWithExemplar method
+// works like the Add method of the Counter interface but also replaces the
+// currently saved exemplar (if any) with a new one, created from the provided
+// value, the current time as timestamp, and the provided labels. Empty Labels
+// will lead to a valid (label-less) exemplar. But if Labels is nil, the current
+// exemplar is left in place. AddWithExemplar panics if the value is < 0, if any
+// of the provided labels are invalid, or if the provided labels contain more
+// than 64 runes in total.
+type ExemplarAdder interface {
+	AddWithExemplar(value float64, exemplar Labels)
+}
+
+// CounterOpts is an alias for Opts. See there for doc comments.
+type CounterOpts Opts
+
+// NewCounter creates a new Counter based on the provided CounterOpts.
+//
+// The returned implementation also implements ExemplarAdder. It is safe to
+// perform the corresponding type assertion.
+//
+// The returned implementation tracks the counter value in two separate
+// variables, a float64 and a uint64. The latter is used to track calls of the
+// Inc method and calls of the Add method with a value that can be represented
+// as a uint64. This allows atomic increments of the counter with optimal
+// performance. (It is common to have an Inc call in very hot execution paths.)
+// Both internal tracking values are added up in the Write method. This has to
+// be taken into account when it comes to precision and overflow behavior.
+func NewCounter(opts CounterOpts) Counter {
+	desc := NewDesc(
+		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		nil,
+		opts.ConstLabels,
+	)
+	result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
+	result.init(result) // Init self-collection.
+	return result
+}
+
+type counter struct {
+	// valBits contains the bits of the represented float64 value, while
+	// valInt stores values that are exact integers. Both have to go first
+	// in the struct to guarantee alignment for atomic operations.
+	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
+	valBits uint64
+	valInt  uint64
+
+	selfCollector
+	desc *Desc
+
+	labelPairs []*dto.LabelPair
+	exemplar   atomic.Value // Containing nil or a *dto.Exemplar.
+
+	now func() time.Time // To mock out time.Now() for testing.
+}
+
+func (c *counter) Desc() *Desc {
+	return c.desc
+}
+
+func (c *counter) Add(v float64) {
+	if v < 0 {
+		panic(errors.New("counter cannot decrease in value"))
+	}
+
+	ival := uint64(v)
+	if float64(ival) == v {
+		atomic.AddUint64(&c.valInt, ival)
+		return
+	}
+
+	for {
+		oldBits := atomic.LoadUint64(&c.valBits)
+		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
+		if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) {
+			return
+		}
+	}
+}
+
+func (c *counter) AddWithExemplar(v float64, e Labels) {
+	c.Add(v)
+	c.updateExemplar(v, e)
+}
+
+func (c *counter) Inc() {
+	atomic.AddUint64(&c.valInt, 1)
+}
+
+func (c *counter) Write(out *dto.Metric) error {
+	fval := math.Float64frombits(atomic.LoadUint64(&c.valBits))
+	ival := atomic.LoadUint64(&c.valInt)
+	val := fval + float64(ival)
+
+	var exemplar *dto.Exemplar
+	if e := c.exemplar.Load(); e != nil {
+		exemplar = e.(*dto.Exemplar)
+	}
+
+	return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
+}
+
+func (c *counter) updateExemplar(v float64, l Labels) {
+	if l == nil {
+		return
+	}
+	e, err := newExemplar(v, c.now(), l)
+	if err != nil {
+		panic(err)
+	}
+	c.exemplar.Store(e)
+}
+
+// CounterVec is a Collector that bundles a set of Counters that all share the
+// same Desc, but have different values for their variable labels. This is used
+// if you want to count the same thing partitioned by various dimensions
+// (e.g. number of HTTP requests, partitioned by response code and
+// method). Create instances with NewCounterVec.
+type CounterVec struct {
+	*MetricVec
+}
+
+// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
+// partitioned by the given label names.
+func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
+	desc := NewDesc(
+		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		labelNames,
+		opts.ConstLabels,
+	)
+	return &CounterVec{
+		MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
+			if len(lvs) != len(desc.variableLabels) {
+				panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
+			}
+			result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
+			result.init(result) // Init self-collection.
+			return result
+		}),
+	}
+}
+
+// GetMetricWithLabelValues returns the Counter for the given slice of label
+// values (same order as the variable labels in Desc). If that combination of
+// label values is accessed for the first time, a new Counter is created.
+//
+// It is possible to call this method without using the returned Counter to only
+// create the new Counter but leave it at its starting value 0. See also the
+// SummaryVec example.
+//
+// Keeping the Counter for later use is possible (and should be considered if
+// performance is critical), but keep in mind that Reset, DeleteLabelValues and
+// Delete can be used to delete the Counter from the CounterVec. In that case,
+// the Counter will still exist, but it will not be exported anymore, even if a
+// Counter with the same label values is created later.
+//
+// An error is returned if the number of label values is not the same as the
+// number of variable labels in Desc (minus any curried labels).
+//
+// Note that for more than one label value, this method is prone to mistakes
+// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
+// an alternative to avoid that type of mistake. For higher label numbers, the
+// latter has a much more readable (albeit more verbose) syntax, but it comes
+// with a performance overhead (for creating and processing the Labels map).
+// See also the GaugeVec example.
+func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
+	metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
+	if metric != nil {
+		return metric.(Counter), err
+	}
+	return nil, err
+}
+
+// GetMetricWith returns the Counter for the given Labels map (the label names
+// must match those of the variable labels in Desc). If that label map is
+// accessed for the first time, a new Counter is created. Implications of
+// creating a Counter without using it and keeping the Counter for later use are
+// the same as for GetMetricWithLabelValues.
+//
+// An error is returned if the number and names of the Labels are inconsistent
+// with those of the variable labels in Desc (minus any curried labels).
+//
+// This method is used for the same purpose as
+// GetMetricWithLabelValues(...string). See there for pros and cons of the two
+// methods.
+func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
+	metric, err := v.MetricVec.GetMetricWith(labels)
+	if metric != nil {
+		return metric.(Counter), err
+	}
+	return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. Not returning an
+// error allows shortcuts like
+//     myVec.WithLabelValues("404", "GET").Add(42)
+func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
+	c, err := v.GetMetricWithLabelValues(lvs...)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. Not returning an error allows shortcuts like
+//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
+func (v *CounterVec) With(labels Labels) Counter {
+	c, err := v.GetMetricWith(labels)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
+
+// CurryWith returns a vector curried with the provided labels, i.e. the
+// returned vector has those labels pre-set for all labeled operations performed
+// on it. The cardinality of the curried vector is reduced accordingly. The
+// order of the remaining labels stays the same (just with the curried labels
+// taken out of the sequence – which is relevant for the
+// (GetMetric)WithLabelValues methods). It is possible to curry a curried
+// vector, but only with labels not yet used for currying before.
+//
+// The metrics contained in the CounterVec are shared between the curried and
+// uncurried vectors. They are just accessed differently. Curried and uncurried
+// vectors behave identically in terms of collection. Only one must be
+// registered with a given registry (usually the uncurried version). The Reset
+// method deletes all metrics, even if called on a curried vector.
+func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
+	vec, err := v.MetricVec.CurryWith(labels)
+	if vec != nil {
+		return &CounterVec{vec}, err
+	}
+	return nil, err
+}
+
+// MustCurryWith works as CurryWith but panics where CurryWith would have
+// returned an error.
+func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec {
+	vec, err := v.CurryWith(labels)
+	if err != nil {
+		panic(err)
+	}
+	return vec
+}
+
+// CounterFunc is a Counter whose value is determined at collect time by calling a
+// provided function.
+//
+// To create CounterFunc instances, use NewCounterFunc.
+type CounterFunc interface {
+	Metric
+	Collector
+}
+
+// NewCounterFunc creates a new CounterFunc based on the provided
+// CounterOpts. The value reported is determined by calling the given function
+// from within the Write method. Take into account that metric collection may
+// happen concurrently. If that results in concurrent calls to Write, like in
+// the case where a CounterFunc is directly registered with Prometheus, the
+// provided function must be concurrency-safe. The function should also honor
+// the contract for a Counter (values only go up, not down), but compliance will
+// not be checked.
+//
+// Check out the ExampleGaugeFunc examples for the similar GaugeFunc.
+func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc {
+	return newValueFunc(NewDesc(
+		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		nil,
+		opts.ConstLabels,
+	), CounterValue, function)
+}

+ 186 - 0
vendor/github.com/prometheus/client_golang/prometheus/desc.go

@@ -0,0 +1,186 @@
+// Copyright 2016 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+	"errors"
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/cespare/xxhash/v2"
+	//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
+	"github.com/golang/protobuf/proto"
+	"github.com/prometheus/common/model"
+
+	dto "github.com/prometheus/client_model/go"
+)
+
+// Desc is the descriptor used by every Prometheus Metric. It is essentially
+// the immutable meta-data of a Metric. The normal Metric implementations
+// included in this package manage their Desc under the hood. Users only have to
+// deal with Desc if they use advanced features like the ExpvarCollector or
+// custom Collectors and Metrics.
+//
+// Descriptors registered with the same registry have to fulfill certain
+// consistency and uniqueness criteria if they share the same fully-qualified
+// name: They must have the same help string and the same label names (aka label
+// dimensions) in each, constLabels and variableLabels, but they must differ in
+// the values of the constLabels.
+//
+// Descriptors that share the same fully-qualified names and the same label
+// values of their constLabels are considered equal.
+//
+// Use NewDesc to create new Desc instances.
+type Desc struct {
+	// fqName has been built from Namespace, Subsystem, and Name.
+	fqName string
+	// help provides some helpful information about this metric.
+	help string
+	// constLabelPairs contains precalculated DTO label pairs based on
+	// the constant labels.
+	constLabelPairs []*dto.LabelPair
+	// variableLabels contains names of labels for which the metric
+	// maintains variable values.
+	variableLabels []string
+	// id is a hash of the values of the ConstLabels and fqName. This
+	// must be unique among all registered descriptors and can therefore be
+	// used as an identifier of the descriptor.
+	id uint64
+	// dimHash is a hash of the label names (preset and variable) and the
+	// Help string. Each Desc with the same fqName must have the same
+	// dimHash.
+	dimHash uint64
+	// err is an error that occurred during construction. It is reported on
+	// registration time.
+	err error
+}
+
+// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
+// and will be reported on registration time. variableLabels and constLabels can
+// be nil if no such labels should be set. fqName must not be empty.
+//
+// variableLabels only contain the label names. Their label values are variable
+// and therefore not part of the Desc. (They are managed within the Metric.)
+//
+// For constLabels, the label values are constant. Therefore, they are fully
+// specified in the Desc. See the Collector example for a usage pattern.
+func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
+	d := &Desc{
+		fqName:         fqName,
+		help:           help,
+		variableLabels: variableLabels,
+	}
+	if !model.IsValidMetricName(model.LabelValue(fqName)) {
+		d.err = fmt.Errorf("%q is not a valid metric name", fqName)
+		return d
+	}
+	// labelValues contains the label values of const labels (in order of
+	// their sorted label names) plus the fqName (at position 0).
+	labelValues := make([]string, 1, len(constLabels)+1)
+	labelValues[0] = fqName
+	labelNames := make([]string, 0, len(constLabels)+len(variableLabels))
+	labelNameSet := map[string]struct{}{}
+	// First add only the const label names and sort them...
+	for labelName := range constLabels {
+		if !checkLabelName(labelName) {
+			d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
+			return d
+		}
+		labelNames = append(labelNames, labelName)
+		labelNameSet[labelName] = struct{}{}
+	}
+	sort.Strings(labelNames)
+	// ... so that we can now add const label values in the order of their names.
+	for _, labelName := range labelNames {
+		labelValues = append(labelValues, constLabels[labelName])
+	}
+	// Validate the const label values. They can't have a wrong cardinality, so
+	// use in len(labelValues) as expectedNumberOfValues.
+	if err := validateLabelValues(labelValues, len(labelValues)); err != nil {
+		d.err = err
+		return d
+	}
+	// Now add the variable label names, but prefix them with something that
+	// cannot be in a regular label name. That prevents matching the label
+	// dimension with a different mix between preset and variable labels.
+	for _, labelName := range variableLabels {
+		if !checkLabelName(labelName) {
+			d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
+			return d
+		}
+		labelNames = append(labelNames, "$"+labelName)
+		labelNameSet[labelName] = struct{}{}
+	}
+	if len(labelNames) != len(labelNameSet) {
+		d.err = errors.New("duplicate label names")
+		return d
+	}
+
+	xxh := xxhash.New()
+	for _, val := range labelValues {
+		xxh.WriteString(val)
+		xxh.Write(separatorByteSlice)
+	}
+	d.id = xxh.Sum64()
+	// Sort labelNames so that order doesn't matter for the hash.
+	sort.Strings(labelNames)
+	// Now hash together (in this order) the help string and the sorted
+	// label names.
+	xxh.Reset()
+	xxh.WriteString(help)
+	xxh.Write(separatorByteSlice)
+	for _, labelName := range labelNames {
+		xxh.WriteString(labelName)
+		xxh.Write(separatorByteSlice)
+	}
+	d.dimHash = xxh.Sum64()
+
+	d.constLabelPairs = make([]*dto.LabelPair, 0, len(constLabels))
+	for n, v := range constLabels {
+		d.constLabelPairs = append(d.constLabelPairs, &dto.LabelPair{
+			Name:  proto.String(n),
+			Value: proto.String(v),
+		})
+	}
+	sort.Sort(labelPairSorter(d.constLabelPairs))
+	return d
+}
+
+// NewInvalidDesc returns an invalid descriptor, i.e. a descriptor with the
+// provided error set. If a collector returning such a descriptor is registered,
+// registration will fail with the provided error. NewInvalidDesc can be used by
+// a Collector to signal inability to describe itself.
+func NewInvalidDesc(err error) *Desc {
+	return &Desc{
+		err: err,
+	}
+}
+
+func (d *Desc) String() string {
+	lpStrings := make([]string, 0, len(d.constLabelPairs))
+	for _, lp := range d.constLabelPairs {
+		lpStrings = append(
+			lpStrings,
+			fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
+		)
+	}
+	return fmt.Sprintf(
+		"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
+		d.fqName,
+		d.help,
+		strings.Join(lpStrings, ","),
+		d.variableLabels,
+	)
+}

+ 199 - 0
vendor/github.com/prometheus/client_golang/prometheus/doc.go

@@ -0,0 +1,199 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package prometheus is the core instrumentation package. It provides metrics
+// primitives to instrument code for monitoring. It also offers a registry for
+// metrics. Sub-packages allow to expose the registered metrics via HTTP
+// (package promhttp) or push them to a Pushgateway (package push). There is
+// also a sub-package promauto, which provides metrics constructors with
+// automatic registration.
+//
+// All exported functions and methods are safe to be used concurrently unless
+// specified otherwise.
+//
+// A Basic Example
+//
+// As a starting point, a very basic usage example:
+//
+//    package main
+//
+//    import (
+//    	"log"
+//    	"net/http"
+//
+//    	"github.com/prometheus/client_golang/prometheus"
+//    	"github.com/prometheus/client_golang/prometheus/promhttp"
+//    )
+//
+//    var (
+//    	cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
+//    		Name: "cpu_temperature_celsius",
+//    		Help: "Current temperature of the CPU.",
+//    	})
+//    	hdFailures = prometheus.NewCounterVec(
+//    		prometheus.CounterOpts{
+//    			Name: "hd_errors_total",
+//    			Help: "Number of hard-disk errors.",
+//    		},
+//    		[]string{"device"},
+//    	)
+//    )
+//
+//    func init() {
+//    	// Metrics have to be registered to be exposed:
+//    	prometheus.MustRegister(cpuTemp)
+//    	prometheus.MustRegister(hdFailures)
+//    }
+//
+//    func main() {
+//    	cpuTemp.Set(65.3)
+//    	hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
+//
+//    	// The Handler function provides a default handler to expose metrics
+//    	// via an HTTP server. "/metrics" is the usual endpoint for that.
+//    	http.Handle("/metrics", promhttp.Handler())
+//    	log.Fatal(http.ListenAndServe(":8080", nil))
+//    }
+//
+//
+// This is a complete program that exports two metrics, a Gauge and a Counter,
+// the latter with a label attached to turn it into a (one-dimensional) vector.
+//
+// Metrics
+//
+// The number of exported identifiers in this package might appear a bit
+// overwhelming. However, in addition to the basic plumbing shown in the example
+// above, you only need to understand the different metric types and their
+// vector versions for basic usage. Furthermore, if you are not concerned with
+// fine-grained control of when and how to register metrics with the registry,
+// have a look at the promauto package, which will effectively allow you to
+// ignore registration altogether in simple cases.
+//
+// Above, you have already touched the Counter and the Gauge. There are two more
+// advanced metric types: the Summary and Histogram. A more thorough description
+// of those four metric types can be found in the Prometheus docs:
+// https://prometheus.io/docs/concepts/metric_types/
+//
+// In addition to the fundamental metric types Gauge, Counter, Summary, and
+// Histogram, a very important part of the Prometheus data model is the
+// partitioning of samples along dimensions called labels, which results in
+// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec,
+// and HistogramVec.
+//
+// While only the fundamental metric types implement the Metric interface, both
+// the metrics and their vector versions implement the Collector interface. A
+// Collector manages the collection of a number of Metrics, but for convenience,
+// a Metric can also “collect itself”. Note that Gauge, Counter, Summary, and
+// Histogram are interfaces themselves while GaugeVec, CounterVec, SummaryVec,
+// and HistogramVec are not.
+//
+// To create instances of Metrics and their vector versions, you need a suitable
+// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, or HistogramOpts.
+//
+// Custom Collectors and constant Metrics
+//
+// While you could create your own implementations of Metric, most likely you
+// will only ever implement the Collector interface on your own. At a first
+// glance, a custom Collector seems handy to bundle Metrics for common
+// registration (with the prime example of the different metric vectors above,
+// which bundle all the metrics of the same name but with different labels).
+//
+// There is a more involved use case, too: If you already have metrics
+// available, created outside of the Prometheus context, you don't need the
+// interface of the various Metric types. You essentially want to mirror the
+// existing numbers into Prometheus Metrics during collection. An own
+// implementation of the Collector interface is perfect for that. You can create
+// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
+// NewConstSummary (and their respective Must… versions). NewConstMetric is used
+// for all metric types with just a float64 as their value: Counter, Gauge, and
+// a special “type” called Untyped. Use the latter if you are not sure if the
+// mirrored metric is a Counter or a Gauge. Creation of the Metric instance
+// happens in the Collect method. The Describe method has to return separate
+// Desc instances, representative of the “throw-away” metrics to be created
+// later.  NewDesc comes in handy to create those Desc instances. Alternatively,
+// you could return no Desc at all, which will mark the Collector “unchecked”.
+// No checks are performed at registration time, but metric consistency will
+// still be ensured at scrape time, i.e. any inconsistencies will lead to scrape
+// errors. Thus, with unchecked Collectors, the responsibility to not collect
+// metrics that lead to inconsistencies in the total scrape result lies with the
+// implementer of the Collector. While this is not a desirable state, it is
+// sometimes necessary. The typical use case is a situation where the exact
+// metrics to be returned by a Collector cannot be predicted at registration
+// time, but the implementer has sufficient knowledge of the whole system to
+// guarantee metric consistency.
+//
+// The Collector example illustrates the use case. You can also look at the
+// source code of the processCollector (mirroring process metrics), the
+// goCollector (mirroring Go metrics), or the expvarCollector (mirroring expvar
+// metrics) as examples that are used in this package itself.
+//
+// If you just need to call a function to get a single float value to collect as
+// a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting
+// shortcuts.
+//
+// Advanced Uses of the Registry
+//
+// While MustRegister is the by far most common way of registering a Collector,
+// sometimes you might want to handle the errors the registration might cause.
+// As suggested by the name, MustRegister panics if an error occurs. With the
+// Register function, the error is returned and can be handled.
+//
+// An error is returned if the registered Collector is incompatible or
+// inconsistent with already registered metrics. The registry aims for
+// consistency of the collected metrics according to the Prometheus data model.
+// Inconsistencies are ideally detected at registration time, not at collect
+// time. The former will usually be detected at start-up time of a program,
+// while the latter will only happen at scrape time, possibly not even on the
+// first scrape if the inconsistency only becomes relevant later. That is the
+// main reason why a Collector and a Metric have to describe themselves to the
+// registry.
+//
+// So far, everything we did operated on the so-called default registry, as it
+// can be found in the global DefaultRegisterer variable. With NewRegistry, you
+// can create a custom registry, or you can even implement the Registerer or
+// Gatherer interfaces yourself. The methods Register and Unregister work in the
+// same way on a custom registry as the global functions Register and Unregister
+// on the default registry.
+//
+// There are a number of uses for custom registries: You can use registries with
+// special properties, see NewPedanticRegistry. You can avoid global state, as
+// it is imposed by the DefaultRegisterer. You can use multiple registries at
+// the same time to expose different metrics in different ways.  You can use
+// separate registries for testing purposes.
+//
+// Also note that the DefaultRegisterer comes registered with a Collector for Go
+// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
+// NewProcessCollector). With a custom registry, you are in control and decide
+// yourself about the Collectors to register.
+//
+// HTTP Exposition
+//
+// The Registry implements the Gatherer interface. The caller of the Gather
+// method can then expose the gathered metrics in some way. Usually, the metrics
+// are served via HTTP on the /metrics endpoint. That's happening in the example
+// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
+//
+// Pushing to the Pushgateway
+//
+// Function for pushing to the Pushgateway can be found in the push sub-package.
+//
+// Graphite Bridge
+//
+// Functions and examples to push metrics from a Gatherer to Graphite can be
+// found in the graphite sub-package.
+//
+// Other Means of Exposition
+//
+// More ways of exposing metrics can easily be added by following the approaches
+// of the existing implementations.
+package prometheus

+ 86 - 0
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go

@@ -0,0 +1,86 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+	"encoding/json"
+	"expvar"
+)
+
+type expvarCollector struct {
+	exports map[string]*Desc
+}
+
+// NewExpvarCollector is the obsolete version of collectors.NewExpvarCollector.
+// See there for documentation.
+//
+// Deprecated: Use collectors.NewExpvarCollector instead.
+func NewExpvarCollector(exports map[string]*Desc) Collector {
+	return &expvarCollector{
+		exports: exports,
+	}
+}
+
+// Describe implements Collector.
+func (e *expvarCollector) Describe(ch chan<- *Desc) {
+	for _, desc := range e.exports {
+		ch <- desc
+	}
+}
+
+// Collect implements Collector.
+func (e *expvarCollector) Collect(ch chan<- Metric) {
+	for name, desc := range e.exports {
+		var m Metric
+		expVar := expvar.Get(name)
+		if expVar == nil {
+			continue
+		}
+		var v interface{}
+		labels := make([]string, len(desc.variableLabels))
+		if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
+			ch <- NewInvalidMetric(desc, err)
+			continue
+		}
+		var processValue func(v interface{}, i int)
+		processValue = func(v interface{}, i int) {
+			if i >= len(labels) {
+				copiedLabels := append(make([]string, 0, len(labels)), labels...)
+				switch v := v.(type) {
+				case float64:
+					m = MustNewConstMetric(desc, UntypedValue, v, copiedLabels...)
+				case bool:
+					if v {
+						m = MustNewConstMetric(desc, UntypedValue, 1, copiedLabels...)
+					} else {
+						m = MustNewConstMetric(desc, UntypedValue, 0, copiedLabels...)
+					}
+				default:
+					return
+				}
+				ch <- m
+				return
+			}
+			vm, ok := v.(map[string]interface{})
+			if !ok {
+				return
+			}
+			for lv, val := range vm {
+				labels[i] = lv
+				processValue(val, i+1)
+			}
+		}
+		processValue(v, 0)
+	}
+}

+ 42 - 0
vendor/github.com/prometheus/client_golang/prometheus/fnv.go

@@ -0,0 +1,42 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+// Inline and byte-free variant of hash/fnv's fnv64a.
+
+const (
+	offset64 = 14695981039346656037
+	prime64  = 1099511628211
+)
+
+// hashNew initializies a new fnv64a hash value.
+func hashNew() uint64 {
+	return offset64
+}
+
+// hashAdd adds a string to a fnv64a hash value, returning the updated hash.
+func hashAdd(h uint64, s string) uint64 {
+	for i := 0; i < len(s); i++ {
+		h ^= uint64(s[i])
+		h *= prime64
+	}
+	return h
+}
+
+// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash.
+func hashAddByte(h uint64, b byte) uint64 {
+	h ^= uint64(b)
+	h *= prime64
+	return h
+}

+ 289 - 0
vendor/github.com/prometheus/client_golang/prometheus/gauge.go

@@ -0,0 +1,289 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+	"math"
+	"sync/atomic"
+	"time"
+
+	dto "github.com/prometheus/client_model/go"
+)
+
+// Gauge is a Metric that represents a single numerical value that can
+// arbitrarily go up and down.
+//
+// A Gauge is typically used for measured values like temperatures or current
+// memory usage, but also "counts" that can go up and down, like the number of
+// running goroutines.
+//
+// To create Gauge instances, use NewGauge.
+type Gauge interface {
+	Metric
+	Collector
+
+	// Set sets the Gauge to an arbitrary value.
+	Set(float64)
+	// Inc increments the Gauge by 1. Use Add to increment it by arbitrary
+	// values.
+	Inc()
+	// Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary
+	// values.
+	Dec()
+	// Add adds the given value to the Gauge. (The value can be negative,
+	// resulting in a decrease of the Gauge.)
+	Add(float64)
+	// Sub subtracts the given value from the Gauge. (The value can be
+	// negative, resulting in an increase of the Gauge.)
+	Sub(float64)
+
+	// SetToCurrentTime sets the Gauge to the current Unix time in seconds.
+	SetToCurrentTime()
+}
+
+// GaugeOpts is an alias for Opts. See there for doc comments.
+type GaugeOpts Opts
+
+// NewGauge creates a new Gauge based on the provided GaugeOpts.
+//
+// The returned implementation is optimized for a fast Set method. If you have a
+// choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick
+// the former. For example, the Inc method of the returned Gauge is slower than
+// the Inc method of a Counter returned by NewCounter. This matches the typical
+// scenarios for Gauges and Counters, where the former tends to be Set-heavy and
+// the latter Inc-heavy.
+func NewGauge(opts GaugeOpts) Gauge {
+	desc := NewDesc(
+		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		nil,
+		opts.ConstLabels,
+	)
+	result := &gauge{desc: desc, labelPairs: desc.constLabelPairs}
+	result.init(result) // Init self-collection.
+	return result
+}
+
+type gauge struct {
+	// valBits contains the bits of the represented float64 value. It has
+	// to go first in the struct to guarantee alignment for atomic
+	// operations.  http://golang.org/pkg/sync/atomic/#pkg-note-BUG
+	valBits uint64
+
+	selfCollector
+
+	desc       *Desc
+	labelPairs []*dto.LabelPair
+}
+
+func (g *gauge) Desc() *Desc {
+	return g.desc
+}
+
+func (g *gauge) Set(val float64) {
+	atomic.StoreUint64(&g.valBits, math.Float64bits(val))
+}
+
+func (g *gauge) SetToCurrentTime() {
+	g.Set(float64(time.Now().UnixNano()) / 1e9)
+}
+
+func (g *gauge) Inc() {
+	g.Add(1)
+}
+
+func (g *gauge) Dec() {
+	g.Add(-1)
+}
+
+func (g *gauge) Add(val float64) {
+	for {
+		oldBits := atomic.LoadUint64(&g.valBits)
+		newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
+		if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) {
+			return
+		}
+	}
+}
+
+func (g *gauge) Sub(val float64) {
+	g.Add(val * -1)
+}
+
+func (g *gauge) Write(out *dto.Metric) error {
+	val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
+	return populateMetric(GaugeValue, val, g.labelPairs, nil, out)
+}
+
+// GaugeVec is a Collector that bundles a set of Gauges that all share the same
+// Desc, but have different values for their variable labels. This is used if
+// you want to count the same thing partitioned by various dimensions
+// (e.g. number of operations queued, partitioned by user and operation
+// type). Create instances with NewGaugeVec.
+type GaugeVec struct {
+	*MetricVec
+}
+
+// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
+// partitioned by the given label names.
+func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
+	desc := NewDesc(
+		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		labelNames,
+		opts.ConstLabels,
+	)
+	return &GaugeVec{
+		MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
+			if len(lvs) != len(desc.variableLabels) {
+				panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
+			}
+			result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
+			result.init(result) // Init self-collection.
+			return result
+		}),
+	}
+}
+
+// GetMetricWithLabelValues returns the Gauge for the given slice of label
+// values (same order as the variable labels in Desc). If that combination of
+// label values is accessed for the first time, a new Gauge is created.
+//
+// It is possible to call this method without using the returned Gauge to only
+// create the new Gauge but leave it at its starting value 0. See also the
+// SummaryVec example.
+//
+// Keeping the Gauge for later use is possible (and should be considered if
+// performance is critical), but keep in mind that Reset, DeleteLabelValues and
+// Delete can be used to delete the Gauge from the GaugeVec. In that case, the
+// Gauge will still exist, but it will not be exported anymore, even if a
+// Gauge with the same label values is created later. See also the CounterVec
+// example.
+//
+// An error is returned if the number of label values is not the same as the
+// number of variable labels in Desc (minus any curried labels).
+//
+// Note that for more than one label value, this method is prone to mistakes
+// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
+// an alternative to avoid that type of mistake. For higher label numbers, the
+// latter has a much more readable (albeit more verbose) syntax, but it comes
+// with a performance overhead (for creating and processing the Labels map).
+func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
+	metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
+	if metric != nil {
+		return metric.(Gauge), err
+	}
+	return nil, err
+}
+
+// GetMetricWith returns the Gauge for the given Labels map (the label names
+// must match those of the variable labels in Desc). If that label map is
+// accessed for the first time, a new Gauge is created. Implications of
+// creating a Gauge without using it and keeping the Gauge for later use are
+// the same as for GetMetricWithLabelValues.
+//
+// An error is returned if the number and names of the Labels are inconsistent
+// with those of the variable labels in Desc (minus any curried labels).
+//
+// This method is used for the same purpose as
+// GetMetricWithLabelValues(...string). See there for pros and cons of the two
+// methods.
+func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
+	metric, err := v.MetricVec.GetMetricWith(labels)
+	if metric != nil {
+		return metric.(Gauge), err
+	}
+	return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. Not returning an
+// error allows shortcuts like
+//     myVec.WithLabelValues("404", "GET").Add(42)
+func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
+	g, err := v.GetMetricWithLabelValues(lvs...)
+	if err != nil {
+		panic(err)
+	}
+	return g
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. Not returning an error allows shortcuts like
+//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
+func (v *GaugeVec) With(labels Labels) Gauge {
+	g, err := v.GetMetricWith(labels)
+	if err != nil {
+		panic(err)
+	}
+	return g
+}
+
+// CurryWith returns a vector curried with the provided labels, i.e. the
+// returned vector has those labels pre-set for all labeled operations performed
+// on it. The cardinality of the curried vector is reduced accordingly. The
+// order of the remaining labels stays the same (just with the curried labels
+// taken out of the sequence – which is relevant for the
+// (GetMetric)WithLabelValues methods). It is possible to curry a curried
+// vector, but only with labels not yet used for currying before.
+//
+// The metrics contained in the GaugeVec are shared between the curried and
+// uncurried vectors. They are just accessed differently. Curried and uncurried
+// vectors behave identically in terms of collection. Only one must be
+// registered with a given registry (usually the uncurried version). The Reset
+// method deletes all metrics, even if called on a curried vector.
+func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
+	vec, err := v.MetricVec.CurryWith(labels)
+	if vec != nil {
+		return &GaugeVec{vec}, err
+	}
+	return nil, err
+}
+
+// MustCurryWith works as CurryWith but panics where CurryWith would have
+// returned an error.
+func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec {
+	vec, err := v.CurryWith(labels)
+	if err != nil {
+		panic(err)
+	}
+	return vec
+}
+
+// GaugeFunc is a Gauge whose value is determined at collect time by calling a
+// provided function.
+//
+// To create GaugeFunc instances, use NewGaugeFunc.
+type GaugeFunc interface {
+	Metric
+	Collector
+}
+
+// NewGaugeFunc creates a new GaugeFunc based on the provided GaugeOpts. The
+// value reported is determined by calling the given function from within the
+// Write method. Take into account that metric collection may happen
+// concurrently. Therefore, it must be safe to call the provided function
+// concurrently.
+//
+// NewGaugeFunc is a good way to create an “info” style metric with a constant
+// value of 1. Example:
+// https://github.com/prometheus/common/blob/8558a5b7db3c84fa38b4766966059a7bd5bfa2ee/version/info.go#L36-L56
+func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc {
+	return newValueFunc(NewDesc(
+		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		nil,
+		opts.ConstLabels,
+	), GaugeValue, function)
+}

+ 367 - 0
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go

@@ -0,0 +1,367 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+	"runtime"
+	"runtime/debug"
+	"sync"
+	"time"
+)
+
+type goCollector struct {
+	goroutinesDesc *Desc
+	threadsDesc    *Desc
+	gcDesc         *Desc
+	goInfoDesc     *Desc
+
+	// ms... are memstats related.
+	msLast          *runtime.MemStats // Previously collected memstats.
+	msLastTimestamp time.Time
+	msMtx           sync.Mutex // Protects msLast and msLastTimestamp.
+	msMetrics       memStatsMetrics
+	msRead          func(*runtime.MemStats) // For mocking in tests.
+	msMaxWait       time.Duration           // Wait time for fresh memstats.
+	msMaxAge        time.Duration           // Maximum allowed age of old memstats.
+}
+
+// NewGoCollector is the obsolete version of collectors.NewGoCollector.
+// See there for documentation.
+//
+// Deprecated: Use collectors.NewGoCollector instead.
+func NewGoCollector() Collector {
+	return &goCollector{
+		goroutinesDesc: NewDesc(
+			"go_goroutines",
+			"Number of goroutines that currently exist.",
+			nil, nil),
+		threadsDesc: NewDesc(
+			"go_threads",
+			"Number of OS threads created.",
+			nil, nil),
+		gcDesc: NewDesc(
+			"go_gc_duration_seconds",
+			"A summary of the pause duration of garbage collection cycles.",
+			nil, nil),
+		goInfoDesc: NewDesc(
+			"go_info",
+			"Information about the Go environment.",
+			nil, Labels{"version": runtime.Version()}),
+		msLast:    &runtime.MemStats{},
+		msRead:    runtime.ReadMemStats,
+		msMaxWait: time.Second,
+		msMaxAge:  5 * time.Minute,
+		msMetrics: memStatsMetrics{
+			{
+				desc: NewDesc(
+					memstatNamespace("alloc_bytes"),
+					"Number of bytes allocated and still in use.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("alloc_bytes_total"),
+					"Total number of bytes allocated, even if freed.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
+				valType: CounterValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("sys_bytes"),
+					"Number of bytes obtained from system.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("lookups_total"),
+					"Total number of pointer lookups.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
+				valType: CounterValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("mallocs_total"),
+					"Total number of mallocs.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
+				valType: CounterValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("frees_total"),
+					"Total number of frees.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
+				valType: CounterValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("heap_alloc_bytes"),
+					"Number of heap bytes allocated and still in use.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("heap_sys_bytes"),
+					"Number of heap bytes obtained from system.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("heap_idle_bytes"),
+					"Number of heap bytes waiting to be used.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("heap_inuse_bytes"),
+					"Number of heap bytes that are in use.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("heap_released_bytes"),
+					"Number of heap bytes released to OS.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("heap_objects"),
+					"Number of allocated objects.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("stack_inuse_bytes"),
+					"Number of bytes in use by the stack allocator.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("stack_sys_bytes"),
+					"Number of bytes obtained from system for stack allocator.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("mspan_inuse_bytes"),
+					"Number of bytes in use by mspan structures.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("mspan_sys_bytes"),
+					"Number of bytes used for mspan structures obtained from system.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("mcache_inuse_bytes"),
+					"Number of bytes in use by mcache structures.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("mcache_sys_bytes"),
+					"Number of bytes used for mcache structures obtained from system.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("buck_hash_sys_bytes"),
+					"Number of bytes used by the profiling bucket hash table.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("gc_sys_bytes"),
+					"Number of bytes used for garbage collection system metadata.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("other_sys_bytes"),
+					"Number of bytes used for other system allocations.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("next_gc_bytes"),
+					"Number of heap bytes when next garbage collection will take place.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("last_gc_time_seconds"),
+					"Number of seconds since 1970 of last garbage collection.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 },
+				valType: GaugeValue,
+			}, {
+				desc: NewDesc(
+					memstatNamespace("gc_cpu_fraction"),
+					"The fraction of this program's available CPU time used by the GC since the program started.",
+					nil, nil,
+				),
+				eval:    func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction },
+				valType: GaugeValue,
+			},
+		},
+	}
+}
+
+func memstatNamespace(s string) string {
+	return "go_memstats_" + s
+}
+
+// Describe returns all descriptions of the collector.
+func (c *goCollector) Describe(ch chan<- *Desc) {
+	ch <- c.goroutinesDesc
+	ch <- c.threadsDesc
+	ch <- c.gcDesc
+	ch <- c.goInfoDesc
+	for _, i := range c.msMetrics {
+		ch <- i.desc
+	}
+}
+
+// Collect returns the current state of all metrics of the collector.
+func (c *goCollector) Collect(ch chan<- Metric) {
+	var (
+		ms   = &runtime.MemStats{}
+		done = make(chan struct{})
+	)
+	// Start reading memstats first as it might take a while.
+	go func() {
+		c.msRead(ms)
+		c.msMtx.Lock()
+		c.msLast = ms
+		c.msLastTimestamp = time.Now()
+		c.msMtx.Unlock()
+		close(done)
+	}()
+
+	ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
+	n, _ := runtime.ThreadCreateProfile(nil)
+	ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
+
+	var stats debug.GCStats
+	stats.PauseQuantiles = make([]time.Duration, 5)
+	debug.ReadGCStats(&stats)
+
+	quantiles := make(map[float64]float64)
+	for idx, pq := range stats.PauseQuantiles[1:] {
+		quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
+	}
+	quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
+	ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles)
+
+	ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1)
+
+	timer := time.NewTimer(c.msMaxWait)
+	select {
+	case <-done: // Our own ReadMemStats succeeded in time. Use it.
+		timer.Stop() // Important for high collection frequencies to not pile up timers.
+		c.msCollect(ch, ms)
+		return
+	case <-timer.C: // Time out, use last memstats if possible. Continue below.
+	}
+	c.msMtx.Lock()
+	if time.Since(c.msLastTimestamp) < c.msMaxAge {
+		// Last memstats are recent enough. Collect from them under the lock.
+		c.msCollect(ch, c.msLast)
+		c.msMtx.Unlock()
+		return
+	}
+	// If we are here, the last memstats are too old or don't exist. We have
+	// to wait until our own ReadMemStats finally completes. For that to
+	// happen, we have to release the lock.
+	c.msMtx.Unlock()
+	<-done
+	c.msCollect(ch, ms)
+}
+
+func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) {
+	for _, i := range c.msMetrics {
+		ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
+	}
+}
+
+// memStatsMetrics provide description, value, and value type for memstat metrics.
+type memStatsMetrics []struct {
+	desc    *Desc
+	eval    func(*runtime.MemStats) float64
+	valType ValueType
+}
+
+// NewBuildInfoCollector is the obsolete version of collectors.NewBuildInfoCollector.
+// See there for documentation.
+//
+// Deprecated: Use collectors.NewBuildInfoCollector instead.
+func NewBuildInfoCollector() Collector {
+	path, version, sum := "unknown", "unknown", "unknown"
+	if bi, ok := debug.ReadBuildInfo(); ok {
+		path = bi.Main.Path
+		version = bi.Main.Version
+		sum = bi.Main.Sum
+	}
+	c := &selfCollector{MustNewConstMetric(
+		NewDesc(
+			"go_build_info",
+			"Build information about the main Go module.",
+			nil, Labels{"path": path, "version": version, "checksum": sum},
+		),
+		GaugeValue, 1)}
+	c.init(c.self)
+	return c
+}

Some files were not shown because too many files changed in this diff