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

Adding dropwizard module #4022

Merged
merged 3 commits into from Apr 20, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Expand Up @@ -127,6 +127,7 @@ https://github.com/elastic/beats/compare/v5.1.1...master[Check the HEAD diff]
- Adding support for custom http headers and TLS for metricbeat modules {pull}3945[3945]
- Add new MetricSet interfaces for developers (`Closer`, `ReportingFetcher`, and `PushMetricSet`). {pull}3908[3908]
- Add kubelet module {pull}3916[3916]
- Add dropwizard module {pull}4022[4022]

*Packetbeat*
- Add `fields` and `fields_under_root` to packetbeat protocols configurations. {pull}3518[3518]
Expand Down
5 changes: 5 additions & 0 deletions metricbeat/docker-compose.yml
Expand Up @@ -19,6 +19,7 @@ services:
- ${PWD}/module/apache/_meta/env
- ${PWD}/module/ceph/_meta/env
- ${PWD}/module/couchbase/_meta/env
- ${PWD}/module/dropwizard/_meta/env
- ${PWD}/module/elasticsearch/_meta/env
- ${PWD}/module/haproxy/_meta/env
- ${PWD}/module/jolokia/_meta/env
Expand All @@ -42,6 +43,7 @@ services:
apache: { condition: service_healthy }
ceph: { condition: service_healthy }
couchbase: { condition: service_healthy }
dropwizard: { condition: service_healthy }
elasticsearch: { condition: service_healthy }
haproxy: { condition: service_healthy }
jolokia: { condition: service_healthy }
Expand All @@ -67,6 +69,9 @@ services:
couchbase:
build: ${PWD}/module/couchbase/_meta

dropwizard:
build: ${PWD}/module/dropwizard/_meta

elasticsearch:
extends:
file: ${ES_BEATS}/testing/environments/${TESTING_ENVIRONMENT}.yml
Expand Down
14 changes: 14 additions & 0 deletions metricbeat/docs/fields.asciidoc
Expand Up @@ -19,6 +19,7 @@ grouped in the following categories:
* <<exported-fields-common>>
* <<exported-fields-couchbase>>
* <<exported-fields-docker>>
* <<exported-fields-dropwizard>>
* <<exported-fields-elasticsearch>>
* <<exported-fields-golang>>
* <<exported-fields-haproxy>>
Expand Down Expand Up @@ -1875,6 +1876,19 @@ type: long
Total number of outgoing packets.


[[exported-fields-dropwizard]]
== Dropwizard Fields

beta[]
Stats collected from Dropwizard.



[float]
== dropwizard Fields



[[exported-fields-elasticsearch]]
== elasticsearch Fields

Expand Down
38 changes: 38 additions & 0 deletions metricbeat/docs/modules/dropwizard.asciidoc
@@ -0,0 +1,38 @@
////
This file is generated! See scripts/docs_collector.py
////

[[metricbeat-module-dropwizard]]
== dropwizard Module

This is the dropwizard Module.
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a link to dropwizard 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.

done.




[float]
=== Example Configuration

The Dropwizard module supports the standard configuration options that are described
in <<configuration-metricbeat>>. Here is an example configuration:

[source,yaml]
----
metricbeat.modules:
#- module: dropwizard
#metricsets: ["collector"]
#enabled: true
#period: 10s
#hosts: ["localhost:8080"]
#metrics_path: /metrics/metrics
#namespace: example
----

[float]
=== Metricsets

The following metricsets are available:

* <<metricbeat-metricset-dropwizard-collector,collector>>

include::dropwizard/collector.asciidoc[]

19 changes: 19 additions & 0 deletions metricbeat/docs/modules/dropwizard/collector.asciidoc
@@ -0,0 +1,19 @@
////
This file is generated! See scripts/docs_collector.py
////

[[metricbeat-metricset-dropwizard-collector]]
include::../../../module/dropwizard/collector/_meta/docs.asciidoc[]


==== Fields

For a description of each field in the metricset, see the
<<exported-fields-dropwizard,exported fields>> section.

Here is an example document generated by this metricset:

