Git Product home page Git Product logo

datadog-go's Introduction

Build Status

Datadog Go

Godoc license

datadog-go is a library that provides a DogStatsD client in Golang.

Go 1.12+ is officially supported. Older versions might work but are not tested.

The following documentation is available:

New major version

The new major version v5 is now the default. All new features will be added to this version and only bugfixes will be backported to v4 (see v4 branch).

v5 introduce a number of breaking changes compare to v4, see the CHANGELOG for more information.

Note that the import paths for v5 and v4 are different:

  • v5: github.com/DataDog/datadog-go/v5/statsd
  • v4: github.com/DataDog/datadog-go/statsd

When migrating to the v5 you will need to upgrade your imports.

Installation

Get the code with:

$ go get github.com/DataDog/datadog-go/v5/statsd

Then create a new DogStatsD client:

package main

import (
    "log"
    "github.com/DataDog/datadog-go/v5/statsd"
)

func main() {
    statsd, err := statsd.New("127.0.0.1:8125")
    if err != nil {
        log.Fatal(err)
    }
}

Find a list of all the available options for your DogStatsD Client in the Datadog-go godoc documentation or in Datadog public DogStatsD documentation.

Supported environment variables

  • If the addr parameter is empty, the client will:
    • First use the DD_DOGSTATSD_URL environment variables to build a target address. This must be a URL that start with either udp:// (to connect using UDP) or with unix:// (to use a Unix Domain Socket). Example for UDP url: DD_DOGSTATSD_URL=udp://localhost:8125 Example for UDS: DD_DOGSTATSD_URL=unix:///var/run/datadog/dsd.socket Example for Windows named pipeDD_AGENT_HOST=\\.\pipe\my_windows_pipe
    • Fallback to the DD_AGENT_HOST environment variables to build a target address. Example: DD_AGENT_HOST=127.0.0.1:8125 for UDP, DD_AGENT_HOST=unix:///path/to/socket for UDS and DD_AGENT_HOST=\\.\pipe\my_windows_pipe for Windows named pipe.
      • If DD_AGENT_HOST has no port it will default the port to 8125
      • You can use DD_AGENT_PORT to set the port if DD_AGENT_HOST does not have a port set for UDP Example: DD_AGENT_HOST=127.0.0.1 and DD_AGENT_PORT=1234 will create a UDP connection to 127.0.0.1:1234.
  • If the DD_ENTITY_ID environment variable is found, its value is injected as a global dd.internal.entity_id tag. The Datadog Agent uses this tag to insert container tags into the metrics.

To enable origin detection and set the DD_ENTITY_ID environment variable, add the following lines to your application manifest:

env:
  - name: DD_ENTITY_ID
    valueFrom:
      fieldRef:
        fieldPath: metadata.uid
  • DD_ENV, DD_SERVICE, and DD_VERSION can be used by the statsd client to set {env, service, version} as global tags for all data emitted.

Unix Domain Sockets Client

Agent v6+ accepts packets through a Unix Socket datagram connection. Details about the advantages of using UDS over UDP are available in the DogStatsD Unix Socket documentation. You can use this protocol by giving a unix:///path/to/dsd.socket address argument to the New constructor.

Usage

In order to use DogStatsD metrics, events, and Service Checks, the Agent must be running and available.

Metrics

After the client is created, you can start sending custom metrics to Datadog. See the dedicated Metric Submission: DogStatsD documentation to see how to submit all supported metric types to Datadog with working code examples:

Metric names must only contain ASCII alphanumerics, underscores, and periods. The client will not replace nor check for invalid characters.

Some options are suppported when submitting metrics, like applying a sample rate to your metrics or tagging your metrics with your custom tags. Find all the available functions to report metrics in the Datadog Go client GoDoc documentation.

Events

After the client is created, you can start sending events to your Datadog Event Stream. See the dedicated Event Submission: DogStatsD documentation to see how to submit an event to your Datadog Event Stream.

Service Checks

After the client is created, you can start sending Service Checks to Datadog. See the dedicated Service Check Submission: DogStatsD documentation to see how to submit a Service Check to Datadog.

Client side aggregation

Starting with version 5.0.0 (and 3.6.0 in beta), the client offers aggregation or value packing on the client side.

This feature aims at reducing both the number of packets sent to the Agent and the packet drops in very high throughput scenarios.

The aggregation window is 2s by default and can be changed through WithAggregationInterval() option. Note that the aggregation window on the Agent side is 10s for DogStatsD metrics. So for example, setting an aggregation window of 3s in the client will produce a spike in your dashboard every 30 second for counts metrics (as the third 10s bucket on the Agent will receive 4 samples from the client).

Aggregation can be disabled using the WithoutClientSideAggregation() option.

The telemetry datadog.dogstatsd.client.metrics is unchanged and represents the number of metrics before aggregation. New metrics datadog.dogstatsd.client.aggregated_context and datadog.dogstatsd.client.aggregated_context_by_type have been introduced. See the Monitoring this client section.

"Basic" aggregation

Enabled by default, the client will aggregate gauge, count and set.

This can be disabled with the WithoutClientSideAggregation() option.

"Extended" aggregation

This feature is only compatible with Agent's version >=6.25.0 && <7.0.0 or Agent's versions >=7.25.0.

Disabled by default, the client can also pack multiple values for histogram, distribution and timing in one message. Real aggregation is not possible for those types since the Agent also aggregates and two aggregation levels would change the final value sent to Datadog.

When this option is enabled, the agent will buffer the metrics by combination of metric name and tags, and send them in the fewest number of messages.

For example, if we sample 3 times the same metric. Instead of sending on the network:

my_distribution_metric:21|d|#all,my,tags
my_distribution_metric:43.2|d|#all,my,tags
my_distribution_metric:1657|d|#all,my,tags

The client will send only one message:

my_distribution_metric:21:43.2:1657|d|#all,my,tags

This will greatly reduce network usage and packet drops but will slightly increase the memory and CPU usage of the client. Looking at the telemetry metrics datadog.dogstatsd.client.metrics_by_type / datadog.dogstatsd.client.aggregated_context_by_type will show the aggregation ratio for each type. This is an interesting data to know how useful extended aggregation is to your app.

This can be enabled with the WithExtendedClientSideAggregation() option.

Maximum samples per context

This feature is best coupled with the previous aggregation mechanism. It allows to limit the number of samples per context for histogram, distribution and timing metrics.

This can be enabled with the WithMaxSamplesPerContext(n int) option. When enabled up to n samples will be kept in per context. The default value is 0 which means no limit.

The selection of the samples is using an algorithm that tries to keep the distribution of kept sample over time uniform.

Performance / Metric drops

Monitoring this client

This client automatically injects telemetry about itself in the DogStatsD stream. Those metrics will not be counted as custom and will not be billed. This feature can be disabled using the WithoutTelemetry option.

See Telemetry documentation to learn more about it.

Tweaking kernel options

In very high throughput environments it is possible to improve performance by changing the values of some kernel options.

Unix Domain Sockets

  • sysctl -w net.unix.max_dgram_qlen=X - Set datagram queue size to X (default value is usually 10).
  • sysctl -w net.core.wmem_max=X - Set the max size of the send buffer for all the host sockets.

Maximum packets size in high-throughput scenarios

In order to have the most efficient use of this library in high-throughput scenarios, default values for the maximum packets size have already been set to have the best usage of the underlying network. However, if you perfectly know your network and you know that a different value for the maximum packets size should be used, you can set it with the option WithMaxBytesPerPayload. Example:

package main

import (
    "log"
    "github.com/DataDog/datadog-go/v5/statsd"
)

func main() {
    statsd, err := statsd.New("127.0.0.1:8125", WithMaxBytesPerPayload(4096))
    if err != nil {
        log.Fatal(err)
    }
}

Development

Run the tests with:

$ go test

License

datadog-go is released under the MIT license.

Credits

Original code by ooyala.

datadog-go's People

Contributors

ahmed-mez avatar alidatadog avatar alrs avatar arbll avatar carlosroman avatar cihangir avatar ganeshkumarsv avatar gbbr avatar gh123man avatar hush-hush avatar iksaif avatar jbarciauskas avatar jeerim avatar jmoiron avatar jovanbrakus avatar l0k0ms avatar l3n41c avatar leocavaille avatar masci avatar matthewdale avatar olivielpeau avatar remeh avatar remh avatar sidgopalan avatar talwai avatar truthbk avatar tummychow avatar vickenty avatar xsleonard avatar xvello avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

datadog-go's Issues

Is it possible to export eventAlertType? (also eventPriority)?

I would like to have a generic function/method in my code to be able to send events to dogstatsd.
I would like to be able to provide to the callers of this function/method the choice of alerting/priority level they want to use.

Something like this:

func (dds DataDogSVC) SendEvent(componentName string, eventName string, eventDescription string, extraTags []string, alertType statsd.EventAlertType) error

Unfortunately, I cannot do this because eventAlertType is not an exported type. (For reference, @Jasrags mentions this too in this PR: #19)

Right now, the method I defined looks like:

func (dds DataDogSVC) SendEvent(componentName string, eventName string, eventDescription string, extraTags []string, alertType string) error {
	eventAlertType := statsd.Info
	if alertType == "warning" {
		eventAlertType = statsd.Warning
	} else if alertType == "error" {
		eventAlertType = statsd.Error
	} else if alertType == "success" {
		eventAlertType = statsd.Success
	}
	//create the event and send it to dogstatsd

	return nil
}

I don't think it would be a tremendous change to export eventAlertType and eventPriority and would be a little bit nicer to use in my opinion.

Commands seem to be only flushed when closing the client

I couldn't verify this from the source code, but my writes with this StatsD client were only successful if I closed the client after writing and instantiating a new client when writing again. Is this how the API of this is supposed to work?

ddtrace.Logger

Hi,

How do I easily make a ddtrace.Logger for use with the tracer.WithLogger option? I can't seem to find anything to help me create one from even a standard log. Do I really need to create my own custom wrapper?

It seems like a strange design decision to make that interface into something that the standard log in go doesn't implement out of the box?

Buffered client is inefficient

I'm investigating some packet drops in dogstatsd, which are caused by a producer that generates a lot of packets, and uses statsd.NewBuffered.

Because this system generates a lot of statsd packets, we (naively) assumed that a buffered statsd client would take load off of dogstatsd. However, we noticed that packets would get dropped frequently, and would follow a step function:

When buffering is enabled, the statsd client will periodically flush its entire internal buffer every 100 milliseconds, which can easily overload dogstatsd, forcing the kernel to drop packets. I think the primary advantage of buffering is to build up a packet so that it's as close to OptimalPayloadSize as it can be, but once we have a packet big enough to flush, it should be flushed so that there's a smoother distribution of packet sends.

Commas in tag values are not escaped

Tags are a slice of strings in the documentation, but datadog-go documentation does not mention valid values for tags. datadog-go removes newlines from tags, but does not remove/escape other invalid characters like comma (,). Comma in tag value will be interpreted as multiple tags since tags are comma-separated in the UDP datagrams.

The page at https://docs.datadoghq.com/developers/guide/what-best-practices-are-recommended-for-naming-metrics-and-tags/ lists recommeded practice for naming tags, perhaps datadog-go should provide a function to sanitize the tag values according to those rules?

Please improve the experience when configuring an app with env vars

12 factor apps use environment variables for configuration. This is also the approach you recommend for setting up Datadog in Kubernetes.

Even when we aren't using the Datadog Agent (eg. in dev environments and in local development), we would like to leave all the metrics sending code mostly unchanged. Since we aren't running the agent, DD_AGENT_HOST is left empty or unset. When we run statsd.New and pass in an empty string so the statsd client gets config from environment variables, if no environment variables are set, we get an error.

When statsd.New can't start because it can't find any environment variables like DD_AGENT_HOST, we'd like to just switch to the NoOpClient so all the code is unchanged and the app can still run. But you don't provide a way to detect this case, so we have to pre-emptively check the same environment variables you check, which breaks the abstraction.

A few ways to fix this:

  • default to 127.0.0.1:8125 if the address isn't set in any other way - that way we don't have to make any changes, and for many people it would "just work"

  • export an error representing cases where no address is set - that way we could check for this case explicitly

Public client methods do not work with nil client

I can see that public statsd.Client methods are generally designed to be no-ops if the Client is nil. This has been a convenient way for us to disable Datadog e.g. for testing.

When coming back to this library after a while to double-check that this is indeed consistent and intended behavior, I noticed a test, TestNilSafe, that confirmed that this is intentional. However, I also discovered that there are several public methods, overlooked by TestNilSafe, that do not work if the client is nil:

  • SetWriteTimeout
  • Flush
  • ServiceCheck
  • SimpleServiceCheck

I figure that, for completeness, all of these should be fixed up โ€“ particularly the service checks...

Runtime error on armv7

Get this when trying to start the Tracer:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x4 pc=0x123e4]
goroutine 1 [running]:
xxxx/vendor/github.com/DataDog/datadog-go/statsd.(*Client).writeMetricUnsafe(0xd282a0, 0x1, 0x0, 0x0, 0xcf67e0, 0x4, 0x6, 0x57161e, 0x16, 0x0, ...)
        xxxxxxxxxxx/vendor/github.com/DataDog/datadog-go/statsd/statsd.go:442 +0x70
xxxx/vendor/github.com/DataDog/datadog-go/statsd.(*Client).addMetric(0xd282a0, 0x1, 0x0, 0x0, 0xcf67e0, 0x4, 0x6, 0x57161e, 0x16, 0x0, ...)
        xxxxxxxxxxx/vendor/github.com/DataDog/datadog-go/statsd/statsd.go:476 +0x64
