2 Commits

Author SHA1 Message Date
Teran McKinney ae4345d928 go mod vendor 10 months ago
Teran McKinney 7f827b16e8 Update url 10 months ago
  1. 2
      README.md
  2. 2
      go.mod
  3. 9
      vendor/gopkg.in/alexcesaro/statsd.v2/.travis.yml
  4. 64
      vendor/gopkg.in/alexcesaro/statsd.v2/CHANGELOG.md
  5. 20
      vendor/gopkg.in/alexcesaro/statsd.v2/LICENSE
  6. 50
      vendor/gopkg.in/alexcesaro/statsd.v2/README.md
  7. 270
      vendor/gopkg.in/alexcesaro/statsd.v2/conn.go
  8. 29
      vendor/gopkg.in/alexcesaro/statsd.v2/doc.go
  9. 250
      vendor/gopkg.in/alexcesaro/statsd.v2/options.go
  10. 169
      vendor/gopkg.in/alexcesaro/statsd.v2/statsd.go
  11. 2
      vendor/modules.txt

2
README.md

@ -2,7 +2,7 @@
## Install
* `go get github.com/teran-mckinney/sslexpirystatsd`
* `go get go-beyond.org/code/sslexpirystatsd`
## Usage

2
go.mod

@ -1,4 +1,4 @@
module github.com/teran-mckinney/sslexpirystatsd
module go-beyond.org/code/sslexpirystatsd
go 1.12

9
vendor/gopkg.in/alexcesaro/statsd.v2/.travis.yml

@ -0,0 +1,9 @@
language: go
go:
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- tip

64
vendor/gopkg.in/alexcesaro/statsd.v2/CHANGELOG.md

@ -0,0 +1,64 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [2.0.0] - 2016-03-20
- `New` signature changed. The default address used is now ":8125". To use
another address use the `Address` option:
Before:
```
statsd.New(":8125")
statsd.New(":9000")
```
After
```
statsd.New()
statsd.New(statsd.Address(":9000"))
```
- The `rate` parameter has been removed from the `Count` and `Timing` methods.
Use the new `SampleRate` option instead.
- `Count`, `Gauge` and `Timing` now accept a `interface{}` instead of an int as
the value parameter. So you can now use any type of integer or float in these
functions.
- The `WithInfluxDBTags` and `WithDatadogTags` options were replaced by the
`TagsFormat` and `Tags` options:
Before:
```
statsd.New(statsd.WithInfluxDBTags("tag", "value"))
statsd.New(statsd.WithDatadogTags("tag", "value"))
```
After
```
statsd.New(statsd.TagsFormat(statsd.InfluxDB), statsd.Tags("tag", "value"))
statsd.New(statsd.TagsFormat(statsd.Datadog), statsd.Tags("tag", "value"))
```
- All options whose named began by `With` had the `With` stripped:
Before:
```
statsd.New(statsd.WithMaxPacketSize(65000))
```
After
```
statsd.New(statsd.MaxPacketSize(65000))
```
- `ChangeGauge` has been removed as it is a bad practice: UDP packets can be
lost so using relative changes can cause unreliable values in the long term.
Use `Gauge` instead which sends an absolute value.
- The `Histogram` method has been added.
- The `Clone` method was added to the `Client`, it allows to create a new
`Client` with different rate / prefix / tags parameters while still using the
same connection.

20
vendor/gopkg.in/alexcesaro/statsd.v2/LICENSE

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015 Alexandre Cesaro
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.

50
vendor/gopkg.in/alexcesaro/statsd.v2/README.md