[source,json]
----
include::../../../module/dropwizard/collector/_meta/data.json[]
----
2 changes: 2 additions & 0 deletions metricbeat/docs/modules_list.asciidoc
Expand Up @@ -6,6 +6,7 @@ This file is generated! See scripts/docs_collector.py
* <<metricbeat-module-ceph,ceph>>
* <<metricbeat-module-couchbase,Couchbase>>
* <<metricbeat-module-docker,Docker>>
* <<metricbeat-module-dropwizard,Dropwizard>>
* <<metricbeat-module-elasticsearch,elasticsearch>>
* <<metricbeat-module-golang,golang>>
* <<metricbeat-module-haproxy,HAProxy>>
Expand All @@ -32,6 +33,7 @@ include::modules/apache.asciidoc[]
include::modules/ceph.asciidoc[]
include::modules/couchbase.asciidoc[]
include::modules/docker.asciidoc[]
include::modules/dropwizard.asciidoc[]
include::modules/elasticsearch.asciidoc[]
include::modules/golang.asciidoc[]
include::modules/haproxy.asciidoc[]
Expand Down
2 changes: 2 additions & 0 deletions metricbeat/include/list.go
Expand Up @@ -28,6 +28,8 @@ import (
_ "github.com/elastic/beats/metricbeat/module/docker/info"
_ "github.com/elastic/beats/metricbeat/module/docker/memory"
_ "github.com/elastic/beats/metricbeat/module/docker/network"
_ "github.com/elastic/beats/metricbeat/module/dropwizard"
_ "github.com/elastic/beats/metricbeat/module/dropwizard/collector"
_ "github.com/elastic/beats/metricbeat/module/elasticsearch"
_ "github.com/elastic/beats/metricbeat/module/elasticsearch/node"
_ "github.com/elastic/beats/metricbeat/module/elasticsearch/node_stats"
Expand Down
9 changes: 9 additions & 0 deletions metricbeat/metricbeat.full.yml
Expand Up @@ -124,6 +124,15 @@ metricbeat.modules:
#certificate: "/etc/pki/client/cert.pem"
#key: "/etc/pki/client/cert.key"

#----------------------------- Dropwizard Module -----------------------------
#- module: dropwizard
#metricsets: ["collector"]
#enabled: true
#period: 10s
#hosts: ["localhost:8080"]
#metrics_path: /metrics/metrics
#namespace: example

#---------------------------- elasticsearch Module ---------------------------
#- module: elasticsearch
# metricsets: ["node", "node_stats", "stats"]
Expand Down
8 changes: 8 additions & 0 deletions metricbeat/module/dropwizard/_meta/Dockerfile
@@ -0,0 +1,8 @@
FROM golang:1.7
RUN apt-get update && apt-get install curl
COPY main.go /go

HEALTHCHECK CMD curl -f http://localhost:9090/metrics/metrics
EXPOSE 9090

CMD go run /go/main.go
7 changes: 7 additions & 0 deletions metricbeat/module/dropwizard/_meta/config.yml
@@ -0,0 +1,7 @@
#- module: dropwizard
#metricsets: ["collector"]
#enabled: true
#period: 10s
#hosts: ["localhost:8080"]
#metrics_path: /metrics/metrics
#namespace: example
4 changes: 4 additions & 0 deletions metricbeat/module/dropwizard/_meta/docs.asciidoc
@@ -0,0 +1,4 @@
== dropwizard Module

This is the dropwizard Module.

2 changes: 2 additions & 0 deletions metricbeat/module/dropwizard/_meta/env
@@ -0,0 +1,2 @@
DROPWIZARD_HOST=dropwizard
DROPWIZARD_PORT=9090
12 changes: 12 additions & 0 deletions metricbeat/module/dropwizard/_meta/fields.yml
@@ -0,0 +1,12 @@
- key: dropwizard
title: "Dropwizard"
description: >
beta[]

Stats collected from Dropwizard.
short_config: false
fields:
- name: dropwizard
type: group
description: >
fields:
22 changes: 22 additions & 0 deletions metricbeat/module/dropwizard/_meta/main.go
@@ -0,0 +1,22 @@
package main

import (
"fmt"
"log"
"net/http"
)

var response = `{"version":"4.0.0","gauges":{"my_gauge{this=that}":{"value":565}},"counters":{"my_counter":{"count":565},"my_counter2{this=that}":{"count":565}},"histograms":{"my_hist":{"count":565,"max":564,"mean":563.3148706761577,"min":0,"p50":564.0,"p75":564.0,"p95":564.0,"p98":564.0,"p99":564.0,"p999":564.0,"stddev":1.0747916190718627}},"meters":{"my_meter":{"count":0,"m1_rate":0.0,"m5_rate":0.0,"m15_rate":0.0,"mean_rate":0.0,"units":"events/second"}},"timers":{"my_timer{this=that}":{"count":0,"max":0.0,"mean":0.0,"min":0.0,"p50":0.0,"p75":0.0,"p95":0.0,"p98":0.0,"p99":0.0,"p999":0.0,"stddev":0.0,"m1_rate":0.0,"m5_rate":0.0,"m15_rate":0.0,"mean_rate":0.0,"duration_units":"seconds","rate_units":"calls/second"}}}`
Copy link
Member

Choose a reason for hiding this comment

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

For testing the JSON output we don't need the integration tests. We could also do this with eventMapping and just load the content.

I would like to have a docker environment with a real dropwizard app inside (if possible). So in case things break, we have an instance around to test and don't have to build one ourself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added a proper dropwizard http endpoint as part of the integration run.


func sendResponse(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, response)
}