xxxx/vendor/github.com/DataDog/datadog-go/statsd.(*Client).Count(0xd282a0, 0x57161e, 0x16, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff00000, ...)
        xxxxxxxxxxx/vendor/github.com/DataDog/datadog-go/statsd/statsd.go:491 +0xb0
xxxx/vendor/github.com/DataDog/datadog-go/statsd.(*Client).Incr(0xd282a0, 0x57161e, 0x16, 0x0, 0x0, 0x0, 0x0, 0x3ff00000, 0xa6cb4210, 0x4582c0)
        xxxxxxxxxxx/vendor/github.com/DataDog/datadog-go/statsd/statsd.go:511 +0x5c
xxxx/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.newTracer(0xcc1de4, 0x1, 0x1, 0x6f)
        xxxxxxxxxxx/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go:175 +0x7c
xxxx/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer.Start(0xcc1de4, 0x1, 0x1)
        xxxxxxxxxxx/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go:96 +0x40
main.main()
        xxxxxxxxxxx

http client

can we get an client for the http interface in addition to the statsd client. I'm happy to do it myself, as long as it's something we want

datadog.dogstatsd.* metrics have namespace prepended

If I create a client with the Namespace set then all metrics will have this prefixed to the metric name. This includes the datadog.dogstatsd.* telemetry metrics as well as my application specific metrics. My assumption is that these "new" metrics will be considered custom (not free), as they have a different name. So I can either use the namespace feature or have custom (free) telemetry stats, but not both. This seems like a design limitation in the current implementation. My proposal would be a fix in which telemetry stats should not have the namespace prefixed to them.

Long living client

Hi, is it possible to call statsd.new only once and reuse that client for the whole application lifetime?

I seem to randomly run into sigsegv: segmentation violation panic on client.push after a few hour of running my application.

I'm using a singleton pattern with sync.once, if that helps

Deadlock in statsd.Client.Close()

I've encountered what I believe to be a deadlock in the Close() logic. When the main goroutine is closing, the mutex for the Client is acquired, we send a signal to all other goroutines to stop, then we wait for those goroutines to stop: https://github.com/DataDog/datadog-go/blob/master/statsd/statsd.go#L547-L555

