-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support timestamp value for DogstatsD metrics #252
Comments
Thanks for the nice writeup here. So we've had three ways so far that flavor-specific extensions have worked:
This to me would be case #1 if I'm understanding correctly? So a new param passed into gauge and count methods which is specific to Datadog. |
Thanks for the reply! I originally considered the first option as well, as it seemed that was how some of the features were already implemented. The reason I didn't immediately go for that was due to the methods using positional arguments where multiple of the arguments are optional already. Taking gauge as an example the resulting type would be something like: gauge(stat: string | string[], value: number, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, sampleRate?: number, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, tags?: Tags, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, timestamp?: Date, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, tags?: Tags, timestamp?: Date, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, sampleRate?: number, timestamp?: Date, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, timestamp?: Date, callback?: StatsCb): void; Since I made the If you still think it's the way to go about it, I can do it in this case (I think), but what do you think about introducing an interface GaugeOptions {
tags?: Tags;
sampleRate?: number;
timestamp?: Date;
}
gauge(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, tags?: Tags, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, sampleRate?: number, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, options?: GaugeOptions, callback?: StatsCb): void; Since Another option could be to replace all the positional arguments with an object. This would be a bigger change for users, although it could still be made backwards compatible. interface StatsFunctionProps {
stat: string | string[];
value: number;
callback?: StatsCb;
}
interface GaugeProps extends StatsFunctionProps {
sampleRate?: number;
tags?: Tags;
timestamp?: Date;
}
gauge(props: GaugeProps): void;
// These overrides would be for backwards compatibility
gauge(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, tags?: Tags, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, callback?: StatsCb): void;
gauge(stat: string | string[], value: number, sampleRate?: number, callback?: StatsCb): void;
// Usage
gauge({
stat: 'foo',
value: 1,
timestamp: new Date()
}) Either option would make it trivial to add additional extensions in the future, such as the container id that I mentioned earlier, without needing to write complex code to figure out which property an optional argument actually refers to based on its type and which other arguments have been passed. The benefit of the last approach is that there's no conflict between the options object and the tags object, reducing complexity and side-stepping the problem where a tag key conflicts with an option name. Note that although my examples here are just for the gauge type, the same would obviously apply to all stat functions. |
Apologies for delay here, have generally not been available much these past two weeks, and thanks for such a detailed and thoughtful reply. I see your point now on how the general arguments-adding solution isn't going to work here. I have had a philosophy in this project of tilting heavily towards backwards-compatibility, and it can show in some of the ugliness of that param growth! I like the first option myself, of adding a new GaugeOptions in there. This looks like a great idea in general to clean up some of the options overgrowth. Happy to review anything related to this if you are still looking to do this. Thanks. |
No worries whatsoever. I'm a maintainer of a reasonably popular open-source project myself, and know full well what it's like to have an inbox full of issues and pending PRs. The only issue I have with the first option is how on earth to differentiate between gauge(stat: string | string[], value: number, options?: GaugeOptions, callback?: StatsCb): void; Would "solve" this, but obviously it comes at a cost :( Otherwise we'd have to do something like: const gaugeOptionKeys = new Set<keyof GaugeOptions>(['sampleRate', 'tags', 'timestamp']);
function isGaugeOptions(maybeGaugeOptions: number | Tags | StatsCb | GaugeOptions | undefined): maybeGaugeOptions is GaugeOptions {
return maybeGaugeOptions != null && typeof maybeGaugeOptions === 'object' && !Array.isArray(maybeGaugeOptions) && Object.keys(maybeGaugeOptions).some(key => key in gaugeOptionKeys);
}
// Followed by one heck of an if-pyramid to figure out what we're dealing with The obvious downside, other than leading to some real nasty code, is that you can't ever have a tag with the same name as an option in Let me know if you have an ideas or thoughts. I'm about to head off to vacation for a few weeks, but I'll be back after. |
Well let me say here that I obviously was waiting for your vacation for a few weeks and it wasn't that I was super behind on emails in general again! 😄 Thanks for the understanding- I like to make sure this project continues to get some love even though I haven't been able to give it much attention lately. And thanks again for a great message on this. I understand there is some downside with the backward compatibility, but I would really like to keep it. There are definitely some other cases like this currently in the code, in places where we can do some interesting checks to keep that backward compatibility (as I'm sure you've already seen in https://github.com/brightcove/hot-shots/blob/master/lib/statsd.js#L158 and elsewhere). This will add to that ugliness, for sure, but I would want to consider a more full param rethink at once if this was to be considered. |
Newer versions of the DogstatsD protocol support an optional timestamp value for count and gauge metrics: https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v13
This is defined as a UNIX timestamp in the past, prefixed with
T
. For example:Looking at the methods exposed on Client it doesn't seem to be possible to pass this along. Maybe if you construct it manually and call
send
, but then the timestamp will come before the tags, which I'm not sure if it's meaningful in the statsd protocol, but at least it doesn't match what's in the Datadog docs.How do you think about these flavor-specific extensions? I might be open to implementing support for this, but I would like some guidance on how you would prefer the interface to work. They also have another extension for container id, which also isn't possible to pass.
The text was updated successfully, but these errors were encountered: