Git Product home page Git Product logo

java-dogstatsd-client's Introduction

Java DogStatsD Client

CircleCI Build Status

A DogStatsD client library implemented in Java. Allows for Java applications to easily communicate with the DataDog Agent. The library supports Java 1.7+.

This version was originally forked from java-dogstatsd-client and java-statsd-client but it is now the canonical home for the java-dogstatsd-client. Collaborating with the former upstream projects we have now combined efforts to provide a single release.

See CHANGELOG.md for changes.

Installation

The client jar is distributed via Maven central, and can be downloaded from Maven.

<dependency>
    <groupId>com.datadoghq</groupId>
    <artifactId>java-dogstatsd-client</artifactId>
    <version>4.4.2</version>
</dependency>

Unix Domain Socket support

As an alternative to UDP, Agent v6 can receive metrics via a UNIX Socket (on Linux only). This library supports transmission via this protocol. To use it use the address() method of the builder and pass the path to the socket with the unix:// prefix:

StatsDClient client = new NonBlockingStatsDClientBuilder()
    .address("unix:///var/run/datadog/dsd.socket")
    .build();

By default, all exceptions are ignored, mimicking UDP behaviour. When using Unix Sockets, transmission errors trigger exceptions you can choose to handle by passing a StatsDClientErrorHandler:

  • Connection error because of an invalid/missing socket triggers a java.io.IOException: No such file or directory.
  • If DogStatsD's reception buffer were to fill up and the non blocking client is used, the send times out after 100ms and throw either a java.io.IOException: No buffer space available or a java.io.IOException: Resource temporarily unavailable.

The default UDS transport is using SOCK_DATAGRAM sockets. We also have experimental support for SOCK_STREAM sockets which can be enabled by using the unixstream:// instead of unix://. This is not recommended for production use at this time.

Configuration

Once your DogStatsD client is installed, instantiate it in your code:

import com.timgroup.statsd.NonBlockingStatsDClientBuilder;
import com.timgroup.statsd.NonBlockingStatsDClient;
import com.timgroup.statsd.StatsDClient;

public class DogStatsdClient {

    public static void main(String[] args) throws Exception {

        StatsDClient client = new NonBlockingStatsDClientBuilder()
            .prefix("statsd")
            .hostname("localhost")
            .port(8125)
            .build();

        // use your client...
    }
}

v2.x

Client version v3.x is now preferred over the older client v2.x release line. We do suggest you upgrade to the newer v3.x at your earliest convenience.

The builder pattern described above was introduced with v2.10.0 in the v2.x series. Earlier releases require you use the deprecated overloaded constructors.

DEPRECATED

import com.timgroup.statsd.NonBlockingStatsDClient;
import com.timgroup.statsd.StatsDClient;

public class DogStatsdClient {

    public static void main(String[] args) throws Exception {

        StatsDClient Statsd = new NonBlockingStatsDClient("statsd", "localhost", 8125);

    }
}

See the full list of available DogStatsD Client instantiation parameters.

Migrating from 2.x to 3.x

Though there are very few breaking changes in 3.x, some code changes might be required for some user to migrate to the latest version. If you are migrating from the v2.x series to v3.x and were using the deprecated constructors, please use the following table to ease your migration to utilizing the builder pattern.

v2.x constructor parameter v2.10.0+ builder method
final Callable addressLookup NonBlockingStatsDClientBuilder addressLookup(Callable val)
final boolean blocking NonBlockingStatsDClientBuilder blocking(boolean val)
final int bufferSize NonBlockingStatsDClientBuilder socketBufferSize(int val)
final String... constantTags NonBlockingStatsDClientBuilder constantTags(String... val)
final boolean enableTelemetry NonBlockingStatsDClientBuilder enableTelemetry(boolean val)
final String entityID NonBlockingStatsDClientBuilder entityID(String val)
final StatsDClientErrorHandler errorHandler NonBlockingStatsDClientBuilder errorHandler(StatsDClientErrorHandler val)
final String hostname NonBlockingStatsDClientBuilder hostname(String val)
final int maxPacketSizeBytes NonBlockingStatsDClientBuilder maxPacketSizeBytes(String... val)
final int processorWorkers NonBlockingStatsDClientBuilder processorWorkers(int val)
final int poolSize NonBlockingStatsDClientBuilder bufferPoolSize(int val)
final int port NonBlockingStatsDClientBuilder port(int val)
final String prefix NonBlockingStatsDClientBuilder prefix(String val)
final int queueSize NonBlockingStatsDClientBuilder queueSize(int val)
final int senderWorkers NonBlockingStatsDClientBuilder senderWorkers(int val)
final Callable telemetryAddressLookup NonBlockingStatsDClientBuilder telemetryAddressLookup(Callable val)
final int telemetryFlushInterval NonBlockingStatsDClientBuilder telemetryFlushInterval(int val)
final int timeout NonBlockingStatsDClientBuilder timeout(int val)

Transport and Maximum Packet Size

As mentioned above the client currently supports two forms of transport: UDP and Unix Domain Sockets (UDS).

The preferred setup for local transport is UDS, while remote setups will require the use of UDP. For both setups we have tried to set convenient maximum default packet sizes that should help with performance by packing multiple statsd metrics into each network packet all while playing nicely with the respective environments. For this reason we have set the following defaults for the max packet size:

  • UDS: 8192 bytes - recommended default.
  • UDP: 1432 bytes - largest possible size given the Ethernet MTU of 1514 Bytes. This should help avoid UDP fragmentation.

These are both configurable should you have other needs:

StatsDClient client = new NonBlockingStatsDClientBuilder()
    .hostname("/var/run/datadog/dsd.socket")
    .port(0) // Necessary for unix socket
    .maxPacketSizeBytes(16384)  // 16kB maximum custom value
    .build();

Origin detection over UDP and UDS

Origin detection is a method to detect which pod DogStatsD packets are coming from in order to add the pod's tags to the tag list. The DogStatsD client attaches an internal tag, entity_id. The value of this tag is the content of the DD_ENTITY_ID environment variable if found, which is the pod's UID. The Datadog Agent uses this tag to add container tags to the metrics. To avoid overwriting this global tag, make sure to only append to the constant_tags list.

