Update weaveworks/common to latest version

```
$ gvt delete github.com/weaveworks/common
$ gvt fetch --revision 4d96fd8dcf2c7b417912c6219b310112cb4a4626 github.com/weaveworks/common
2018/07/23 15:31:11 Fetching: github.com/weaveworks/common
2018/07/23 15:31:14 · Skipping (existing): github.com/golang/protobuf/ptypes/any
2018/07/23 15:31:14 · Fetching recursive dependency: github.com/pkg/errors
2018/07/23 15:31:16 · Skipping (existing): github.com/aws/aws-sdk-go/aws
2018/07/23 15:31:16 · Fetching recursive dependency: github.com/sirupsen/logrus
2018/07/23 15:31:18 ·· Skipping (existing): golang.org/x/sys/unix
2018/07/23 15:31:18 ·· Skipping (existing): golang.org/x/crypto/ssh/terminal
2018/07/23 15:31:18 · Skipping (existing): google.golang.org/grpc/status
2018/07/23 15:31:18 · Skipping (existing): github.com/gorilla/mux
2018/07/23 15:31:18 · Fetching recursive dependency: github.com/opentracing-contrib/go-stdlib/nethttp
2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:20 · Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:31:20 · Skipping (existing): google.golang.org/grpc
2018/07/23 15:31:20 · Skipping (existing): github.com/pmezard/go-difflib/difflib
2018/07/23 15:31:20 · Fetching recursive dependency: github.com/go-kit/kit/log
2018/07/23 15:31:23 ·· Fetching recursive dependency: github.com/go-logfmt/logfmt
2018/07/23 15:31:25 ··· Fetching recursive dependency: github.com/kr/logfmt
2018/07/23 15:31:27 ·· Fetching recursive dependency: github.com/go-stack/stack
2018/07/23 15:31:29 · Fetching recursive dependency: google.golang.org/genproto/googleapis/rpc/status
2018/07/23 15:31:37 ·· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:31:37 ·· Skipping (existing): github.com/golang/protobuf/ptypes/any
2018/07/23 15:31:37 · Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:37 · Fetching recursive dependency: github.com/sercand/kuberesolver
2018/07/23 15:31:39 ·· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:31:39 ·· Skipping (existing): google.golang.org/grpc/resolver
2018/07/23 15:31:39 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:31:39 · Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:31:39 · Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:39 · Skipping (existing): github.com/armon/go-socks5
2018/07/23 15:31:39 · Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:39 · Skipping (existing): github.com/davecgh/go-spew/spew
2018/07/23 15:31:39 · Skipping (existing): github.com/golang/protobuf/ptypes
2018/07/23 15:31:39 · Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:31:39 · Fetching recursive dependency: github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc
2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:41 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:31:41 ·· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/status
2018/07/23 15:31:41 · Fetching recursive dependency: github.com/uber/jaeger-client-go/config
2018/07/23 15:31:44 ·· Fetching recursive dependency: github.com/uber/jaeger-client-go/internal/throttler/remote
2018/07/23 15:31:44 ··· Fetching recursive dependency: github.com/uber/jaeger-client-go/utils
2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift
2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/agent
2018/07/23 15:31:44 ····· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/jaeger
2018/07/23 15:31:44 ····· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/zipkincore
2018/07/23 15:31:44 ··· Fetching recursive dependency: github.com/uber/jaeger-client-go
2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/crossdock/crossdock-go
2018/07/23 15:31:46 ····· Skipping (existing): github.com/davecgh/go-spew/spew
2018/07/23 15:31:46 ····· Skipping (existing): golang.org/x/net/context/ctxhttp
2018/07/23 15:31:46 ····· Skipping (existing): golang.org/x/net/context
2018/07/23 15:31:46 ····· Skipping (existing): github.com/pmezard/go-difflib/difflib
2018/07/23 15:31:46 ···· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:46 ···· Fetching recursive dependency: go.uber.org/zap/zapcore
2018/07/23 15:31:49 ····· Fetching recursive dependency: go.uber.org/atomic
2018/07/23 15:31:51 ····· Fetching recursive dependency: go.uber.org/zap/internal/bufferpool
2018/07/23 15:31:51 ······ Fetching recursive dependency: go.uber.org/zap/buffer
2018/07/23 15:31:51 ····· Fetching recursive dependency: go.uber.org/multierr
2018/07/23 15:31:54 ····· Fetching recursive dependency: go.uber.org/zap/internal/exit
2018/07/23 15:31:54 ····· Fetching recursive dependency: go.uber.org/zap/internal/color
2018/07/23 15:31:54 ···· Fetching recursive dependency: go.uber.org/zap
2018/07/23 15:31:54 ···· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:54 ···· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:54 ···· Fetching recursive dependency: github.com/uber/jaeger-lib/metrics
2018/07/23 15:31:56 ····· Fetching recursive dependency: github.com/uber-go/tally
2018/07/23 15:31:58 ······ Fetching recursive dependency: github.com/m3db/prometheus_client_golang/prometheus/promhttp
2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/common/expfmt
2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/client_model/go
2018/07/23 15:32:00 ······ Fetching recursive dependency: gopkg.in/validator.v2
2018/07/23 15:32:06 ······ Fetching recursive dependency: github.com/cactus/go-statsd-client/statsd
2018/07/23 15:32:08 ······ Skipping (existing): gopkg.in/yaml.v2
2018/07/23 15:32:08 ······ Fetching recursive dependency: github.com/m3db/prometheus_client_golang/prometheus
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/procfs
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/client_model/go
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/common/expfmt
2018/07/23 15:32:08 ······· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:08 ······· Skipping (existing): github.com/beorn7/perks/quantile
2018/07/23 15:32:08 ······· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/common/model
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:08 ······ Fetching recursive dependency: github.com/apache/thrift/lib/go/thrift
2018/07/23 15:32:13 ····· Skipping (existing): github.com/stretchr/testify/assert
2018/07/23 15:32:13 ····· Fetching recursive dependency: github.com/go-kit/kit/metrics/influx
2018/07/23 15:32:13 ······ Fetching recursive dependency: github.com/influxdata/influxdb/client/v2
2018/07/23 15:32:17 ······· Fetching recursive dependency: github.com/influxdata/influxdb/models
2018/07/23 15:32:17 ········ Fetching recursive dependency: github.com/influxdata/influxdb/pkg/escape
2018/07/23 15:32:17 ······ Fetching recursive dependency: github.com/go-kit/kit/metrics
2018/07/23 15:32:17 ······· Fetching recursive dependency: github.com/performancecopilot/speed
2018/07/23 15:32:19 ······· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/aws
2018/07/23 15:32:28 ········ Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/internal/sdk
2018/07/23 15:32:28 ········ Skipping (existing): github.com/go-ini/ini
2018/07/23 15:32:28 ········ Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/service/sts
2018/07/23 15:32:28 ········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/private/protocol/query
2018/07/23 15:32:28 ·········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/private/protocol
2018/07/23 15:32:28 ········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/internal/awsutil
2018/07/23 15:32:28 ·········· Skipping (existing): github.com/jmespath/go-jmespath
2018/07/23 15:32:28 ······· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/service/cloudwatch
2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/aws
2018/07/23 15:32:29 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/service/cloudwatch
2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface
2018/07/23 15:32:29 ······· Fetching recursive dependency: golang.org/x/sync/errgroup
2018/07/23 15:32:31 ········ Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:31 ······· Fetching recursive dependency: github.com/go-kit/kit/util/conn
2018/07/23 15:32:31 ······· Fetching recursive dependency: github.com/VividCortex/gohistogram
2018/07/23 15:32:33 ····· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:33 ····· Fetching recursive dependency: github.com/codahale/hdrhistogram
2018/07/23 15:32:35 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:32:35 · Fetching recursive dependency: github.com/mwitkow/go-grpc-middleware
2018/07/23 15:32:37 ·· Fetching recursive dependency: github.com/grpc-ecosystem/go-grpc-middleware/logging
2018/07/23 15:32:39 ··· Fetching recursive dependency: github.com/grpc-ecosystem/go-grpc-middleware
2018/07/23 15:32:39 ···· Fetching recursive dependency: github.com/golang/protobuf/jsonpb
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/timestamp
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/duration
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/any
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/struct
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/wrappers
2018/07/23 15:32:42 ···· Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:32:42 ···· Fetching recursive dependency: github.com/stretchr/testify/suite
2018/07/23 15:32:45 ····· Skipping (existing): github.com/stretchr/testify/assert
2018/07/23 15:32:45 ····· Fetching recursive dependency: github.com/stretchr/testify/require
2018/07/23 15:32:45 ······ Skipping (existing): github.com/stretchr/testify/assert
2018/07/23 15:32:45 ···· Skipping (existing): google.golang.org/grpc/peer
2018/07/23 15:32:45 ···· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:45 ···· Skipping (existing): golang.org/x/net/trace
2018/07/23 15:32:45 ···· Fetching recursive dependency: github.com/gogo/protobuf/gogoproto
2018/07/23 15:32:48 ····· Fetching recursive dependency: github.com/gogo/protobuf/protoc-gen-gogo/descriptor
2018/07/23 15:32:48 ······ Skipping (existing): github.com/gogo/protobuf/proto
2018/07/23 15:32:48 ····· Skipping (existing): github.com/gogo/protobuf/proto
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/credentials
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc
2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:32:48 ···· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:32:48 ··· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc
2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:32:48 ··· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc
2018/07/23 15:32:48 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/peer
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/credentials
2018/07/23 15:32:48 ·· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:48 ·· Skipping (existing): golang.org/x/net/trace
2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:32:48 · Fetching recursive dependency: github.com/weaveworks/promrus
2018/07/23 15:32:53 ·· Skipping (existing): gopkg.in/yaml.v2
2018/07/23 15:32:53 ·· Skipping (existing): golang.org/x/net/context/ctxhttp
2018/07/23 15:32:53 ·· Fetching recursive dependency: github.com/stretchr/objx
2018/07/23 15:32:55 ·· Fetching recursive dependency: gopkg.in/alecthomas/kingpin.v2
2018/07/23 15:32:58 ··· Fetching recursive dependency: github.com/alecthomas/units
2018/07/23 15:33:00 ··· Fetching recursive dependency: github.com/alecthomas/template
2018/07/23 15:33:02 ·· Fetching recursive dependency: github.com/julienschmidt/httprouter
2018/07/23 15:33:05 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:33:05 · Skipping (existing): github.com/aws/aws-sdk-go/aws/credentials
2018/07/23 15:33:05 · Skipping (existing): github.com/golang/protobuf/ptypes/empty
2018/07/23 15:33:05 · Skipping (existing): golang.org/x/net/context
2018/07/23 15:33:05 · Skipping (existing): golang.org/x/tools/cover
2018/07/23 15:33:05 · Skipping (existing): github.com/mgutz/ansi
```
This commit is contained in:
Marc Carré
2018-07-23 15:33:58 +02:00
parent 0153f016d3
commit 652cc90f98
1466 changed files with 394515 additions and 359 deletions

