2020-06-05 07:09:22 +08:00
|
|
|
package shim
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
2025-04-23 21:47:39 +08:00
|
|
|
"os"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
2020-06-05 07:09:22 +08:00
|
|
|
|
|
|
|
|
"github.com/influxdata/telegraf"
|
2024-05-11 05:43:43 +08:00
|
|
|
"github.com/influxdata/telegraf/models"
|
2022-07-07 04:23:13 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
2020-06-05 07:09:22 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AddOutput adds the input to the shim. Later calls to Run() will run this.
|
|
|
|
|
func (s *Shim) AddOutput(output telegraf.Output) error {
|
2024-05-11 05:43:43 +08:00
|
|
|
models.SetLoggerOnPlugin(output, s.Log())
|
2020-06-05 07:09:22 +08:00
|
|
|
if p, ok := output.(telegraf.Initializer); ok {
|
|
|
|
|
err := p.Init()
|
|
|
|
|
if err != nil {
|
2023-02-22 19:08:46 +08:00
|
|
|
return fmt.Errorf("failed to init input: %w", err)
|
2020-06-05 07:09:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.Output = output
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Shim) RunOutput() error {
|
2025-04-23 21:47:39 +08:00
|
|
|
// Create a parser for receiving the metrics in line-protocol format
|
2022-07-07 04:23:13 +08:00
|
|
|
parser := influx.Parser{}
|
2025-04-23 21:47:39 +08:00
|
|
|
if err := parser.Init(); err != nil {
|
2022-10-12 00:31:44 +08:00
|
|
|
return fmt.Errorf("failed to create new parser: %w", err)
|
2020-06-05 07:09:22 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-23 21:47:39 +08:00
|
|
|
// Connect the output
|
|
|
|
|
if err := s.Output.Connect(); err != nil {
|
2020-06-05 07:09:22 +08:00
|
|
|
return fmt.Errorf("failed to start processor: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer s.Output.Close()
|
|
|
|
|
|
2025-04-23 21:47:39 +08:00
|
|
|
// Collect the metrics from stdin. Note, we need to flush the metrics
|
|
|
|
|
// when the batch is full or after the configured time, whatever comes
|
|
|
|
|
// first. We need to lock the batch as we run into race conditions
|
|
|
|
|
// otherwise.
|
|
|
|
|
var mu sync.Mutex
|
|
|
|
|
metrics := make([]telegraf.Metric, 0, s.BatchSize)
|
2020-06-05 07:09:22 +08:00
|
|
|
|
2025-04-23 21:47:39 +08:00
|
|
|
// Prepare the flush timer...
|
|
|
|
|
flush := func(whole bool) {
|
|
|
|
|
mu.Lock()
|
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
|
|
// Exit early if there is nothing to do
|
|
|
|
|
if len(metrics) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine the threshold on when to stop flushing depending on the
|
|
|
|
|
// given flag.
|
|
|
|
|
var threshold int
|
|
|
|
|
if whole {
|
|
|
|
|
threshold = s.BatchSize
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Flush out the metrics in batches of the configured size until we
|
|
|
|
|
// got all of them out or if there is less than a whole batch left.
|
|
|
|
|
for len(metrics) > 0 && len(metrics) >= threshold {
|
|
|
|
|
// Write the metrics and remove the batch
|
|
|
|
|
batch := metrics[:min(len(metrics), s.BatchSize)]
|
|
|
|
|
if err := s.Output.Write(batch); err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "Failed to write metrics: %s\n", err)
|
|
|
|
|
}
|
|
|
|
|
metrics = metrics[len(batch):]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup the time-based flush
|
|
|
|
|
var timer *time.Timer
|
|
|
|
|
if s.BatchTimeout > 0 {
|
|
|
|
|
timer = time.AfterFunc(s.BatchTimeout, func() { flush(false) })
|
|
|
|
|
defer func() {
|
|
|
|
|
if timer != nil {
|
|
|
|
|
timer.Stop()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start the processing loop
|
2020-06-05 07:09:22 +08:00
|
|
|
scanner := bufio.NewScanner(s.stdin)
|
|
|
|
|
for scanner.Scan() {
|
2025-04-23 21:47:39 +08:00
|
|
|
// Read metrics from stdin
|
|
|
|
|
m, err := parser.ParseLine(scanner.Text())
|
2020-06-05 07:09:22 +08:00
|
|
|
if err != nil {
|
|
|
|
|
fmt.Fprintf(s.stderr, "Failed to parse metric: %s\n", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-04-23 21:47:39 +08:00
|
|
|
mu.Lock()
|
|
|
|
|
metrics = append(metrics, m)
|
|
|
|
|
shouldFlush := len(metrics) >= s.BatchSize
|
|
|
|
|
mu.Unlock()
|
|
|
|
|
|
|
|
|
|
// If we got more enough metrics to fill the batch flush it out and
|
|
|
|
|
// reset the time-based guard.
|
|
|
|
|
if shouldFlush {
|
|
|
|
|
if timer != nil {
|
|
|
|
|
timer.Stop()
|
|
|
|
|
}
|
|
|
|
|
flush(true)
|
|
|
|
|
if s.BatchTimeout > 0 {
|
|
|
|
|
timer = time.AfterFunc(s.BatchTimeout, func() { flush(false) })
|
|
|
|
|
}
|
2020-06-05 07:09:22 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-23 21:47:39 +08:00
|
|
|
// Output all remaining metrics
|
|
|
|
|
if timer != nil {
|
|
|
|
|
timer.Stop()
|
|
|
|
|
}
|
|
|
|
|
flush(false)
|
|
|
|
|
|
2020-06-05 07:09:22 +08:00
|
|
|
return nil
|
|
|
|
|
}
|