Skip to content

Commit

Permalink
Add statsdreceiver skeleton (#566)
Browse files Browse the repository at this point in the history
* Add statsdreceiver skeleton

* Add DogStatsD parser and UDP server transport

* Add unit tests and copyright headers

* Remove unused timeout configuration option

* Use componenterror package errors

* Parse counters

* Remove docker dependency from go.sum

* Remove unused defaultTimeout variable

* Adopt confignet.NetAddr configuration struct

* Fix import ordering

* Add Reporter construct and expand test coverage

* Improve metric name and value parsing

* Handle float values gracefully in counter messages

* Fill in receiver e2e test

* Initialize transport based on configuration option

* Update README and add TODOs from PR feedback

* Replace ReceiverFactoryOld usage in favor of receiverhelper package
  • Loading branch information
sonofachamp committed Aug 19, 2020
1 parent 19d53ca commit 2559612
Show file tree
Hide file tree
Showing 26 changed files with 3,289 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/otelcontribcol/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/sapmreceiver"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/signalfxreceiver"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/simpleprometheusreceiver"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/wavefrontreceiver"
)

Expand Down Expand Up @@ -85,6 +86,7 @@ func components() (component.Factories, error) {
k8sclusterreceiver.NewFactory(),
prometheusexecreceiver.NewFactory(),
receivercreator.NewFactory(),
statsdreceiver.NewFactory(),
}
for _, rcv := range factories.Receivers {
receivers = append(receivers, rcv)
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/sapmreceiver v0.0.0-00010101000000-000000000000
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/signalfxreceiver v0.0.0-00010101000000-000000000000
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/simpleprometheusreceiver v0.0.0-00010101000000-000000000000
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver v0.0.0-00010101000000-000000000000
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/wavefrontreceiver v0.0.0-00010101000000-000000000000
github.com/pavius/impi v0.0.3
github.com/stretchr/testify v1.6.1
Expand Down Expand Up @@ -91,6 +92,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/carbo

replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver => ./receiver/collectdreceiver

replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver => ./receiver/statsdreceiver

replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver => ./receiver/kubeletstatsreceiver

replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/redisreceiver => ./receiver/redisreceiver
Expand Down
349 changes: 349 additions & 0 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions receiver/statsdreceiver/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
69 changes: 69 additions & 0 deletions receiver/statsdreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# StatsD Receiver

StatsD receiver for ingesting StatsD messages into the OpenTelemetry Collector.

## Status

This plugin is still being developed and is **not** ready to be used in a production grade environment.

## Configuration

```yaml
receivers:
statsd:
endpoint: "localhost:8125" # default
```

### endpoint

The `"<host>:<port>"` to listen on. By default listen on `"localhost:8125"`.

## Aggregation

Currently the `statsdreceiver` is not providing any aggregation. There are ideas such as the [Metrics Transform Processor Proposal](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/332) that intend to enable control over Metric aggregation in a processor.

An alternative will be to implement some simple aggregation in this receiver.

## Metrics

General format is:

`<name>:<value>|<type>|@<sample-rate>|#<tag1-key>:<tag1-value>,<tag2-k/v>`

### Counter

`<name>:<value>|c|@<sample-rate>|#<tag1-key>:<tag1-value>`

<!-- ### Gauge
`<name>:<value>|g|@<sample-rate>|#<tag1-key>:<tag1-value>`
### Timer/Histogram
`<name>:<value>|<ms/h>|@<sample-rate>|#<tag1-key>:<tag1-value>` -->

## Testing

### Full sample collector config

```yaml
receivers:
statsd:
endpoint: "localhost:8125" # default

exporters:
file:
path: ./test.json

service:
pipelines:
metrics:
receivers: [statsd]
exporters: [file]
```

### Send StatsD message into the receiver

A simple way to send a metric to `localhost:8125`:

`echo "test.metric:1|c" | nc -w 1 -u localhost 8125`
26 changes: 26 additions & 0 deletions receiver/statsdreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package statsdreceiver

import (
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/config/confignet"
)

// Config defines configuration for StatsD receiver.
type Config struct {
configmodels.ReceiverSettings `mapstructure:",squash"`
NetAddr confignet.NetAddr `mapstructure:",squash"`
}
58 changes: 58 additions & 0 deletions receiver/statsdreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package statsdreceiver

import (
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/config/confignet"
"go.opentelemetry.io/collector/config/configtest"
)

func TestLoadConfig(t *testing.T) {
factories, err := componenttest.ExampleComponents()
assert.Nil(t, err)

factory := NewFactory()
factories.Receivers[configmodels.Type(typeStr)] = factory
cfg, err := configtest.LoadConfigFile(
t, path.Join(".", "testdata", "config.yaml"), factories,
)

require.NoError(t, err)
require.NotNil(t, cfg)

assert.Equal(t, len(cfg.Receivers), 2)

r0 := cfg.Receivers["statsd"]
assert.Equal(t, factory.CreateDefaultConfig(), r0)

r1 := cfg.Receivers["statsd/receiver_settings"]
assert.Equal(t, &Config{
ReceiverSettings: configmodels.ReceiverSettings{
TypeVal: configmodels.Type(typeStr),
NameVal: "statsd/receiver_settings",
},
NetAddr: confignet.NetAddr{
Endpoint: "localhost:12345",
Transport: "custom_transport",
},
}, r1)
}
18 changes: 18 additions & 0 deletions receiver/statsdreceiver/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package statsdreceiver implements a collector receiver that listens
// on UDP port 8125 by default for incoming StatsD messages and parses
// them into OTLP equivalent metric representations.
package statsdreceiver
64 changes: 64 additions & 0 deletions receiver/statsdreceiver/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package statsdreceiver

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configmodels"
"go.opentelemetry.io/collector/config/confignet"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver/receiverhelper"
)