To enable origin detection over UDP, add the following lines to your application manifest

env:
  - name: DD_ENTITY_ID
    valueFrom:
      fieldRef:
        fieldPath: metadata.uid

Aggregation

As of version v2.11.0, client-side aggregation has been introduced in the java client side for basic types (gauges, counts, sets). Aggregation remains unavailable at the time of this writing for histograms, distributions, service checks and events due to message relevance and statistical significance of these types. The feature is enabled by default as of v3.0.0, and remains available but disabled by default for prior versions.

The goal of this feature is to reduce the number of messages submitted to the Datadog Agent. Minimizing message volume allows us to reduce load on the dogstatsd server side and mitigate packet drops. The feature has been implemented such that impact on CPU and memory should be quite minimal on the client side. Users might be concerned with what could be perceived as a loss of resolution by resorting to aggregation on the client, this should not be the case. It's worth noting the dogstatsd server implemented in the Datadog Agent already aggregates messages over a certain flush period, therefore so long as the flush interval configured on the client side is smaller than said flush interval on the server side there should no loss in resolution.

Configuration

Enabling aggregation is simple, you just need to set the appropriate options with the client builder.

You can just enable aggregation by calling the enableAggregation(bool) method on the builder.

There are two clent-side aggregation knobs available:

  • aggregationShards(int): determines the number of shards in the aggregator, this feature is aimed at mitigating the effects of map locking in highly concurrent scenarios. Defaults to 4.
  • aggregationFlushInterval(int): sets the period of time in milliseconds in which the aggregator will flush its metrics into the sender. Defaults to 3000 milliseconds.
StatsDClient client = new NonBlockingStatsDClientBuilder()
    .hostname("localhost")
    .port(8125)
    .enableAggregation(true)
    .aggregationFlushInterval(3000)  // optional: in milliseconds
    .aggregationShards(8)  // optional: defaults to 4
    .build();

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:

Some options are suppported when submitting metrics, like applying a Sample Rate to your metrics or tagging your metrics with your custom tags.

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.

java-dogstatsd-client's People

Contributors

ahmed-mez avatar alidatadog avatar benaiad avatar blemale avatar camerondavison avatar carlosroman avatar charles-dyfis-net avatar derekwbrown avatar gh123man avatar hush-hush avatar iksaif avatar indeedeng-release avatar jasonculverhouse avatar jbarciauskas avatar l0k0ms avatar lpriima avatar masci avatar mcculls avatar nmuesch avatar ogaca-dd avatar ovesh avatar randomanderson avatar richardstartin avatar scarytom avatar schilling avatar sgnn7 avatar truthbk avatar vickenty avatar xvello avatar yannmh 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

java-dogstatsd-client's Issues

NonBlockingStatsDClient loses events

Hey!
I observe that some dd events are geting lost for my application.
It happens with last events before shutdown.
I checked the code of NonBlockingStatsDClient and I see that method "stop" stops sending events even the queue is not empty.
I could reproduce it with this test:

    @Test(timeout = 15000L)
    public void shutdown2() throws Exception {
        final int port = 17257;
        final NonBlockingStatsDClient client = new NonBlockingStatsDClient("my.prefix.shutdownTest", "localhost", port);
        final DummyStatsDServer server = new DummyStatsDServer(port);
        final ExecutorService executorService = Executors.newFixedThreadPool(16);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        client.count("mycounter", 5);
                    }
                }
            });
        }
        executorService.shutdown();
        executorService.awaitTermination(5, TimeUnit.SECONDS);
        client.stop();
        assertEquals(1000 * 1000, server.messagesReceived().size());
    }

I propose to implement graceful shutdown: wait until the queue is empty or timeout happens.
Please see my fix : #58

Need equals() and hashcode() for data objects

Certain data object classes such as Event and ServiceCheck should have equals() and hashcode() so that we can do comparisons and put them in data structures. You can even generate them in IDEA.

Support gauge delta

Hi,

apparently, statsd supports sending deltas for gauges.
Sifting through the code, it doesn't look like deltas are supported at the moment. Is this a known issue, an oversight, or an intentional design choice?

If desired, I can submit a PR myself when I find the time.

Cheers,
Dom

Help needed: Increment counter specified times

Hi,
I want to know how can I increase a counter with a specified value. For example if I need to increase the counter by 300, I can do

for(int i=0;i<300;i++)
            statsDClient.increment("pushed.from.inventory")

but is it possible to do without a loop?

Thanks

choice of DEFAULT_FLUSH_INTERVAL for StatsDAggregator

The aggregation documentation says:

...so long as the flush interval configured configured on the client side is smaller than said flush interval on the server side there should no loss in resolution

But from what I can tell, the default values for the client here in java-dogstatsd-client of 3 seconds is quite a bit bigger than the default flush interval in the datadog agent of 100 milliseconds. From what I can see the default in the datadog agent hasn't changed in some time, if ever.

Does it make sense to choose a smaller number here to make the defaults "safe"?

Version 2.6 is broken

Hello, using 2.5 everything is fine, but when updating to 2.6 then our dashboards are empty. Nothing seems to be received by Datadog. We are trying to find where is the regression.

No exception are logged using StatsDClientErrorHandler.
How can we help you to troubleshoot this?

Dependency on JFFI when sending metrics to Unix socket

Hi,

I added java-dogstatsd-client 2.6.1 to my Java 1.8 project using Ivy + Maven and then configured the client to send metrics via Unix socket. The project compiled successfully, however I started getting a bunch of runtime errors related to JFFI. I don't have all the stacktraces unfortunately but there were things like:

