Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
112
plugins/outputs/riemann/README.md
Normal file
112
plugins/outputs/riemann/README.md
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Riemann Output Plugin
|
||||
|
||||
This plugin writes metric to the [Riemann][riemann] serice via TCP or UDP.
|
||||
|
||||
⭐ Telegraf v1.3.0
|
||||
🏷️ datastore
|
||||
💻 all
|
||||
|
||||
[riemann]: http://riemann.io
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Configuration for Riemann to send metrics to
|
||||
[[outputs.riemann]]
|
||||
## The full TCP or UDP URL of the Riemann server
|
||||
url = "tcp://localhost:5555"
|
||||
|
||||
## Riemann event TTL, floating-point time in seconds.
|
||||
## Defines how long that an event is considered valid for in Riemann
|
||||
# ttl = 30.0
|
||||
|
||||
## Separator to use between measurement and field name in Riemann service name
|
||||
## This does not have any effect if 'measurement_as_attribute' is set to 'true'
|
||||
separator = "/"
|
||||
|
||||
## Set measurement name as Riemann attribute 'measurement', instead of prepending it to the Riemann service name
|
||||
# measurement_as_attribute = false
|
||||
|
||||
## Send string metrics as Riemann event states.
|
||||
## Unless enabled all string metrics will be ignored
|
||||
# string_as_state = false
|
||||
|
||||
## A list of tag keys whose values get sent as Riemann tags.
|
||||
## If empty, all Telegraf tag values will be sent as tags
|
||||
# tag_keys = ["telegraf","custom_tag"]
|
||||
|
||||
## Additional Riemann tags to send.
|
||||
# tags = ["telegraf-output"]
|
||||
|
||||
## Description for Riemann event
|
||||
# description_text = "metrics collected from telegraf"
|
||||
|
||||
## Riemann client write timeout, defaults to "5s" if not set.
|
||||
# timeout = "5s"
|
||||
```
|
||||
|
||||
### Required parameters
|
||||
|
||||
* `url`: The full TCP or UDP URL of the Riemann server to send events to.
|
||||
|
||||
### Optional parameters
|
||||
|
||||
* `ttl`: Riemann event TTL, floating-point time in seconds. Defines how long
|
||||
that an event is considered valid for in Riemann.
|
||||
* `separator`: Separator to use between measurement and field name in Riemann
|
||||
service name.
|
||||
* `measurement_as_attribute`: Set measurement name as a Riemann attribute,
|
||||
instead of prepending it to the Riemann service name.
|
||||
* `string_as_state`: Send string metrics as Riemann event states. If this is not
|
||||
enabled then all string metrics will be ignored.
|
||||
* `tag_keys`: A list of tag keys whose values get sent as Riemann tags. If
|
||||
empty, all Telegraf tag values will be sent as tags.
|
||||
* `tags`: Additional Riemann tags that will be sent.
|
||||
* `description_text`: Description text for Riemann event.
|
||||
|
||||
## Example Events
|
||||
|
||||
Riemann event emitted by Telegraf with default configuration:
|
||||
|
||||
```text
|
||||
#riemann.codec.Event{
|
||||
:host "postgresql-1e612b44-e92f-4d27-9f30-5e2f53947870", :state nil, :description nil, :ttl 30.0,
|
||||
:service "disk/used_percent", :metric 73.16736001949994, :path "/boot", :fstype "ext4", :time 1475605021}
|
||||
```
|
||||
|
||||
Telegraf emitting the same Riemann event with `measurement_as_attribute` set to
|
||||
`true`:
|
||||
|
||||
```text
|
||||
#riemann.codec.Event{ ...
|
||||
:measurement "disk", :service "used_percent", :metric 73.16736001949994,
|
||||
... :time 1475605021}
|
||||
```
|
||||
|
||||
Telegraf emitting the same Riemann event with additional Riemann tags defined:
|
||||
|
||||
```text
|
||||
#riemann.codec.Event{
|
||||
:host "postgresql-1e612b44-e92f-4d27-9f30-5e2f53947870", :state nil, :description nil, :ttl 30.0,
|
||||
:service "disk/used_percent", :metric 73.16736001949994, :path "/boot", :fstype "ext4", :time 1475605021,
|
||||
:tags ["telegraf" "postgres_cluster"]}
|
||||
```
|
||||
|
||||
Telegraf emitting a Riemann event with a status text and `string_as_state` set
|
||||
to `true`, and a `description_text` defined:
|
||||
|
||||
```text
|
||||
#riemann.codec.Event{
|
||||
:host "postgresql-1e612b44-e92f-4d27-9f30-5e2f53947870", :state "Running", :ttl 30.0,
|
||||
:description "PostgreSQL master node is up and running",
|
||||
:service "status", :time 1475605021}
|
||||
```
|
192
plugins/outputs/riemann/riemann.go
Normal file
192
plugins/outputs/riemann/riemann.go
Normal file
|
@ -0,0 +1,192 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package riemann
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/amir/raidman"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/outputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
type Riemann struct {
|
||||
URL string `toml:"url"`
|
||||
TTL float32 `toml:"ttl"`
|
||||
Separator string `toml:"separator"`
|
||||
MeasurementAsAttribute bool `toml:"measurement_as_attribute"`
|
||||
StringAsState bool `toml:"string_as_state"`
|
||||
TagKeys []string `toml:"tag_keys"`
|
||||
Tags []string `toml:"tags"`
|
||||
DescriptionText string `toml:"description_text"`
|
||||
Timeout config.Duration `toml:"timeout"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
client *raidman.Client
|
||||
}
|
||||
|
||||
func (*Riemann) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (r *Riemann) Connect() error {
|
||||
parsedURL, err := url.Parse(r.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := raidman.DialWithTimeout(parsedURL.Scheme, parsedURL.Host, time.Duration(r.Timeout))
|
||||
if err != nil {
|
||||
r.client = nil
|
||||
return err
|
||||
}
|
||||
|
||||
r.client = client
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Riemann) Close() (err error) {
|
||||
if r.client != nil {
|
||||
err = r.client.Close()
|
||||
r.client = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Riemann) Write(metrics []telegraf.Metric) error {
|
||||
if len(metrics) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.client == nil {
|
||||
if err := r.Connect(); err != nil {
|
||||
return fmt.Errorf("failed to (re)connect to Riemann: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// build list of Riemann events to send
|
||||
var events []*raidman.Event
|
||||
for _, m := range metrics {
|
||||
evs := r.buildRiemannEvents(m)
|
||||
events = append(events, evs...)
|
||||
}
|
||||
|
||||
if err := r.client.SendMulti(events); err != nil {
|
||||
r.Close()
|
||||
return fmt.Errorf("failed to send riemann message: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Riemann) buildRiemannEvents(m telegraf.Metric) []*raidman.Event {
|
||||
events := make([]*raidman.Event, 0, len(m.Fields()))
|
||||
for fieldName, value := range m.Fields() {
|
||||
// get host for Riemann event
|
||||
host, ok := m.Tags()["host"]
|
||||
if !ok {
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
host = hostname
|
||||
} else {
|
||||
host = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
event := &raidman.Event{
|
||||
Host: host,
|
||||
Ttl: r.TTL,
|
||||
Description: r.DescriptionText,
|
||||
Time: m.Time().Unix(),
|
||||
|
||||
Attributes: r.attributes(m.Name(), m.Tags()),
|
||||
Service: r.service(m.Name(), fieldName),
|
||||
Tags: r.tags(m.Tags()),
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
// only send string metrics if explicitly enabled, skip otherwise
|
||||
if !r.StringAsState {
|
||||
r.Log.Debugf("Riemann event states disabled, skipping metric value [%s]", value)
|
||||
continue
|
||||
}
|
||||
event.State = value
|
||||
case int, int64, uint64, float32, float64:
|
||||
event.Metric = value
|
||||
default:
|
||||
r.Log.Debugf("Riemann does not support metric value [%s]", value)
|
||||
continue
|
||||
}
|
||||
|
||||
events = append(events, event)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func (r *Riemann) attributes(name string, tags map[string]string) map[string]string {
|
||||
if r.MeasurementAsAttribute {
|
||||
tags["measurement"] = name
|
||||
}
|
||||
|
||||
delete(tags, "host") // exclude 'host' tag
|
||||
return tags
|
||||
}
|
||||
|
||||
func (r *Riemann) service(name, field string) string {
|
||||
var serviceStrings []string
|
||||
|
||||
// if measurement is not enabled as an attribute then prepend it to service name
|
||||
if !r.MeasurementAsAttribute {
|
||||
serviceStrings = append(serviceStrings, name)
|
||||
}
|
||||
serviceStrings = append(serviceStrings, field)
|
||||
|
||||
return strings.Join(serviceStrings, r.Separator)
|
||||
}
|
||||
|
||||
func (r *Riemann) tags(tags map[string]string) []string {
|
||||
// always add specified Riemann tags
|
||||
values := r.Tags
|
||||
|
||||
// if tag_keys are specified, add those and return tag list
|
||||
if len(r.TagKeys) > 0 {
|
||||
for _, tagName := range r.TagKeys {
|
||||
value, ok := tags[tagName]
|
||||
if ok {
|
||||
values = append(values, value)
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// otherwise add all values from telegraf tag key/value pairs
|
||||
keys := make([]string, 0, len(tags))
|
||||
for key := range tags {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
if key != "host" { // exclude 'host' tag
|
||||
values = append(values, tags[key])
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func init() {
|
||||
outputs.Add("riemann", func() telegraf.Output {
|
||||
return &Riemann{
|
||||
Timeout: config.Duration(time.Second * 5),
|
||||
}
|
||||
})
|
||||
}
|
169
plugins/outputs/riemann/riemann_test.go
Normal file
169
plugins/outputs/riemann/riemann_test.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package riemann
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/amir/raidman"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestAttributes(t *testing.T) {
|
||||
tags := map[string]string{"tag1": "value1", "tag2": "value2"}
|
||||
|
||||
r := &Riemann{
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
require.Equal(t,
|
||||
map[string]string{"tag1": "value1", "tag2": "value2"},
|
||||
r.attributes("test", tags))
|
||||
|
||||
// enable measurement as attribute, should now be included
|
||||
r.MeasurementAsAttribute = true
|
||||
require.Equal(t,
|
||||
map[string]string{"tag1": "value1", "tag2": "value2", "measurement": "test"},
|
||||
r.attributes("test", tags))
|
||||
}
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
r := &Riemann{
|
||||
Separator: "/",
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
require.Equal(t, "test/value", r.service("test", "value"))
|
||||
|
||||
// enable measurement as attribute, should not be part of service name anymore
|
||||
r.MeasurementAsAttribute = true
|
||||
require.Equal(t, "value", r.service("test", "value"))
|
||||
}
|
||||
|
||||
func TestTags(t *testing.T) {
|
||||
tags := map[string]string{"tag1": "value1", "tag2": "value2"}
|
||||
|
||||
// all tag values plus additional tag should be present
|
||||
r := &Riemann{
|
||||
Tags: []string{"test"},
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
require.Equal(t,
|
||||
[]string{"test", "value1", "value2"},
|
||||
r.tags(tags))
|
||||
|
||||
// only tag2 value plus additional tag should be present
|
||||
r.TagKeys = []string{"tag2"}
|
||||
require.Equal(t,
|
||||
[]string{"test", "value2"},
|
||||
r.tags(tags))
|
||||
|
||||
// only tag1 value should be present
|
||||
r.Tags = nil
|
||||
r.TagKeys = []string{"tag1"}
|
||||
require.Equal(t,
|
||||
[]string{"value1"},
|
||||
r.tags(tags))
|
||||
}
|
||||
|
||||
func TestMetricEvents(t *testing.T) {
|
||||
r := &Riemann{
|
||||
TTL: 20.0,
|
||||
Separator: "/",
|
||||
MeasurementAsAttribute: false,
|
||||
DescriptionText: "metrics from telegraf",
|
||||
Tags: []string{"telegraf"},
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
|
||||
// build a single event
|
||||
m := metric.New(
|
||||
"test1",
|
||||
map[string]string{"tag1": "value1", "host": "abc123"},
|
||||
map[string]interface{}{"value": 5.6},
|
||||
time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||
)
|
||||
|
||||
events := r.buildRiemannEvents(m)
|
||||
require.Len(t, events, 1)
|
||||
|
||||
// is event as expected?
|
||||
expectedEvent := &raidman.Event{
|
||||
Ttl: 20.0,
|
||||
Time: 1257894000,
|
||||
Tags: []string{"telegraf", "value1"},
|
||||
Host: "abc123",
|
||||
State: "",
|
||||
Service: "test1/value",
|
||||
Metric: 5.6,
|
||||
Description: "metrics from telegraf",
|
||||
Attributes: map[string]string{"tag1": "value1"},
|
||||
}
|
||||
require.Equal(t, expectedEvent, events[0])
|
||||
|
||||
// build 2 events
|
||||
m = metric.New(
|
||||
"test2",
|
||||
map[string]string{"host": "xyz987"},
|
||||
map[string]interface{}{"point": 1},
|
||||
time.Date(2012, time.November, 2, 3, 0, 0, 0, time.UTC),
|
||||
)
|
||||
|
||||
events = append(events, r.buildRiemannEvents(m)...)
|
||||
require.Len(t, events, 2)
|
||||
|
||||
// first event should still be the same
|
||||
require.Equal(t, expectedEvent, events[0])
|
||||
|
||||
// second event
|
||||
expectedEvent = &raidman.Event{
|
||||
Ttl: 20.0,
|
||||
Time: 1351825200,
|
||||
Tags: []string{"telegraf"},
|
||||
Host: "xyz987",
|
||||
State: "",
|
||||
Service: "test2/point",
|
||||
Metric: int64(1),
|
||||
Description: "metrics from telegraf",
|
||||
Attributes: map[string]string{},
|
||||
}
|
||||
require.Equal(t, expectedEvent, events[1])
|
||||
}
|
||||
|
||||
func TestStateEvents(t *testing.T) {
|
||||
r := &Riemann{
|
||||
MeasurementAsAttribute: true,
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
|
||||
// string metrics will be skipped unless explicitly enabled
|
||||
m := metric.New(
|
||||
"test",
|
||||
map[string]string{"host": "host"},
|
||||
map[string]interface{}{"value": "running"},
|
||||
time.Date(2015, time.November, 9, 22, 0, 0, 0, time.UTC),
|
||||
)
|
||||
|
||||
events := r.buildRiemannEvents(m)
|
||||
// no event should be present
|
||||
require.Empty(t, events)
|
||||
|
||||
// enable string metrics as event states
|
||||
r.StringAsState = true
|
||||
events = r.buildRiemannEvents(m)
|
||||
require.Len(t, events, 1)
|
||||
|
||||
// is event as expected?
|
||||
expectedEvent := &raidman.Event{
|
||||
Ttl: 0,
|
||||
Time: 1447106400,
|
||||
Tags: nil,
|
||||
Host: "host",
|
||||
State: "running",
|
||||
Service: "value",
|
||||
Metric: nil,
|
||||
Description: "",
|
||||
Attributes: map[string]string{"measurement": "test"},
|
||||
}
|
||||
require.Equal(t, expectedEvent, events[0])
|
||||
}
|
32
plugins/outputs/riemann/sample.conf
Normal file
32
plugins/outputs/riemann/sample.conf
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Configuration for Riemann to send metrics to
|
||||
[[outputs.riemann]]
|
||||
## The full TCP or UDP URL of the Riemann server
|
||||
url = "tcp://localhost:5555"
|
||||
|
||||
## Riemann event TTL, floating-point time in seconds.
|
||||
## Defines how long that an event is considered valid for in Riemann
|
||||
# ttl = 30.0
|
||||
|
||||
## Separator to use between measurement and field name in Riemann service name
|
||||
## This does not have any effect if 'measurement_as_attribute' is set to 'true'
|
||||
separator = "/"
|
||||
|
||||
## Set measurement name as Riemann attribute 'measurement', instead of prepending it to the Riemann service name
|
||||
# measurement_as_attribute = false
|
||||
|
||||
## Send string metrics as Riemann event states.
|
||||
## Unless enabled all string metrics will be ignored
|
||||
# string_as_state = false
|
||||
|
||||
## A list of tag keys whose values get sent as Riemann tags.
|
||||
## If empty, all Telegraf tag values will be sent as tags
|
||||
# tag_keys = ["telegraf","custom_tag"]
|
||||
|
||||
## Additional Riemann tags to send.
|
||||
# tags = ["telegraf-output"]
|
||||
|
||||
## Description for Riemann event
|
||||
# description_text = "metrics collected from telegraf"
|
||||
|
||||
## Riemann client write timeout, defaults to "5s" if not set.
|
||||
# timeout = "5s"
|
Loading…
Add table
Add a link
Reference in a new issue