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  }