2015-12-10 02:07:55 +00:00
|
|
|
package kinesis
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/service/kinesis"
|
2019-11-08 01:39:19 +00:00
|
|
|
"github.com/gofrs/uuid"
|
2016-01-27 21:21:36 +00:00
|
|
|
"github.com/influxdata/telegraf"
|
2020-05-04 18:09:10 +00:00
|
|
|
internalaws "github.com/influxdata/telegraf/config/aws"
|
2016-01-27 23:15:14 +00:00
|
|
|
"github.com/influxdata/telegraf/plugins/outputs"
|
2016-12-20 18:49:28 +00:00
|
|
|
"github.com/influxdata/telegraf/plugins/serializers"
|
2015-12-10 02:07:55 +00:00
|
|
|
)
|
|
|
|
|
2017-08-28 23:24:38 +00:00
|
|
|
type (
|
|
|
|
KinesisOutput struct {
|
2018-07-31 22:07:21 +00:00
|
|
|
Region string `toml:"region"`
|
|
|
|
AccessKey string `toml:"access_key"`
|
|
|
|
SecretKey string `toml:"secret_key"`
|
|
|
|
RoleARN string `toml:"role_arn"`
|
|
|
|
Profile string `toml:"profile"`
|
|
|
|
Filename string `toml:"shared_credential_file"`
|
|
|
|
Token string `toml:"token"`
|
|
|
|
EndpointURL string `toml:"endpoint_url"`
|
2017-08-28 23:24:38 +00:00
|
|
|
|
|
|
|
StreamName string `toml:"streamname"`
|
|
|
|
PartitionKey string `toml:"partitionkey"`
|
|
|
|
RandomPartitionKey bool `toml:"use_random_partitionkey"`
|
|
|
|
Partition *Partition `toml:"partition"`
|
|
|
|
Debug bool `toml:"debug"`
|
|
|
|
svc *kinesis.Kinesis
|
|
|
|
|
|
|
|
serializer serializers.Serializer
|
|
|
|
}
|
|
|
|
|
|
|
|
Partition struct {
|
2018-10-26 06:51:14 +00:00
|
|
|
Method string `toml:"method"`
|
|
|
|
Key string `toml:"key"`
|
|
|
|
Default string `toml:"default"`
|
2017-08-28 23:24:38 +00:00
|
|
|
}
|
|
|
|
)
|
2015-12-10 02:07:55 +00:00
|
|
|
|
|
|
|
var sampleConfig = `
|
2016-02-18 21:26:51 +00:00
|
|
|
## Amazon REGION of kinesis endpoint.
|
2015-12-10 02:07:55 +00:00
|
|
|
region = "ap-southeast-2"
|
2016-04-23 18:19:04 +00:00
|
|
|
|
|
|
|
## Amazon Credentials
|
|
|
|
## Credentials are loaded in the following order
|
2016-05-25 11:30:39 +00:00
|
|
|
## 1) Assumed credentials via STS if role_arn is specified
|
|
|
|
## 2) explicit credentials from 'access_key' and 'secret_key'
|
|
|
|
## 3) shared profile from 'profile'
|
|
|
|
## 4) environment variables
|
|
|
|
## 5) shared credentials file
|
|
|
|
## 6) EC2 Instance Profile
|
2016-04-23 18:19:04 +00:00
|
|
|
#access_key = ""
|
|
|
|
#secret_key = ""
|
2016-05-25 11:30:39 +00:00
|
|
|
#token = ""
|
|
|
|
#role_arn = ""
|
|
|
|
#profile = ""
|
|
|
|
#shared_credential_file = ""
|
2016-04-23 18:19:04 +00:00
|
|
|
|
2018-07-31 22:07:21 +00:00
|
|
|
## Endpoint to make request against, the correct endpoint is automatically
|
|
|
|
## determined and this option should only be set if you wish to override the
|
|
|
|
## default.
|
|
|
|
## ex: endpoint_url = "http://localhost:8000"
|
|
|
|
# endpoint_url = ""
|
|
|
|
|
2016-02-18 21:26:51 +00:00
|
|
|
## Kinesis StreamName must exist prior to starting telegraf.
|
2015-12-10 02:07:55 +00:00
|
|
|
streamname = "StreamName"
|
2017-08-28 23:24:38 +00:00
|
|
|
## DEPRECATED: PartitionKey as used for sharding data.
|
2015-12-10 02:07:55 +00:00
|
|
|
partitionkey = "PartitionKey"
|
2017-08-28 23:24:38 +00:00
|
|
|
## DEPRECATED: If set the paritionKey will be a random UUID on every put.
|
2017-04-26 17:54:24 +00:00
|
|
|
## This allows for scaling across multiple shards in a stream.
|
|
|
|
## This will cause issues with ordering.
|
|
|
|
use_random_partitionkey = false
|
2017-08-28 23:24:38 +00:00
|
|
|
## The partition key can be calculated using one of several methods:
|
|
|
|
##
|
|
|
|
## Use a static value for all writes:
|
|
|
|
# [outputs.kinesis.partition]
|
|
|
|
# method = "static"
|
|
|
|
# key = "howdy"
|
|
|
|
#
|
|
|
|
## Use a random partition key on each write:
|
|
|
|
# [outputs.kinesis.partition]
|
|
|
|
# method = "random"
|
|
|
|
#
|
|
|
|
## Use the measurement name as the partition key:
|
|
|
|
# [outputs.kinesis.partition]
|
|
|
|
# method = "measurement"
|
|
|
|
#
|
|
|
|
## Use the value of a tag for all writes, if the tag is not set the empty
|
2018-10-26 06:51:14 +00:00
|
|
|
## default option will be used. When no default, defaults to "telegraf"
|
2017-08-28 23:24:38 +00:00
|
|
|
# [outputs.kinesis.partition]
|
|
|
|
# method = "tag"
|
|
|
|
# key = "host"
|
2018-10-26 06:51:14 +00:00
|
|
|
# default = "mykey"
|
2017-04-26 17:54:24 +00:00
|
|
|
|
2016-12-20 18:49:28 +00:00
|
|
|
|
|
|
|
## Data format to output.
|
2017-04-27 21:59:18 +00:00
|
|
|
## Each data format has its own unique set of configuration options, read
|
2016-12-20 18:49:28 +00:00
|
|
|
## more about them here:
|
|
|
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
|
|
|
data_format = "influx"
|
|
|
|
|
2016-02-18 21:26:51 +00:00
|
|
|
## debug will show upstream aws messages.
|
2015-12-10 02:07:55 +00:00
|
|
|
debug = false
|
|
|
|
`
|
|
|
|
|
|
|
|
func (k *KinesisOutput) SampleConfig() string {
|
|
|
|
return sampleConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KinesisOutput) Description() string {
|
|
|
|
return "Configuration for the AWS Kinesis output."
|
|
|
|
}
|
|
|
|
|
2018-10-18 20:05:43 +00:00
|
|
|
func (k *KinesisOutput) Connect() error {
|
|
|
|
if k.Partition == nil {
|
|
|
|
log.Print("E! kinesis : Deprecated paritionkey configuration in use, please consider using outputs.kinesis.partition")
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We attempt first to create a session to Kinesis using an IAMS role, if that fails it will fall through to using
|
|
|
|
// environment variables, and then Shared Credentials.
|
|
|
|
if k.Debug {
|
2019-01-22 22:05:20 +00:00
|
|
|
log.Printf("I! kinesis: Establishing a connection to Kinesis in %s", k.Region)
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
2016-05-25 11:30:39 +00:00
|
|
|
|
|
|
|
credentialConfig := &internalaws.CredentialConfig{
|
2018-07-31 22:07:21 +00:00
|
|
|
Region: k.Region,
|
|
|
|
AccessKey: k.AccessKey,
|
|
|
|
SecretKey: k.SecretKey,
|
|
|
|
RoleARN: k.RoleARN,
|
|
|
|
Profile: k.Profile,
|
|
|
|
Filename: k.Filename,
|
|
|
|
Token: k.Token,
|
|
|
|
EndpointURL: k.EndpointURL,
|
2016-04-23 18:19:04 +00:00
|
|
|
}
|
2016-05-25 11:30:39 +00:00
|
|
|
configProvider := credentialConfig.Credentials()
|
|
|
|
svc := kinesis.New(configProvider)
|
2015-12-10 02:07:55 +00:00
|
|
|
|
2018-10-18 20:05:43 +00:00
|
|
|
_, err := svc.DescribeStreamSummary(&kinesis.DescribeStreamSummaryInput{
|
|
|
|
StreamName: aws.String(k.StreamName),
|
|
|
|
})
|
|
|
|
k.svc = svc
|
2015-12-10 02:07:55 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *KinesisOutput) Close() error {
|
2016-01-23 00:26:48 +00:00
|
|
|
return nil
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-12-20 18:49:28 +00:00
|
|
|
func (k *KinesisOutput) SetSerializer(serializer serializers.Serializer) {
|
|
|
|
k.serializer = serializer
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func writekinesis(k *KinesisOutput, r []*kinesis.PutRecordsRequestEntry) time.Duration {
|
|
|
|
start := time.Now()
|
|
|
|
payload := &kinesis.PutRecordsInput{
|
|
|
|
Records: r,
|
|
|
|
StreamName: aws.String(k.StreamName),
|
|
|
|
}
|
|
|
|
|
|
|
|
if k.Debug {
|
|
|
|
resp, err := k.svc.PutRecords(payload)
|
|
|
|
if err != nil {
|
2019-01-22 22:05:20 +00:00
|
|
|
log.Printf("E! kinesis: Unable to write to Kinesis : %s", err.Error())
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
2019-01-22 22:05:20 +00:00
|
|
|
log.Printf("I! Wrote: '%+v'", resp)
|
2015-12-10 02:07:55 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
_, err := k.svc.PutRecords(payload)
|
|
|
|
if err != nil {
|
2019-01-22 22:05:20 +00:00
|
|
|
log.Printf("E! kinesis: Unable to write to Kinesis : %s", err.Error())
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return time.Since(start)
|
|
|
|
}
|
|
|
|
|
2017-08-28 23:24:38 +00:00
|
|
|
func (k *KinesisOutput) getPartitionKey(metric telegraf.Metric) string {
|
|
|
|
if k.Partition != nil {
|
|
|
|
switch k.Partition.Method {
|
|
|
|
case "static":
|
|
|
|
return k.Partition.Key
|
|
|
|
case "random":
|
2019-11-08 01:39:19 +00:00
|
|
|
u, err := uuid.NewV4()
|
|
|
|
if err != nil {
|
|
|
|
return k.Partition.Default
|
|
|
|
}
|
2017-08-28 23:24:38 +00:00
|
|
|
return u.String()
|
|
|
|
case "measurement":
|
|
|
|
return metric.Name()
|
|
|
|
case "tag":
|
2018-10-26 06:51:14 +00:00
|
|
|
if t, ok := metric.GetTag(k.Partition.Key); ok {
|
|
|
|
return t
|
|
|
|
} else if len(k.Partition.Default) > 0 {
|
|
|
|
return k.Partition.Default
|
2017-08-28 23:24:38 +00:00
|
|
|
}
|
2018-10-26 06:51:14 +00:00
|
|
|
// Default partition name if default is not set
|
|
|
|
return "telegraf"
|
2017-08-28 23:24:38 +00:00
|
|
|
default:
|
2019-01-22 22:05:20 +00:00
|
|
|
log.Printf("E! kinesis : You have configured a Partition method of '%s' which is not supported", k.Partition.Method)
|
2017-08-28 23:24:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if k.RandomPartitionKey {
|
2019-11-08 01:39:19 +00:00
|
|
|
u, err := uuid.NewV4()
|
|
|
|
if err != nil {
|
|
|
|
return k.Partition.Default
|
|
|
|
}
|
2017-08-28 23:24:38 +00:00
|
|
|
return u.String()
|
|
|
|
}
|
|
|
|
return k.PartitionKey
|
|
|
|
}
|
|
|
|
|
2016-01-27 23:15:14 +00:00
|
|
|
func (k *KinesisOutput) Write(metrics []telegraf.Metric) error {
|
2016-12-20 18:49:28 +00:00
|
|
|
var sz uint32
|
2015-12-10 02:07:55 +00:00
|
|
|
|
2016-01-27 23:15:14 +00:00
|
|
|
if len(metrics) == 0 {
|
2015-12-10 02:07:55 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
r := []*kinesis.PutRecordsRequestEntry{}
|
|
|
|
|
2016-12-20 18:49:28 +00:00
|
|
|
for _, metric := range metrics {
|
|
|
|
sz++
|
|
|
|
|
|
|
|
values, err := k.serializer.Serialize(metric)
|
|
|
|
if err != nil {
|
2019-06-04 00:34:48 +00:00
|
|
|
log.Printf("D! [outputs.kinesis] Could not serialize metric: %v", err)
|
|
|
|
continue
|
2016-12-20 18:49:28 +00:00
|
|
|
}
|
2015-12-10 02:07:55 +00:00
|
|
|
|
2017-08-28 23:24:38 +00:00
|
|
|
partitionKey := k.getPartitionKey(metric)
|
2017-04-26 17:54:24 +00:00
|
|
|
|
2015-12-10 02:07:55 +00:00
|
|
|
d := kinesis.PutRecordsRequestEntry{
|
2016-12-20 18:49:28 +00:00
|
|
|
Data: values,
|
2017-04-26 17:54:24 +00:00
|
|
|
PartitionKey: aws.String(partitionKey),
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
2016-12-20 18:49:28 +00:00
|
|
|
|
2015-12-10 02:07:55 +00:00
|
|
|
r = append(r, &d)
|
|
|
|
|
|
|
|
if sz == 500 {
|
|
|
|
// Max Messages Per PutRecordRequest is 500
|
|
|
|
elapsed := writekinesis(k, r)
|
2019-02-25 20:02:57 +00:00
|
|
|
log.Printf("D! Wrote a %d point batch to Kinesis in %+v.", sz, elapsed)
|
2016-12-20 18:49:28 +00:00
|
|
|
sz = 0
|
2015-12-10 02:07:55 +00:00
|
|
|
r = nil
|
|
|
|
}
|
2016-12-20 18:49:28 +00:00
|
|
|
|
2015-12-10 02:07:55 +00:00
|
|
|
}
|
2017-08-28 23:24:38 +00:00
|
|
|
if sz > 0 {
|
|
|
|
elapsed := writekinesis(k, r)
|
2019-02-25 20:02:57 +00:00
|
|
|
log.Printf("D! Wrote a %d point batch to Kinesis in %+v.", sz, elapsed)
|
2017-08-28 23:24:38 +00:00
|
|
|
}
|
2015-12-10 02:07:55 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2016-01-27 21:21:36 +00:00
|
|
|
outputs.Add("kinesis", func() telegraf.Output {
|
2015-12-10 02:07:55 +00:00
|
|
|
return &KinesisOutput{}
|
|
|
|
})
|
|
|
|
}
|