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,79 @@
# Graylog Output Plugin
This plugin writes metrics to a [Graylog][graylog] instance using the
[GELF data format][gelf].
⭐ Telegraf v1.0.0
🏷️ datastore, logging
💻 all
[gelf]: https://docs.graylog.org/en/3.1/pages/gelf.html#gelf-payload-specification
[graylog]: https://graylog.org/
## GELF Fields
The [GELF spec][] spec defines a number of specific fields in a GELF payload.
These fields may have specific requirements set by the spec and users of the
Graylog plugin need to follow these requirements or metrics may be rejected due
to invalid data.
For example, the timestamp field defined in the GELF spec, is required to be a
UNIX timestamp. This output plugin will not modify or check the timestamp field
if one is present and send it as-is to Graylog. If the field is absent then
Telegraf will set the timestamp to the current time.
Any field not defined by the spec will have an underscore (e.g. `_`) prefixed to
the field name.
[GELF spec]: https://docs.graylog.org/docs/gelf#gelf-payload-specification
## 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
# Send telegraf metrics to graylog
[[outputs.graylog]]
## Endpoints for your graylog instances.
servers = ["udp://127.0.0.1:12201"]
## Connection timeout.
# timeout = "5s"
## The field to use as the GELF short_message, if unset the static string
## "telegraf" will be used.
## example: short_message_field = "message"
# short_message_field = ""
## According to GELF payload specification, additional fields names must be prefixed
## with an underscore. Previous versions did not prefix custom field 'name' with underscore.
## Set to true for backward compatibility.
# name_field_no_prefix = false
## Connection retry options
## Attempt to connect to the endpoints if the initial connection fails.
## If 'false', Telegraf will give up after 3 connection attempt and will
## exit with an error. If set to 'true', the plugin will retry to connect
## to the unconnected endpoints infinitely.
# connection_retry = false
## Time to wait between connection retry attempts.
# connection_retry_wait_time = "15s"
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
```
Server endpoint may be specified without UDP or TCP scheme
(eg. "127.0.0.1:12201"). In such case, UDP protocol is assumed. TLS config is
ignored for UDP endpoints.

View file

@ -0,0 +1,538 @@
//go:generate ../../../tools/readme_config_includer/generator
package graylog
import (
"bytes"
"compress/zlib"
"crypto/rand"
"crypto/tls"
_ "embed"
"encoding/binary"
ejson "encoding/json"
"fmt"
"io"
"math"
"net"
"os"
"strings"
"sync"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
common_tls "github.com/influxdata/telegraf/plugins/common/tls"
"github.com/influxdata/telegraf/plugins/outputs"
)
//go:embed sample.conf
var sampleConfig string
const (
defaultEndpoint = "127.0.0.1:12201"
defaultConnection = "wan"
defaultMaxChunkSizeWan = 1420
defaultMaxChunkSizeLan = 8154
defaultScheme = "udp"
defaultTimeout = 5 * time.Second
defaultReconnectionTime = 15 * time.Second
)
var defaultSpecFields = []string{"version", "host", "short_message", "full_message", "timestamp", "level", "facility", "line", "file"}
type gelfConfig struct {
Endpoint string
Connection string
MaxChunkSizeWan int
MaxChunkSizeLan int
}
type gelf interface {
io.WriteCloser
Connect() error
}
type gelfCommon struct {
gelfConfig
dialer *net.Dialer
conn net.Conn
}
type gelfUDP struct {
gelfCommon
}
type gelfTCP struct {
gelfCommon
tlsConfig *tls.Config
}
func newGelfWriter(cfg gelfConfig, dialer *net.Dialer, tlsConfig *tls.Config) gelf {
if cfg.Endpoint == "" {
cfg.Endpoint = defaultEndpoint
}
if cfg.Connection == "" {
cfg.Connection = defaultConnection
}
if cfg.MaxChunkSizeWan == 0 {
cfg.MaxChunkSizeWan = defaultMaxChunkSizeWan
}
if cfg.MaxChunkSizeLan == 0 {
cfg.MaxChunkSizeLan = defaultMaxChunkSizeLan
}
scheme := defaultScheme
parts := strings.SplitN(cfg.Endpoint, "://", 2)
if len(parts) == 2 {
scheme = strings.ToLower(parts[0])
cfg.Endpoint = parts[1]
}
common := gelfCommon{
gelfConfig: cfg,
dialer: dialer,
}
var g gelf
switch scheme {
case "tcp":
g = &gelfTCP{gelfCommon: common, tlsConfig: tlsConfig}
default:
g = &gelfUDP{gelfCommon: common}
}
return g
}
func (g *gelfUDP) Write(message []byte) (n int, err error) {
compressed, err := g.compress(message)
if err != nil {
return 0, err
}
chunksize := g.gelfConfig.MaxChunkSizeWan
length := compressed.Len()
if length > chunksize {
chunkCountInt := int(math.Ceil(float64(length) / float64(chunksize)))
id := make([]byte, 8)
_, err = rand.Read(id)
if err != nil {
return 0, err
}
for i, index := 0, 0; i < length; i, index = i+chunksize, index+1 {
packet, err := g.createChunkedMessage(index, chunkCountInt, id, &compressed)
if err != nil {
return 0, err
}
err = g.send(packet.Bytes())
if err != nil {
return 0, err
}
}
} else {
err = g.send(compressed.Bytes())
if err != nil {
return 0, err
}
}
n = len(message)
return n, nil
}
func (g *gelfUDP) Close() (err error) {
if g.conn != nil {
err = g.conn.Close()
g.conn = nil
}
return err
}
func (g *gelfUDP) createChunkedMessage(index, chunkCountInt int, id []byte, compressed *bytes.Buffer) (bytes.Buffer, error) {
var packet bytes.Buffer
chunksize := g.getChunksize()
b, err := g.intToBytes(30)
if err != nil {
return packet, err
}
packet.Write(b)
b, err = g.intToBytes(15)
if err != nil {
return packet, err
}
packet.Write(b)
packet.Write(id)
b, err = g.intToBytes(index)
if err != nil {
return packet, err
}
packet.Write(b)
b, err = g.intToBytes(chunkCountInt)
if err != nil {
return packet, err
}
packet.Write(b)
packet.Write(compressed.Next(chunksize))
return packet, nil
}
func (g *gelfUDP) getChunksize() int {
if g.gelfConfig.Connection == "wan" {
return g.gelfConfig.MaxChunkSizeWan
}
if g.gelfConfig.Connection == "lan" {
return g.gelfConfig.MaxChunkSizeLan
}
return g.gelfConfig.MaxChunkSizeWan
}
func (*gelfUDP) intToBytes(i int) ([]byte, error) {
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, int8(i))
if err != nil {
return nil, err
}
return buf.Bytes(), err
}
func (*gelfUDP) compress(b []byte) (bytes.Buffer, error) {
var buf bytes.Buffer
comp := zlib.NewWriter(&buf)
if _, err := comp.Write(b); err != nil {
return bytes.Buffer{}, err
}
if err := comp.Close(); err != nil {
return bytes.Buffer{}, err
}
return buf, nil
}
func (g *gelfUDP) Connect() error {
conn, err := g.dialer.Dial("udp", g.gelfConfig.Endpoint)
if err != nil {
return err
}
g.conn = conn
return nil
}
func (g *gelfUDP) send(b []byte) error {
if g.conn == nil {
err := g.Connect()
if err != nil {
return err
}
}
_, err := g.conn.Write(b)
if err != nil {
_ = g.conn.Close()
g.conn = nil
}
return err
}
func (g *gelfTCP) Write(message []byte) (n int, err error) {
err = g.send(message)
if err != nil {
return 0, err
}
n = len(message)
return n, nil
}
func (g *gelfTCP) Close() (err error) {
if g.conn != nil {
err = g.conn.Close()
g.conn = nil
}
return err
}
func (g *gelfTCP) Connect() error {
var err error
var conn net.Conn
if g.tlsConfig == nil {
conn, err = g.dialer.Dial("tcp", g.gelfConfig.Endpoint)
} else {
conn, err = tls.DialWithDialer(g.dialer, "tcp", g.gelfConfig.Endpoint, g.tlsConfig)
}
if err != nil {
return err
}
g.conn = conn
return nil
}
func (g *gelfTCP) send(b []byte) error {
if g.conn == nil {
err := g.Connect()
if err != nil {
return err
}
}
_, err := g.conn.Write(b)
if err != nil {
_ = g.conn.Close()
g.conn = nil
} else {
_, err = g.conn.Write([]byte{0}) // message delimiter
if err != nil {
_ = g.conn.Close()
g.conn = nil
}
}
return err
}
type Graylog struct {
Servers []string `toml:"servers"`
ShortMessageField string `toml:"short_message_field"`
NameFieldNoPrefix bool `toml:"name_field_noprefix"`
Timeout config.Duration `toml:"timeout"`
Reconnection bool `toml:"connection_retry"`
ReconnectionTime config.Duration `toml:"connection_retry_wait_time"`
Log telegraf.Logger `toml:"-"`
common_tls.ClientConfig
writer io.Writer
closers []io.WriteCloser
unconnected []string
stopRetry bool
wg sync.WaitGroup
sync.Mutex
}
func (*Graylog) SampleConfig() string {
return sampleConfig
}
func (g *Graylog) Connect() error {
if len(g.Servers) == 0 {
g.Servers = append(g.Servers, "localhost:12201")
}
tlsCfg, err := g.ClientConfig.TLSConfig()
if err != nil {
return err
}
if g.Reconnection {
go g.connectRetry(tlsCfg)
return nil
}
unconnected, gelfs := g.connectEndpoints(g.Servers, tlsCfg)
if len(unconnected) > 0 {
servers := strings.Join(unconnected, ",")
return fmt.Errorf("connect: connection failed for %s", servers)
}
writers := make([]io.Writer, 0, len(gelfs))
closers := make([]io.WriteCloser, 0, len(gelfs))
for _, w := range gelfs {
writers = append(writers, w)
closers = append(closers, w)
}
g.Lock()
defer g.Unlock()
g.writer = io.MultiWriter(writers...)
g.closers = closers
return nil
}
func (g *Graylog) connectRetry(tlsCfg *tls.Config) {
var writers []io.Writer
var closers []io.WriteCloser
var attempt int64
g.wg.Add(1)
servers := make([]string, 0, len(g.Servers))
servers = append(servers, g.Servers...)
for {
unconnected, gelfs := g.connectEndpoints(servers, tlsCfg)
for _, w := range gelfs {
writers = append(writers, w)
closers = append(closers, w)
}
g.Lock()
g.unconnected = unconnected
stopRetry := g.stopRetry
g.Unlock()
if stopRetry {
g.Log.Info("Stopping connection retries...")
break
}
if len(unconnected) == 0 {
break
}
attempt++
servers := strings.Join(unconnected, ",")
g.Log.Infof("Not connected to endpoints %s after attempt #%d...", servers, attempt)
time.Sleep(time.Duration(g.ReconnectionTime))
}
g.Log.Info("Connected!")
g.Lock()
g.writer = io.MultiWriter(writers...)
g.closers = closers
g.Unlock()
g.wg.Done()
}
func (g *Graylog) connectEndpoints(servers []string, tlsCfg *tls.Config) ([]string, []gelf) {
writers := make([]gelf, 0, len(servers))
unconnected := make([]string, 0, len(servers))
dialer := &net.Dialer{Timeout: time.Duration(g.Timeout)}
for _, server := range servers {
w := newGelfWriter(gelfConfig{Endpoint: server}, dialer, tlsCfg)
if err := w.Connect(); err != nil {
g.Log.Warnf("failed to connect to server [%s]: %v", server, err)
unconnected = append(unconnected, server)
continue
}
writers = append(writers, w)
}
return unconnected, writers
}
func (g *Graylog) Close() error {
g.Lock()
g.stopRetry = true
g.Unlock()
g.wg.Wait()
for _, closer := range g.closers {
_ = closer.Close()
}
return nil
}
func (g *Graylog) Write(metrics []telegraf.Metric) error {
g.Lock()
writer := g.writer
g.Unlock()
if writer == nil {
g.Lock()
unconnected := strings.Join(g.unconnected, ",")
g.Unlock()
return fmt.Errorf("not connected to %s", unconnected)
}
for _, metric := range metrics {
values, err := g.serialize(metric)
if err != nil {
return err
}
for _, value := range values {
_, err = writer.Write([]byte(value))
if err != nil {
return fmt.Errorf("error writing message: %q: %w", value, err)
}
}
}
return nil
}
func (g *Graylog) serialize(metric telegraf.Metric) ([]string, error) {
m := make(map[string]interface{})
m["version"] = "1.1"
m["timestamp"] = float64(metric.Time().UnixNano()) / 1_000_000_000
m["short_message"] = "telegraf"
if g.NameFieldNoPrefix {
m["name"] = metric.Name()
} else {
m["_name"] = metric.Name()
}
if host, ok := metric.GetTag("host"); ok {
m["host"] = host
} else {
host, err := os.Hostname()
if err != nil {
return nil, err
}
m["host"] = host
}
for _, tag := range metric.TagList() {
if tag.Key == "host" {
continue
}
if fieldInSpec(tag.Key) {
m[tag.Key] = tag.Value
} else {
m["_"+tag.Key] = tag.Value
}
}
for _, field := range metric.FieldList() {
if field.Key == g.ShortMessageField {
m["short_message"] = field.Value
} else if fieldInSpec(field.Key) {
m[field.Key] = field.Value
} else {
m["_"+field.Key] = field.Value
}
}
serialized, err := ejson.Marshal(m)
if err != nil {
return nil, err
}
return []string{string(serialized)}, nil
}
func fieldInSpec(field string) bool {
for _, specField := range defaultSpecFields {
if specField == field {
return true
}
}
return false
}
func init() {
outputs.Add("graylog", func() telegraf.Output {
return &Graylog{
Timeout: config.Duration(defaultTimeout),
ReconnectionTime: config.Duration(defaultReconnectionTime),
}
})
}

View file

@ -0,0 +1,51 @@
package graylog
import (
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/metric"
)
func TestSerializer(t *testing.T) {
m1 := metric.New("testing",
map[string]string{
"verb": "GET",
"host": "hostname",
},
map[string]interface{}{
"full_message": "full",
"short_message": "short",
"level": "1",
"facility": "demo",
"line": "42",
"file": "graylog.go",
},
time.Now(),
)
graylog := Graylog{}
result, err := graylog.serialize(m1)
require.NoError(t, err)
for _, r := range result {
obj := make(map[string]interface{})
err = json.Unmarshal([]byte(r), &obj)
require.NoError(t, err)
require.Equal(t, "1.1", obj["version"])
require.Equal(t, "testing", obj["_name"])
require.Equal(t, "GET", obj["_verb"])
require.Equal(t, "hostname", obj["host"])
require.Equal(t, "full", obj["full_message"])
require.Equal(t, "short", obj["short_message"])
require.Equal(t, "1", obj["level"])
require.Equal(t, "demo", obj["facility"])
require.Equal(t, "42", obj["line"])
require.Equal(t, "graylog.go", obj["file"])
}
}

View file

@ -0,0 +1,462 @@
//go:build !windows && !darwin
package graylog
import (
"bytes"
"compress/zlib"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"strings"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/config"
common_tls "github.com/influxdata/telegraf/plugins/common/tls"
"github.com/influxdata/telegraf/testutil"
)
func TestWriteUDP(t *testing.T) {
tests := []struct {
name string
namefieldnoprefix bool
}{
{
name: "default without scheme",
},
{
name: "UDP",
},
{
name: "UDP non-standard name field",
namefieldnoprefix: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var wg sync.WaitGroup
address := UDPServer(t, &wg, tt.namefieldnoprefix)
plugin := Graylog{
NameFieldNoPrefix: tt.namefieldnoprefix,
Servers: []string{"udp://" + address},
}
require.NoError(t, plugin.Connect())
defer plugin.Close()
defer wg.Wait()
metrics := testutil.MockMetrics()
// UDP scenario:
// 4 messages are send
require.NoError(t, plugin.Write(metrics))
require.NoError(t, plugin.Write(metrics))
require.NoError(t, plugin.Write(metrics))
require.NoError(t, plugin.Write(metrics))
})
}
}
func TestWriteTCP(t *testing.T) {
pki := testutil.NewPKI("../../../testutil/pki")
tlsClientConfig := pki.TLSClientConfig()
tlsServerConfig, err := pki.TLSServerConfig().TLSConfig()
require.NoError(t, err)
tests := []struct {
name string
tlsClientCfg common_tls.ClientConfig
}{
{
name: "TCP",
},
{
name: "TLS",
tlsClientCfg: common_tls.ClientConfig{
ServerName: "localhost",
TLSCA: tlsClientConfig.TLSCA,
TLSKey: tlsClientConfig.TLSKey,
TLSCert: tlsClientConfig.TLSCert,
},
},
{
name: "TLS no validation",
tlsClientCfg: common_tls.ClientConfig{
InsecureSkipVerify: true,
ServerName: "localhost",
TLSKey: tlsClientConfig.TLSKey,
TLSCert: tlsClientConfig.TLSCert,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var wg sync.WaitGroup
errs := make(chan error)
address := TCPServer(t, &wg, tlsServerConfig, errs)
plugin := Graylog{
ClientConfig: common_tls.ClientConfig{
InsecureSkipVerify: true,
ServerName: "localhost",
TLSKey: tlsClientConfig.TLSKey,
TLSCert: tlsClientConfig.TLSCert,
},
Servers: []string{"tcp://" + address},
}
require.NoError(t, plugin.Connect())
defer plugin.Close()
defer wg.Wait()
metrics := testutil.MockMetrics()
// TCP scenario:
// 4 messages are send
// -> connection gets forcefully broken after the 2nd message (server closes connection)
// -> the 3rd write fails with error
// -> during the 4th write connection is restored and write is successful
require.NoError(t, plugin.Write(metrics))
require.NoError(t, plugin.Write(metrics))
require.NoError(t, <-errs)
require.ErrorContains(t, plugin.Write(metrics), "error writing message")
require.NoError(t, plugin.Write(metrics))
})
}
}
type GelfObject map[string]interface{}
func UDPServer(t *testing.T, wg *sync.WaitGroup, namefieldnoprefix bool) string {
udpServer, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
recv := func() error {
bufR := make([]byte, 1024)
n, _, err := udpServer.ReadFrom(bufR)
if err != nil {
return err
}
b := bytes.NewReader(bufR[0:n])
r, err := zlib.NewReader(b)
if err != nil {
return err
}
var maxDecompressionSize int64 = 500 * 1024 * 1024
bufW := bytes.NewBuffer(nil)
written, err := io.CopyN(bufW, r, maxDecompressionSize)
if err != nil && !errors.Is(err, io.EOF) {
return err
} else if written == maxDecompressionSize {
return fmt.Errorf("size of decoded data exceeds allowed size %d", maxDecompressionSize)
}
err = r.Close()
if err != nil {
return err
}
var obj GelfObject
err = json.Unmarshal(bufW.Bytes(), &obj)
if err != nil {
return err
}
require.Equal(t, "telegraf", obj["short_message"])
if namefieldnoprefix {
require.Equal(t, "test1", obj["name"])
} else {
require.Equal(t, "test1", obj["_name"])
}
require.Equal(t, "value1", obj["_tag1"])
require.InDelta(t, float64(1), obj["_value"], testutil.DefaultDelta)
return nil
}
// Send the address with the random port to the channel for the graylog instance to use it
address := udpServer.LocalAddr().String()
wg.Add(1)
go func() {
defer udpServer.Close()
defer wg.Done()
// in UDP scenario all 4 messages are received
err := recv()
if err != nil {
t.Error(err)
}
err = recv()
if err != nil {
t.Error(err)
}
err = recv()
if err != nil {
t.Error(err)
}
err = recv()
if err != nil {
t.Error(err)
}
}()
return address
}
func TCPServer(t *testing.T, wg *sync.WaitGroup, tlsConfig *tls.Config, errs chan error) string {
tcpServer, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
// Send the address with the random port to the channel for the graylog instance to use it
address := tcpServer.Addr().String()
accept := func() (net.Conn, error) {
conn, err := tcpServer.Accept()
require.NoError(t, err)
if tcpConn, ok := conn.(*net.TCPConn); ok {
err = tcpConn.SetLinger(0)
if err != nil {
return nil, err
}
}
err = conn.SetDeadline(time.Now().Add(15 * time.Second))
if err != nil {
return nil, err
}
if tlsConfig != nil {
conn = tls.Server(conn, tlsConfig)
}
return conn, nil
}
recv := func(conn net.Conn) error {
bufR := make([]byte, 1)
bufW := bytes.NewBuffer(nil)
for {
n, err := conn.Read(bufR)
if err != nil {
return err
}
if n > 0 {
if bufR[0] == 0 { // message delimiter found
break
}
bufW.Write(bufR)
}
}
var obj GelfObject
err = json.Unmarshal(bufW.Bytes(), &obj)
require.NoError(t, err)
require.Equal(t, "telegraf", obj["short_message"])
require.Equal(t, "test1", obj["_name"])
require.Equal(t, "value1", obj["_tag1"])
require.InDelta(t, float64(1), obj["_value"], testutil.DefaultDelta)
return nil
}
wg.Add(1)
go func() {
defer tcpServer.Close()
defer wg.Done()
fmt.Println("server: opening connection")
conn, err := accept()
if err != nil {
fmt.Println(err)
}
defer conn.Close()
// in TCP scenario only 3 messages are received, the 3rd is lost due to simulated connection break after the 2nd
fmt.Println("server: receiving packet 1")
err = recv(conn)
if err != nil {
fmt.Println(err)
}
fmt.Println("server: receiving packet 2")
err = recv(conn)
if err != nil {
fmt.Println(err)
}
fmt.Println("server: closing connection")
err = conn.Close()
if err != nil {
fmt.Println(err)
}
errs <- err
if err != nil {
return
}
fmt.Println("server: re-opening connection")
conn, err = accept()
if err != nil {
fmt.Println(err)
}
defer conn.Close()
fmt.Println("server: receiving packet 4")
err = recv(conn)
if err != nil {
fmt.Println(err)
}
}()
return address
}
func TestWriteUDPServerDown(t *testing.T) {
dummy, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"udp://" + dummy.LocalAddr().String()},
Log: testutil.Logger{},
}
require.NoError(t, dummy.Close())
require.NoError(t, plugin.Connect())
}
func TestWriteUDPServerUnavailableOnWrite(t *testing.T) {
dummy, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"udp://" + dummy.LocalAddr().String()},
Log: testutil.Logger{},
}
require.NoError(t, plugin.Connect())
require.NoError(t, dummy.Close())
require.NoError(t, plugin.Write(testutil.MockMetrics()))
}
func TestWriteTCPServerDown(t *testing.T) {
dummy, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"tcp://" + dummy.Addr().String()},
Log: testutil.Logger{},
}
require.NoError(t, dummy.Close())
require.ErrorContains(t, plugin.Connect(), "connect: connection refused")
}
func TestWriteTCPServerUnavailableOnWrite(t *testing.T) {
dummy, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"tcp://" + dummy.Addr().String()},
Log: testutil.Logger{},
}
require.NoError(t, plugin.Connect())
require.NoError(t, dummy.Close())
err = plugin.Write(testutil.MockMetrics())
require.ErrorContains(t, err, "error writing message")
}
func TestWriteUDPServerDownRetry(t *testing.T) {
dummy, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"udp://" + dummy.LocalAddr().String()},
Reconnection: true,
Log: testutil.Logger{},
}
require.NoError(t, dummy.Close())
require.NoError(t, plugin.Connect())
require.NoError(t, plugin.Close())
}
func TestWriteUDPServerUnavailableOnWriteRetry(t *testing.T) {
dummy, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"udp://" + dummy.LocalAddr().String()},
Reconnection: true,
Log: testutil.Logger{},
}
require.NoError(t, plugin.Connect())
require.NoError(t, dummy.Close())
err = plugin.Write(testutil.MockMetrics())
require.ErrorContains(t, err, "not connected")
require.NoError(t, plugin.Close())
}
func TestWriteTCPServerDownRetry(t *testing.T) {
dummy, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
logger := &testutil.CaptureLogger{}
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"tcp://" + dummy.Addr().String()},
Reconnection: true,
ReconnectionTime: config.Duration(100 * time.Millisecond),
Log: logger,
}
require.NoError(t, dummy.Close())
require.NoError(t, plugin.Connect())
require.Eventually(t, func() bool {
return strings.Contains(logger.LastError(), "after attempt #5...")
}, 5*time.Second, 100*time.Millisecond)
require.NoError(t, plugin.Close())
}
func TestWriteTCPServerUnavailableOnWriteRetry(t *testing.T) {
dummy, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"tcp://" + dummy.Addr().String()},
Reconnection: true,
Log: testutil.Logger{},
}
require.NoError(t, plugin.Connect())
require.NoError(t, dummy.Close())
err = plugin.Write(testutil.MockMetrics())
require.ErrorContains(t, err, "not connected")
require.NoError(t, plugin.Close())
}
func TestWriteTCPRetryStopping(t *testing.T) {
dummy, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
logger := &testutil.CaptureLogger{}
plugin := Graylog{
NameFieldNoPrefix: true,
Servers: []string{"tcp://" + dummy.Addr().String()},
Reconnection: true,
ReconnectionTime: config.Duration(10 * time.Millisecond),
Log: logger,
}
require.NoError(t, dummy.Close())
require.NoError(t, plugin.Connect())
time.Sleep(100 * time.Millisecond)
require.NoError(t, plugin.Close())
}

View file

@ -0,0 +1,33 @@
# Send telegraf metrics to graylog
[[outputs.graylog]]
## Endpoints for your graylog instances.
servers = ["udp://127.0.0.1:12201"]
## Connection timeout.
# timeout = "5s"
## The field to use as the GELF short_message, if unset the static string
## "telegraf" will be used.
## example: short_message_field = "message"
# short_message_field = ""
## According to GELF payload specification, additional fields names must be prefixed
## with an underscore. Previous versions did not prefix custom field 'name' with underscore.
## Set to true for backward compatibility.
# name_field_no_prefix = false
## Connection retry options
## Attempt to connect to the endpoints if the initial connection fails.
## If 'false', Telegraf will give up after 3 connection attempt and will
## exit with an error. If set to 'true', the plugin will retry to connect
## to the unconnected endpoints infinitely.
# connection_retry = false
## Time to wait between connection retry attempts.
# connection_retry_wait_time = "15s"
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false