LoadError: Could not load FFI Provider: (NotImplementedError) FFI not available: null
Exception in thread "main" java.lang.UnsatisfiedLinkError: could not load FFI provider jnr.ffi.provider.jffi.Provider
    at jnr.ffi.provider.InvalidRuntime.newLoadError(InvalidRuntime.java:101)
    at jnr.ffi.provider.InvalidRuntime.findType(InvalidRuntime.java:42)
    at jnr.ffi.Struct$NumberField.<init>(Struct.java:872)
    at jnr.ffi.Struct$Unsigned8.<init>(Struct.java:1113)
    at jnr.unixsocket.SockAddrUnix$BSDSockAddrUnix.<init>(SockAddrUnix.java:187)
    at jnr.unixsocket.SockAddrUnix.create(SockAddrUnix.java:174)
    at jnr.unixsocket.UnixSocketAddress.<init>(UnixSocketAddress.java:53)
    at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1073)
    at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1070)
    at com.timgroup.statsd.NonBlockingStatsDClient.staticAddressResolution(NonBlockingStatsDClient.java:1090)
    at com.timgroup.statsd.NonBlockingStatsDClient.staticStatsDAddressResolution(NonBlockingStatsDClient.java:1100)
    at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:283)
    at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:171)
    at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:146)
    at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:20)
    at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:9)
    at com.mortardata.mhc.metrics.StatsD$SingletonHolder.<clinit>(StatsD.java:28)
    at com.mortardata.mhc.metrics.StatsD.getInstance(StatsD.java:32)
    at com.mortardata.mhc.Main.main(Main.java:69)
Caused by: java.lang.NoClassDefFoundError: com/kenai/jffi/Type
    at jnr.ffi.provider.jffi.NativeRuntime.jafflType(NativeRuntime.java:202)
    at jnr.ffi.provider.jffi.NativeRuntime.buildTypeMap(NativeRuntime.java:85)
    at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:66)
    at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:41)
    at jnr.ffi.provider.jffi.NativeRuntime$SingletonHolder.<clinit>(NativeRuntime.java:62)
    at jnr.ffi.provider.jffi.NativeRuntime.getInstance(NativeRuntime.java:58)
    at jnr.ffi.provider.jffi.Provider.<init>(Provider.java:29)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.getInstance(FFIProvider.java:68)
    at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.<clinit>(FFIProvider.java:57)
    at jnr.ffi.provider.FFIProvider.getSystemProvider(FFIProvider.java:35)
    at jnr.ffi.Runtime$SingletonHolder.<clinit>(Runtime.java:82)
    at jnr.ffi.Runtime.getSystemRuntime(Runtime.java:67)
    at jnr.unixsocket.SockAddrUnix.<init>(SockAddrUnix.java:46)
    at jnr.unixsocket.SockAddrUnix$BSDSockAddrUnix.<init>(SockAddrUnix.java:185)
    ... 14 more
Caused by: java.lang.ClassNotFoundException: com.kenai.jffi.Type
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 33 more
Exception in thread "main" java.lang.UnsatisfiedLinkError: could not load FFI provider jnr.ffi.provider.jffi.Provider
	at jnr.ffi.provider.InvalidRuntime.newLoadError(InvalidRuntime.java:101)
	at jnr.ffi.provider.InvalidRuntime.findType(InvalidRuntime.java:42)
	at jnr.ffi.Struct$NumberField.<init>(Struct.java:872)
	at jnr.ffi.Struct$Unsigned8.<init>(Struct.java:1113)
	at jnr.unixsocket.SockAddrUnix$BSDSockAddrUnix.<init>(SockAddrUnix.java:187)
	at jnr.unixsocket.SockAddrUnix.create(SockAddrUnix.java:174)
	at jnr.unixsocket.UnixSocketAddress.<init>(UnixSocketAddress.java:53)
	at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1073)
	at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1070)
	at com.timgroup.statsd.NonBlockingStatsDClient.staticAddressResolution(NonBlockingStatsDClient.java:1090)
	at com.timgroup.statsd.NonBlockingStatsDClient.staticStatsDAddressResolution(NonBlockingStatsDClient.java:1100)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:283)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:171)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:146)
	at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:20)
	at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:9)
	at com.mortardata.mhc.metrics.StatsD$SingletonHolder.<clinit>(StatsD.java:28)
	at com.mortardata.mhc.metrics.StatsD.getInstance(StatsD.java:32)
	at com.mortardata.mhc.Main.main(Main.java:69)
Caused by: java.lang.UnsatisfiedLinkError: could not get native definition for type: POINTER
	at com.kenai.jffi.Type$Builtin.lookupTypeInfo(Type.java:251)
	at com.kenai.jffi.Type$Builtin.getTypeInfo(Type.java:237)
	at com.kenai.jffi.Type.resolveSize(Type.java:155)
	at com.kenai.jffi.Type.size(Type.java:138)
	at jnr.ffi.provider.jffi.NativeRuntime$TypeDelegate.size(NativeRuntime.java:178)
	at jnr.ffi.provider.AbstractRuntime.<init>(AbstractRuntime.java:48)
	at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:57)
	at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:41)
	at jnr.ffi.provider.jffi.NativeRuntime$SingletonHolder.<clinit>(NativeRuntime.java:53)
	at jnr.ffi.provider.jffi.NativeRuntime.getInstance(NativeRuntime.java:49)
	at jnr.ffi.provider.jffi.Provider.<init>(Provider.java:29)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.getInstance(FFIProvider.java:68)
	at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.<clinit>(FFIProvider.java:57)
	at jnr.ffi.provider.FFIProvider.getSystemProvider(FFIProvider.java:35)
	at jnr.ffi.Runtime$SingletonHolder.<clinit>(Runtime.java:82)
	at jnr.ffi.Runtime.getSystemRuntime(Runtime.java:67)
	at jnr.unixsocket.SockAddrUnix.<init>(SockAddrUnix.java:46)
	at jnr.unixsocket.SockAddrUnix$BSDSockAddrUnix.<init>(SockAddrUnix.java:185)
	... 14 more
