telegraf/plugins/inputs/zipkin/zipkin.go

161 lines
4.3 KiB
Go
Raw Normal View History

//go:generate ../../../tools/readme_config_includer/generator
2017-08-03 08:58:26 +08:00
package zipkin
import (
"context"
_ "embed"
2017-08-03 08:58:26 +08:00
"fmt"
"net"
"net/http"
"strconv"
"sync"
"time"
2017-08-03 08:58:26 +08:00
"github.com/gorilla/mux"
2017-08-03 08:58:26 +08:00
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
2017-08-03 08:58:26 +08:00
"github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/plugins/inputs/zipkin/trace"
2017-08-03 08:58:26 +08:00
)
//go:embed sample.conf
var sampleConfig string
2017-08-03 08:58:26 +08:00
const (
// defaultPort is the default port zipkin listens on, which zipkin implementations expect.
defaultPort = 9411
2017-08-03 08:58:26 +08:00
// defaultRoute is the default route zipkin uses, and zipkin implementations expect.
defaultRoute = "/api/v1/spans"
2017-08-03 08:58:26 +08:00
// defaultShutdownTimeout is the max amount of time telegraf will wait for the plugin to shut down
defaultShutdownTimeout = 5 * time.Second
defaultReadTimeout = 10 * time.Second
defaultWriteTimeout = 10 * time.Second
2017-08-03 08:58:26 +08:00
)
var (
// defaultNetwork is the network to listen on; use only in tests.
defaultNetwork = "tcp"
)
2017-08-03 08:58:26 +08:00
// Recorder represents a type which can record zipkin trace data as well as
// any accompanying errors, and process that data.
type Recorder interface {
Record(trace.Trace) error
2017-08-03 08:58:26 +08:00
Error(error)
}
// Handler represents a type which can register itself with a router for
// http routing, and a Recorder for trace data collection.
type Handler interface {
Register(router *mux.Router, recorder Recorder) error
}
// Zipkin is a telegraf configuration structure for the zipkin input plugin,
// but it also contains fields for the management of a separate, concurrent
// zipkin http server
type Zipkin struct {
Port int `toml:"port"`
Path string `toml:"path"`
ReadTimeout config.Duration `toml:"read_timeout"`
WriteTimeout config.Duration `toml:"write_timeout"`
2017-08-03 08:58:26 +08:00
Log telegraf.Logger
2017-08-03 08:58:26 +08:00
address string
handler Handler
server *http.Server
waitGroup *sync.WaitGroup
}
func (*Zipkin) SampleConfig() string {
return sampleConfig
}
2017-08-03 08:58:26 +08:00
// Gather is empty for the zipkin plugin; all gathering is done through
// the separate goroutine launched in (*Zipkin).Start()
func (z *Zipkin) Gather(_ telegraf.Accumulator) error { return nil }
2017-08-03 08:58:26 +08:00
// Start launches a separate goroutine for collecting zipkin client http requests,
// passing in a telegraf.Accumulator such that data can be collected.
func (z *Zipkin) Start(acc telegraf.Accumulator) error {
if z.ReadTimeout < config.Duration(time.Second) {
z.ReadTimeout = config.Duration(defaultReadTimeout)
}
if z.WriteTimeout < config.Duration(time.Second) {
z.WriteTimeout = config.Duration(defaultWriteTimeout)
}
2017-08-03 08:58:26 +08:00
z.handler = NewSpanHandler(z.Path)
var wg sync.WaitGroup
z.waitGroup = &wg
router := mux.NewRouter()
converter := NewLineProtocolConverter(acc)
if err := z.handler.Register(router, converter); err != nil {
return err
}
2017-08-03 08:58:26 +08:00
z.server = &http.Server{
Handler: router,
ReadTimeout: time.Duration(z.ReadTimeout),
WriteTimeout: time.Duration(z.WriteTimeout),
2017-08-03 08:58:26 +08:00
}
addr := ":" + strconv.Itoa(z.Port)
ln, err := net.Listen(defaultNetwork, addr)
2017-08-03 08:58:26 +08:00
if err != nil {
return err
}
z.address = ln.Addr().String()
z.Log.Infof("Started the zipkin listener on %s", z.address)
2017-08-03 08:58:26 +08:00
wg.Add(1)
2017-08-03 08:58:26 +08:00
go func() {
defer wg.Done()
z.Listen(ln, acc)
}()
return nil
}
// Stop shuts the internal http server down with via context.Context
func (z *Zipkin) Stop() {
ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout)
2017-08-03 08:58:26 +08:00
defer z.waitGroup.Wait()
defer cancel()
z.server.Shutdown(ctx) //nolint:errcheck // Ignore the returned error as we cannot do anything about it anyway
2017-08-03 08:58:26 +08:00
}
// Listen creates a http server on the zipkin instance it is called with, and
2017-08-03 08:58:26 +08:00
// serves http until it is stopped by Zipkin's (*Zipkin).Stop() method.
func (z *Zipkin) Listen(ln net.Listener, acc telegraf.Accumulator) {
if err := z.server.Serve(ln); err != nil {
// Because of the clean shutdown in `(*Zipkin).Stop()`
// We're expecting a server closed error at some point
// So we don't want to display it as an error.
// This interferes with telegraf's internal data collection,
// by making it appear as if a serious error occurred.
if err != http.ErrServerClosed {
acc.AddError(fmt.Errorf("error listening: %w", err))
2017-08-03 08:58:26 +08:00
}
}
}
func init() {
inputs.Add("zipkin", func() telegraf.Input {
return &Zipkin{
Path: defaultRoute,
Port: defaultPort,
2017-08-03 08:58:26 +08:00
}
})
}