func main() {

http.HandleFunc("/metrics/metrics", sendResponse) // set router
err := http.ListenAndServe(":9090", nil) // set listen port
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
29 changes: 29 additions & 0 deletions metricbeat/module/dropwizard/collector/_meta/data.json
@@ -0,0 +1,29 @@
{
"@timestamp": "2017-04-16T06:42:37.441Z",
"beat": {
"hostname": "beathost",
"name": "beathost",
"version": "6.0.0-alpha1"
},
"dropwizard": {
"test": {
Copy link
Member

Choose a reason for hiding this comment

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

As it is a "dynamic" metricset I'm a bit undecided what we should do here. In other dynamic metricsets we leave it empty here (https://github.com/elastic/beats/blob/master/metricbeat/module/windows/perfmon/_meta/data.json) but provide an example output in the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed the custom metrics

"counter1": {
"count": 1337
},
"gauge1": {
"value": 1337
},
"tags": {
"this": "that"
}
}
},
"metricset": {
"host": "localhost:8080",
"module": "dropwizard",
"name": "collector",
"namespace": "test",
"rtt": 8008
},
"type": "metricsets"
}
3 changes: 3 additions & 0 deletions metricbeat/module/dropwizard/collector/_meta/docs.asciidoc
@@ -0,0 +1,3 @@
=== dropwizard collector MetricSet

This is the collector metricset of the module dropwizard.
Empty file.
94 changes: 94 additions & 0 deletions metricbeat/module/dropwizard/collector/collector.go
@@ -0,0 +1,94 @@
package collector

import (
"encoding/json"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/metricbeat/helper"
"github.com/elastic/beats/metricbeat/mb"
"github.com/elastic/beats/metricbeat/mb/parse"
"strings"
)

const (
defaultScheme = "http"
defaultPath = "/metrics/metrics"
)

var (
hostParser = parse.URLHostParserBuilder{
DefaultScheme: defaultScheme,
DefaultPath: defaultPath,
PathConfigKey: "metrics_path",
}.Build()
)

// init registers the MetricSet with the central registry.
// The New method will be called after the setup of the module and before starting to fetch data

func init() {
if err := mb.Registry.AddMetricSet("dropwizard", "collector", New, hostParser); err != nil {
panic(err)
}
}

// MetricSet type defines all fields of the MetricSet
// As a minimum it must inherit the mb.BaseMetricSet fields, but can be extended with
// additional entries. These variables can be used to persist data or configuration between
// multiple fetch calls.
type MetricSet struct {
mb.BaseMetricSet
http *helper.HTTP
namespace string
}

// New create a new instance of the MetricSet
// Part of new is also setting up the configuration by processing additional
// configuration entries if needed.
func New(base mb.BaseMetricSet) (mb.MetricSet, error) {
logp.Beta("The dropwizard collector metricset is beta")
config := struct {
Namespace string `config:"namespace" validate:"required"`
}{}

if err := base.Module().UnpackConfig(&config); err != nil {
return nil, err
}

return &MetricSet{
BaseMetricSet: base,
http: helper.NewHTTP(base),
namespace: config.Namespace,
}, nil
}

// Fetch methods implements the data gathering and data conversion to the right format
// It returns the event which is then forward to the output. In case of an error, a
// descriptive error must be returned.
func (m *MetricSet) Fetch() ([]common.MapStr, error) {
body, err := m.http.FetchContent()
if err != nil {
return nil, err
}
dw := map[string]interface{}{}

d := json.NewDecoder(strings.NewReader(string(body)))
d.UseNumber()
Copy link
Member

Choose a reason for hiding this comment

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

Interesting. Is there something special about the floats returned so we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


err = d.Decode(&dw)
if err != nil {
return nil, err
}

eventList := eventMapping(dw)

// Converts hash list to slice
events := []common.MapStr{}
for _, e := range eventList {
e["_namespace"] = m.namespace
events = append(events, e)
}

return events, err

}