Caused by: java.lang.UnsatisfiedLinkError: java.lang.UnsatisfiedLinkError: could not locate stub library in jar file.  Tried [jni/Darwin/libjffi-1.2.dylib, /jni/Darwin/libjffi-1.2.dylib]
	at com.kenai.jffi.internal.StubLoader.getStubLibraryStream(StubLoader.java:412)
	at com.kenai.jffi.internal.StubLoader.loadFromJar(StubLoader.java:355)
	at com.kenai.jffi.internal.StubLoader.load(StubLoader.java:258)
	at com.kenai.jffi.internal.StubLoader.<clinit>(StubLoader.java:449)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at com.kenai.jffi.Init.load(Init.java:68)
	at com.kenai.jffi.Foreign$InstanceHolder.getInstanceHolder(Foreign.java:49)
	at com.kenai.jffi.Foreign$InstanceHolder.<clinit>(Foreign.java:45)
	at com.kenai.jffi.Foreign.getInstance(Foreign.java:103)
	at com.kenai.jffi.Type$Builtin.lookupTypeInfo(Type.java:242)
	at com.kenai.jffi.Type$Builtin.getTypeInfo(Type.java:237)
	at com.kenai.jffi.Type.resolveSize(Type.java:155)
	at com.kenai.jffi.Type.size(Type.java:138)
	at jnr.ffi.provider.jffi.NativeRuntime$TypeDelegate.size(NativeRuntime.java:178)
	at jnr.ffi.provider.AbstractRuntime.<init>(AbstractRuntime.java:48)
	at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:57)
	at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:41)
	at jnr.ffi.provider.jffi.NativeRuntime$SingletonHolder.<clinit>(NativeRuntime.java:53)
	at jnr.ffi.provider.jffi.NativeRuntime.getInstance(NativeRuntime.java:49)
	at jnr.ffi.provider.jffi.Provider.<init>(Provider.java:29)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.getInstance(FFIProvider.java:68)
	at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.<clinit>(FFIProvider.java:57)
	at jnr.ffi.provider.FFIProvider.getSystemProvider(FFIProvider.java:35)
	at jnr.ffi.Runtime$SingletonHolder.<clinit>(Runtime.java:82)
	at jnr.ffi.Runtime.getSystemRuntime(Runtime.java:67)
	at jnr.unixsocket.SockAddrUnix.<init>(SockAddrUnix.java:46)
	at jnr.unixsocket.SockAddrUnix$BSDSockAddrUnix.<init>(SockAddrUnix.java:185)
	at jnr.unixsocket.SockAddrUnix.create(SockAddrUnix.java:174)
	at jnr.unixsocket.UnixSocketAddress.<init>(UnixSocketAddress.java:53)
	at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1073)
	at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1070)
	at com.timgroup.statsd.NonBlockingStatsDClient.staticAddressResolution(NonBlockingStatsDClient.java:1090)
	at com.timgroup.statsd.NonBlockingStatsDClient.staticStatsDAddressResolution(NonBlockingStatsDClient.java:1100)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:283)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:171)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:146)
	at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:20)
	at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:9)
	at com.mortardata.mhc.metrics.StatsD$SingletonHolder.<clinit>(StatsD.java:28)
	at com.mortardata.mhc.metrics.StatsD.getInstance(StatsD.java:32)
	at com.mortardata.mhc.Main.main(Main.java:69)

	at com.kenai.jffi.Foreign.newLoadError(Foreign.java:72)
	at com.kenai.jffi.Foreign.access$300(Foreign.java:42)
	at com.kenai.jffi.Foreign$InValidInstanceHolder.getForeign(Foreign.java:98)
	at com.kenai.jffi.Foreign.getInstance(Foreign.java:103)
	at com.kenai.jffi.Type$Builtin.lookupTypeInfo(Type.java:242)
	... 36 more
Caused by: java.lang.UnsatisfiedLinkError: java.lang.UnsatisfiedLinkError: could not locate stub library in jar file.  Tried [jni/Darwin/libjffi-1.2.dylib, /jni/Darwin/libjffi-1.2.dylib]
	at com.kenai.jffi.internal.StubLoader.getStubLibraryStream(StubLoader.java:412)
	at com.kenai.jffi.internal.StubLoader.loadFromJar(StubLoader.java:355)
	at com.kenai.jffi.internal.StubLoader.load(StubLoader.java:258)
	at com.kenai.jffi.internal.StubLoader.<clinit>(StubLoader.java:449)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at com.kenai.jffi.Init.load(Init.java:68)
	at com.kenai.jffi.Foreign$InstanceHolder.getInstanceHolder(Foreign.java:49)
	at com.kenai.jffi.Foreign$InstanceHolder.<clinit>(Foreign.java:45)
	at com.kenai.jffi.Foreign.getInstance(Foreign.java:103)
	at com.kenai.jffi.Type$Builtin.lookupTypeInfo(Type.java:242)
	at com.kenai.jffi.Type$Builtin.getTypeInfo(Type.java:237)
	at com.kenai.jffi.Type.resolveSize(Type.java:155)
	at com.kenai.jffi.Type.size(Type.java:138)
	at jnr.ffi.provider.jffi.NativeRuntime$TypeDelegate.size(NativeRuntime.java:178)
	at jnr.ffi.provider.AbstractRuntime.<init>(AbstractRuntime.java:48)
	at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:57)
	at jnr.ffi.provider.jffi.NativeRuntime.<init>(NativeRuntime.java:41)
	at jnr.ffi.provider.jffi.NativeRuntime$SingletonHolder.<clinit>(NativeRuntime.java:53)
	at jnr.ffi.provider.jffi.NativeRuntime.getInstance(NativeRuntime.java:49)
	at jnr.ffi.provider.jffi.Provider.<init>(Provider.java:29)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.getInstance(FFIProvider.java:68)
	at jnr.ffi.provider.FFIProvider$SystemProviderSingletonHolder.<clinit>(FFIProvider.java:57)
	at jnr.ffi.provider.FFIProvider.getSystemProvider(FFIProvider.java:35)
	at jnr.ffi.Runtime$SingletonHolder.<clinit>(Runtime.java:82)
	at jnr.ffi.Runtime.getSystemRuntime(Runtime.java:67)
	at jnr.unixsocket.SockAddrUnix.<init>(SockAddrUnix.java:46)
	at jnr.unixsocket.SockAddrUnix$BSDSockAddrUnix.<init>(SockAddrUnix.java:185)
	at jnr.unixsocket.SockAddrUnix.create(SockAddrUnix.java:174)
	at jnr.unixsocket.UnixSocketAddress.<init>(UnixSocketAddress.java:53)
	at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1073)
	at com.timgroup.statsd.NonBlockingStatsDClient$5.call(NonBlockingStatsDClient.java:1070)
	at com.timgroup.statsd.NonBlockingStatsDClient.staticAddressResolution(NonBlockingStatsDClient.java:1090)
	at com.timgroup.statsd.NonBlockingStatsDClient.staticStatsDAddressResolution(NonBlockingStatsDClient.java:1100)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:283)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:171)
	at com.timgroup.statsd.NonBlockingStatsDClient.<init>(NonBlockingStatsDClient.java:146)
	at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:20)
	at com.mortardata.mhc.metrics.StatsD.<init>(StatsD.java:9)
	at com.mortardata.mhc.metrics.StatsD$SingletonHolder.<clinit>(StatsD.java:28)
	at com.mortardata.mhc.metrics.StatsD.getInstance(StatsD.java:32)
	at com.mortardata.mhc.Main.main(Main.java:69)

	at com.kenai.jffi.internal.StubLoader.load(StubLoader.java:270)
	at com.kenai.jffi.internal.StubLoader.<clinit>(StubLoader.java:449)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at com.kenai.jffi.Init.load(Init.java:68)
	at com.kenai.jffi.Foreign$InstanceHolder.getInstanceHolder(Foreign.java:49)
	at com.kenai.jffi.Foreign$InstanceHolder.<clinit>(Foreign.java:45)
	... 38 more

