Skip to content
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

Feature/datadog #411

Closed
wants to merge 24 commits into from
Closed

Feature/datadog #411

wants to merge 24 commits into from

Conversation

ivoreis
Copy link
Contributor

@ivoreis ivoreis commented Dec 12, 2017

I have a use case for this tool but I need to integrate with our current StatsD report tool.

The goal of this PR is to create a simple Datadog integration and it's still a WIP.

DogStatsD implements the StatsD protocol (doc)

Based on the feedback the idea is to provide 2 new collectors:

Usage:

// STATSD_PORT, STATSD_BUFFER_SIZE can also be passed to override the defaults
STATSD_ADDR=address k6 run sample.js -o statsd
// DATADOG_PORT, DATADOG_BUFFER_SIZE, DATADOG_TAG_WHITELIST can also be passed to override the defaults
DATADOG_ADDR=address DATADOG_NAMESPACE=k6.myapp. k6 run sample.js -o datadog

Relates to #343, #196.

Feedback welcome :)

@liclac
Copy link
Contributor

liclac commented Dec 12, 2017

Hm, could you revert the core-js version bump there? I don't mind if you want me to update it/send a PR that does it, but it doesn't seem relevant to this PR.

@liclac
Copy link
Contributor

liclac commented Dec 12, 2017

My biggest question wrt the design in general: is this specific to Datadog, or is it general-purpose for any StatsD server? I see that Datadog extended the StatsD protocol a bit, but are those changes portable to other servers?

What I'd probably do is PR this as two collectors in one: one that just talks to a statsd server, one that wraps that one (with its own configuration/login) and talks specifically to datadog (hardcoding their statsd address, using their login system if any, etc.).

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 12, 2017

AFIK the major difference is that dogstatsd extends statsd and adds tags. If tags are sent to a generic statsd server the server will output bad line packet.

Taking the path of a double collector both implementations would be very similar varying only the ability to pass extra tags.

@liclac
Copy link
Contributor

liclac commented Dec 12, 2017

I'd like it a lot if you did that, and then changed the environment variables, etc. of this collector to be datadog-specific (defaulting to their server address!), that'll avoid a lot of confusion.

@liclac
Copy link
Contributor

liclac commented Dec 12, 2017

Of course, if you'd rather just contribute the datadog collector, that's fine too!

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 12, 2017

Sure I can do that. Currently they still use the same ENVs but in order to avoid confusion I'll split them out. Just gets a bit tricky whilst trying to avoid duplication because both collectors a roughly the same.

@liclac
Copy link
Contributor

liclac commented Dec 13, 2017

The cloud collector/executor (currently only on a feature branch) puts shared code in one place and the other is more like a skeleton that calls stuff from the other place, you could do something like that?

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 13, 2017

Yep I'm not happy with my current implementation either, I'll have a look how the cloud bit is handled.

@liclac
Copy link
Contributor

liclac commented Dec 15, 2017

Ah, core/... is for executors, eg. local, cloud, cluster, etc. Keep collectors in stats.

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 17, 2017

Sure I've made that change. Anything else you see that needs improvement?

@liclac
Copy link
Contributor

liclac commented Dec 18, 2017

Please don't change js/lib/core-js ^^;;

collectorJSON = "json"
collectorCloud = "cloud"
collectorStatsD = "statsd"
collectorDogStatsD = "dogstatsd"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I kinda feel like "datadog" would be a more obvious name here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the only benefit of having as datadog is that it's a shorter name, other than that statsd and dogstatsd makes more sense in terms of convention because in practice it's an implementation of a statsd protocol. Both implement statsd protocal but dogstatsd extends it. I believe is more transparent that way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm thinking is that most people will come looking for "datadog" and instead find "dogstatsd", then get confused by what the difference is, while people who are running dogstatsd know what they're doing. I could be wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. Do you think that can be solved with documentation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly, but I feel like it's better to avoid the confusion in the first place.

Unfortunately, a lot of people seem to really hate reading the effing manual.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liclac I've updated the collector name to be datadog

Config: conf,
Logger: log.WithField("type", statsd.DogStatsD.String()),
Type: statsd.DogStatsD,
Tagger: tagHandler,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be cleaner to do &TagHandler{} here?

I don't see it used anywhere else in this function...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted.

import (
"encoding/json"

statsd "github.com/loadimpact/k6/stats/statsd/shared"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

common as a package name would be more consistent with how the JS runner is organised.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update

Config: conf,
Logger: log.WithField("type", statsd.StatsD.String()),
Type: statsd.StatsD,
Tagger: tagHandler,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here, &TagHandler{} here seems cleaner?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

type Config ConfigFields

// Address returns client address
func (c Config) Address() string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, why are these accessors? They don't seem to do anything.

Copy link
Contributor Author

@ivoreis ivoreis Dec 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use the same struct and just ignore the fields one doesn't use?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible workaround:

// package shared
type Config {
  Addr string `envconfig:"ADDR"`
}

// package statsd
type Config struct {
  shared.Config `envconfig:"STATSD"`
}

// package dogstatsd
type Config struct {
  shared.Config `envconfig:"DOGSTATSD"`
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm not sure about this. Just feels a bit weird and the interface solutions looks cleaner. What do you think the trade-off is here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your version is… really verbose, and it's essentially solving a parsing problem by adapting the data, which feels backwards to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also do something like just setting envconfig:"ADDR" and then using a different prefix in envconfig.Process().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, is really verbose. I think using process is a good idea, let me give it a go.


// Run the collector
func (c *Collector) Run(ctx context.Context) {
c.Logger.Debugf("%s: Running!", c.Type.String())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use WithField()/WithFields() rather than Levelf().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean here. Type is used to prefix the string not as a meta/tagging. I guess if that goes again't any project convention I'll have to change all of the other similar instances.

@liclac
Copy link
Contributor

liclac commented Dec 18, 2017

Getting there! The only big question I have is about the naming - datadog feels like a more obvious name than dogstatsd.

Also: consider implementing k6 login datadog and/or k6 login statsd commands while you're at it.

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 18, 2017

@liclac What do you mean with k6 login datadog and statsd? There's no need to authenticate with statsd / dogstatsd.

@liclac
Copy link
Contributor

liclac commented Dec 18, 2017

Huh, how do you push metrics to datadog in that case? (I've never used it before ^^;;)

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 18, 2017

You just need an endpoint and voila :) Ref and doc. Normally you an agent running and you send data to it. The client doesn't authenticate, just the agent.

@liclac
Copy link
Contributor

liclac commented Dec 18, 2017

Ah! That makes sense.

Maybe you could just add a login command that lets you specify a default endpoint? Because what saved credentials essentially do is just let you store default configs for when you just do -o [type] with no arguments - -o influxdb isn't too bad, typing -o influxdb=http://localhost:8086/k6 over and over is just annoying.

@ivoreis
Copy link
Contributor Author

ivoreis commented Dec 18, 2017

That's a good point. In the case of passing an url I guess passing addr and port becomes redundant. What's the difference between constructing the url viaADD+PORT and args from command influxdb=... Is there any preferable option?

@@ -56,8 +56,8 @@ func parseCollector(s string) (t, arg string) {
}

func newCollector(t, arg string, src *lib.SourceData, conf Config) (lib.Collector, error) {
loadConfig := func(out encoding.TextUnmarshaler) error {
if err := envconfig.Process("k6", out); err != nil {
loadConfig := func(out encoding.TextUnmarshaler, namespace string) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an extra arg to loadConfig func. What are the use cases for K6 env var?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've extracted the hard-coded k6 string and made it a var that is set when the config is loaded. The question was to understand what are the use cases for k6 usage and if we would need to provide a k6+statsd / k6+datadog combination.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, what I answered in my other comment?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, I'm not sure what the question is here... ^^;;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^:: No worries. Probably I'm just overcomplicating things. Are you happy with this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, at this point you could just change the loadConfig calls to envconfig.Process calls wholesale. That function basically existed to not have to type (and potentially typo "k6") all the time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree but I tried to keep it consistent to how was done before. Happy to do other PR to simplify loadConfig stuff.

@liclac
Copy link
Contributor

liclac commented Dec 19, 2017

Optimally, people should be able to use whichever one they prefer, and possibly whichever works better in the context they're working in.

On my laptop, I have one local InfluxDB server, so I want to just be able to do k6 run -o influxdb ...; but if I was running on, say, Kubernetes, I'd probably want to have a password-protected influxdb server and use Secrets, which can be easily bound to environment variables (K6_INFLUXDB_USERNAME, K6_INFLUXDB_PASSWORD, ...).

stats/datadog/collector.go Show resolved Hide resolved
@@ -0,0 +1,30 @@
package statsd
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package should be called common.

@codecov-io
Copy link

Codecov Report

Merging #411 into master will decrease coverage by 0.08%.
The diff coverage is 12.5%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #411      +/-   ##
==========================================
- Coverage   62.52%   62.43%   -0.09%     
==========================================
  Files          89       89              
  Lines        6234     6246      +12     
==========================================
+ Hits         3898     3900       +2     
- Misses       2122     2132      +10     
  Partials      214      214
Impacted Files Coverage Δ
cmd/collectors.go 0% <0%> (ø) ⬆️
cmd/config.go 35.48% <100%> (+2.15%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5d50aad...3b587a4. Read the comment docs.

@robingustafsson
Copy link
Member

@ivoreis I'm going to have a look at this. What is still WIP so I know when testing?

@mstoykov
Copy link
Contributor

mstoykov commented Jan 11, 2019

closed in favour of #893

@mstoykov mstoykov closed this Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants