Skip to content

Commit

Permalink
Add Datadob support
Browse files Browse the repository at this point in the history
  • Loading branch information
rs committed Mar 4, 2018
1 parent 3602d51 commit e2b1c44
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 2 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,11 @@ jplot --url http://:8080/debug/vars \

![](doc/memstats.png)

### Datadog

To graph metrics from Datadob, you have to pass a Datadog API and Application key to jplot as follow:

```
jplot --url datadog://<api_key>/<app_key> metrics…
```

97 changes: 97 additions & 0 deletions data/datadog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package data

import (
"fmt"
"time"

"github.com/elgs/gojq"
"gopkg.in/zorkian/go-datadog-api.v2"
)

type datadogSource struct {
client *datadog.Client
specs []Spec
c chan res
done chan struct{}

// state
lastQueryTime int64
}

// FromDatadog fetches data from Datadog service (http://datadog.com).
func FromDatadog(apiKey, appKey string, specs []Spec, interval time.Duration, size int) *Points {
client := datadog.NewClient(apiKey, appKey)
s := &datadogSource{
client: client,
specs: specs,
c: make(chan res),
done: make(chan struct{}),
lastQueryTime: time.Now().Unix(),
}
go s.run(interval)
return &Points{
Size: size,
Source: s,
}
}

func (s *datadogSource) run(interval time.Duration) {
t := time.NewTicker(interval)
defer t.Stop()
for {
select {
case <-t.C:
s.fetch()
case <-s.done:
close(s.c)
return
}
}
}

func (s *datadogSource) fetch() {
maxUpdateTimestamp := int64(s.lastQueryTime)
dataPoints := make(map[string]datadog.DataPoint, len(s.specs))
var err error
for _, spec := range s.specs {
for _, field := range spec.Fields {
query := s.formatQuery(field)
series, err := s.client.QueryMetrics(s.lastQueryTime, time.Now().Unix(), query)
if err != nil {
s.c <- res{err: err}
return
}
if len(series) == 0 {
s.c <- res{err: fmt.Errorf("no data for %s", field.Name)}
return
}
endTs := int64(series[0].GetEnd() / 1000)
if endTs > maxUpdateTimestamp {
maxUpdateTimestamp = endTs
}
// assume the last data point is the latest
dataPoints[field.ID] = series[0].Points[len(series[0].Points)-1]
}
}

jq := gojq.NewQuery(dataPoints)
s.c <- res{jq: jq, err: err}
}

func (s *datadogSource) formatQuery(field Field) string {
querySuffix := ""
if field.IsCounter {
querySuffix = ".as_count()"
}
return fmt.Sprintf("%s%s", field.Name, querySuffix)
}

func (s *datadogSource) Get() (*gojq.JQ, error) {
res := <-s.c
return res.jq, res.err
}

func (s *datadogSource) Close() error {
close(s.done)
return nil
}
2 changes: 1 addition & 1 deletion data/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type res struct {
err error
}

// FromHTTP fetch data points from url every interval and keep size points.
// FromHTTP fetches data points from url every interval and keep size points.
func FromHTTP(url string, interval time.Duration, size int) *Points {
h := httpSource{
c: make(chan res),
Expand Down
10 changes: 9 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,15 @@ func main() {
}
var dp *data.Points
if *url != "" {
dp = data.FromHTTP(*url, *interval, *steps)
if strings.HasPrefix(*url, "datadog://") {
keys := strings.Split((*url)[10:], "/")
if len(keys) != 2 {
fatal("invalid datadog url, format is datadog://apiKey/appKey")
}
dp = data.FromDatadog(keys[0], keys[1], specs, *interval, *steps)
} else {
dp = data.FromHTTP(*url, *interval, *steps)
}
} else {
dp = data.FromStdin(*steps)
}
Expand Down

0 comments on commit e2b1c44

Please sign in to comment.