View File

@@ -0,0 +1,19 @@
Copyright (c) 2012-2016 Eli Janssen
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.

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import (
"bytes"
"sync"
)
type bufferPool struct {
*sync.Pool
}
func newBufferPool() *bufferPool {
return &bufferPool{
&sync.Pool{New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, 1700))
}},
}
}
func (bp *bufferPool) Get() *bytes.Buffer {
return (bp.Pool.Get()).(*bytes.Buffer)
}
func (bp *bufferPool) Put(b *bytes.Buffer) {
b.Truncate(0)
bp.Pool.Put(b)
}

View File

@@ -0,0 +1,327 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import (
"fmt"
"math/rand"
"strconv"
"strings"
"time"
)
var bufPool = newBufferPool()
// The StatSender interface wraps all the statsd metric methods
type StatSender interface {
Inc(string, int64, float32) error
Dec(string, int64, float32) error
Gauge(string, int64, float32) error
GaugeDelta(string, int64, float32) error
Timing(string, int64, float32) error
TimingDuration(string, time.Duration, float32) error
Set(string, string, float32) error
SetInt(string, int64, float32) error
Raw(string, string, float32) error
}
// The Statter interface defines the behavior of a stat client
type Statter interface {
StatSender
NewSubStatter(string) SubStatter
SetPrefix(string)
Close() error
}
// The SubStatter interface defines the behavior of a stat child/subclient
type SubStatter interface {
StatSender
SetSamplerFunc(SamplerFunc)
NewSubStatter(string) SubStatter
}
// The SamplerFunc type defines a function that can serve
// as a Client sampler function.
type SamplerFunc func(float32) bool
// DefaultSampler is the default rate sampler function
func DefaultSampler(rate float32) bool {
if rate < 1 {
return rand.Float32() < rate
}
return true
}
// A Client is a statsd client.
type Client struct {
// prefix for statsd name
prefix string
// packet sender
sender Sender
// sampler method
sampler SamplerFunc
}
// Close closes the connection and cleans up.
func (s *Client) Close() error {
if s == nil {
return nil
}
err := s.sender.Close()
return err
}
// Inc increments a statsd count type.
// stat is a string name for the metric.
// value is the integer value
// rate is the sample rate (0.0 to 1.0)
func (s *Client) Inc(stat string, value int64, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", value, "|c", rate)
}
// Dec decrements a statsd count type.
// stat is a string name for the metric.
// value is the integer value.
// rate is the sample rate (0.0 to 1.0).
func (s *Client) Dec(stat string, value int64, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", -value, "|c", rate)
}
// Gauge submits/updates a statsd gauge type.
// stat is a string name for the metric.
// value is the integer value.
// rate is the sample rate (0.0 to 1.0).
func (s *Client) Gauge(stat string, value int64, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", value, "|g", rate)
}
// GaugeDelta submits a delta to a statsd gauge.
// stat is the string name for the metric.
// value is the (positive or negative) change.
// rate is the sample rate (0.0 to 1.0).
func (s *Client) GaugeDelta(stat string, value int64, rate float32) error {
if !s.includeStat(rate) {
return nil
}
// if negative, the submit formatter will prefix with a - already
// so only special case the positive value
if value >= 0 {
return s.submit(stat, "+", value, "|g", rate)
}
return s.submit(stat, "", value, "|g", rate)
}
// Timing submits a statsd timing type.
// stat is a string name for the metric.
// delta is the time duration value in milliseconds
// rate is the sample rate (0.0 to 1.0).
func (s *Client) Timing(stat string, delta int64, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", delta, "|ms", rate)
}
// TimingDuration submits a statsd timing type.
// stat is a string name for the metric.
// delta is the timing value as time.Duration
// rate is the sample rate (0.0 to 1.0).
func (s *Client) TimingDuration(stat string, delta time.Duration, rate float32) error {
if !s.includeStat(rate) {
return nil
}
ms := float64(delta) / float64(time.Millisecond)
return s.submit(stat, "", ms, "|ms", rate)
}
// Set submits a stats set type
// stat is a string name for the metric.
// value is the string value
// rate is the sample rate (0.0 to 1.0).
func (s *Client) Set(stat string, value string, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", value, "|s", rate)
}
// SetInt submits a number as a stats set type.
// stat is a string name for the metric.
// value is the integer value
// rate is the sample rate (0.0 to 1.0).
func (s *Client) SetInt(stat string, value int64, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", value, "|s", rate)
}
// Raw submits a preformatted value.
// stat is the string name for the metric.
// value is a preformatted "raw" value string.
// rate is the sample rate (0.0 to 1.0).
func (s *Client) Raw(stat string, value string, rate float32) error {
if !s.includeStat(rate) {
return nil
}
return s.submit(stat, "", value, "", rate)
}
// SetSamplerFunc sets a sampler function to something other than the default
// sampler is a function that determines whether the metric is
// to be accepted, or discarded.
// An example use case is for submitted pre-sampled metrics.
func (s *Client) SetSamplerFunc(sampler SamplerFunc) {
s.sampler = sampler
}
// submit an already sampled raw stat
func (s *Client) submit(stat, vprefix string, value interface{}, suffix string, rate float32) error {
data := bufPool.Get()
defer bufPool.Put(data)
if s.prefix != "" {
data.WriteString(s.prefix)
data.WriteString(".")
}
data.WriteString(stat)
data.WriteString(":")
if vprefix != "" {
data.WriteString(vprefix)
}
// sadly, no way to jam this back into the bytes.Buffer without
// doing a few allocations... avoiding those is the whole point here...
// so from here on out just use it as a raw []byte
b := data.Bytes()
switch v := value.(type) {
case string:
b = append(b, v...)
case int64:
b = strconv.AppendInt(b, v, 10)
case float64:
b = strconv.AppendFloat(b, v, 'f', -1, 64)
default:
return fmt.Errorf("No matching type format")
}
if suffix != "" {
b = append(b, suffix...)
}
if rate < 1 {
b = append(b, "|@"...)
b = strconv.AppendFloat(b, float64(rate), 'f', 6, 32)
}
_, err := s.sender.Send(b)
return err
}
// check for nil client, and perform sampling calculation
func (s *Client) includeStat(rate float32) bool {
if s == nil {
return false
}
// test for nil in case someone builds their own
// client without calling new (result is nil sampler)
if s.sampler != nil {
return s.sampler(rate)
}
return DefaultSampler(rate)
}
// SetPrefix sets/updates the statsd client prefix.
// Note: Does not change the prefix of any SubStatters.
func (s *Client) SetPrefix(prefix string) {
if s == nil {
return
}
s.prefix = prefix
}
// NewSubStatter returns a SubStatter with appended prefix
func (s *Client) NewSubStatter(prefix string) SubStatter {
var c *Client
if s != nil {
c = &Client{
prefix: joinPathComp(s.prefix, prefix),
sender: s.sender,
sampler: s.sampler,
}
}
return c
}
// NewClient returns a pointer to a new Client, and an error.
//
// addr is a string of the format "hostname:port", and must be parsable by
// net.ResolveUDPAddr.
//
// prefix is the statsd client prefix. Can be "" if no prefix is desired.
func NewClient(addr, prefix string) (Statter, error) {
sender, err := NewSimpleSender(addr)
if err != nil {
return nil, err
}
return &Client{prefix: prefix, sender: sender}, nil
}
// NewClientWithSender returns a pointer to a new Client and an error.
//
// sender is an instance of a statsd.Sender interface and may not be nil
//
// prefix is the stastd client prefix. Can be "" if no prefix is desired.
func NewClientWithSender(sender Sender, prefix string) (Statter, error) {
if sender == nil {
return nil, fmt.Errorf("Client sender may not be nil")
}
return &Client{prefix: prefix, sender: sender}, nil
}
// joinPathComp is a helper that ensures we combine path components with a dot
// when it's appropriate to do so; prefix is the existing prefix and suffix is
// the new component being added.
//
// It returns the joined prefix.
func joinPathComp(prefix, suffix string) string {
suffix = strings.TrimLeft(suffix, ".")
if prefix != "" && suffix != "" {
return prefix + "." + suffix
}
return prefix + suffix
}
// Dial is a compatibility alias for NewClient
var Dial = NewClient
// New is a compatibility alias for NewClient
var New = NewClient

View File

@@ -0,0 +1,48 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import "time"
// NewBufferedClient returns a new BufferedClient
//
// addr is a string of the format "hostname:port", and must be parsable by
// net.ResolveUDPAddr.
//
// prefix is the statsd client prefix. Can be "" if no prefix is desired.
//
// flushInterval is a time.Duration, and specifies the maximum interval for
// packet sending. Note that if you send lots of metrics, you will send more
// often. This is just a maximal threshold.
//
// If flushInterval is 0ms, defaults to 300ms.
//
// flushBytes specifies the maximum udp packet size you wish to send. If adding
// a metric would result in a larger packet than flushBytes, the packet will
// first be send, then the new data will be added to the next packet.
//
// If flushBytes is 0, defaults to 1432 bytes, which is considered safe
// for local traffic. If sending over the public internet, 512 bytes is
// the recommended value.
func NewBufferedClient(addr, prefix string, flushInterval time.Duration, flushBytes int) (Statter, error) {
if flushBytes <= 0 {
// https://github.com/etsy/statsd/blob/master/docs/metric_types.md#multi-metric-packets
flushBytes = 1432
}
if flushInterval <= time.Duration(0) {
flushInterval = 300 * time.Millisecond
}
sender, err := NewBufferedSender(addr, flushInterval, flushBytes)
if err != nil {
return nil, err
}
client := &Client{
prefix: prefix,
sender: sender,
}
return client, nil
}

View File

@@ -0,0 +1,111 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import "time"
// A NoopClient is a client that does nothing.
type NoopClient struct{}
// Close closes the connection and cleans up.
func (s *NoopClient) Close() error {
return nil
}
// Inc increments a statsd count type.
// stat is a string name for the metric.
// value is the integer value
// rate is the sample rate (0.0 to 1.0)
func (s *NoopClient) Inc(stat string, value int64, rate float32) error {
return nil
}
// Dec decrements a statsd count type.
// stat is a string name for the metric.
// value is the integer value.
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) Dec(stat string, value int64, rate float32) error {
return nil
}
// Gauge submits/Updates a statsd gauge type.
// stat is a string name for the metric.
// value is the integer value.
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) Gauge(stat string, value int64, rate float32) error {
return nil
}
// GaugeDelta submits a delta to a statsd gauge.
// stat is the string name for the metric.
// value is the (positive or negative) change.
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) GaugeDelta(stat string, value int64, rate float32) error {
return nil
}
// Timing submits a statsd timing type.
// stat is a string name for the metric.
// delta is the time duration value in milliseconds
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) Timing(stat string, delta int64, rate float32) error {
return nil
}
// TimingDuration submits a statsd timing type.
// stat is a string name for the metric.
// delta is the timing value as time.Duration
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) TimingDuration(stat string, delta time.Duration, rate float32) error {
return nil
}
// Set submits a stats set type.
// stat is a string name for the metric.
// value is the string value
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) Set(stat string, value string, rate float32) error {
return nil
}
// SetInt submits a number as a stats set type.
// convenience method for Set with number.
// stat is a string name for the metric.
// value is the integer value
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) SetInt(stat string, value int64, rate float32) error {
return nil
}
// Raw formats the statsd event data, handles sampling, prepares it,
// and sends it to the server.
// stat is the string name for the metric.
// value is the preformatted "raw" value string.
// rate is the sample rate (0.0 to 1.0).
func (s *NoopClient) Raw(stat string, value string, rate float32) error {
return nil
}
// SetPrefix sets/updates the statsd client prefix
func (s *NoopClient) SetPrefix(prefix string) {}
// NewSubStatter returns a SubStatter with appended prefix
func (s *NoopClient) NewSubStatter(prefix string) SubStatter {
return &NoopClient{}
}
// SetSamplerFunc sets the sampler function
func (s *NoopClient) SetSamplerFunc(sampler SamplerFunc) {}
// NewNoopClient returns a pointer to a new NoopClient, and an error (always
// nil, just supplied to support api convention).
// Use variadic arguments to support identical format as NewClient, or a more
// conventional no argument form.
func NewNoopClient(a ...interface{}) (Statter, error) {
return &NoopClient{}, nil
}
// NewNoop is a compatibility alias for NewNoopClient
var NewNoop = NewNoopClient

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
/*
Package statsd provides a StatsD client implementation that is safe for
concurrent use by multiple goroutines and for efficiency can be created and
reused.
Example usage:
// first create a client
client, err := statsd.NewClient("127.0.0.1:8125", "test-client")
// handle any errors
if err != nil {
log.Fatal(err)
}
// make sure to clean up
defer client.Close()
// Send a stat
err = client.Inc("stat1", 42, 1.0)
// handle any errors
if err != nil {
log.Printf("Error sending metric: %+v", err)
}
*/
package statsd

View File

@@ -0,0 +1,69 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import (
"errors"
"net"
)
// The Sender interface wraps a Send and Close
type Sender interface {
Send(data []byte) (int, error)
Close() error
}
// SimpleSender provides a socket send interface.
type SimpleSender struct {
// underlying connection
c net.PacketConn
// resolved udp address
ra *net.UDPAddr
}
// Send sends the data to the server endpoint.
func (s *SimpleSender) Send(data []byte) (int, error) {
// no need for locking here, as the underlying fdNet
// already serialized writes
n, err := s.c.(*net.UDPConn).WriteToUDP(data, s.ra)
if err != nil {
return 0, err
}
if n == 0 {
return n, errors.New("Wrote no bytes")
}
return n, nil
}
// Close closes the SimpleSender
func (s *SimpleSender) Close() error {
err := s.c.Close()
return err
}
// NewSimpleSender returns a new SimpleSender for sending to the supplied
// addresss.
//
// addr is a string of the format "hostname:port", and must be parsable by
// net.ResolveUDPAddr.
func NewSimpleSender(addr string) (Sender, error) {
c, err := net.ListenPacket("udp", ":0")
if err != nil {
return nil, err
}
ra, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
c.Close()
return nil, err
}
sender := &SimpleSender{
c: c,
ra: ra,
}
return sender, nil
}