@ -0,0 +1,50 @@
# statsd
[![Build Status](https://travis-ci.org/alexcesaro/statsd.svg?branch=v2)](https://travis-ci.org/alexcesaro/statsd) [![Code Coverage](http://gocover.io/_badge/gopkg.in/alexcesaro/statsd.v2)](http://gocover.io/gopkg.in/alexcesaro/statsd.v2) [![Documentation](https://godoc.org/gopkg.in/alexcesaro/statsd.v2?status.svg)](https://godoc.org/gopkg.in/alexcesaro/statsd.v2)
## Introduction
statsd is a simple and efficient [Statsd](https://github.com/etsy/statsd)
client.
See the [benchmark](https://github.com/alexcesaro/statsdbench) for a comparison
with other Go StatsD clients.
## Features
- Supports all StatsD metrics: counter, gauge, timing and set
- Supports InfluxDB and Datadog tags
- Fast and GC-friendly: all functions for sending metrics do not allocate
- Efficient: metrics are buffered by default
- Simple and clean API
- 100% test coverage
- Versioned API using gopkg.in
## Documentation
https://godoc.org/gopkg.in/alexcesaro/statsd.v2
## Download
go get gopkg.in/alexcesaro/statsd.v2
## Example
See the [examples in the documentation](https://godoc.org/gopkg.in/alexcesaro/statsd.v2#example-package).
## License
[MIT](LICENSE)
## Contribute
Do you have any question the documentation does not answer? Is there a use case
that you feel is common and is not well-addressed by the current API?
If so you are more than welcome to ask questions in the
[thread on golang-nuts](https://groups.google.com/d/topic/golang-nuts/Tz6t4_iLgnw/discussion)
or open an issue or send a pull-request here on Github.

270
vendor/gopkg.in/alexcesaro/statsd.v2/conn.go

@ -0,0 +1,270 @@
package statsd
import (
"io"
"math/rand"
"net"
"strconv"
"sync"
"time"
)
type conn struct {
// Fields settable with options at Client's creation.
addr string
errorHandler func(error)
flushPeriod time.Duration
maxPacketSize int
network string
tagFormat TagFormat
mu sync.Mutex
// Fields guarded by the mutex.
closed bool
w io.WriteCloser
buf []byte
rateCache map[float32]string
}
func newConn(conf connConfig, muted bool) (*conn, error) {
c := &conn{
addr: conf.Addr,
errorHandler: conf.ErrorHandler,
flushPeriod: conf.FlushPeriod,
maxPacketSize: conf.MaxPacketSize,
network: conf.Network,
tagFormat: conf.TagFormat,
}
if muted {
return c, nil
}
var err error
c.w, err = dialTimeout(c.network, c.addr, 5*time.Second)
if err != nil {
return c, err
}
// When using UDP do a quick check to see if something is listening on the
// given port to return an error as soon as possible.
if c.network[:3] == "udp" {
for i := 0; i < 2; i++ {
_, err = c.w.Write(nil)
if err != nil {
_ = c.w.Close()
c.w = nil
return c, err
}
}
}
// To prevent a buffer overflow add some capacity to the buffer to allow for
// an additional metric.
c.buf = make([]byte, 0, c.maxPacketSize+200)
if c.flushPeriod > 0 {
go func() {
ticker := time.NewTicker(c.flushPeriod)
for _ = range ticker.C {
c.mu.Lock()
if c.closed {
ticker.Stop()
c.mu.Unlock()
return
}
c.flush(0)
c.mu.Unlock()
}
}()
}
return c, nil
}
func (c *conn) metric(prefix, bucket string, n interface{}, typ string, rate float32, tags string) {
c.mu.Lock()
l := len(c.buf)
c.appendBucket(prefix, bucket, tags)
c.appendNumber(n)
c.appendType(typ)
c.appendRate(rate)
c.closeMetric(tags)
c.flushIfBufferFull(l)
c.mu.Unlock()
}
func (c *conn) gauge(prefix, bucket string, value interface{}, tags string) {
c.mu.Lock()
l := len(c.buf)
// To set a gauge to a negative value we must first set it to 0.
// https://github.com/etsy/statsd/blob/master/docs/metric_types.md#gauges
if isNegative(value) {
c.appendBucket(prefix, bucket, tags)
c.appendGauge(0, tags)
}
c.appendBucket(prefix, bucket, tags)
c.appendGauge(value, tags)
c.flushIfBufferFull(l)
c.mu.Unlock()
}
func (c *conn) appendGauge(value interface{}, tags string) {
c.appendNumber(value)
c.appendType("g")
c.closeMetric(tags)
}
func (c *conn) unique(prefix, bucket string, value string, tags string) {
c.mu.Lock()
l := len(c.buf)
c.appendBucket(prefix, bucket, tags)
c.appendString(value)
c.appendType("s")
c.closeMetric(tags)
c.flushIfBufferFull(l)
c.mu.Unlock()
}
func (c *conn) appendByte(b byte) {
c.buf = append(c.buf, b)
}
func (c *conn) appendString(s string) {
c.buf = append(c.buf, s...)
}
func (c *conn) appendNumber(v interface{}) {
switch n := v.(type) {
case int:
c.buf = strconv.AppendInt(c.buf, int64(n), 10)
case uint:
c.buf = strconv.AppendUint(c.buf, uint64(n), 10)
case int64:
c.buf = strconv.AppendInt(c.buf, n, 10)
case uint64:
c.buf = strconv.AppendUint(c.buf, n, 10)
case int32:
c.buf = strconv.AppendInt(c.buf, int64(n), 10)
case uint32:
c.buf = strconv.AppendUint(c.buf, uint64(n), 10)
case int16:
c.buf = strconv.AppendInt(c.buf, int64(n), 10)
case uint16:
c.buf = strconv.AppendUint(c.buf, uint64(n), 10)
case int8:
c.buf = strconv.AppendInt(c.buf, int64(n), 10)
case uint8:
c.buf = strconv.AppendUint(c.buf, uint64(n), 10)
case float64:
c.buf = strconv.AppendFloat(c.buf, n, 'f', -1, 64)
case float32:
c.buf = strconv.AppendFloat(c.buf, float64(n), 'f', -1, 32)
}
}
func isNegative(v interface{}) bool {
switch n := v.(type) {
case int:
return n < 0
case uint:
return n < 0
case int64:
return n < 0
case uint64:
return n < 0
case int32:
return n < 0
case uint32:
return n < 0
case int16:
return n < 0
case uint16:
return n < 0
case int8:
return n < 0
case uint8:
return n < 0
case float64:
return n < 0
case float32:
return n < 0
}
return false
}
func (c *conn) appendBucket(prefix, bucket string, tags string) {
c.appendString(prefix)
c.appendString(bucket)
if c.tagFormat == InfluxDB {
c.appendString(tags)
}
c.appendByte(':')
}
func (c *conn) appendType(t string) {
c.appendByte('|')
c.appendString(t)
}
func (c *conn) appendRate(rate float32) {
if rate == 1 {
return
}
if c.rateCache == nil {
c.rateCache = make(map[float32]string)
}
c.appendString("|@")
if s, ok := c.rateCache[rate]; ok {
c.appendString(s)
} else {
s = strconv.FormatFloat(float64(rate), 'f', -1, 32)
c.rateCache[rate] = s
c.appendString(s)
}
}
func (c *conn) closeMetric(tags string) {
if c.tagFormat == Datadog {
c.appendString(tags)
}
c.appendByte('\n')
}
func (c *conn) flushIfBufferFull(lastSafeLen int) {
if len(c.buf) > c.maxPacketSize {
c.flush(lastSafeLen)
}
}
// flush flushes the first n bytes of the buffer.
// If n is 0, the whole buffer is flushed.
func (c *conn) flush(n int) {
if len(c.buf) == 0 {
return
}
if n == 0 {
n = len(c.buf)
}
// Trim the last \n, StatsD does not like it.
_, err := c.w.Write(c.buf[:n-1])
c.handleError(err)
if n < len(c.buf) {
copy(c.buf, c.buf[n:])
}
c.buf = c.buf[:len(c.buf)-n]
}
func (c *conn) handleError(err error) {
if err != nil && c.errorHandler != nil {
c.errorHandler(err)
}
}
// Stubbed out for testing.
var (
dialTimeout = net.DialTimeout
now = time.Now
randFloat = rand.Float32
)

29
vendor/gopkg.in/alexcesaro/statsd.v2/doc.go

@ -0,0 +1,29 @@
/*
Package statsd is a simple and efficient StatsD client.
Options
Use options to configure the Client: target host/port, sampling rate, tags, etc.
Whenever you want to use different options (e.g. other tags, different sampling
rate), you should use the Clone() method of the Client.
Because when cloning a Client, the same connection is reused so this is way
cheaper and more efficient than creating another Client using New().
Internals
Client's methods buffer metrics. The buffer is flushed when either:
- the background goroutine flushes the buffer (every 100ms by default)
- the buffer is full (1440 bytes by default so that IP packets are not
fragmented)
The background goroutine can be disabled using the FlushPeriod(0) option.
Buffering can be disabled using the MaxPacketSize(0) option.
StatsD homepage: https://github.com/etsy/statsd
*/
package statsd

250
vendor/gopkg.in/alexcesaro/statsd.v2/options.go

@ -0,0 +1,250 @@
package statsd
import (
"bytes"
"strings"
"time"
)
type config struct {
Conn connConfig
Client clientConfig
}
type clientConfig struct {
Muted bool
Rate float32
Prefix string
Tags []tag
}
type connConfig struct {
Addr string
ErrorHandler func(error)
FlushPeriod time.Duration
MaxPacketSize int
Network string
TagFormat TagFormat
}
// An Option represents an option for a Client. It must be used as an
// argument to New() or Client.Clone().
type Option func(*config)
// Address sets the address of the StatsD daemon.
//
// By default, ":8125" is used. This option is ignored in Client.Clone().
func Address(addr string) Option {
return Option(func(c *config) {
c.Conn.Addr = addr
})
}
// ErrorHandler sets the function called when an error happens when sending
// metrics (e.g. the StatsD daemon is not listening anymore).
//
// By default, these errors are ignored. This option is ignored in
// Client.Clone().
func ErrorHandler(h func(error)) Option {
return Option(func(c *config) {
c.Conn.ErrorHandler = h
})
}
// FlushPeriod sets how often the Client's buffer is flushed. If p is 0, the
// goroutine that periodically flush the buffer is not lauched and the buffer
// is only flushed when it is full.
//
// By default, the flush period is 100 ms. This option is ignored in
// Client.Clone().
func FlushPeriod(p time.Duration) Option {
return Option(func(c *config) {
c.Conn.FlushPeriod = p
})
}
// MaxPacketSize sets the maximum packet size in bytes sent by the Client.
//
// By default, it is 1440 to avoid IP fragmentation. This option is ignored in
// Client.Clone().
func MaxPacketSize(n int) Option {
return Option(func(c *config) {
c.Conn.MaxPacketSize = n
})
}
// Network sets the network (udp, tcp, etc) used by the client. See the
// net.Dial documentation (https://golang.org/pkg/net/#Dial) for the available
// network options.
//
// By default, network is udp. This option is ignored in Client.Clone().
func Network(network string) Option {
return Option(func(c *config) {
c.Conn.Network = network
})
}
// Mute sets whether the Client is muted. All methods of a muted Client do
// nothing and return immedialtly.
//
// This option can be used in Client.Clone() only if the parent Client is not
// muted. The clones of a muted Client are always muted.
func Mute(b bool) Option {
return Option(func(c *config) {
c.Client.Muted = b
})
}
// SampleRate sets the sample rate of the Client. It allows sending the metrics
// less often which can be useful for performance intensive code paths.
func SampleRate(rate float32) Option {
return Option(func(c *config) {
c.Client.Rate = rate
})
}
// Prefix appends the prefix that will be used in every bucket name.
//
// Note that when used in cloned, the prefix of the parent Client is not
// replaced but is prepended to the given prefix.
func Prefix(p string) Option {
return Option(func(c *config) {
c.Client.Prefix += strings.TrimSuffix(p, ".") + "."
})
}
// TagFormat represents the format of tags sent by a Client.
type TagFormat uint8
// TagsFormat sets the format of tags.
func TagsFormat(tf TagFormat) Option {
return Option(func(c *config) {
c.Conn.TagFormat = tf
})
}
// Tags appends the given tags to the tags sent with every metrics. If a tag
// already exists, it is replaced.
//
// The tags must be set as key-value pairs. If the number of tags is not even,
// Tags panics.
//
// If the format of tags have not been set using the TagsFormat option, the tags
// will be ignored.
func Tags(tags ...string) Option {
if len(tags)%2 != 0 {
panic("statsd: Tags only accepts an even number of arguments")
}
return Option(func(c *config) {
if len(tags) == 0 {
return
}
newTags := make([]tag, len(tags)/2)
for i := 0; i < len(tags)/2; i++ {
newTags[i] = tag{K: tags[2*i], V: tags[2*i+1]}
}
for _, newTag := range newTags {
exists := false
for _, oldTag := range c.Client.Tags {
if newTag.K == oldTag.K {
exists = true
oldTag.V = newTag.V
}
}
if !exists {
c.Client.Tags = append(c.Client.Tags, tag{
K: newTag.K,
V: newTag.V,
})
}
}
})
}
type tag struct {
K, V string
}
func joinTags(tf TagFormat, tags []tag) string {
if len(tags) == 0 || tf == 0 {
return ""
}
join := joinFuncs[tf]
return join(tags)
}
func splitTags(tf TagFormat, tags string) []tag {
if len(tags) == 0 || tf == 0 {
return nil
}
split := splitFuncs[tf]
return split(tags)
}
const (
// InfluxDB tag format.
// See https://influxdb.com/blog/2015/11/03/getting_started_with_influx_statsd.html
InfluxDB TagFormat = iota + 1
// Datadog tag format.
// See http://docs.datadoghq.com/guides/metrics/#tags
Datadog
)
var (
joinFuncs = map[TagFormat]func([]tag) string{
// InfluxDB tag format: ,tag1=payroll,region=us-west
// https://influxdb.com/blog/2015/11/03/getting_started_with_influx_statsd.html
InfluxDB: func(tags []tag) string {
var buf bytes.Buffer
for _, tag := range tags {
_ = buf.WriteByte(',')
_, _ = buf.WriteString(tag.K)
_ = buf.WriteByte('=')
_, _ = buf.WriteString(tag.V)
}
return buf.String()
},
// Datadog tag format: |#tag1:value1,tag2:value2
// http://docs.datadoghq.com/guides/dogstatsd/#datagram-format
Datadog: func(tags []tag) string {
buf := bytes.NewBufferString("|#")
first := true
for _, tag := range tags {
if first {
first = false
} else {
_ = buf.WriteByte(',')
}
_, _ = buf.WriteString(tag.K)
_ = buf.WriteByte(':')
_, _ = buf.WriteString(tag.V)
}
return buf.String()
},
}
splitFuncs = map[TagFormat]func(string) []tag{
InfluxDB: func(s string) []tag {
s = s[1:]
pairs := strings.Split(s, ",")
tags := make([]tag, len(pairs))
for i, pair := range pairs {
kv := strings.Split(pair, "=")
tags[i] = tag{K: kv[0], V: kv[1]}
}
return tags
},
Datadog: func(s string) []tag {
s = s[2:]
pairs := strings.Split(s, ",")
tags := make([]tag, len(pairs))
for i, pair := range pairs {
kv := strings.Split(pair, ":")
tags[i] = tag{K: kv[0], V: kv[1]}
}
return tags
},
}
)

169
vendor/gopkg.in/alexcesaro/statsd.v2/statsd.go

@ -0,0 +1,169 @@
package statsd
import "time"
// A Client represents a StatsD client.
type Client struct {
conn *conn
muted bool
rate float32
prefix string
tags string
}
// New returns a new Client.
func New(opts ...Option) (*Client, error) {
// The default configuration.
conf := &config{
Client: clientConfig{
Rate: 1,
},
Conn: connConfig{
Addr: ":8125",
FlushPeriod: 100 * time.Millisecond,
// Worst-case scenario:
// Ethernet MTU - IPv6 Header - TCP Header = 1500 - 40 - 20 = 1440
MaxPacketSize: 1440,
Network: "udp",
},
}
for _, o := range opts {
o(conf)
}
conn, err := newConn(conf.Conn, conf.Client.Muted)
c := &Client{
conn: conn,
muted: conf.Client.Muted,
}
if err != nil {
c.muted = true
return c, err
}
c.rate = conf.Client.Rate
c.prefix = conf.Client.Prefix
c.tags = joinTags(conf.Conn.TagFormat, conf.Client.Tags)
return c, nil
}
// Clone returns a clone of the Client. The cloned Client inherits its
// configuration from its parent.
//
// All cloned Clients share the same connection, so cloning a Client is a cheap
// operation.
func (c *Client) Clone(opts ...Option) *Client {
tf := c.conn.tagFormat
conf := &config{
Client: clientConfig{
Rate: c.rate,
Prefix: c.prefix,
Tags: splitTags(tf, c.tags),
},
}
for _, o := range opts {
o(conf)
}
clone := &Client{
conn: c.conn,
muted: c.muted || conf.Client.Muted,
rate: conf.Client.Rate,
prefix: conf.Client.Prefix,
tags: joinTags(tf, conf.Client.Tags),
}
clone.conn = c.conn
return clone
}
// Count adds n to bucket.
func (c *Client) Count(bucket string, n interface{}) {
if c.skip() {
return
}
c.conn.metric(c.prefix, bucket, n, "c", c.rate, c.tags)
}
func (c *Client) skip() bool {
return c.muted || (c.rate != 1 && randFloat() > c.rate)
}
// Increment increment the given bucket. It is equivalent to Count(bucket, 1).
func (c *Client) Increment(bucket string) {
c.Count(bucket, 1)
}
// Gauge records an absolute value for the given bucket.
func (c *Client) Gauge(bucket string, value interface{}) {
if c.skip() {
return
}
c.conn.gauge(c.prefix, bucket, value, c.tags)
}
// Timing sends a timing value to a bucket.
func (c *Client) Timing(bucket string, value interface{}) {
if c.skip() {
return
}
c.conn.metric(c.prefix, bucket, value, "ms", c.rate, c.tags)
}
// Histogram sends an histogram value to a bucket.
func (c *Client) Histogram(bucket string, value interface{}) {
if c.skip() {
return
}
c.conn.metric(c.prefix, bucket, value, "h", c.rate, c.tags)
}
// A Timing is an helper object that eases sending timing values.
type Timing struct {
start time.Time
c *Client
}
// NewTiming creates a new Timing.
func (c *Client) NewTiming() Timing {
return Timing{start: now(), c: c}
}
// Send sends the time elapsed since the creation of the Timing.
func (t Timing) Send(bucket string) {
t.c.Timing(bucket, int(t.Duration()/time.Millisecond))
}
// Duration returns the time elapsed since the creation of the Timing.
func (t Timing) Duration() time.Duration {
return now().Sub(t.start)
}
// Unique sends the given value to a set bucket.
func (c *Client) Unique(bucket string, value string) {
if c.skip() {
return
}
c.conn.unique(c.prefix, bucket, value, c.tags)
}
// Flush flushes the Client's buffer.
func (c *Client) Flush() {
if c.muted {
return
}
c.conn.mu.Lock()
c.conn.flush(0)
c.conn.mu.Unlock()
}
// Close flushes the Client's buffer and releases the associated ressources. The
// Client and all the cloned Clients must not be used afterward.
func (c *Client) Close() {
if c.muted {
return
}
c.conn.mu.Lock()
c.conn.flush(0)
c.conn.handleError(c.conn.w.Close())
c.conn.closed = true
c.conn.mu.Unlock()
}

2
vendor/modules.txt

@ -0,0 +1,2 @@
# gopkg.in/alexcesaro/statsd.v2 v2.0.0
gopkg.in/alexcesaro/statsd.v2
Loading…
Cancel
Save