replace zxq.co/ripple/hanayo
This commit is contained in:
52
vendor/github.com/DataDog/datadog-go/statsd/README.md
generated
vendored
Normal file
52
vendor/github.com/DataDog/datadog-go/statsd/README.md
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
## Overview
|
||||
|
||||
Package `statsd` provides a Go [dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/) client. Dogstatsd extends Statsd, adding tags
|
||||
and histograms.
|
||||
|
||||
## Get the code
|
||||
|
||||
$ go get github.com/DataDog/datadog-go/statsd
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
// Create the client
|
||||
c, err := statsd.New("127.0.0.1:8125")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Prefix every metric with the app name
|
||||
c.Namespace = "flubber."
|
||||
// Send the EC2 availability zone as a tag with every metric
|
||||
c.Tags = append(c.Tags, "us-east-1a")
|
||||
|
||||
// Do some metrics!
|
||||
err = c.Gauge("request.queue_depth", 12, nil, 1)
|
||||
err = c.Timing("request.duration", duration, nil, 1) // Uses a time.Duration!
|
||||
err = c.TimeInMilliseconds("request", 12, nil, 1)
|
||||
err = c.Incr("request.count_total", nil, 1)
|
||||
err = c.Decr("request.count_total", nil, 1)
|
||||
err = c.Count("request.count_total", 2, nil, 1)
|
||||
```
|
||||
|
||||
## Buffering Client
|
||||
|
||||
DogStatsD accepts packets with multiple statsd payloads in them. Using the BufferingClient via `NewBufferingClient` will buffer up commands and send them when the buffer is reached or after 100msec.
|
||||
|
||||
## Development
|
||||
|
||||
Run the tests with:
|
||||
|
||||
$ go test
|
||||
|
||||
## Documentation
|
||||
|
||||
Please see: http://godoc.org/github.com/DataDog/datadog-go/statsd
|
||||
|
||||
## License
|
||||
|
||||
go-dogstatsd is released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
|
||||
|
||||
## Credits
|
||||
|
||||
Original code by [ooyala](https://github.com/ooyala/go-dogstatsd).
|
580
vendor/github.com/DataDog/datadog-go/statsd/statsd.go
generated
vendored
Normal file
580
vendor/github.com/DataDog/datadog-go/statsd/statsd.go
generated
vendored
Normal file
@@ -0,0 +1,580 @@
|
||||
// Copyright 2013 Ooyala, Inc.
|
||||
|
||||
/*
|
||||
Package statsd provides a Go dogstatsd client. Dogstatsd extends the popular statsd,
|
||||
adding tags and histograms and pushing upstream to Datadog.
|
||||
|
||||
Refer to http://docs.datadoghq.com/guides/dogstatsd/ for information about DogStatsD.
|
||||
|
||||
Example Usage:
|
||||
|
||||
// Create the client
|
||||
c, err := statsd.New("127.0.0.1:8125")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Prefix every metric with the app name
|
||||
c.Namespace = "flubber."
|
||||
// Send the EC2 availability zone as a tag with every metric
|
||||
c.Tags = append(c.Tags, "us-east-1a")
|
||||
err = c.Gauge("request.duration", 1.2, nil, 1)
|
||||
|
||||
statsd is based on go-statsd-client.
|
||||
*/
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
OptimalPayloadSize defines the optimal payload size for a UDP datagram, 1432 bytes
|
||||
is optimal for regular networks with an MTU of 1500 so datagrams don't get
|
||||
fragmented. It's generally recommended not to fragment UDP datagrams as losing
|
||||
a single fragment will cause the entire datagram to be lost.
|
||||
|
||||
This can be increased if your network has a greater MTU or you don't mind UDP
|
||||
datagrams getting fragmented. The practical limit is MaxUDPPayloadSize
|
||||
*/
|
||||
const OptimalPayloadSize = 1432
|
||||
|
||||
/*
|
||||
MaxUDPPayloadSize defines the maximum payload size for a UDP datagram.
|
||||
Its value comes from the calculation: 65535 bytes Max UDP datagram size -
|
||||
8byte UDP header - 60byte max IP headers
|
||||
any number greater than that will see frames being cut out.
|
||||
*/
|
||||
const MaxUDPPayloadSize = 65467
|
||||
|
||||
// A Client is a handle for sending udp messages to dogstatsd. It is safe to
|
||||
// use one Client from multiple goroutines simultaneously.
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
// Namespace to prepend to all statsd calls
|
||||
Namespace string
|
||||
// Tags are global tags to be added to every statsd call
|
||||
Tags []string
|
||||
// BufferLength is the length of the buffer in commands.
|
||||
bufferLength int
|
||||
flushTime time.Duration
|
||||
commands []string
|
||||
buffer bytes.Buffer
|
||||
stop bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// New returns a pointer to a new Client given an addr in the format "hostname:port".
|
||||
func New(addr string) (*Client, error) {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.DialUDP("udp", nil, udpAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &Client{conn: conn}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewBuffered returns a Client that buffers its output and sends it in chunks.
|
||||
// Buflen is the length of the buffer in number of commands.
|
||||
func NewBuffered(addr string, buflen int) (*Client, error) {
|
||||
client, err := New(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.bufferLength = buflen
|
||||
client.commands = make([]string, 0, buflen)
|
||||
client.flushTime = time.Millisecond * 100
|
||||
go client.watch()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// format a message from its name, value, tags and rate. Also adds global
|
||||
// namespace and tags.
|
||||
func (c *Client) format(name, value string, tags []string, rate float64) string {
|
||||
var buf bytes.Buffer
|
||||
if c.Namespace != "" {
|
||||
buf.WriteString(c.Namespace)
|
||||
}
|
||||
buf.WriteString(name)
|
||||
buf.WriteString(":")
|
||||
buf.WriteString(value)
|
||||
if rate < 1 {
|
||||
buf.WriteString(`|@`)
|
||||
buf.WriteString(strconv.FormatFloat(rate, 'f', -1, 64))
|
||||
}
|
||||
|
||||
writeTagString(&buf, c.Tags, tags)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (c *Client) watch() {
|
||||
for _ = range time.Tick(c.flushTime) {
|
||||
if c.stop {
|
||||
return
|
||||
}
|
||||
c.Lock()
|
||||
if len(c.commands) > 0 {
|
||||
// FIXME: eating error here
|
||||
c.flush()
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) append(cmd string) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.commands = append(c.commands, cmd)
|
||||
// if we should flush, lets do it
|
||||
if len(c.commands) == c.bufferLength {
|
||||
if err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) joinMaxSize(cmds []string, sep string, maxSize int) ([][]byte, []int) {
|
||||
c.buffer.Reset() //clear buffer
|
||||
|
||||
var frames [][]byte
|
||||
var ncmds []int
|
||||
sepBytes := []byte(sep)
|
||||
sepLen := len(sep)
|
||||
|
||||
elem := 0
|
||||
for _, cmd := range cmds {
|
||||
needed := len(cmd)
|
||||
|
||||
if elem != 0 {
|
||||
needed = needed + sepLen
|
||||
}
|
||||
|
||||
if c.buffer.Len()+needed <= maxSize {
|
||||
if elem != 0 {
|
||||
c.buffer.Write(sepBytes)
|
||||
}
|
||||
c.buffer.WriteString(cmd)
|
||||
elem++
|
||||
} else {
|
||||
frames = append(frames, copyAndResetBuffer(&c.buffer))
|
||||
ncmds = append(ncmds, elem)
|
||||
// if cmd is bigger than maxSize it will get flushed on next loop
|
||||
c.buffer.WriteString(cmd)
|
||||
elem = 1
|
||||
}
|
||||
}
|
||||
|
||||
//add whatever is left! if there's actually something
|
||||
if c.buffer.Len() > 0 {
|
||||
frames = append(frames, copyAndResetBuffer(&c.buffer))
|
||||
ncmds = append(ncmds, elem)
|
||||
}
|
||||
|
||||
return frames, ncmds
|
||||
}
|
||||
|
||||
func copyAndResetBuffer(buf *bytes.Buffer) []byte {
|
||||
tmpBuf := make([]byte, buf.Len())
|
||||
copy(tmpBuf, buf.Bytes())
|
||||
buf.Reset()
|
||||
return tmpBuf
|
||||
}
|
||||
|
||||
// flush the commands in the buffer. Lock must be held by caller.
|
||||
func (c *Client) flush() error {
|
||||
frames, flushable := c.joinMaxSize(c.commands, "\n", OptimalPayloadSize)
|
||||
var err error
|
||||
cmdsFlushed := 0
|
||||
for i, data := range frames {
|
||||
_, e := c.conn.Write(data)
|
||||
if e != nil {
|
||||
err = e
|
||||
break
|
||||
}
|
||||
cmdsFlushed += flushable[i]
|
||||
}
|
||||
|
||||
// clear the slice with a slice op, doesn't realloc
|
||||
if cmdsFlushed == len(c.commands) {
|
||||
c.commands = c.commands[:0]
|
||||
} else {
|
||||
//this case will cause a future realloc...
|
||||
// drop problematic command though (sorry).
|
||||
c.commands = c.commands[cmdsFlushed+1:]
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) sendMsg(msg string) error {
|
||||
// return an error if message is bigger than MaxUDPPayloadSize
|
||||
if len(msg) > MaxUDPPayloadSize {
|
||||
return errors.New("message size exceeds MaxUDPPayloadSize")
|
||||
}
|
||||
|
||||
// if this client is buffered, then we'll just append this
|
||||
if c.bufferLength > 0 {
|
||||
return c.append(msg)
|
||||
}
|
||||
|
||||
_, err := c.conn.Write([]byte(msg))
|
||||
return err
|
||||
}
|
||||
|
||||
// send handles sampling and sends the message over UDP. It also adds global namespace prefixes and tags.
|
||||
func (c *Client) send(name, value string, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
if rate < 1 && rand.Float64() > rate {
|
||||
return nil
|
||||
}
|
||||
data := c.format(name, value, tags, rate)
|
||||
return c.sendMsg(data)
|
||||
}
|
||||
|
||||
// Gauge measures the value of a metric at a particular time.
|
||||
func (c *Client) Gauge(name string, value float64, tags []string, rate float64) error {
|
||||
stat := fmt.Sprintf("%f|g", value)
|
||||
return c.send(name, stat, tags, rate)
|
||||
}
|
||||
|
||||
// Count tracks how many times something happened per second.
|
||||
func (c *Client) Count(name string, value int64, tags []string, rate float64) error {
|
||||
stat := fmt.Sprintf("%d|c", value)
|
||||
return c.send(name, stat, tags, rate)
|
||||
}
|
||||
|
||||
// Histogram tracks the statistical distribution of a set of values.
|
||||
func (c *Client) Histogram(name string, value float64, tags []string, rate float64) error {
|
||||
stat := fmt.Sprintf("%f|h", value)
|
||||
return c.send(name, stat, tags, rate)
|
||||
}
|
||||
|
||||
// Decr is just Count of -1
|
||||
func (c *Client) Decr(name string, tags []string, rate float64) error {
|
||||
return c.send(name, "-1|c", tags, rate)
|
||||
}
|
||||
|
||||
// Incr is just Count of 1
|
||||
func (c *Client) Incr(name string, tags []string, rate float64) error {
|
||||
return c.send(name, "1|c", tags, rate)
|
||||
}
|
||||
|
||||
// Set counts the number of unique elements in a group.
|
||||
func (c *Client) Set(name string, value string, tags []string, rate float64) error {
|
||||
stat := fmt.Sprintf("%s|s", value)
|
||||
return c.send(name, stat, tags, rate)
|
||||
}
|
||||
|
||||
// Timing sends timing information, it is an alias for TimeInMilliseconds
|
||||
func (c *Client) Timing(name string, value time.Duration, tags []string, rate float64) error {
|
||||
return c.TimeInMilliseconds(name, value.Seconds()*1000, tags, rate)
|
||||
}
|
||||
|
||||
// TimeInMilliseconds sends timing information in milliseconds.
|
||||
// It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing)
|
||||
func (c *Client) TimeInMilliseconds(name string, value float64, tags []string, rate float64) error {
|
||||
stat := fmt.Sprintf("%f|ms", value)
|
||||
return c.send(name, stat, tags, rate)
|
||||
}
|
||||
|
||||
// Event sends the provided Event.
|
||||
func (c *Client) Event(e *Event) error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
stat, err := e.Encode(c.Tags...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.sendMsg(stat)
|
||||
}
|
||||
|
||||
// SimpleEvent sends an event with the provided title and text.
|
||||
func (c *Client) SimpleEvent(title, text string) error {
|
||||
e := NewEvent(title, text)
|
||||
return c.Event(e)
|
||||
}
|
||||
|
||||
// ServiceCheck sends the provided ServiceCheck.
|
||||
func (c *Client) ServiceCheck(sc *ServiceCheck) error {
|
||||
stat, err := sc.Encode(c.Tags...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.sendMsg(stat)
|
||||
}
|
||||
|
||||
// SimpleServiceCheck sends an serviceCheck with the provided name and status.
|
||||
func (c *Client) SimpleServiceCheck(name string, status ServiceCheckStatus) error {
|
||||
sc := NewServiceCheck(name, status)
|
||||
return c.ServiceCheck(sc)
|
||||
}
|
||||
|
||||
// Close the client connection.
|
||||
func (c *Client) Close() error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
c.stop = true
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// Events support
|
||||
|
||||
type eventAlertType string
|
||||
|
||||
const (
|
||||
// Info is the "info" AlertType for events
|
||||
Info eventAlertType = "info"
|
||||
// Error is the "error" AlertType for events
|
||||
Error eventAlertType = "error"
|
||||
// Warning is the "warning" AlertType for events
|
||||
Warning eventAlertType = "warning"
|
||||
// Success is the "success" AlertType for events
|
||||
Success eventAlertType = "success"
|
||||
)
|
||||
|
||||
type eventPriority string
|
||||
|
||||
const (
|
||||
// Normal is the "normal" Priority for events
|
||||
Normal eventPriority = "normal"
|
||||
// Low is the "low" Priority for events
|
||||
Low eventPriority = "low"
|
||||
)
|
||||
|
||||
// An Event is an object that can be posted to your DataDog event stream.
|
||||
type Event struct {
|
||||
// Title of the event. Required.
|
||||
Title string
|
||||
// Text is the description of the event. Required.
|
||||
Text string
|
||||
// Timestamp is a timestamp for the event. If not provided, the dogstatsd
|
||||
// server will set this to the current time.
|
||||
Timestamp time.Time
|
||||
// Hostname for the event.
|
||||
Hostname string
|
||||
// AggregationKey groups this event with others of the same key.
|
||||
AggregationKey string
|
||||
// Priority of the event. Can be statsd.Low or statsd.Normal.
|
||||
Priority eventPriority
|
||||
// SourceTypeName is a source type for the event.
|
||||
SourceTypeName string
|
||||
// AlertType can be statsd.Info, statsd.Error, statsd.Warning, or statsd.Success.
|
||||
// If absent, the default value applied by the dogstatsd server is Info.
|
||||
AlertType eventAlertType
|
||||
// Tags for the event.
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// NewEvent creates a new event with the given title and text. Error checking
|
||||
// against these values is done at send-time, or upon running e.Check.
|
||||
func NewEvent(title, text string) *Event {
|
||||
return &Event{
|
||||
Title: title,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Check verifies that an event is valid.
|
||||
func (e Event) Check() error {
|
||||
if len(e.Title) == 0 {
|
||||
return fmt.Errorf("statsd.Event title is required")
|
||||
}
|
||||
if len(e.Text) == 0 {
|
||||
return fmt.Errorf("statsd.Event text is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode returns the dogstatsd wire protocol representation for an event.
|
||||
// Tags may be passed which will be added to the encoded output but not to
|
||||
// the Event's list of tags, eg. for default tags.
|
||||
func (e Event) Encode(tags ...string) (string, error) {
|
||||
err := e.Check()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
text := e.escapedText()
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString("_e{")
|
||||
buffer.WriteString(strconv.FormatInt(int64(len(e.Title)), 10))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(strconv.FormatInt(int64(len(text)), 10))
|
||||
buffer.WriteString("}:")
|
||||
buffer.WriteString(e.Title)
|
||||
buffer.WriteRune('|')
|
||||
buffer.WriteString(text)
|
||||
|
||||
if !e.Timestamp.IsZero() {
|
||||
buffer.WriteString("|d:")
|
||||
buffer.WriteString(strconv.FormatInt(int64(e.Timestamp.Unix()), 10))
|
||||
}
|
||||
|
||||
if len(e.Hostname) != 0 {
|
||||
buffer.WriteString("|h:")
|
||||
buffer.WriteString(e.Hostname)
|
||||
}
|
||||
|
||||
if len(e.AggregationKey) != 0 {
|
||||
buffer.WriteString("|k:")
|
||||
buffer.WriteString(e.AggregationKey)
|
||||
|
||||
}
|
||||
|
||||
if len(e.Priority) != 0 {
|
||||
buffer.WriteString("|p:")
|
||||
buffer.WriteString(string(e.Priority))
|
||||
}
|
||||
|
||||
if len(e.SourceTypeName) != 0 {
|
||||
buffer.WriteString("|s:")
|
||||
buffer.WriteString(e.SourceTypeName)
|
||||
}
|
||||
|
||||
if len(e.AlertType) != 0 {
|
||||
buffer.WriteString("|t:")
|
||||
buffer.WriteString(string(e.AlertType))
|
||||
}
|
||||
|
||||
writeTagString(&buffer, tags, e.Tags)
|
||||
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
// ServiceCheck support
|
||||
|
||||
type ServiceCheckStatus byte
|
||||
|
||||
const (
|
||||
// Ok is the "ok" ServiceCheck status
|
||||
Ok ServiceCheckStatus = 0
|
||||
// Warn is the "warning" ServiceCheck status
|
||||
Warn ServiceCheckStatus = 1
|
||||
// Critical is the "critical" ServiceCheck status
|
||||
Critical ServiceCheckStatus = 2
|
||||
// Unknown is the "unknown" ServiceCheck status
|
||||
Unknown ServiceCheckStatus = 3
|
||||
)
|
||||
|
||||
// An ServiceCheck is an object that contains status of DataDog service check.
|
||||
type ServiceCheck struct {
|
||||
// Name of the service check. Required.
|
||||
Name string
|
||||
// Status of service check. Required.
|
||||
Status ServiceCheckStatus
|
||||
// Timestamp is a timestamp for the serviceCheck. If not provided, the dogstatsd
|
||||
// server will set this to the current time.
|
||||
Timestamp time.Time
|
||||
// Hostname for the serviceCheck.
|
||||
Hostname string
|
||||
// A message describing the current state of the serviceCheck.
|
||||
Message string
|
||||
// Tags for the serviceCheck.
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// NewServiceCheck creates a new serviceCheck with the given name and status. Error checking
|
||||
// against these values is done at send-time, or upon running sc.Check.
|
||||
func NewServiceCheck(name string, status ServiceCheckStatus) *ServiceCheck {
|
||||
return &ServiceCheck{
|
||||
Name: name,
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
|
||||
// Check verifies that an event is valid.
|
||||
func (sc ServiceCheck) Check() error {
|
||||
if len(sc.Name) == 0 {
|
||||
return fmt.Errorf("statsd.ServiceCheck name is required")
|
||||
}
|
||||
if byte(sc.Status) < 0 || byte(sc.Status) > 3 {
|
||||
return fmt.Errorf("statsd.ServiceCheck status has invalid value")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode returns the dogstatsd wire protocol representation for an serviceCheck.
|
||||
// Tags may be passed which will be added to the encoded output but not to
|
||||
// the Event's list of tags, eg. for default tags.
|
||||
func (sc ServiceCheck) Encode(tags ...string) (string, error) {
|
||||
err := sc.Check()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
message := sc.escapedMessage()
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString("_sc|")
|
||||
buffer.WriteString(sc.Name)
|
||||
buffer.WriteRune('|')
|
||||
buffer.WriteString(strconv.FormatInt(int64(sc.Status), 10))
|
||||
|
||||
if !sc.Timestamp.IsZero() {
|
||||
buffer.WriteString("|d:")
|
||||
buffer.WriteString(strconv.FormatInt(int64(sc.Timestamp.Unix()), 10))
|
||||
}
|
||||
|
||||
if len(sc.Hostname) != 0 {
|
||||
buffer.WriteString("|h:")
|
||||
buffer.WriteString(sc.Hostname)
|
||||
}
|
||||
|
||||
writeTagString(&buffer, tags, sc.Tags)
|
||||
|
||||
if len(message) != 0 {
|
||||
buffer.WriteString("|m:")
|
||||
buffer.WriteString(message)
|
||||
}
|
||||
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
func (e Event) escapedText() string {
|
||||
return strings.Replace(e.Text, "\n", "\\n", -1)
|
||||
}
|
||||
|
||||
func (sc ServiceCheck) escapedMessage() string {
|
||||
msg := strings.Replace(sc.Message, "\n", "\\n", -1)
|
||||
return strings.Replace(msg, "m:", `m\:`, -1)
|
||||
}
|
||||
|
||||
func removeNewlines(str string) string {
|
||||
return strings.Replace(str, "\n", "", -1)
|
||||
}
|
||||
|
||||
func writeTagString(w io.Writer, tagList1, tagList2 []string) {
|
||||
// the tag lists may be shared with other callers, so we cannot modify
|
||||
// them in any way (which means we cannot append to them either)
|
||||
// therefore we must make an entirely separate copy just for this call
|
||||
totalLen := len(tagList1) + len(tagList2)
|
||||
if totalLen == 0 {
|
||||
return
|
||||
}
|
||||
tags := make([]string, 0, totalLen)
|
||||
tags = append(tags, tagList1...)
|
||||
tags = append(tags, tagList2...)
|
||||
|
||||
io.WriteString(w, "|#")
|
||||
io.WriteString(w, removeNewlines(tags[0]))
|
||||
for _, tag := range tags[1:] {
|
||||
io.WriteString(w, ",")
|
||||
io.WriteString(w, removeNewlines(tag))
|
||||
}
|
||||
}
|
620
vendor/github.com/DataDog/datadog-go/statsd/statsd_test.go
generated
vendored
Normal file
620
vendor/github.com/DataDog/datadog-go/statsd/statsd_test.go
generated
vendored
Normal file
@@ -0,0 +1,620 @@
|
||||
// Copyright 2013 Ooyala, Inc.
|
||||
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dogstatsdTests = []struct {
|
||||
GlobalNamespace string
|
||||
GlobalTags []string
|
||||
Method string
|
||||
Metric string
|
||||
Value interface{}
|
||||
Tags []string
|
||||
Rate float64
|
||||
Expected string
|
||||
}{
|
||||
{"", nil, "Gauge", "test.gauge", 1.0, nil, 1.0, "test.gauge:1.000000|g"},
|
||||
{"", nil, "Gauge", "test.gauge", 1.0, nil, 0.999999, "test.gauge:1.000000|g|@0.999999"},
|
||||
{"", nil, "Gauge", "test.gauge", 1.0, []string{"tagA"}, 1.0, "test.gauge:1.000000|g|#tagA"},
|
||||
{"", nil, "Gauge", "test.gauge", 1.0, []string{"tagA", "tagB"}, 1.0, "test.gauge:1.000000|g|#tagA,tagB"},
|
||||
{"", nil, "Gauge", "test.gauge", 1.0, []string{"tagA"}, 0.999999, "test.gauge:1.000000|g|@0.999999|#tagA"},
|
||||
{"", nil, "Count", "test.count", int64(1), []string{"tagA"}, 1.0, "test.count:1|c|#tagA"},
|
||||
{"", nil, "Count", "test.count", int64(-1), []string{"tagA"}, 1.0, "test.count:-1|c|#tagA"},
|
||||
{"", nil, "Histogram", "test.histogram", 2.3, []string{"tagA"}, 1.0, "test.histogram:2.300000|h|#tagA"},
|
||||
{"", nil, "Set", "test.set", "uuid", []string{"tagA"}, 1.0, "test.set:uuid|s|#tagA"},
|
||||
{"flubber.", nil, "Set", "test.set", "uuid", []string{"tagA"}, 1.0, "flubber.test.set:uuid|s|#tagA"},
|
||||
{"", []string{"tagC"}, "Set", "test.set", "uuid", []string{"tagA"}, 1.0, "test.set:uuid|s|#tagC,tagA"},
|
||||
{"", nil, "Count", "test.count", int64(1), []string{"hello\nworld"}, 1.0, "test.count:1|c|#helloworld"},
|
||||
}
|
||||
|
||||
func assertNotPanics(t *testing.T, f func()) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}()
|
||||
f()
|
||||
}
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
addr := "localhost:1201"
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
client, err := New(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range dogstatsdTests {
|
||||
client.Namespace = tt.GlobalNamespace
|
||||
client.Tags = tt.GlobalTags
|
||||
method := reflect.ValueOf(client).MethodByName(tt.Method)
|
||||
e := method.Call([]reflect.Value{
|
||||
reflect.ValueOf(tt.Metric),
|
||||
reflect.ValueOf(tt.Value),
|
||||
reflect.ValueOf(tt.Tags),
|
||||
reflect.ValueOf(tt.Rate)})[0]
|
||||
errInter := e.Interface()
|
||||
if errInter != nil {
|
||||
t.Fatal(errInter.(error))
|
||||
}
|
||||
|
||||
bytes := make([]byte, 1024)
|
||||
n, err := server.Read(bytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
message := bytes[:n]
|
||||
if string(message) != tt.Expected {
|
||||
t.Errorf("Expected: %s. Actual: %s", tt.Expected, string(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufferedClient(t *testing.T) {
|
||||
addr := "localhost:1201"
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
conn, err := net.DialUDP("udp", nil, udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bufferLength := 8
|
||||
client := &Client{
|
||||
conn: conn,
|
||||
commands: make([]string, 0, bufferLength),
|
||||
bufferLength: bufferLength,
|
||||
}
|
||||
|
||||
client.Namespace = "foo."
|
||||
client.Tags = []string{"dd:2"}
|
||||
|
||||
dur, _ := time.ParseDuration("123us")
|
||||
|
||||
client.Incr("ic", nil, 1)
|
||||
client.Decr("dc", nil, 1)
|
||||
client.Count("cc", 1, nil, 1)
|
||||
client.Gauge("gg", 10, nil, 1)
|
||||
client.Histogram("hh", 1, nil, 1)
|
||||
client.Timing("tt", dur, nil, 1)
|
||||
client.Set("ss", "ss", nil, 1)
|
||||
|
||||
if len(client.commands) != 7 {
|
||||
t.Errorf("Expected client to have buffered 7 commands, but found %d\n", len(client.commands))
|
||||
}
|
||||
|
||||
client.Set("ss", "xx", nil, 1)
|
||||
err = client.flush()
|
||||
if err != nil {
|
||||
t.Errorf("Error sending: %s", err)
|
||||
}
|
||||
|
||||
if len(client.commands) != 0 {
|
||||
t.Errorf("Expecting send to flush commands, but found %d\n", len(client.commands))
|
||||
}
|
||||
|
||||
buffer := make([]byte, 4096)
|
||||
n, err := io.ReadAtLeast(server, buffer, 1)
|
||||
result := string(buffer[:n])
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
expected := []string{
|
||||
`foo.ic:1|c|#dd:2`,
|
||||
`foo.dc:-1|c|#dd:2`,
|
||||
`foo.cc:1|c|#dd:2`,
|
||||
`foo.gg:10.000000|g|#dd:2`,
|
||||
`foo.hh:1.000000|h|#dd:2`,
|
||||
`foo.tt:0.123000|ms|#dd:2`,
|
||||
`foo.ss:ss|s|#dd:2`,
|
||||
`foo.ss:xx|s|#dd:2`,
|
||||
}
|
||||
|
||||
for i, res := range strings.Split(result, "\n") {
|
||||
if res != expected[i] {
|
||||
t.Errorf("Got `%s`, expected `%s`", res, expected[i])
|
||||
}
|
||||
}
|
||||
|
||||
client.Event(&Event{Title: "title1", Text: "text1", Priority: Normal, AlertType: Success, Tags: []string{"tagg"}})
|
||||
client.SimpleEvent("event1", "text1")
|
||||
|
||||
if len(client.commands) != 2 {
|
||||
t.Errorf("Expected to find %d commands, but found %d\n", 2, len(client.commands))
|
||||
}
|
||||
|
||||
err = client.flush()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error sending: %s", err)
|
||||
}
|
||||
|
||||
if len(client.commands) != 0 {
|
||||
t.Errorf("Expecting send to flush commands, but found %d\n", len(client.commands))
|
||||
}
|
||||
|
||||
buffer = make([]byte, 1024)
|
||||
n, err = io.ReadAtLeast(server, buffer, 1)
|
||||
result = string(buffer[:n])
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
t.Errorf("Read 0 bytes but expected more.")
|
||||
}
|
||||
|
||||
expected = []string{
|
||||
`_e{6,5}:title1|text1|p:normal|t:success|#dd:2,tagg`,
|
||||
`_e{6,5}:event1|text1|#dd:2`,
|
||||
}
|
||||
|
||||
for i, res := range strings.Split(result, "\n") {
|
||||
if res != expected[i] {
|
||||
t.Errorf("Got `%s`, expected `%s`", res, expected[i])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestJoinMaxSize(t *testing.T) {
|
||||
c := Client{}
|
||||
elements := []string{"abc", "abcd", "ab", "xyz", "foobaz", "x", "wwxxyyzz"}
|
||||
res, n := c.joinMaxSize(elements, " ", 8)
|
||||
|
||||
if len(res) != len(n) && len(res) != 4 {
|
||||
t.Errorf("Was expecting 4 frames to flush but got: %v - %v", n, res)
|
||||
}
|
||||
if n[0] != 2 {
|
||||
t.Errorf("Was expecting 2 elements in first frame but got: %v", n[0])
|
||||
}
|
||||
if string(res[0]) != "abc abcd" {
|
||||
t.Errorf("Join should have returned \"abc abcd\" in frame, but found: %s", res[0])
|
||||
}
|
||||
if n[1] != 2 {
|
||||
t.Errorf("Was expecting 2 elements in second frame but got: %v - %v", n[1], n)
|
||||
}
|
||||
if string(res[1]) != "ab xyz" {
|
||||
t.Errorf("Join should have returned \"ab xyz\" in frame, but found: %s", res[1])
|
||||
}
|
||||
if n[2] != 2 {
|
||||
t.Errorf("Was expecting 2 elements in third frame but got: %v - %v", n[2], n)
|
||||
}
|
||||
if string(res[2]) != "foobaz x" {
|
||||
t.Errorf("Join should have returned \"foobaz x\" in frame, but found: %s", res[2])
|
||||
}
|
||||
if n[3] != 1 {
|
||||
t.Errorf("Was expecting 1 element in fourth frame but got: %v - %v", n[3], n)
|
||||
}
|
||||
if string(res[3]) != "wwxxyyzz" {
|
||||
t.Errorf("Join should have returned \"wwxxyyzz\" in frame, but found: %s", res[3])
|
||||
}
|
||||
|
||||
res, n = c.joinMaxSize(elements, " ", 11)
|
||||
|
||||
if len(res) != len(n) && len(res) != 3 {
|
||||
t.Errorf("Was expecting 3 frames to flush but got: %v - %v", n, res)
|
||||
}
|
||||
if n[0] != 3 {
|
||||
t.Errorf("Was expecting 3 elements in first frame but got: %v", n[0])
|
||||
}
|
||||
if string(res[0]) != "abc abcd ab" {
|
||||
t.Errorf("Join should have returned \"abc abcd ab\" in frame, but got: %s", res[0])
|
||||
}
|
||||
if n[1] != 2 {
|
||||
t.Errorf("Was expecting 2 elements in second frame but got: %v", n[1])
|
||||
}
|
||||
if string(res[1]) != "xyz foobaz" {
|
||||
t.Errorf("Join should have returned \"xyz foobaz\" in frame, but got: %s", res[1])
|
||||
}
|
||||
if n[2] != 2 {
|
||||
t.Errorf("Was expecting 2 elements in third frame but got: %v", n[2])
|
||||
}
|
||||
if string(res[2]) != "x wwxxyyzz" {
|
||||
t.Errorf("Join should have returned \"x wwxxyyzz\" in frame, but got: %s", res[2])
|
||||
}
|
||||
|
||||
res, n = c.joinMaxSize(elements, " ", 8)
|
||||
|
||||
if len(res) != len(n) && len(res) != 7 {
|
||||
t.Errorf("Was expecting 7 frames to flush but got: %v - %v", n, res)
|
||||
}
|
||||
if n[0] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in frame but got: %d - %v", n[0], res)
|
||||
}
|
||||
if string(res[0]) != "abc" {
|
||||
t.Errorf("Join should have returned \"abc\" in first frame, but got: %s", res)
|
||||
}
|
||||
if n[1] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in frame but got: %d - %v", n[1], res)
|
||||
}
|
||||
if string(res[1]) != "abcd" {
|
||||
t.Errorf("Join should have returned \"abcd\" in second frame, but got: %s", res[1])
|
||||
}
|
||||
if n[2] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in third frame but got: %d - %v", n[2], res)
|
||||
}
|
||||
if string(res[2]) != "ab" {
|
||||
t.Errorf("Join should have returned \"ab\" in third frame, but got: %s", res[2])
|
||||
}
|
||||
if n[3] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in fourth frame but got: %d - %v", n[3], res)
|
||||
}
|
||||
if string(res[3]) != "xyz" {
|
||||
t.Errorf("Join should have returned \"xyz\" in fourth frame, but got: %s", res[3])
|
||||
}
|
||||
if n[4] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in fifth frame but got: %d - %v", n[4], res)
|
||||
}
|
||||
if string(res[4]) != "foobaz" {
|
||||
t.Errorf("Join should have returned \"foobaz\" in fifth frame, but got: %s", res[4])
|
||||
}
|
||||
if n[5] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in sixth frame but got: %d - %v", n[5], res)
|
||||
}
|
||||
if string(res[5]) != "x" {
|
||||
t.Errorf("Join should have returned \"x\" in sixth frame, but got: %s", res[5])
|
||||
}
|
||||
if n[6] != 1 {
|
||||
t.Errorf("Separator is long, expected a single element in seventh frame but got: %d - %v", n[6], res)
|
||||
}
|
||||
if string(res[6]) != "wwxxyyzz" {
|
||||
t.Errorf("Join should have returned \"wwxxyyzz\" in seventh frame, but got: %s", res[6])
|
||||
}
|
||||
|
||||
res, n = c.joinMaxSize(elements[4:], " ", 6)
|
||||
if len(res) != len(n) && len(res) != 3 {
|
||||
t.Errorf("Was expecting 3 frames to flush but got: %v - %v", n, res)
|
||||
|
||||
}
|
||||
if n[0] != 1 {
|
||||
t.Errorf("Element should just fit in frame - expected single element in frame: %d - %v", n[0], res)
|
||||
}
|
||||
if string(res[0]) != "foobaz" {
|
||||
t.Errorf("Join should have returned \"foobaz\" in first frame, but got: %s", res[0])
|
||||
}
|
||||
if n[1] != 1 {
|
||||
t.Errorf("Single element expected in frame, but got. %d - %v", n[1], res)
|
||||
}
|
||||
if string(res[1]) != "x" {
|
||||
t.Errorf("Join should' have returned \"x\" in second frame, but got: %s", res[1])
|
||||
}
|
||||
if n[2] != 1 {
|
||||
t.Errorf("Even though element is greater then max size we still try to send it. %d - %v", n[2], res)
|
||||
}
|
||||
if string(res[2]) != "wwxxyyzz" {
|
||||
t.Errorf("Join should have returned \"wwxxyyzz\" in third frame, but got: %s", res[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMsg(t *testing.T) {
|
||||
addr := "localhost:1201"
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
conn, err := net.DialUDP("udp", nil, udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := &Client{
|
||||
conn: conn,
|
||||
bufferLength: 0,
|
||||
}
|
||||
|
||||
err = client.sendMsg(strings.Repeat("x", MaxUDPPayloadSize+1))
|
||||
if err == nil {
|
||||
t.Error("Expected error to be returned if message size is bigger than MaxUDPPayloadSize")
|
||||
}
|
||||
|
||||
longMsg := strings.Repeat("x", MaxUDPPayloadSize)
|
||||
|
||||
err = client.sendMsg(longMsg)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error to be returned if message size is smaller or equal to MaxUDPPayloadSize, got: %s", err.Error())
|
||||
}
|
||||
|
||||
buffer := make([]byte, MaxUDPPayloadSize+1)
|
||||
n, err := io.ReadAtLeast(server, buffer, 1)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error to be returned reading the buffer, got: %s", err.Error())
|
||||
}
|
||||
|
||||
if n != MaxUDPPayloadSize {
|
||||
t.Fatalf("Failed to read full message from buffer. Got size `%d` expected `%d`", n, MaxUDPPayloadSize)
|
||||
}
|
||||
|
||||
if string(buffer[:n]) != longMsg {
|
||||
t.Fatalf("The received message did not match what we expect.")
|
||||
}
|
||||
|
||||
client = &Client{
|
||||
conn: conn,
|
||||
commands: make([]string, 0, 1),
|
||||
bufferLength: 1,
|
||||
}
|
||||
|
||||
err = client.sendMsg(strings.Repeat("x", MaxUDPPayloadSize+1))
|
||||
if err == nil {
|
||||
t.Error("Expected error to be returned if message size is bigger than MaxUDPPayloadSize")
|
||||
}
|
||||
|
||||
err = client.sendMsg(longMsg)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error to be returned if message size is smaller or equal to MaxUDPPayloadSize, got: %s", err.Error())
|
||||
}
|
||||
|
||||
client.Lock()
|
||||
err = client.flush()
|
||||
client.Unlock()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error to be returned flushing the client, got: %s", err.Error())
|
||||
}
|
||||
|
||||
buffer = make([]byte, MaxUDPPayloadSize+1)
|
||||
n, err = io.ReadAtLeast(server, buffer, 1)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error to be returned reading the buffer, got: %s", err.Error())
|
||||
}
|
||||
|
||||
if n != MaxUDPPayloadSize {
|
||||
t.Fatalf("Failed to read full message from buffer. Got size `%d` expected `%d`", n, MaxUDPPayloadSize)
|
||||
}
|
||||
|
||||
if string(buffer[:n]) != longMsg {
|
||||
t.Fatalf("The received message did not match what we expect.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilSafe(t *testing.T) {
|
||||
var c *Client
|
||||
assertNotPanics(t, func() { c.Close() })
|
||||
assertNotPanics(t, func() { c.Count("", 0, nil, 1) })
|
||||
assertNotPanics(t, func() { c.Histogram("", 0, nil, 1) })
|
||||
assertNotPanics(t, func() { c.Gauge("", 0, nil, 1) })
|
||||
assertNotPanics(t, func() { c.Set("", "", nil, 1) })
|
||||
assertNotPanics(t, func() { c.send("", "", nil, 1) })
|
||||
assertNotPanics(t, func() { c.SimpleEvent("", "") })
|
||||
}
|
||||
|
||||
func TestEvents(t *testing.T) {
|
||||
matrix := []struct {
|
||||
event *Event
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
NewEvent("Hello", "Something happened to my event"),
|
||||
`_e{5,30}:Hello|Something happened to my event`,
|
||||
}, {
|
||||
&Event{Title: "hi", Text: "okay", AggregationKey: "foo"},
|
||||
`_e{2,4}:hi|okay|k:foo`,
|
||||
}, {
|
||||
&Event{Title: "hi", Text: "okay", AggregationKey: "foo", AlertType: Info},
|
||||
`_e{2,4}:hi|okay|k:foo|t:info`,
|
||||
}, {
|
||||
&Event{Title: "hi", Text: "w/e", AlertType: Error, Priority: Normal},
|
||||
`_e{2,3}:hi|w/e|p:normal|t:error`,
|
||||
}, {
|
||||
&Event{Title: "hi", Text: "uh", Tags: []string{"host:foo", "app:bar"}},
|
||||
`_e{2,2}:hi|uh|#host:foo,app:bar`,
|
||||
}, {
|
||||
&Event{Title: "hi", Text: "line1\nline2", Tags: []string{"hello\nworld"}},
|
||||
`_e{2,12}:hi|line1\nline2|#helloworld`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, m := range matrix {
|
||||
r, err := m.event.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("Error encoding: %s\n", err)
|
||||
continue
|
||||
}
|
||||
if r != m.encoded {
|
||||
t.Errorf("Expected `%s`, got `%s`\n", m.encoded, r)
|
||||
}
|
||||
}
|
||||
|
||||
e := NewEvent("", "hi")
|
||||
if _, err := e.Encode(); err == nil {
|
||||
t.Errorf("Expected error on empty Title.")
|
||||
}
|
||||
|
||||
e = NewEvent("hi", "")
|
||||
if _, err := e.Encode(); err == nil {
|
||||
t.Errorf("Expected error on empty Text.")
|
||||
}
|
||||
|
||||
e = NewEvent("hello", "world")
|
||||
s, err := e.Encode("tag1", "tag2")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := "_e{5,5}:hello|world|#tag1,tag2"
|
||||
if s != expected {
|
||||
t.Errorf("Expected %s, got %s", expected, s)
|
||||
}
|
||||
if len(e.Tags) != 0 {
|
||||
t.Errorf("Modified event in place illegally.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceChecks(t *testing.T) {
|
||||
matrix := []struct {
|
||||
serviceCheck *ServiceCheck
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
NewServiceCheck("DataCatService", Ok),
|
||||
`_sc|DataCatService|0`,
|
||||
}, {
|
||||
NewServiceCheck("DataCatService", Warn),
|
||||
`_sc|DataCatService|1`,
|
||||
}, {
|
||||
NewServiceCheck("DataCatService", Critical),
|
||||
`_sc|DataCatService|2`,
|
||||
}, {
|
||||
NewServiceCheck("DataCatService", Unknown),
|
||||
`_sc|DataCatService|3`,
|
||||
}, {
|
||||
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat"},
|
||||
`_sc|DataCatService|0|h:DataStation.Cat`,
|
||||
}, {
|
||||
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes valuable message"},
|
||||
`_sc|DataCatService|0|h:DataStation.Cat|m:Here goes valuable message`,
|
||||
}, {
|
||||
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here are some cyrillic chars: к л м н о п р с т у ф х ц ч ш"},
|
||||
`_sc|DataCatService|0|h:DataStation.Cat|m:Here are some cyrillic chars: к л м н о п р с т у ф х ц ч ш`,
|
||||
}, {
|
||||
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes valuable message", Tags: []string{"host:foo", "app:bar"}},
|
||||
`_sc|DataCatService|0|h:DataStation.Cat|#host:foo,app:bar|m:Here goes valuable message`,
|
||||
}, {
|
||||
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes \n that should be escaped", Tags: []string{"host:foo", "app:b\nar"}},
|
||||
`_sc|DataCatService|0|h:DataStation.Cat|#host:foo,app:bar|m:Here goes \n that should be escaped`,
|
||||
}, {
|
||||
&ServiceCheck{Name: "DataCatService", Status: Ok, Hostname: "DataStation.Cat", Message: "Here goes m: that should be escaped", Tags: []string{"host:foo", "app:bar"}},
|
||||
`_sc|DataCatService|0|h:DataStation.Cat|#host:foo,app:bar|m:Here goes m\: that should be escaped`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, m := range matrix {
|
||||
r, err := m.serviceCheck.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("Error encoding: %s\n", err)
|
||||
continue
|
||||
}
|
||||
if r != m.encoded {
|
||||
t.Errorf("Expected `%s`, got `%s`\n", m.encoded, r)
|
||||
}
|
||||
}
|
||||
|
||||
sc := NewServiceCheck("", Ok)
|
||||
if _, err := sc.Encode(); err == nil {
|
||||
t.Errorf("Expected error on empty Name.")
|
||||
}
|
||||
|
||||
sc = NewServiceCheck("sc", ServiceCheckStatus(5))
|
||||
if _, err := sc.Encode(); err == nil {
|
||||
t.Errorf("Expected error on invalid status value.")
|
||||
}
|
||||
|
||||
sc = NewServiceCheck("hello", Warn)
|
||||
s, err := sc.Encode("tag1", "tag2")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := "_sc|hello|1|#tag1,tag2"
|
||||
if s != expected {
|
||||
t.Errorf("Expected %s, got %s", expected, s)
|
||||
}
|
||||
if len(sc.Tags) != 0 {
|
||||
t.Errorf("Modified serviceCheck in place illegally.")
|
||||
}
|
||||
}
|
||||
|
||||
// These benchmarks show that using a buffer instead of sprintf-ing together
|
||||
// a bunch of intermediate strings is 4-5x faster
|
||||
|
||||
func BenchmarkFormatNew(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c := &Client{}
|
||||
c.Namespace = "foo.bar."
|
||||
c.Tags = []string{"app:foo", "host:bar"}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.format("system.cpu.idle", "10", []string{"foo"}, 1)
|
||||
c.format("system.cpu.load", "0.1", nil, 0.9)
|
||||
}
|
||||
}
|
||||
|
||||
// Old formatting function, added to client for tests
|
||||
func (c *Client) formatOld(name, value string, tags []string, rate float64) string {
|
||||
if rate < 1 {
|
||||
value = fmt.Sprintf("%s|@%f", value, rate)
|
||||
}
|
||||
if c.Namespace != "" {
|
||||
name = fmt.Sprintf("%s%s", c.Namespace, name)
|
||||
}
|
||||
|
||||
tags = append(c.Tags, tags...)
|
||||
if len(tags) > 0 {
|
||||
value = fmt.Sprintf("%s|#%s", value, strings.Join(tags, ","))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%s", name, value)
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkFormatOld(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c := &Client{}
|
||||
c.Namespace = "foo.bar."
|
||||
c.Tags = []string{"app:foo", "host:bar"}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.formatOld("system.cpu.idle", "10", []string{"foo"}, 1)
|
||||
c.formatOld("system.cpu.load", "0.1", nil, 0.9)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user