|
| 1 | +// Copyright (C) 2022 Storj Labs, Inc. |
| 2 | +// See LICENSE for copying information. |
| 3 | + |
| 4 | +package eventstat |
| 5 | + |
| 6 | +import ( |
| 7 | + "context" |
| 8 | + "os" |
| 9 | + "strings" |
| 10 | + "time" |
| 11 | + |
| 12 | + "storj.io/common/telemetry" |
| 13 | +) |
| 14 | + |
| 15 | +// ClientOpts allows you to set Client Options. |
| 16 | +type ClientOpts struct { |
| 17 | + // Interval is how frequently stats from the provided Registry will be |
| 18 | + // sent up. Note that this interval is "jittered", so the actual interval |
| 19 | + // is taken from a normal distribution with a mean of Interval and a |
| 20 | + // variance of Interval/4. Defaults to DefaultInterval. |
| 21 | + Interval time.Duration |
| 22 | + |
| 23 | + // Application is the application name, usually prepended to metric names. |
| 24 | + // By default it will be os.Args[0]. |
| 25 | + Application string |
| 26 | + |
| 27 | + // Instance is a string that identifies this particular server. Could be a |
| 28 | + // node id, but defaults to the result of DefaultInstanceId(). |
| 29 | + Instance string |
| 30 | + |
| 31 | + // PacketSize controls how we fragment the data as it goes out in UDP |
| 32 | + // packets. Defaults to DefaultPacketSize. |
| 33 | + PacketSize int |
| 34 | +} |
| 35 | + |
| 36 | +// UDPPublisher is an eventstat telemetry client for sending UDP packets at a regular interval. |
| 37 | +type UDPPublisher struct { |
| 38 | + reporter *telemetry.Reporter |
| 39 | +} |
| 40 | + |
| 41 | +// NewUDPPublisher constructs a telemetry client that sends packets to remoteAddr |
| 42 | +// over UDP. |
| 43 | +func NewUDPPublisher(remoteAddr string, registry *Registry, opts ClientOpts) (rv *UDPPublisher, err error) { |
| 44 | + if opts.Interval == 0 { |
| 45 | + opts.Interval = telemetry.DefaultInterval |
| 46 | + } |
| 47 | + if opts.Application == "" { |
| 48 | + if len(os.Args) > 0 { |
| 49 | + opts.Application = os.Args[0] |
| 50 | + } else { |
| 51 | + // what the actual heck |
| 52 | + opts.Application = telemetry.DefaultApplication |
| 53 | + } |
| 54 | + } |
| 55 | + if opts.Instance == "" { |
| 56 | + opts.Instance = telemetry.DefaultInstanceID() |
| 57 | + } |
| 58 | + if opts.PacketSize == 0 { |
| 59 | + opts.PacketSize = telemetry.DefaultPacketSize |
| 60 | + } |
| 61 | + |
| 62 | + udpOptions := telemetry.Options{ |
| 63 | + Application: opts.Application, |
| 64 | + InstanceID: []byte(opts.Instance), |
| 65 | + Address: remoteAddr, |
| 66 | + PacketSize: opts.PacketSize, |
| 67 | + } |
| 68 | + reporter, err := telemetry.NewReporter(opts.Interval, func(ctx context.Context) error { |
| 69 | + return telemetry.Send(ctx, udpOptions, func(publishEntry func(key string, value float64)) { |
| 70 | + registry.PublishAndReset(func(name string, tags Tags, value float64) { |
| 71 | + telemetryKey := telemetryKey(name, tags) |
| 72 | + publishEntry(telemetryKey, value) |
| 73 | + }) |
| 74 | + }) |
| 75 | + }) |
| 76 | + if err != nil { |
| 77 | + return nil, err |
| 78 | + } |
| 79 | + return &UDPPublisher{ |
| 80 | + reporter: reporter, |
| 81 | + }, nil |
| 82 | +} |
| 83 | + |
| 84 | +func telemetryKey(name string, tags Tags) string { |
| 85 | + builder := strings.Builder{} |
| 86 | + writeTag(&builder, name) |
| 87 | + if len(tags) > 0 { |
| 88 | + builder.WriteString(",") |
| 89 | + builder.WriteString(tags.String()) |
| 90 | + } |
| 91 | + builder.WriteString(" value") |
| 92 | + telemetryKey := builder.String() |
| 93 | + return telemetryKey |
| 94 | +} |
| 95 | + |
| 96 | +// Run calls Report roughly every Interval. |
| 97 | +func (c *UDPPublisher) Run(ctx context.Context) { |
| 98 | + c.reporter.Run(ctx) |
| 99 | +} |
| 100 | + |
| 101 | +// publish sends out message immediately independent on Interval. |
| 102 | +func (c *UDPPublisher) publish(ctx context.Context) error { |
| 103 | + return c.reporter.Publish(ctx) |
| 104 | +} |
| 105 | + |
| 106 | +// writeTag writes a tag key, value, or field key to the builder. |
| 107 | +func writeTag(builder *strings.Builder, tag string) { |
| 108 | + if strings.IndexByte(tag, ',') == -1 && |
| 109 | + strings.IndexByte(tag, '=') == -1 && |
| 110 | + strings.IndexByte(tag, ' ') == -1 { |
| 111 | + |
| 112 | + builder.WriteString(tag) |
| 113 | + return |
| 114 | + } |
| 115 | + |
| 116 | + for i := 0; i < len(tag); i++ { |
| 117 | + if tag[i] == ',' || |
| 118 | + tag[i] == '=' || |
| 119 | + tag[i] == ' ' { |
| 120 | + builder.WriteByte('\\') |
| 121 | + } |
| 122 | + builder.WriteByte(tag[i]) |
| 123 | + } |
| 124 | +} |
0 commit comments