View File

@@ -0,0 +1,175 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import (
"bytes"
"fmt"
"sync"
"time"
)
var senderPool = newBufferPool()
// BufferedSender provides a buffered statsd udp, sending multiple
// metrics, where possible.
type BufferedSender struct {
sender Sender
flushBytes int
flushInterval time.Duration
// buffers
bufmx sync.Mutex
buffer *bytes.Buffer
bufs chan *bytes.Buffer
// lifecycle
runmx sync.RWMutex
shutdown chan chan error
running bool
}
// Send bytes.
func (s *BufferedSender) Send(data []byte) (int, error) {
s.runmx.RLock()
if !s.running {
s.runmx.RUnlock()
return 0, fmt.Errorf("BufferedSender is not running")
}
s.withBufferLock(func() {
blen := s.buffer.Len()
if blen > 0 && blen+len(data)+1 >= s.flushBytes {
s.swapnqueue()
}
s.buffer.Write(data)
s.buffer.WriteByte('\n')
if s.buffer.Len() >= s.flushBytes {
s.swapnqueue()
}
})
s.runmx.RUnlock()
return len(data), nil
}
// Close Buffered Sender
func (s *BufferedSender) Close() error {
// since we are running, write lock during cleanup
s.runmx.Lock()
defer s.runmx.Unlock()
if !s.running {
return nil
}
errChan := make(chan error)
s.running = false
s.shutdown <- errChan
return <-errChan
}
// Start Buffered Sender
// Begins ticker and read loop
func (s *BufferedSender) Start() {
// write lock to start running
s.runmx.Lock()
defer s.runmx.Unlock()
if s.running {
return
}
s.running = true
s.bufs = make(chan *bytes.Buffer, 32)
go s.run()
}
func (s *BufferedSender) withBufferLock(fn func()) {
s.bufmx.Lock()
fn()
s.bufmx.Unlock()
}
func (s *BufferedSender) swapnqueue() {
if s.buffer.Len() == 0 {
return
}
ob := s.buffer
nb := senderPool.Get()
s.buffer = nb
s.bufs <- ob
}
func (s *BufferedSender) run() {
ticker := time.NewTicker(s.flushInterval)
defer ticker.Stop()
doneChan := make(chan bool)
go func() {
for buf := range s.bufs {
s.flush(buf)
senderPool.Put(buf)
}
doneChan <- true
}()
for {
select {
case <-ticker.C:
s.withBufferLock(func() {
s.swapnqueue()
})
case errChan := <-s.shutdown:
s.withBufferLock(func() {
s.swapnqueue()
})
close(s.bufs)
<-doneChan
errChan <- s.sender.Close()
return
}
}
}
// send to remove endpoint and truncate buffer
func (s *BufferedSender) flush(b *bytes.Buffer) (int, error) {
bb := b.Bytes()
bbl := len(bb)
if bb[bbl-1] == '\n' {
bb = bb[:bbl-1]
}
//n, err := s.sender.Send(bytes.TrimSuffix(b.Bytes(), []byte("\n")))
n, err := s.sender.Send(bb)
b.Truncate(0) // clear the buffer
return n, err
}
// NewBufferedSender returns a new BufferedSender
//
// addr is a string of the format "hostname:port", and must be parsable by
// net.ResolveUDPAddr.
//
// flushInterval is a time.Duration, and specifies the maximum interval for
// packet sending. Note that if you send lots of metrics, you will send more
// often. This is just a maximal threshold.
//
// flushBytes specifies the maximum udp packet size you wish to send. If adding
// a metric would result in a larger packet than flushBytes, the packet will
// first be send, then the new data will be added to the next packet.
func NewBufferedSender(addr string, flushInterval time.Duration, flushBytes int) (Sender, error) {
simpleSender, err := NewSimpleSender(addr)
if err != nil {
return nil, err
}
sender := &BufferedSender{
flushBytes: flushBytes,
flushInterval: flushInterval,
sender: simpleSender,
buffer: senderPool.Get(),
shutdown: make(chan chan error),
}
sender.Start()
return sender, nil
}

