2022-05-25 22:48:59 +08:00
|
|
|
//go:generate ../../../tools/readme_config_includer/generator
|
2015-09-16 02:16:53 +08:00
|
|
|
package amqp
|
|
|
|
|
|
|
|
|
|
import (
|
2018-06-04 09:12:48 +08:00
|
|
|
"bytes"
|
2022-05-25 22:48:59 +08:00
|
|
|
_ "embed"
|
2016-03-17 02:44:11 +08:00
|
|
|
"strings"
|
2015-09-24 01:02:34 +08:00
|
|
|
"time"
|
2015-09-16 02:16:53 +08:00
|
|
|
|
2022-05-26 03:16:13 +08:00
|
|
|
amqp "github.com/rabbitmq/amqp091-go"
|
2021-11-19 00:26:24 +08:00
|
|
|
|
2016-01-28 05:21:36 +08:00
|
|
|
"github.com/influxdata/telegraf"
|
2021-04-10 01:15:04 +08:00
|
|
|
"github.com/influxdata/telegraf/config"
|
2016-02-04 03:59:34 +08:00
|
|
|
"github.com/influxdata/telegraf/internal"
|
2022-08-10 05:16:44 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/common/proxy"
|
2020-06-26 02:44:22 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/common/tls"
|
2016-01-28 07:15:14 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/outputs"
|
2016-02-11 06:50:07 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/serializers"
|
2015-09-16 02:16:53 +08:00
|
|
|
)
|
|
|
|
|
|
2022-05-25 22:48:59 +08:00
|
|
|
//go:embed sample.conf
|
|
|
|
|
var sampleConfig string
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
const (
|
|
|
|
|
DefaultURL = "amqp://localhost:5672/influxdb"
|
|
|
|
|
DefaultAuthMethod = "PLAIN"
|
|
|
|
|
DefaultExchangeType = "topic"
|
|
|
|
|
DefaultRetentionPolicy = "default"
|
|
|
|
|
DefaultDatabase = "telegraf"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type externalAuth struct{}
|
|
|
|
|
|
|
|
|
|
func (a *externalAuth) Mechanism() string {
|
|
|
|
|
return "EXTERNAL"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *externalAuth) Response() string {
|
2021-03-26 01:57:01 +08:00
|
|
|
return "\000"
|
2017-04-28 02:10:30 +08:00
|
|
|
}
|
|
|
|
|
|
2015-09-16 02:16:53 +08:00
|
|
|
type AMQP struct {
|
2022-03-02 06:05:53 +08:00
|
|
|
URL string `toml:"url" deprecated:"1.7.0;use 'brokers' instead"`
|
2018-06-04 08:05:33 +08:00
|
|
|
Brokers []string `toml:"brokers"`
|
2018-06-04 07:31:11 +08:00
|
|
|
Exchange string `toml:"exchange"`
|
|
|
|
|
ExchangeType string `toml:"exchange_type"`
|
|
|
|
|
ExchangePassive bool `toml:"exchange_passive"`
|
2018-06-04 09:12:48 +08:00
|
|
|
ExchangeDurability string `toml:"exchange_durability"`
|
2018-06-04 07:31:11 +08:00
|
|
|
ExchangeArguments map[string]string `toml:"exchange_arguments"`
|
2018-06-04 09:12:48 +08:00
|
|
|
Username string `toml:"username"`
|
|
|
|
|
Password string `toml:"password"`
|
|
|
|
|
MaxMessages int `toml:"max_messages"`
|
|
|
|
|
AuthMethod string `toml:"auth_method"`
|
|
|
|
|
RoutingTag string `toml:"routing_tag"`
|
|
|
|
|
RoutingKey string `toml:"routing_key"`
|
|
|
|
|
DeliveryMode string `toml:"delivery_mode"`
|
2022-03-02 06:05:53 +08:00
|
|
|
Database string `toml:"database" deprecated:"1.7.0;use 'headers' instead"`
|
|
|
|
|
RetentionPolicy string `toml:"retention_policy" deprecated:"1.7.0;use 'headers' instead"`
|
|
|
|
|
Precision string `toml:"precision" deprecated:"1.2.0;option is ignored"`
|
2018-06-04 09:12:48 +08:00
|
|
|
Headers map[string]string `toml:"headers"`
|
2021-04-10 01:15:04 +08:00
|
|
|
Timeout config.Duration `toml:"timeout"`
|
2018-06-04 09:12:48 +08:00
|
|
|
UseBatchFormat bool `toml:"use_batch_format"`
|
2019-05-21 05:36:23 +08:00
|
|
|
ContentEncoding string `toml:"content_encoding"`
|
2021-02-09 00:18:40 +08:00
|
|
|
Log telegraf.Logger `toml:"-"`
|
2018-05-05 07:33:23 +08:00
|
|
|
tls.ClientConfig
|
2022-08-10 05:16:44 +08:00
|
|
|
proxy.TCPProxy
|
2016-02-04 03:59:34 +08:00
|
|
|
|
2017-12-01 10:40:12 +08:00
|
|
|
serializer serializers.Serializer
|
2018-06-04 09:12:48 +08:00
|
|
|
connect func(*ClientConfig) (Client, error)
|
|
|
|
|
client Client
|
|
|
|
|
config *ClientConfig
|
|
|
|
|
sentMessages int
|
2019-05-21 05:36:23 +08:00
|
|
|
encoder internal.ContentEncoder
|
2015-09-16 02:16:53 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
type Client interface {
|
|
|
|
|
Publish(key string, body []byte) error
|
|
|
|
|
Close() error
|
2016-03-17 02:44:11 +08:00
|
|
|
}
|
2015-10-30 17:02:14 +08:00
|
|
|
|
2022-05-25 22:48:59 +08:00
|
|
|
func (*AMQP) SampleConfig() string {
|
|
|
|
|
return sampleConfig
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
func (q *AMQP) SetSerializer(serializer serializers.Serializer) {
|
|
|
|
|
q.serializer = serializer
|
|
|
|
|
}
|
2017-12-01 10:40:12 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
func (q *AMQP) Connect() error {
|
|
|
|
|
if q.config == nil {
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig, err := q.makeClientConfig()
|
2018-06-04 09:12:48 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-11-19 00:26:24 +08:00
|
|
|
q.config = clientConfig
|
2015-10-30 17:02:14 +08:00
|
|
|
}
|
|
|
|
|
|
2019-05-21 05:36:23 +08:00
|
|
|
var err error
|
|
|
|
|
q.encoder, err = internal.NewContentEncoder(q.ContentEncoding)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
q.client, err = q.connect(q.config)
|
2016-02-04 03:59:34 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2016-01-15 20:35:43 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
2018-06-04 08:05:33 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
func (q *AMQP) Close() error {
|
|
|
|
|
if q.client != nil {
|
|
|
|
|
return q.client.Close()
|
2016-03-17 02:44:11 +08:00
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
2016-03-17 02:44:11 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
func (q *AMQP) routingKey(metric telegraf.Metric) string {
|
|
|
|
|
if q.RoutingTag != "" {
|
|
|
|
|
key, ok := metric.GetTag(q.RoutingTag)
|
|
|
|
|
if ok {
|
|
|
|
|
return key
|
|
|
|
|
}
|
2016-01-15 20:35:43 +08:00
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
return q.RoutingKey
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (q *AMQP) Write(metrics []telegraf.Metric) error {
|
|
|
|
|
batches := make(map[string][]telegraf.Metric)
|
2019-05-18 04:46:13 +08:00
|
|
|
if q.ExchangeType == "header" {
|
|
|
|
|
// Since the routing_key is ignored for this exchange type send as a
|
2018-06-04 09:12:48 +08:00
|
|
|
// single batch.
|
|
|
|
|
batches[""] = metrics
|
|
|
|
|
} else {
|
|
|
|
|
for _, metric := range metrics {
|
|
|
|
|
routingKey := q.routingKey(metric)
|
|
|
|
|
if _, ok := batches[routingKey]; !ok {
|
|
|
|
|
batches[routingKey] = make([]telegraf.Metric, 0)
|
|
|
|
|
}
|
2016-03-17 02:44:11 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
batches[routingKey] = append(batches[routingKey], metric)
|
2018-06-04 08:05:33 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
first := true
|
|
|
|
|
for key, metrics := range batches {
|
|
|
|
|
body, err := q.serialize(metrics)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-03-04 02:24:50 +08:00
|
|
|
|
2019-05-21 05:36:23 +08:00
|
|
|
body, err = q.encoder.Encode(body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
err = q.publish(key, body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// If this is the first attempt to publish and the connection is
|
|
|
|
|
// closed, try to reconnect and retry once.
|
2022-10-26 18:06:08 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
if aerr, ok := err.(*amqp.Error); first && ok && aerr == amqp.ErrClosed {
|
|
|
|
|
q.client = nil
|
|
|
|
|
err := q.publish(key, body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2022-02-16 01:21:20 +08:00
|
|
|
} else if q.client != nil {
|
2022-01-07 06:04:46 +08:00
|
|
|
if err := q.client.Close(); err != nil {
|
|
|
|
|
q.Log.Errorf("Closing connection failed: %v", err)
|
|
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
q.client = nil
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
first = false
|
2015-09-16 02:16:53 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
if q.sentMessages >= q.MaxMessages && q.MaxMessages > 0 {
|
2021-02-09 00:18:40 +08:00
|
|
|
q.Log.Debug("Sent MaxMessages; closing connection")
|
2021-11-19 00:26:24 +08:00
|
|
|
if err := q.client.Close(); err != nil {
|
|
|
|
|
q.Log.Errorf("Closing connection failed: %v", err)
|
|
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
q.client = nil
|
2018-06-04 07:31:11 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (q *AMQP) publish(key string, body []byte) error {
|
|
|
|
|
if q.client == nil {
|
|
|
|
|
client, err := q.connect(q.config)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
q.sentMessages = 0
|
|
|
|
|
q.client = client
|
2018-06-04 07:31:11 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
err := q.client.Publish(key, body)
|
2015-09-16 02:16:53 +08:00
|
|
|
if err != nil {
|
2018-06-04 07:31:11 +08:00
|
|
|
return err
|
2015-09-16 02:16:53 +08:00
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
q.sentMessages++
|
2015-09-16 02:16:53 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
func (q *AMQP) serialize(metrics []telegraf.Metric) ([]byte, error) {
|
|
|
|
|
if q.UseBatchFormat {
|
|
|
|
|
return q.serializer.SerializeBatch(metrics)
|
2021-02-09 00:18:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
for _, metric := range metrics {
|
|
|
|
|
octets, err := q.serializer.Serialize(metric)
|
|
|
|
|
if err != nil {
|
|
|
|
|
q.Log.Debugf("Could not serialize metric: %v", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
_, err = buf.Write(octets)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2018-06-04 09:12:48 +08:00
|
|
|
}
|
2018-06-04 07:31:11 +08:00
|
|
|
}
|
2021-02-09 00:18:40 +08:00
|
|
|
body := buf.Bytes()
|
|
|
|
|
return body, nil
|
2018-06-04 07:31:11 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
func (q *AMQP) makeClientConfig() (*ClientConfig, error) {
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig := &ClientConfig{
|
2018-06-04 09:12:48 +08:00
|
|
|
exchange: q.Exchange,
|
|
|
|
|
exchangeType: q.ExchangeType,
|
|
|
|
|
exchangePassive: q.ExchangePassive,
|
2019-05-21 05:36:23 +08:00
|
|
|
encoding: q.ContentEncoding,
|
2021-04-10 01:15:04 +08:00
|
|
|
timeout: time.Duration(q.Timeout),
|
2021-11-19 00:26:24 +08:00
|
|
|
log: q.Log,
|
2017-04-28 02:10:30 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
switch q.ExchangeDurability {
|
|
|
|
|
case "transient":
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.exchangeDurable = false
|
2018-06-04 09:12:48 +08:00
|
|
|
default:
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.exchangeDurable = true
|
2017-03-04 02:24:50 +08:00
|
|
|
}
|
2015-09-16 02:16:53 +08:00
|
|
|
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.brokers = q.Brokers
|
|
|
|
|
if len(clientConfig.brokers) == 0 {
|
|
|
|
|
clientConfig.brokers = []string{q.URL}
|
2015-09-16 02:16:53 +08:00
|
|
|
}
|
2017-04-28 02:10:30 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
switch q.DeliveryMode {
|
|
|
|
|
case "transient":
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.deliveryMode = amqp.Transient
|
2018-06-04 09:12:48 +08:00
|
|
|
case "persistent":
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.deliveryMode = amqp.Persistent
|
2018-06-04 09:12:48 +08:00
|
|
|
default:
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.deliveryMode = amqp.Transient
|
2017-04-28 02:10:30 +08:00
|
|
|
}
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
if len(q.Headers) > 0 {
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.headers = make(amqp.Table, len(q.Headers))
|
2018-06-04 09:12:48 +08:00
|
|
|
for k, v := range q.Headers {
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.headers[k] = v
|
2018-06-04 06:52:00 +08:00
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
} else {
|
|
|
|
|
// Copy deprecated fields into message header
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.headers = amqp.Table{
|
2018-06-04 09:12:48 +08:00
|
|
|
"database": q.Database,
|
|
|
|
|
"retention_policy": q.RetentionPolicy,
|
2015-09-16 02:16:53 +08:00
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
}
|
2015-09-16 02:16:53 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
if len(q.ExchangeArguments) > 0 {
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.exchangeArguments = make(amqp.Table, len(q.ExchangeArguments))
|
2018-06-04 09:12:48 +08:00
|
|
|
for k, v := range q.ExchangeArguments {
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.exchangeArguments[k] = v
|
2016-02-11 06:50:07 +08:00
|
|
|
}
|
2018-06-04 09:12:48 +08:00
|
|
|
}
|
2016-02-11 06:50:07 +08:00
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
tlsConfig, err := q.ClientConfig.TLSConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2015-10-22 01:25:36 +08:00
|
|
|
}
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.tlsConfig = tlsConfig
|
2016-02-11 06:50:07 +08:00
|
|
|
|
2022-08-10 05:16:44 +08:00
|
|
|
dialer, err := q.TCPProxy.Proxy()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
clientConfig.dialer = dialer
|
|
|
|
|
|
2018-06-04 09:12:48 +08:00
|
|
|
var auth []amqp.Authentication
|
|
|
|
|
if strings.ToUpper(q.AuthMethod) == "EXTERNAL" {
|
|
|
|
|
auth = []amqp.Authentication{&externalAuth{}}
|
|
|
|
|
} else if q.Username != "" || q.Password != "" {
|
|
|
|
|
auth = []amqp.Authentication{
|
|
|
|
|
&amqp.PlainAuth{
|
|
|
|
|
Username: q.Username,
|
|
|
|
|
Password: q.Password,
|
|
|
|
|
},
|
2015-09-16 02:16:53 +08:00
|
|
|
}
|
|
|
|
|
}
|
2021-11-19 00:26:24 +08:00
|
|
|
clientConfig.auth = auth
|
2015-09-16 02:16:53 +08:00
|
|
|
|
2021-11-19 00:26:24 +08:00
|
|
|
return clientConfig, nil
|
2017-04-28 02:10:30 +08:00
|
|
|
}
|
|
|
|
|
|
2021-11-19 00:26:24 +08:00
|
|
|
func connect(clientConfig *ClientConfig) (Client, error) {
|
2022-08-03 20:20:51 +08:00
|
|
|
return newClient(clientConfig)
|
2017-04-28 02:10:30 +08:00
|
|
|
}
|
|
|
|
|
|
2015-09-16 02:16:53 +08:00
|
|
|
func init() {
|
2016-01-28 05:21:36 +08:00
|
|
|
outputs.Add("amqp", func() telegraf.Output {
|
2015-10-30 17:02:14 +08:00
|
|
|
return &AMQP{
|
2018-06-04 09:12:48 +08:00
|
|
|
URL: DefaultURL,
|
|
|
|
|
ExchangeType: DefaultExchangeType,
|
|
|
|
|
AuthMethod: DefaultAuthMethod,
|
|
|
|
|
Database: DefaultDatabase,
|
|
|
|
|
RetentionPolicy: DefaultRetentionPolicy,
|
2021-04-10 01:15:04 +08:00
|
|
|
Timeout: config.Duration(time.Second * 5),
|
2018-06-04 09:12:48 +08:00
|
|
|
connect: connect,
|
2015-10-30 17:02:14 +08:00
|
|
|
}
|
2015-09-16 02:16:53 +08:00
|
|
|
})
|
|
|
|
|
}
|