github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/telemetry/telemeter.go (about) 1 package telemetry 2 3 import ( 4 "context" 5 "strconv" 6 "sync" 7 "time" 8 9 sm "github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring" 10 11 prom "github.com/prometheus/client_golang/prometheus" 12 "github.com/rs/zerolog" 13 ) 14 15 const ( 16 metricNamespace = "sm_agent" 17 metricSubsystem = "telemetry" 18 regionIDLabelName = "region_id" 19 agentInstanceLabelName = "agent_instance" 20 ) 21 22 // Telemeter maintains the telemetry data for all the tenants running checks 23 // in the agent instance, organized by region. 24 type Telemeter struct { 25 ctx context.Context 26 client sm.TelemetryClient 27 logger zerolog.Logger 28 29 instance string 30 pushTimeSpan time.Duration 31 32 pushers map[int32]*RegionPusher // Indexed by region ID 33 pushersMu sync.RWMutex 34 35 metrics metrics 36 } 37 38 type metrics struct { 39 pushRequestsActive *prom.GaugeVec 40 pushRequestsDuration *prom.HistogramVec 41 pushRequestsTotal *prom.CounterVec 42 pushRequestsError *prom.CounterVec 43 44 addExecutionDuration *prom.HistogramVec 45 } 46 47 // Execution represents the telemetry for a check execution. 48 type Execution struct { 49 LocalTenantID int64 50 RegionID int32 51 CheckClass sm.CheckClass 52 Duration time.Duration 53 CostAttributionLabels []sm.CostAttributionLabel 54 } 55 56 // NewTelemeter creates a new Telemeter component. 57 func NewTelemeter( 58 ctx context.Context, instance string, pushTimeSpan time.Duration, client sm.TelemetryClient, 59 logger zerolog.Logger, registerer prom.Registerer, 60 ) *Telemeter { 61 t := &Telemeter{ 62 ctx: ctx, 63 client: client, 64 logger: logger, 65 instance: instance, 66 pushTimeSpan: pushTimeSpan, 67 pushers: make(map[int32]*RegionPusher), 68 } 69 70 t.registerMetrics(registerer) 71 72 return t 73 } 74 75 // AddExecution adds a new execution to the agent's telemetry. 76 func (t *Telemeter) AddExecution(e Execution) { 77 t.pushersMu.RLock() 78 79 if p, ok := t.pushers[e.RegionID]; ok { 80 p.AddExecution(e) 81 t.pushersMu.RUnlock() 82 83 return 84 } 85 86 t.pushersMu.RUnlock() 87 88 // There is a minimal time window here on which a concurrent request could 89 // acquire the lock for the same region, therefore, acquiere the W lock as 90 // soon as the R lock has been released to avoid overlapping work, and 91 // verify that once acquired there is still no pusher for the region. 92 // This section will only be executed "N regions" times. 93 t.pushersMu.Lock() 94 defer t.pushersMu.Unlock() 95 96 if p, ok := t.pushers[e.RegionID]; ok { 97 p.AddExecution(e) 98 return 99 } 100 101 // If we do not have a pusher for this region, create it 102 l := t.logger.With(). 103 Str("component", "region-pusher"). 104 Str("agentInstance", t.instance). 105 Int32("regionId", e.RegionID). 106 Logger() 107 labels := prom.Labels{ 108 regionIDLabelName: strconv.FormatInt(int64(e.RegionID), 10), 109 } 110 m := RegionMetrics{ 111 t.metrics.pushRequestsActive.With(labels), 112 t.metrics.pushRequestsDuration.With(labels), 113 t.metrics.pushRequestsTotal.With(labels), 114 t.metrics.pushRequestsError.With(labels), 115 t.metrics.addExecutionDuration.With(labels), 116 } 117 p := NewRegionPusher( 118 t.ctx, t.pushTimeSpan, t.client, 119 l, t.instance, e.RegionID, m, 120 ) 121 p.AddExecution(e) 122 123 t.pushers[e.RegionID] = p 124 } 125 126 func (t *Telemeter) registerMetrics(registerer prom.Registerer) { 127 t.metrics.pushRequestsActive = prom.NewGaugeVec(prom.GaugeOpts{ 128 Namespace: metricNamespace, 129 Subsystem: metricSubsystem, 130 Name: "push_requests_active", 131 Help: "Active push telemetry requests", 132 ConstLabels: prom.Labels{agentInstanceLabelName: t.instance}, 133 }, []string{regionIDLabelName}) 134 t.metrics.pushRequestsDuration = prom.NewHistogramVec(prom.HistogramOpts{ 135 Namespace: metricNamespace, 136 Subsystem: metricSubsystem, 137 Name: "push_requests_duration_seconds", 138 Help: "Duration of push telemetry requests", 139 Buckets: prom.ExponentialBucketsRange(0.01, 2.0, 10), 140 ConstLabels: prom.Labels{agentInstanceLabelName: t.instance}, 141 }, []string{regionIDLabelName}) 142 t.metrics.pushRequestsTotal = prom.NewCounterVec(prom.CounterOpts{ 143 Namespace: metricNamespace, 144 Subsystem: metricSubsystem, 145 Name: "push_requests_total", 146 Help: "Total count of push telemetry requests", 147 ConstLabels: prom.Labels{agentInstanceLabelName: t.instance}, 148 }, []string{regionIDLabelName}) 149 t.metrics.pushRequestsError = prom.NewCounterVec(prom.CounterOpts{ 150 Namespace: metricNamespace, 151 Subsystem: metricSubsystem, 152 Name: "push_requests_errors_total", 153 Help: "Total count of errored push telemetry requests", 154 ConstLabels: prom.Labels{agentInstanceLabelName: t.instance}, 155 }, []string{regionIDLabelName}) 156 157 t.metrics.addExecutionDuration = prom.NewHistogramVec(prom.HistogramOpts{ 158 Namespace: metricNamespace, 159 Subsystem: metricSubsystem, 160 Name: "add_execution_duration_seconds", 161 Help: "Duration of add telemetry executions", 162 Buckets: []float64{.00001, .00002, .00005, .0001, .0002, .0005, .001, .002, .005, .01, .02, .05, .1, .2}, 163 NativeHistogramBucketFactor: 1.1, 164 NativeHistogramMaxBucketNumber: 100, 165 NativeHistogramMinResetDuration: time.Hour, 166 ConstLabels: prom.Labels{agentInstanceLabelName: t.instance}, 167 }, []string{regionIDLabelName}) 168 169 registerer.MustRegister(t.metrics.pushRequestsActive) 170 registerer.MustRegister(t.metrics.pushRequestsDuration) 171 registerer.MustRegister(t.metrics.pushRequestsTotal) 172 registerer.MustRegister(t.metrics.pushRequestsError) 173 registerer.MustRegister(t.metrics.addExecutionDuration) 174 }