1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View 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}
```

View 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),
}
})
}

View 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])
}

View 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"