func (c *Client) Close() error {
	if c == nil {
		return ErrNoClient
	}
	c.Lock() // <- acquire the lock here
	defer c.Unlock()
	select {
	case <-c.stop:
		return nil
	default:
	}
	close(c.stop)
	c.wg.Wait()

However, in the watch() goroutine, its possible for the ticker to fire after the main thread has acquired the mutex, but before the stop channel is closed. This means that the watch() goroutine will also try and acquire the same mutex here: https://github.com/DataDog/datadog-go/blob/master/statsd/statsd.go#L334 and block.

func (c *Client) watch() {
	ticker := time.NewTicker(c.flushTime)

	for {
		select {
		case <-ticker.C:
			c.Lock() // <- This blocks because the main goroutine holds the lock

Since the main goroutine waits for the watch() goroutine to exit here: https://github.com/DataDog/datadog-go/blob/master/statsd/statsd.go#L555, we have a deadlock.

func (c *Client) Close() error {
	if c == nil {
		return ErrNoClient
	}
	c.Lock() // <- acquire the lock here
	defer c.Unlock()
	select {
	case <-c.stop:
		return nil
	default:
	}
	close(c.stop)
	c.wg.Wait() # <- This deadlocks because `watch()` is waiting for c.Lock() to be released

documentation for Count is unclear

func (c *Client) Count(name string, value int64, tags []string, rate float64) error

Count tracks how many times something happened per second. 

The relationship between value and rate is unspecified. Are they both used? How do they interact? If I'm just counting an event, usually I'm counting "total occurrences", not "times per second", because I'll compute times per second later.

Reading elsewhere suggests that "rate" should be a 0-1 value to control how often to actually send packets, so, a rate of "0.5" would indicate that 50% of packets should actually be sent, but I can't find anything in the datadog-go/statsd documentation that makes this explicit.

Go Modules Support

Currently there are no go.mod and go.sum files in the repo. go mod is currently adding v<version>-incompatible tags to go.mod files but they are prone to breaking whenever we go get -u the dependency in modules mode.

It'd be very helpful to have Go Modules support to make dependency management more deterministic.

Optimal UDP payload size for localhost

Currently the "optimal" payload size is set to 1432 for UDP even if both statsd client and agent are running on the same host.

Using an MTU of slightly less than 1500 is a pretty conservative approach, appropriate at best for sending UDP datagrams over the internet or heterogeneous networks. The argument that fragmented UDP packets are completely lost if any one of their fragments is lost, while technically correct, is already unlikely to be a real factor in properly run datacenter networks and, even more so, when the communication happens purely over localhost.

On my mac, the localhost MTU defaults to 16K. On linux it also used to default to 16K, but has been 64K for quite a few years. In Windows the loopback MTU seems to be basically unlimited. On the BSDs I can only offer anecdotal evidence that the loopback MTU defaults to 16K (in line with that of macs).

I think it would be safe to raise the UDP MTU to slightly less than 16K if the agent address resolves to a loopback address, or even better to whatever number is returned by net.Interface.MTU for the loopback interface. This would bring benefits as it would cut down by an order of magnitude the number of syscalls made by the client (and by the agent).

In a nutshell, the proposed change would be adding here something along the lines of:

	if o.MaxBytesPerPayload == 0 {
		if isLoopback(c.addrOption) {
			o.MaxBytesPerPayload = OptimalLoopbackUDPPayloadSize // e.g. slightly less than 16K
		} else {   
			o.MaxBytesPerPayload = OptimalUDPPayloadSize
		}
	}

where isLoopback would parse/resolve the address and use net.IP.IsLoopback to check if the agent is running on loopback. This won't unfortunately cover the rather common case of client and agent being on the same host, but not talking via loopback (e.g. different pods on the same host in kubernetes) but it's a start (we could also further extend this to check if the IP of the agent matches one of the IPs of the interfaces of the host the client is running on, but this is a bit more sketchy so maybe it should be done later).

We would probably also need to change the buffer size of dogstatsd from 8K to 16K (even better, should be raised to 64K since MaxUDPPayloadSize is ~64K) and lowering dogstatsd_queue_size as needed to maintain the same memory usage. If this is not possible right away, it should be still feasible to raise the client's loopback optimal UDP payload size to slightly less than 8K. Note also that there seem to be no reason to not simply hardcode a 64K buffer for receiving UDP messages in dogstatsd, since we use only a single buffer: I opened a draft PR for that here.

Make "statsdWriter" public

Hello,

The API has a public function

func NewWithWriter(w statsdWriter) (*Client, error) {
	client := &Client{writer: w, SkipErrors: false}
	return client, nil
}

but statsdWriter is a private type, so only the package and tests can use this function. So, either make NewWithWriter private (rename to newWithWriter), or expose statsdWriter.

type statsdWriter interface {
	Write(data []byte) (n int, err error)
	SetWriteTimeout(time.Duration) error
	Close() error
}

With the later, one would then write TCP writers, and even direct to datadog via HTTP API writers.

happy to whip up a PR as well, but I wanted to get thoughts before doing that.

regards,

n

Unix socket path is not extracted correctly

According to the documentation, the way to pass a UDS path to statsd.New is by prefixing the string with unix://. However, an extra slash is passed down because of

w, err = newUDSWriter(addr[len(UnixAddressPrefix)-1:])
, e.g unix:///var/run/datadog/dsd.socket -> //var/run/datadog/dsd.socket

Use of rand.Float64() reduces parallelism

The global default math/rand generator is protected by a mutex. In a highly concurrent application making lots of calls to the DD library and making use of the rate parameter to the API sample metrics so as to not drop packets and overload the statsd server, the use of rand.Float64 in statsd.Client.send can become a bottleneck.

Ideally, there would be an alternative API that allows the caller to pass in goroutine local math.rand.Rand, which is not protected by a mutex.

Confusing tagging in README

In the example code the tag is added like c.Tags = append(c.Tags, "us-east-1a") however if I understand correctly, this should be a pair.

I've checked the example in python library, and found the tag is : splitted string, making it a key and a value.

Is go-version library having special way setting tags or is it a typo?

Race condition when setting statsd.Client.Namespace as per doc example

I run my application with -race and found out a race condition when setting the namespace as per the docs:

// Create the client
c, err := statsd.New("127.0.0.1:8125")
...
// Prefix every metric with the app name
c.Namespace = "flubber."

I wrote a wee snippet to reproduce the problem:

package main

import (
	"log"
	"time"

	"github.com/DataDog/datadog-go/statsd"
)

func main() {
	client, err := statsd.New(
		"localhost:8888")
	if err != nil {
		log.Println("could not get datadog client")
		panic("could not get datadog client:" + err.Error())
	}

	client.Namespace = "another namespace"

	time.Sleep(time.Minute)
}

go run -race main.go

output

$ go run -race main.go
==================
WARNING: DATA RACE
Read at 0x00c000146008 by goroutine 19:
  github.com/DataDog/datadog-go/statsd.(*Client).send()
      /Users/aqueiroz/devel/go/pkg/mod/github.com/!data!dog/[email protected]+incompatible/statsd/statsd.go:467 +0x99
  github.com/DataDog/datadog-go/statsd.(*Client).telemetry()
      /Users/aqueiroz/devel/go/pkg/mod/github.com/!data!dog/[email protected]+incompatible/statsd/statsd.go:402 +0x252
  github.com/DataDog/datadog-go/statsd.newWithWriter.func2()
      /Users/aqueiroz/devel/go/pkg/mod/github.com/!data!dog/[email protected]+incompatible/statsd/statsd.go:357 +0xa7

Previous write at 0x00c000146008 by main goroutine:
  main.main()
      /Users/aqueiroz/devel/github.com/blacklane/ride-matcher/ddrace/main.go:20 +0x85

Goroutine 19 (running) created at:
  github.com/DataDog/datadog-go/statsd.newWithWriter()
      /Users/aqueiroz/devel/go/pkg/mod/github.com/!data!dog/[email protected]+incompatible/statsd/statsd.go:354 +0xfe6
  github.com/DataDog/datadog-go/statsd.New()
      /Users/aqueiroz/devel/go/pkg/mod/github.com/!data!dog/[email protected]+incompatible/statsd/statsd.go:272 +0x2b4
  main.main()
      /Users/aqueiroz/devel/github.com/blacklane/ride-matcher/ddrace/main.go:11 +0x58
==================
Found 1 data race(s)
exit status 66

Using statsd.WithNamespace instead of setting it latter on does not cause the race condition:

	client, err := statsd.New(
		"localhost:8888",
		statsd.WithNamespace("myApp."),
	)

Also statsd.Client states it's goroutine safe.

I'd suggest to do not export the Namespace field.

However it'd be a breaking change, so maybe deprecate the field, adjust the dos and put a warning on the statsd.Client doc?

Please tag 1.1.0 release

Hello,

Could you guys please tag a 1.1.0 release? I am using glide and currently i have to bind my dep to a sha to pick up any new code. A 1.1.0 tag would help greatly! Thanks!

New release?

The last release was last April. Since then Unix Domain Socket support was added. Time for a new release?

Is there an option to cancel the host resolving on the statsd creator

Hi,

Is there an option to cancel the host resolving on the statsd creator?
My code running on docker while the agent running on another docker. So the container is not always run while the agent is already exists and we get an error that the lookup failed and "no such host".

In this case, is there any option to retry the resolving each time metric is send?

Tnx!

Events not sent

I'm trying to send events using the following code:

r.statasd, err := statsd.New("127.0.0.1:8125")
event := statsd.NewEvent("New version published", 
		fmt.Sprintf("A new version (%d) of %s was deployed to namespace %s, using chart %s. Deploy status - %s", release.Version, release.Name, release.Namespace, release.Chart, status))

	event.Priority = statsd.Normal

	if (status == "DEPLOYED"){
		event.AlertType = statsd.Info
	} else {
		event.AlertType = statsd.Error
	}
	event.Hostname = release.Name

	event.Tags = []string {fmt.Sprintf("namespace:%s", release.Namespace)}

	r.statsd.Event(event)

But for some reason, I don't see the event on Datadog, no relevant logs on the local agent or the statsd (which has no logs :(). Agent is running using the following docker command:

DOCKER_CONTENT_TRUST=1 docker run -d --name dd-agent -v /var/run/docker.sock:/var/run/docker.sock:ro -v /proc/:/host/proc/:ro -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro -e DD_API_KEY=<> -e DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true -p 8125:8125 datadog/agent:latest

What am I missing?

please tag and version this project

Hello,

Can you please tag and version this project?

I am the Debian Maintainer for datadog-go and versioning would help Debian keep up with development.

No way to change OptimalPayloadSize

The comment says This can be increased if your network has a greater MTU or you don't mind UDP datagrams getting fragmented. However, OptimalPayloadSize is a const. This probably should have a way to change it per client. I'm happy to do a PR if there is interest.

bug: sendMsg function doesn't always validate length

Hey there,

I was doing some reading of the codebase and I think stumbled upon a bug in the sendMsg method. Specifically, it seems that the length of the message is only checked if the client is in a buffered mode:

  • datadog-go/statsd/statsd.go

    Lines 224 to 228 in 426ff2e

    if c.bufferLength > 0 {
    // return an error if message is bigger than OptimalPayloadSize
    if len(msg) > MaxUDPPayloadSize {
    return errors.New("message size exceeds MaxUDPPayloadSize")
    }

Looking at it further, it seems the comment above the length check is at odds with what the code actually codes:

It's hard to know what the expected behavior truly is because the tests aren't currently ran for the function because it's unexported. Even then it seems to be at adds with the code itself:

  • func testSendMsg(t *testing.T) {
    c := Client{bufferLength: 1}
    err := c.sendMsg(strings.Repeat("x", OptimalPayloadSize))
    if err != nil {
    t.Errorf("Expected no error to be returned if message size is smaller or equal to MaxPayloadSize, got: %s", err.Error())
    }
    err = c.sendMsg(strings.Repeat("x", OptimalPayloadSize+1))
    if err == nil {
    t.Error("Expected error to be returned if message size is bigger that MaxPayloadSize")
    }
    }

If you export the function and try to run the test, it results in a panic which is probably why it is unexported:

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x58 pc=0x7ea3b]

goroutine 26 [running]:
panic(0x17df00, 0xc4200140c0)
        /usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:500 +0x1a1
testing.tRunner.func1(0xc42009c480)
        /usr/local/Cellar/go/1.7.4/libexec/src/testing/testing.go:579 +0x25d
panic(0x17df00, 0xc4200140c0)
        /usr/local/Cellar/go/1.7.4/libexec/src/runtime/panic.go:458 +0x243
github.com/DataDog/datadog-go/statsd.(*Client).flush(0xc4200ae2a0, 0x0, 0x0)
        /Users/theckman/go/src/github.com/DataDog/datadog-go/statsd/statsd.go:201 +0xdb
github.com/DataDog/datadog-go/statsd.(*Client).append(0xc4200ae2a0, 0xc4200a8c00, 0x598, 0xc4200a8c00, 0x598)
        /Users/theckman/go/src/github.com/DataDog/datadog-go/statsd/statsd.go:141 +0xab
github.com/DataDog/datadog-go/statsd.(*Client).sendMsg(0xc4200ae2a0, 0xc4200a8c00, 0x598, 0x0, 0x0)
        /Users/theckman/go/src/github.com/DataDog/datadog-go/statsd/statsd.go:229 +0x124
github.com/DataDog/datadog-go/statsd.TestSendMsg(0xc42009c480)
        /Users/theckman/go/src/github.com/DataDog/datadog-go/statsd/statsd_test.go:343 +0xee
testing.tRunner(0xc42009c480, 0x1be4e8)
        /usr/local/Cellar/go/1.7.4/libexec/src/testing/testing.go:610 +0x81
created by testing.(*T).Run
        /usr/local/Cellar/go/1.7.4/libexec/src/testing/testing.go:646 +0x2ec
exit status 2

In short, I think the sendMsg method should check the length of the message whether or not it's a buffered client. Secondly, I think either the function's logic needs to be updated or the comment/tests need to be updated to match the expected behavior. Lastly, I think the changes in #22 should also be applied to sendMsg() / append().

DataDog build in master is broken

go get github.com/DataDog/datadog-go/statsd produces the following error:

murmur/signalsd/src/github.com/DataDog/datadog-go/statsd/statsd.go:358: e.text undefined (type Event has no field or method text, but does have Text)

Closing client does not flush metrics

Hello,

I am observing no metrics being sent after upgrading to version 3.4.1 from 2.3.0.

I wrote a simple test to reproduce this behaviour and I am using Wireshark to observe the traffic I send. I can see via Wireshark that on version 3.4.1. the metrics are not being sent, whereas they are on 2.3.0.

If on version 3.4.1 I add a client.Flush() (as commented out in the below test) the metrics are sent. I expected the closing of the client to flush the metrics, as seen in the following lines
https://github.com/DataDog/datadog-go/blob/master/statsd/statsd.go#L568-L569'

Is this to be expected?

Go version: 1.14.0

Thanks,
Fraser

import (
	"testing"

	"github.com/DataDog/datadog-go/statsd"
)

func Test_BasicMetric(t *testing.T){
	client, err := statsd.New("127.0.0.1:8125")
	if err != nil {
		t.Fatalf("failed to create client: %s", err)
	}

	if err := client.Count("name", 1, []string{"tag"}, 1); err != nil {
		t.Fatalf("failed to send metric: %s", err)
	}

	// _ = client.Flush()

	if err := client.Close(); err != nil {
		t.Fatalf("failed to close client: %s", err)
	}
}

Client doesn't reconnect if agent restarts

We've been using datadog in a kubernetes environment. We have datadog agent running as a daemonset and the client in our go apps running inside kubes on the cluster. However if the agent restarts (due to a daemonset config change, upgrade, etc) we don't see any metrics flowing even after the agent comes back online. It would be great if the client could automatically attempt to reconnect in the background so we don't have to roll the app to get stats flowing again.

Missing WithAsyncUDS option?

Hi, in the README.md the asynchronous mode is recommended, but I couldn't find the WithAsyncUDS option mentioned. Is it missing from the code?

From https://github.com/DataDog/datadog-go#blocking-vs-asynchronous-behavior:

Currently, in 2.x, "blocking" is the default behavior to ensure backward compatibility. To use the "asynchronous" behavior, use the statsd.WithAsyncUDS() option. We recommend enabling the "asynchronous" mode.

If there're no plans to implement this option, it is safe to run a call in a goroutine if I don't care about the error?

go statsd.Incr(...)

Thanks!

Invalid metric names not escaped

Howdy, I'm using dogstatsd-go 3.3.0 and seeing this error:

ERROR | (pkg/dogstatsd/server.go:298 in parsePacket) | Dogstatsd: error parsing metrics: invalid metric value for
"faktory.jobs.BareStripe::WebhookReceiverWorker.perform:23.737378|ms|#env:production,env:production,env:production,env:production,env:production,queue:import_stripe_webhooks,jobtype:BareStripe::WebhookReceiverWorker"

(the env:production repetition is a separate issue)

Any idea why the API is sending metric lines that can't be parsed by the agent? I would expect either an error or for this API to escape any illegal characters before sending over the network. I couldn't find any documentation in the README or linked that talks about allowed characters in metric and tag names/values and I couldn't find any code which escaped characters in this repo.

Telemetry added in PR #91 is unable to be disabled

Hello,

This new code landed in v3.0.0

Telemetry was first introduced in this PR: #91

This is the specific commit on the PR where the telemetry initially landed: b4f4ba0

These lines were ultimately added in master:

datadog-go/statsd/statsd.go

Lines 256 to 275 in b6a27b8

func (c *Client) telemetry() {
ticker := time.NewTicker(TelemetryInterval)
for {
select {
case <-ticker.C:
metrics := c.sender.flushMetrics()
c.telemetryCount("datadog.dogstatsd.client.packets_sent", int64(metrics.TotalSentPayloads), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.bytes_sent", int64(metrics.TotalSentBytes), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.packets_dropped", int64(metrics.TotalDroppedPayloads), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.bytes_dropped", int64(metrics.TotalDroppedBytes), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.packets_dropped_queue", int64(metrics.TotalDroppedPayloadsQueueFull), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.bytes_dropped_queue", int64(metrics.TotalDroppedBytesQueueFull), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.packets_dropped_writer", int64(metrics.TotalDroppedPayloadsWriter), c.telemetryTags, 1)
c.telemetryCount("datadog.dogstatsd.client.bytes_dropped_writer", int64(metrics.TotalDroppedBytesWriter), c.telemetryTags, 1)
case <-c.stop:
ticker.Stop()
return
}
}
}

There is no mention in the PR description of the concept of telemetry being added. Furthermore, there are no documentations or configs mentioning telemetry.

I found these metrics being emitted during a read through of a TCP dump of the statsd traffic on our application:

sudo tcpdump -i any -n udp dst port 8125 -lev -s 0 -A
datadog.dogstatsd.client.packets_sent:1|c|#client:go,transport:udp
datadog.dogstatsd.client.bytes_sent:575|c|#client:go,transport:udp
datadog.dogstatsd.client.packets_dropped:1|c|#client:go,transport:udp
datadog.dogstatsd.client.bytes_dropped:521|c|#client:go,transport:udp
datadog.dogstatsd.client.packets_dropped_queue:0|c|#client:go,transport:udp
datadog.dogstatsd.client.bytes_dropped_queue:0|c|#client:go,transport:udp
datadog.dogstatsd.client.packets_dropped_writer:1|c|#client:go,transport:udp
datadog.dogstatsd.client.bytes_dropped_writer:521|c|#client:go,transport:udp

There should to be configuration available to turn this telemetry off, but there isn't, as far as I can tell.

Regards,
Cody

Bad line issue while adding tags.

Is there any workaround this issue? or am I doing something wrong here?

I am using test code as it is :

`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)`

Response:

DEBUG: Bad line: 12.000000,g,#us-east-1a in msg "flubber.request.queue_depth:12.000000|g|#us-east-1a"

I am running this statsd agent locally (https://github.com/etsy/statsd, https://anomaly.io/statsd-install-and-config/)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.