2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-05-07 19:03:40 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package telemetry
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/zeebo/admission/admmonkit"
|
|
|
|
"github.com/zeebo/admission/admproto"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2018-05-08 23:02:01 +01:00
|
|
|
// DefaultInterval is the default amount of time between metric payload sends
|
2018-06-05 02:50:41 +01:00
|
|
|
DefaultInterval = time.Minute
|
2018-05-07 19:03:40 +01:00
|
|
|
|
2018-05-08 23:02:01 +01:00
|
|
|
// DefaultPacketSize sets the target packet size. MTUs are often 1500,
|
|
|
|
// though a good argument could be made for 512
|
2018-05-07 19:03:40 +01:00
|
|
|
DefaultPacketSize = 1000
|
2018-07-19 20:41:29 +01:00
|
|
|
|
|
|
|
// DefaultApplication is the default values for application name. Should be used
|
|
|
|
// when value in ClientOpts.Application is not set and len(os.Args) == 0
|
|
|
|
DefaultApplication = "unknown"
|
2018-05-07 19:03:40 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// ClientOpts allows you to set Client Options
|
|
|
|
type ClientOpts struct {
|
|
|
|
// Interval is how frequently stats from the provided Registry will be
|
|
|
|
// sent up. Note that this interval is "jittered", so the actual interval
|
|
|
|
// is taken from a normal distribution with a mean of Interval and a
|
|
|
|
// variance of Interval/4. Defaults to DefaultInterval
|
|
|
|
Interval time.Duration
|
|
|
|
|
|
|
|
// Application is the application name, usually prepended to metric names.
|
|
|
|
// By default it will be os.Args[0]
|
|
|
|
Application string
|
|
|
|
|
|
|
|
// Instance is a string that identifies this particular server. Could be a
|
2018-05-30 15:03:44 +01:00
|
|
|
// node id, but defaults to the result of DefaultInstanceId()
|
2018-05-07 19:03:40 +01:00
|
|
|
Instance string
|
|
|
|
|
|
|
|
// PacketSize controls how we fragment the data as it goes out in UDP
|
|
|
|
// packets. Defaults to DefaultPacketSize
|
|
|
|
PacketSize int
|
|
|
|
|
|
|
|
// Registry is where to get stats from. Defaults to monkit.Default
|
|
|
|
Registry *monkit.Registry
|
|
|
|
|
|
|
|
// FloatEncoding is how floats should be encoded on the wire.
|
|
|
|
// Default is float16.
|
|
|
|
FloatEncoding admproto.FloatEncoding
|
|
|
|
}
|
|
|
|
|
|
|
|
// Client is a telemetry client for sending UDP packets at a regular interval
|
|
|
|
// from a monkit.Registry
|
|
|
|
type Client struct {
|
2019-07-31 15:38:44 +01:00
|
|
|
log *zap.Logger
|
2018-05-07 19:03:40 +01:00
|
|
|
interval time.Duration
|
|
|
|
opts admmonkit.Options
|
2018-09-26 15:00:54 +01:00
|
|
|
send func(context.Context, admmonkit.Options) error
|
2018-05-07 19:03:40 +01:00
|
|
|
}
|
|
|
|
|
2018-05-08 23:02:01 +01:00
|
|
|
// NewClient constructs a telemetry client that sends packets to remoteAddr
|
2018-05-07 19:03:40 +01:00
|
|
|
// over UDP.
|
2019-07-31 15:38:44 +01:00
|
|
|
func NewClient(log *zap.Logger, remoteAddr string, opts ClientOpts) (rv *Client, err error) {
|
2018-05-07 19:03:40 +01:00
|
|
|
if opts.Interval == 0 {
|
|
|
|
opts.Interval = DefaultInterval
|
|
|
|
}
|
|
|
|
if opts.Application == "" {
|
|
|
|
if len(os.Args) > 0 {
|
|
|
|
opts.Application = os.Args[0]
|
|
|
|
} else {
|
|
|
|
// what the actual heck
|
2018-07-19 20:41:29 +01:00
|
|
|
opts.Application = DefaultApplication
|
2018-05-07 19:03:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if opts.Instance == "" {
|
2018-05-30 15:03:44 +01:00
|
|
|
opts.Instance = DefaultInstanceID()
|
2018-05-07 19:03:40 +01:00
|
|
|
}
|
|
|
|
if opts.Registry == nil {
|
|
|
|
opts.Registry = monkit.Default
|
|
|
|
}
|
|
|
|
if opts.PacketSize == 0 {
|
|
|
|
opts.PacketSize = DefaultPacketSize
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Client{
|
2019-07-31 15:38:44 +01:00
|
|
|
log: log,
|
2018-05-07 19:03:40 +01:00
|
|
|
interval: opts.Interval,
|
2018-09-26 15:00:54 +01:00
|
|
|
send: admmonkit.Send,
|
2018-05-07 19:03:40 +01:00
|
|
|
opts: admmonkit.Options{
|
|
|
|
Application: opts.Application,
|
|
|
|
InstanceId: []byte(opts.Instance),
|
2018-05-08 23:02:01 +01:00
|
|
|
Address: remoteAddr,
|
2018-05-07 19:03:40 +01:00
|
|
|
PacketSize: opts.PacketSize,
|
|
|
|
Registry: opts.Registry,
|
|
|
|
ProtoOpts: admproto.Options{FloatEncoding: opts.FloatEncoding},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run calls Report roughly every Interval
|
|
|
|
func (c *Client) Run(ctx context.Context) {
|
2019-09-10 08:32:53 +01:00
|
|
|
c.log.Sugar().Debugf("Initialized batcher with id = %q", c.opts.InstanceId)
|
2018-05-07 19:03:40 +01:00
|
|
|
for {
|
|
|
|
time.Sleep(jitter(c.interval))
|
|
|
|
if ctx.Err() != nil {
|
|
|
|
return
|
|
|
|
}
|
2018-09-26 15:00:54 +01:00
|
|
|
|
2018-05-07 19:03:40 +01:00
|
|
|
err := c.Report(ctx)
|
|
|
|
if err != nil {
|
2019-09-10 08:32:53 +01:00
|
|
|
c.log.Sugar().Errorf("failed sending report: %v", err)
|
2018-05-07 19:03:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report bundles up all the current stats and writes them out as UDP packets
|
2019-06-04 12:36:27 +01:00
|
|
|
func (c *Client) Report(ctx context.Context) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-09-26 15:00:54 +01:00
|
|
|
return c.send(ctx, c.opts)
|
2018-05-07 19:03:40 +01:00
|
|
|
}
|