const (
// The value of "type" key in configuration.
typeStr = "statsd"
defaultBindEndpoint = "localhost:8125"
defaultTransport = "udp"
)

// NewFactory creates a factory for the StatsD receiver.
func NewFactory() component.ReceiverFactory {
return receiverhelper.NewFactory(
typeStr,
createDefaultConfig,
receiverhelper.WithMetrics(createMetricsReceiver),
)
}

func createDefaultConfig() configmodels.Receiver {
return &Config{
ReceiverSettings: configmodels.ReceiverSettings{
TypeVal: configmodels.Type(typeStr),
NameVal: typeStr,
},
NetAddr: confignet.NetAddr{
Endpoint: defaultBindEndpoint,
Transport: defaultTransport,
},
}
}

func createMetricsReceiver(
_ context.Context,
params component.ReceiverCreateParams,
cfg configmodels.Receiver,
consumer consumer.MetricsConsumer,
) (component.MetricsReceiver, error) {
c := cfg.(*Config)
return New(params.Logger, *c, consumer)
}
43 changes: 43 additions & 0 deletions receiver/statsdreceiver/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package statsdreceiver

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configcheck"
"go.opentelemetry.io/collector/exporter/exportertest"
"go.uber.org/zap"
)

func TestCreateDefaultConfig(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
assert.NoError(t, configcheck.ValidateConfig(cfg))
}

func TestCreateReceiver(t *testing.T) {
cfg := createDefaultConfig().(*Config)
cfg.NetAddr.Endpoint = "localhost:0" // Endpoint is required, not going to be used here.

params := component.ReceiverCreateParams{Logger: zap.NewNop()}
tReceiver, err := createMetricsReceiver(context.Background(), params, cfg, exportertest.NewNopMetricsExporter())
assert.NoError(t, err)
assert.NotNil(t, tReceiver, "receiver creation failed")
}
13 changes: 13 additions & 0 deletions receiver/statsdreceiver/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver

go 1.14

require (
github.com/census-instrumentation/opencensus-proto v0.3.0
github.com/golang/protobuf v1.4.2
github.com/shirou/gopsutil v2.20.4+incompatible // indirect
github.com/stretchr/testify v1.6.1
go.opencensus.io v0.22.4
go.opentelemetry.io/collector v0.5.1-0.20200723232356-d4053cc823a0
go.uber.org/zap v1.15.0
)
Loading

0 comments on commit 2559612

Please sign in to comment.