Finally, after some trial and error, what solved it for me was adding this dependency explicitely:

<dependency org="com.github.jnr" name="jffi" rev="1.2.18"/>

and then also adding jffi-1.2.18-native.jar manually since Ivy was not pulling it correctly for some reason.

So I think the dependency on JFFI should be either explicitly added to the POM or at least documented in the README.

Looks like java-dogstatsd-client correctly specifies its dependency on jnr-unixsocket 0.18 which in turn depends on the stuff that caused me problems. So I think
it might not be an issue with java-dogstatsd-client itself but probably with the way jnr-unixsocket dependencies are specified or with my project setup.

APi key

I want use this library for datadog api,

how can I pass api key for datadog?

Support Posting Events / Sync with Upstream Branch

I'd like to see this project support the posting of events to dogstatsd the same way the .net client does.
Looks like this was PR'ed into indeedeng/java-dogstatsd-client on march 1st and there hasn't been any activity on this project for close to a year.
Can someone pull down the latest changes from the parent repo and build it? I would be happy to help test.

Gracefull shutdown

How to shutdown the client gracefully?
Does the StatsDClient.close() method guarantees all buffers will be flushed and data sent to the server?
Thanks.

Delete ":" from metric names to allow statsd server insert them

(Also opened for the original java statsd client at tim-group/java-statsd-client#46)

Hi all!

I'm currently using statsd to automatically record all the metrics provided by some spring boot application.
In my case, spring is creating some metrics with ":" in the name (for some hystrix with feign stats), and these ones are skipped by the statsd server (https://github.com/etsy/statsd).

That implementation allow to insert multiple metric types to the same metric:

$ echo "xxx:1|c:3|g" | nc -u -w0 127.0.0.1 8125 && echo "counters" | nc localhost 8126 && echo "gauges" | nc localhost 8126

Produces this output:

{ 'statsd.bad_lines_seen': 0,
  'statsd.packets_received': 1,
  'statsd.metrics_received': 1,
  xxx: 1 }
END

{ xxx: 3 }
END

This (undocumented) statsd feature does not allow me to make a PR to statsd repository... So, That's why I'm here,
would be acceptable to make you a PR (or request for the change) to simply delete the ":" in the metric names?

The statsd server is already deleting some characters from metric name:
https://github.com/etsy/statsd/blob/master/stats.js#L170

Release new version

Hi Team
Can there be a new release 2.8? I want to try out some features like Max packet size and also need other improvements that are on master but not on 2.7

Breaking change in a minor version update

Hey,
I believe that changing configuration setup from NonBlockingStatsDClient to NonBlockingStatsDClientBuilder is a breaking change which wasn't communicated in release notes and through the major version bump. I would have to fix our code if you bumped the major version, so I am going to do that now, but at least I wouldn't discover that in staging.

I don't expect any action at this point. Cheers

recordDistributionValue not found

cannot find symbol: method recordDistributionValue(java.lang.String,int)
location: variable statsd1 of type com.timgroup.statsd.StatsDClient

Maven:

	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	<java.version>1.8</java.version>
	<elasticsearch.version>2.3.2</elasticsearch.version>
</properties>

<dependencies>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-mongodb</artifactId>
		<version>LATEST</version>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-amqp</artifactId>
	</dependency>

	<dependency>
		<groupId>org.json</groupId>
		<artifactId>json</artifactId>
		<version>20160810</version>
	</dependency>

	<dependency>
		<groupId>com.datadoghq</groupId>
		<artifactId>java-dogstatsd-client</artifactId>
		<version>2.5</version>
	</dependency>

	<dependency>
		<groupId>javax.faces</groupId>
		<artifactId>jsf-api</artifactId>
		<version>2.1</version>
	</dependency>

	<dependency>
		<groupId>org.primefaces</groupId>
		<artifactId>primefaces</artifactId>
		<version>LATEST</version>
	</dependency>

	<dependency>
		<groupId>org.springframework.data</groupId>
		<artifactId>spring-data-elasticsearch</artifactId>
		<version>LATEST</version>
	</dependency>

	<dependency>
		<groupId>net.sourceforge.htmlunit</groupId>
		<artifactId>htmlunit</artifactId>
		<version>2.27</version>
	</dependency>


</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
	</plugins>
</build>

<repositories>
	<repository>
		<id>spring-snapshots</id>
		<name>Spring Snapshots</name>
		<url>https://repo.spring.io/snapshot</url>
		<snapshots>
			<enabled>true</enabled>
		</snapshots>
	</repository>
	<repository>
		<id>spring-milestones</id>
		<name>Spring Milestones</name>
		<url>https://repo.spring.io/milestone</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>
<pluginRepositories>
	<pluginRepository>
		<id>spring-snapshots</id>
		<name>Spring Snapshots</name>
		<url>https://repo.spring.io/snapshot</url>
		<snapshots>
			<enabled>true</enabled>
		</snapshots>
	</pluginRepository>
	<pluginRepository>
		<id>spring-milestones</id>
		<name>Spring Milestones</name>
		<url>https://repo.spring.io/milestone</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</pluginRepository>
</pluginRepositories>

Add a blocking client?

I'd love to have a client that uses blockingSend directly rather than going through a queue + executor
(the monitoring library I use already ensures that backend clients are called on a separate threadpool.)
Any chance you guys could add a BlockingStatsdClient implementation? Don't really know how to properly factor out the commonalities in Java or I'd submit a PR, sorry!

Commas in Tag values

I have a tag like this:
key:value1, value2, value3

Both key and value is of String type.

So when I pass it to statsd I get 3 different tags on DD like this:

key:value1
value2
value3

I guess this is because statsd uses comma to separate the tags ? However not sure how it allows a tag without key or allows tags with key and with null values?

Weird Behavior When Using `status.hostIP` as datadog hostname in kubernetes

Hi,

We have encountered some problem related to networking in kubernetes GKE. This is very weird issue. To give you better understanding about our issue, let me explain the background a little bit.

So, we're using daemonset datadog, to ensure only 1 pod/instance of dd-agent (the name of datadog container/pod) in every node. We expose the hostPort:8125 for the daemonset
Here is the snippet of our dd-agent daemonset object:

ports:
- name: dogstatsdport
  containerPort: 8125
  hostPort: 8125
  protocol: UDP

By using the hostPort:8125, other pods in the same node/host can communicate with dd-agent pod via status.hostIP (Downward API). Why we prefer this way (using status.hostIP), because the locality and correctness aspect (the pod should communicate with dd-agent in the same node for better latency and correctness of tagging the metrics with the same hostname/node).

Hopefully you have better understanding at this point.

So, let me explain the problem.

We're using java-datadog-statsd-client (https://github.com/DataDog/java-dogstatsd-client), it uses UDP protocol to send metrics to dd-agent statsd server.
When the dd-agent is up prior to the client pod (the pod who send the metrics), the metrics is sent successfully (indicated by the metrics shown in datadog dashboard).
However, when we restart the dd-agent pod (by killing the pod -> to let the daemonset spawn another dd-agent pod in the same node), the metrics doesn't show up in the datadog dashboard. To solve this, we have to manually restart the client pod, then the metrics is shown.

So, to summarize, in GKE environment, this scenario:

  • dd-agent pod is up
  • client pod is up
  • client pod sending the metrics to dd-agent pod in the same node/host
  • metrics/data is shown at datadog dashboard
  • dd-agent is restarted (killed, and new pod is spawned to replace the killed ones, by the daemonset controller)
  • the metrics is stopped being shown at datadog dashboard
  • restart the client pod -> metrics is shown successfully.

Note: restarting the client pod everytime we need to restart dd-agent is not ideal for us, since we have many client pod running owned by various internal teams.

  1. I also successfully reproduce the same scenario by using the custom client application (java) (from our office network, my laptop) to send the metrics to dd-agent hostIp:port (our office is allowed to access internal network of where gke cluster residing)
  2. However, I can't reproduce the same scenario if I am using a dd-agent outside the gke network/cluster. In this case, i deployed dd-agent in my laptop. The metrics is shown eventhough I restarted the dd-agent service (in my laptop) multiple times.

Do you have any idea why this is happening? java-datadog-statsd client is using Datagram in their NonBlockingStatsdClient.java (https://github.com/DataDog/java-dogstatsd-client/blob/master/src/main/java/com/timgroup/statsd/NonBlockingStatsDClient.java). I also tried to logging any error in the NonBlockingStatsDClient.java, but no error found.
I am not a network expert, but I suspect that when the dd-agent pod is killed, the datagram object on the client pod(udp protocol though) still pointing the same socket of the killed dd-agent one

Event sends, but no event in the dogstatsd.log and not in the DataDog events console

I succeeded with statsd .recordExecutionTime method with the same statsd client.
Any special configuration is needed?

Code example:
private void recordEndLoadGraph(String routerId, String failReason) {

    String endLoadText = StringUtils.isEmpty(failReason) ? ", success" : ", fail reason: " + failReason;
    Event event = Event.builder().withDate(System.currentTimeMillis()).
            withTitle("LoadGraphEnd").
            withAggregationKey(routerId).
            withAlertType(StringUtils.isEmpty(failReason) ? Event.AlertType.INFO : Event.AlertType.ERROR).
            withPriority(Event.Priority.NORMAL).
            withText("End loading graph, router ID: " + routerId + endLoadText).build();
    statsd.recordEvent(event);
}

DD client integrated with its own metrics system

Unfortunately the DataDog client code here is highly embedded with an internal representation about what a metric is. Please consider separating out the count, incrementCounter, gauge, and time type methods from the send, QueueConsumer, executor, and other true client code. Otherwise this code is only useful as a reference implementation if someone already has their own metrics system.

Thanks.

Temporary loss of socket is not recoverable

We're running the Datadog agent as a DaemonSet (although I suspect the same issue will happen when running as a sidecar) in Kubernetes and the client is a singleton in a JVM process running in a pod.

The communication between the client and agent is done via UDS.

When instantiating NonBlockingStatsDClient it open()s the DatagramChannel:
https://github.com/DataDog/java-dogstatsd-client/blob/master/src/main/java/com/timgroup/statsd/NonBlockingStatsDClient.java#L642

Everything works as expected.

Now let's say we updated our Datadog agent's template. We apply the template and that causes the DaemonSet to gradually restart the agent processes running on nodes.

When the agent restarts, the socket path is temporarily unavailable and we see a

 java.io.IOException: No such file or directory
    at jnr.unixsocket.UnixDatagramChannel.send(UnixDatagramChannel.java:164)
    at com.timgroup.statsd.StatsDSender.blockingSend(StatsDSender.java:88)
    at com.timgroup.statsd.StatsDSender.run(StatsDSender.java:71)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)

The exception handler we supply to NonBlockingStatsDClient cannot do anything to tell the client to re-open the DatagramChannel, or retry in any way.

When we've seen this happen before, we had to restart all of our pods.

From a code perspective, our only option in this case is to mark the client as "dead" and have other code observe that state and re-instantiate it. But, if we do that, we lose all of the metrics that are currently in the queue.

Instead, the client should be able to recover from such an error.

Make Unix Domain Socket functionality optional

Not all usages of the library need Unix Domain Socket transport. Let's figure out how to make it optional.

Some wins we can get by making Unix Domain Socket transport optional:

  • Extra dependencies. java-dogstatsd-client only depends on JARs brought in by com.github.jnr:jnr-unixsocket. If we remove this dependency, projects not requiring Unix Domain Socket transport will have to import 1 JAR instead of 14.
  • Get rid of issues caused by ASM not being repackaged. See jnr/jnr-ffi#193 for more details. Here is an example of an issue caused by those dependencies: #68

Some approaches we could take to address this:

  1. Split Unix Domain Socket transport to a separate JAR.
  2. Make com.github.jnr:jnr-unixsocket an optional dependency and add some workarounds to prevent eager loading of classes from that JAR.

Metrics not sent when the stasD server changes the IP

I have the statsd server running in kubernetes. Every time I restart the statsd container it gets a new IP and it doesn't receive any messages. If I restart the application that sends the metrics, it's working but I would like a 'reconnect' mechanism on the client.

sending metrics inside of scala execution context will not work

 Telemetry.qc_success(1, "test1")
    val futures = datasets.map( (ds) => {
      Telemetry.qc_success(1, "test2")
      val future = Future {
      Telemetry.qc_success(1, "test3")
        blocking{
      Telemetry.qc_success(1, "test4")
          actualCode()
        }
      }
      Telemetry.qc_success(1 "test5")
      future
    }).map( (x) => {
      Telemetry.qc_success(1, "test6")
      Await.result(x, 2 hours)
      Telemetry.qc_success(1, "test7")
    })
    Telemetry.qc_success(1, "test8")

In the above code, only test1, test2 and test8 will be successfully sent. Seems anything inside of scala execution context will not work.

Replace LinkedBlockingQueue with ConcurrentLinkedQueue

Hi Team
We are noticing performance issue when using this client on a java app that emits high volume of metric updates/sec ie; in the order of > 600k (600 thousand) metric updates/sec. We did see improvement in throughput of the app when we replaced LinkedBlockingQueue with ConcurrentLinkedQueue.

Our app consumes messages from kafka and writes to Elasticsearch , in peak it has a throughput of 70k-90k messages per host per second and it emits/updates metrics for every consumed message.

The performance issue when using the LinkedBlockingQueue is due to the fact multiple threads of the app are contending to acquire lock to the queue and are in waiting state(as per thread dump and flamegraphs). Are there any reasons to not use ConcurrentLinkedQueue. Would you accept the upstream PR to replace the queue implementation or making the queue implementation configurable.

Event sends, but it not processed by dogstatsd

This ticket follows work between myself and your support staff (see zendesk ticket 69869).

Summary: I can send an event to dogstatsd, even see it in a tcpdump on the dogstatds server, but dogstatsd itself does not recognise it and thus it does not get seen within the DataDog site.

Here's the Java code:

Event event = Event.builder()
    .withTitle("Sender has marked the Gateway platform as down")
    .withText("Sender has marked the Gateway platform as down by recording the key " + GATEWAY_DOWN_REDIS_KEY + " in Redis")
    .withAlertType(Event.AlertType.WARNING)
    .withDate(instant.getEpochSecond())
    .withPriority(Event.Priority.NORMAL)
    .withSourceTypeName("gateway")
    .build();
statsDClient.recordEvent(event);

Here's the fragment of UDP traffic spotted by tcpdump:

        0x0020:  7761 792e 6465 6c69 7665 7279 2e72 6570  way.delivery.rep
        0x0030:  6f72 742e 7265 6365 6976 6564 3a34 7c67  ort.received:4|g
        0x0040:  7c23 7370 7269 6e67 2e61 7070 6c69 6361  |#spring.applica
        0x0050:  7469 6f6e 2e6e 616d 653a 6e67 772d 6565  tion.name:ngw-ee
        0x0060:  2d72 6563 6f6e 6369 6c65 722c 6170 703a  -reconciler,app:
        0x0070:  6e67 772c 656e 763a 7465 7374            ngw,env:test
14:55:47.470059 IP (tos 0x0, ttl 126, id 28861, offset 0, flags [none], proto UDP (17), length 231)
    10.0.0.90.62353 > 172.25.0.10.8125: UDP, length 203
        0x0000:  4500 00e7 70bd 0000 7e11 14cc 0a00 005a  E...p...~......Z
        0x0010:  ac19 000a f391 1fbd 00d3 e07b 5f65 7b34  ...........{_e{4
        0x0020:  362c 3939 7d3a 5365 6e64 6572 2068 6173  6,99}:Sender.has
        0x0030:  206d 6172 6b65 6420 7468 6520 4761 7465  .marked.the.Gate
        0x0040:  7761 7920 706c 6174 666f 726d 2061 7320  way.platform.as.
        0x0050:  646f 776e 7c53 656e 6465 7220 6861 7320  down|Sender.has.
        0x0060:  6d61 726b 6564 2074 6865 2047 6174 6577  marked.the.Gatew
        0x0070:  6179 2070 6c61 7466 6f72 6d20 6173 2064  ay.platform.as.d
        0x0080:  6f77 6e20 6279 2072 6563 6f72 6469 6e67  own.by.recording
        0x0090:  2074 6865 206b 6579 2047 6174 6577 6179  .the.key.Gateway
        0x00a0:  3a50 6c61 7466 6f72 6d49 7344 6f77 6e20  :PlatformIsDown.
        0x00b0:  696e 2052 6564 6973 7c64 3a31 3437 3734  in.Redis|d:14774
        0x00c0:  3933 7c70 3a6e 6f72 6d61 6c7c 743a 7761  93|p:normal|t:wa
        0x00d0:  726e 696e 677c 2365 6e76 3a74 6573 742c  rning|#env:test,
        0x00e0:  6170 703a 6e67 77                        app:ngw
14:55:49.766856 IP (tos 0x0, ttl 64, id 41066, offset 0, flags [DF], proto UDP (17), length 147)
    172.25.0.19.34919 > 172.25.0.10.8125: UDP, length 119
        0x0000:  4500 0093 a06a 4000 4011 41a0 ac19 0013  E....j@[email protected].....
        0x0010:  ac19 000a 8867 1fbd 007f 58e0 6e67 772e  .....g....X.ngw.
        0x0020:  6565 2e71 7565 7279 2e64 656c 6976 6572  ee.query.deliver
        0x0030:  6965 732e 6a76 6d2e 6763 2e50 532d 4d61  ies.jvm.gc.PS-Ma
        0x0040:  726b 5377 6565 702e 636f 756e 743a 377c  rkSweep.count:7|
        0x0050:  677c 2373 7072 696e 672e 6170 706c 6963  g|#spring.applic

We are running datadog/docker-dd-agent:latest-dogstatsd (image: sha256:cabb3d6a7850c0816e639ce6ab5ee3fd91b89726e535b817a55571ad2d286678). I sent up a flare but the logs show the dogstatsd software did not recognise the event (none were sent on).

Support question whether there's a bug in this library or if the formatting of the event might be incorrect somehow?

Allow address resolution before send

The StatsD client currently is not able to cope with address changes on the DNS name throughout its lifetime. We raised indeedeng#26 in order to provide a configuration option for the client to change this behaviour, so that resolution is attempted prior sending data (relying on java.net.InetAddress's DNS cache).

Your fork is consumed by coursera/metrics-datadog, which is our dependency. We would like to see the above mentioned changes merged in and a release cut, in order to raise a PR against metrics-datadog that allows this configuration to be passed through. Would you prefer me to raise a PR with just the above mentioned changes or would you like to sync with the upstream fork (that would cover #1 as well afaics)?

Reduce heap and GC overhead

Reduce heap and GC overhead by rendering packets on the caller threads and queuing byte arrays or byte buffers.

The messages are currently rendered to instances of java.lang.String and are queued up in StatsDSender. Rendering the packets on caller threads and queuing byte[] or ByteBuffer instances will:

  • Yield a 2x reduction in heap volume occupied by potentially not-so-short-lived objects
  • Reduce the GC load - smaller message objects are cheaper to copy between generations
  • Reduce the amount of work done on the StatsDSender thread and increase the throughput

We could also skip the intermediate StringBuilder representation and render straight to ByteBuffer.

Errors in NonBlockingStatsDClient.QueueConsumer are not recoverable

QueueConsumer does not recover from java.lang.Error instances and there's no API to re-schedule another QueueConsumer. That results in the message queue getting filled up and no metrics getting emitted.

I had an application instance that had an OutOfMemoryError thrown in QueueConsumer. Here's the stack trace of the thread that was supposed to run QueueConsumer:

 StatsD-pool-1-thread-1 tid=23 [WAITING] [DAEMON]
sun.misc.Unsafe.park(boolean, long) Unsafe.java
java.util.concurrent.locks.LockSupport.park(Object) LockSupport.java:175
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await() AbstractQueuedSynchronizer.java:2039
java.util.concurrent.LinkedBlockingQueue.take() LinkedBlockingQueue.java:442
java.util.concurrent.ThreadPoolExecutor.getTask() ThreadPoolExecutor.java:1067
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) ThreadPoolExecutor.java:1127
java.util.concurrent.ThreadPoolExecutor$Worker.run() ThreadPoolExecutor.java:617
java.lang.Thread.run() Thread.java:745

Here are some things I think we could do to mitigate that:

  • Minimize the number of allocations in QueueConsumer#run. In particular, packet encoding could be performed in the client threads
  • Add API for re-scheduling the failed QueueConsumer
  • Handle OutOfMemoryError (are there other recoverable errors?) in QueueConsumer

API Key

How to provide Datadog API key to the client?

recordDistributionValue how it's works?

Hi,
My code looks like:

StatsDClient statsd1 = new NonBlockingStatsDClient(
"",
"localhost",
8125,
new String[] {"good"}
);

statsd1.recordDistributionValue("sdw.lumi.test", 11);

Then I run it, in datadog panel I didn't see any metrics

2.10.0 breaks my Datadog metrics

So this is how I currently setup my StatsDClient:

new NonBlockingStatsDClient("", "localhost", 8125);

I just tried updating the version to 2.10.0, and I've tried using the old deprecated constructor and then this:

new NonBlockingStatsDClientBuilder().prefix("").hostname("localhost").port(8125).build();

And Datadog metrics stopped showing up if I use either of those. And as soon as I downgraded to 2.9.0, metrics started working again.
So am I doing something wrong? Is there some additional thing I need to do in 2.10.0?
Thanks!

Is the client object lightweight?

Is the NonBlockingStatsDClient a lightweight object? I'm planning to use this in a large application and wondering about creating multiple instances of this client, one for each prefix?

Reusing one instance across the application would require us to explicitly provide the prefix for each metric (or aspect), which can be noisy.

What would you recommend?

Thread pools in StatsDNonBlockingProcessor not properly closed

My application using this library doesn't properly shut down. I already updated to the latest version 2.10.2 which fixed another thread pool issue (PR #115 ) , but this one seems to be still wrong.

Thread[pool-3-thread-1,5,main], id: 19, daemon: false
    [email protected]/java.lang.Thread.sleep(Native Method)
    app//com.timgroup.statsd.StatsDNonBlockingProcessor$ProcessingTask.run(StatsDNonBlockingProcessor.java:35)
    [email protected]/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    [email protected]/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    [email protected]/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    [email protected]/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    [email protected]/java.lang.Thread.run(Thread.java:834)

BufferOverflowException when sending events with large text bodies

Using version 2.3.

Not sure if this is limitation or not, but I could not find an obvious answer.
When sending events with the text body larger than the buffer limit (1400 bytes in the 2.3 version), a BufferOverflowException occurs.
When building the Event object, there's no mention of the body size limit, perhaps there needs to be? Or can there be a way to handle events with big/larger text bodies?

An example of the exception received in the handler I register with the client looks like:

java.nio.BufferOverflowException
	at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189)
	at java.nio.ByteBuffer.put(ByteBuffer.java:859)
	at com.timgroup.statsd.NonBlockingStatsDClient$QueueConsumer.run(NonBlockingStatsDClient.java:872)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

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.