View File

@@ -0,0 +1,79 @@
package statsdtest
import (
"errors"
"sync"
)
// RecordingSender implements statsd.Sender but parses individual Stats into a
// buffer that can be later inspected instead of sending to some server. It
// should constructed with NewRecordingSender().
type RecordingSender struct {
m sync.Mutex
buffer Stats
closed bool
}
// NewRecordingSender creates a new RecordingSender for use by a statsd.Client.
func NewRecordingSender() *RecordingSender {
rs := &RecordingSender{}
rs.buffer = make(Stats, 0)
return rs
}
// GetSent returns the stats that have been sent. Locks and copies the current
// state of the sent Stats.
//
// The entire buffer of Stat objects (including Stat.Raw is copied).
func (rs *RecordingSender) GetSent() Stats {
rs.m.Lock()
defer rs.m.Unlock()
results := make(Stats, len(rs.buffer))
for i, e := range rs.buffer {
results[i] = e
results[i].Raw = make([]byte, len(e.Raw))
copy(results[i].Raw, e.Raw)
}
return results
}
// ClearSent locks the sender and clears any Stats that have been recorded.
func (rs *RecordingSender) ClearSent() {
rs.m.Lock()
defer rs.m.Unlock()
rs.buffer = rs.buffer[:0]
}
// Send parses the provided []byte into stat objects and then appends these to
// the buffer of sent stats. Buffer operations are synchronized so it is safe
// to call this from multiple goroutines (though contenion will impact
// performance so don't use this during a benchmark). Send treats '\n' as a
// delimiter between multiple sats in the same []byte.
//
// Calling after the Sender has been closed will return an error (and not
// mutate the buffer).
func (rs *RecordingSender) Send(data []byte) (int, error) {
sent := ParseStats(data)
rs.m.Lock()
defer rs.m.Unlock()
if rs.closed {
return 0, errors.New("writing to a closed sender")
}
rs.buffer = append(rs.buffer, sent...)
return len(data), nil
}
// Close marks this sender as closed. Subsequent attempts to Send stats will
// result in an error.
func (rs *RecordingSender) Close() error {
rs.m.Lock()
defer rs.m.Unlock()
rs.closed = true
return nil
}

View File

@@ -0,0 +1,139 @@
package statsdtest
import (
"bytes"
"fmt"
"strings"
)
// Stat contains the raw and extracted stat information from a stat that was
// sent by the RecordingSender. Raw will always have the content that was
// consumed for this specific stat and Parsed will be set if no errors were hit
// pulling information out of it.
type Stat struct {
Raw []byte
Stat string
Value string
Tag string
Rate string
Parsed bool
}
// String fulfils the stringer interface
func (s *Stat) String() string {
return fmt.Sprintf("%s %s %s", s.Stat, s.Value, s.Rate)
}
// ParseStats takes a sequence of bytes destined for a Statsd server and parses
// it out into one or more Stat structs. Each struct includes both the raw
// bytes (copied, so the src []byte may be reused if desired) as well as each
// component it was able to parse out. If parsing was incomplete Stat.Parsed
// will be set to false but no error is returned / kept.
func ParseStats(src []byte) Stats {
d := make([]byte, len(src))
copy(d, src)
// standard protocol indicates one stat per line
entries := bytes.Split(d, []byte{'\n'})
result := make(Stats, len(entries))
for i, e := range entries {
result[i] = Stat{Raw: e}
ss := &result[i]
// : deliniates the stat name from the stat data
marker := bytes.IndexByte(e, ':')
if marker == -1 {
continue
}
ss.Stat = string(e[0:marker])
// stat data folows ':' with the form {value}|{type tag}[|@{sample rate}]
e = e[marker+1:]
marker = bytes.IndexByte(e, '|')
if marker == -1 {
continue
}
ss.Value = string(e[:marker])
e = e[marker+1:]
marker = bytes.IndexByte(e, '|')
if marker == -1 {
// no sample rate
ss.Tag = string(e)
} else {
ss.Tag = string(e[:marker])
e = e[marker+1:]
if len(e) == 0 || e[0] != '@' {
// sample rate should be prefixed with '@'; bail otherwise
continue
}
ss.Rate = string(e[1:])
}
ss.Parsed = true
}
return result
}
// Stats is a slice of Stat
type Stats []Stat
// Unparsed returns any stats that were unable to be completely parsed.
func (s Stats) Unparsed() Stats {
var r Stats
for _, e := range s {
if !e.Parsed {
r = append(r, e)
}
}
return r
}
// CollectNamed returns all data sent for a given stat name.
func (s Stats) CollectNamed(statName string) Stats {
return s.Collect(func(e Stat) bool {
return e.Stat == statName
})
}
// Collect gathers all stats that make some predicate true.
func (s Stats) Collect(pred func(Stat) bool) Stats {
var r Stats
for _, e := range s {
if pred(e) {
r = append(r, e)
}
}
return r
}
// Values returns the values associated with this Stats object.
func (s Stats) Values() []string {
if len(s) == 0 {
return nil
}
r := make([]string, len(s))
for i, e := range s {
r[i] = e.Value
}
return r
}
// String fulfils the stringer interface
func (s Stats) String() string {
if len(s) == 0 {
return ""
}
r := make([]string, len(s))
for i, e := range s {
r[i] = e.String()
}
return strings.Join(r, "\n")
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2012-2016 Eli Janssen
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package statsd
import (
"fmt"
"regexp"
)
// The ValidatorFunc type defines a function that can serve
// as a stat name validation function.
type ValidatorFunc func(string) error
var safeName = regexp.MustCompile(`^[a-zA-Z0-9\-_.]+$`)
// CheckName may be used to validate whether a stat name contains invalid
// characters. If invalid characters are found, the function will return an
// error.
func CheckName(stat string) error {
if !safeName.MatchString(stat) {
return fmt.Errorf("invalid stat name: %s", stat)
}
return nil
}