router/pkg/metric/prom_metric_store.go (118 lines of code) (raw):
package metric
import (
"context"
"go.opentelemetry.io/otel/attribute"
otelmetric "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk/metric"
"go.uber.org/zap"
)
const (
cosmoRouterPrometheusMeterName = "cosmo.router.prometheus"
cosmoRouterPrometheusMeterVersion = "0.0.1"
)
type PromMetricStore struct {
meter otelmetric.Meter
meterProvider *metric.MeterProvider
logger *zap.Logger
measurements *Measurements
}
func NewPromMetricStore(logger *zap.Logger, meterProvider *metric.MeterProvider) (Provider, error) {
meter := meterProvider.Meter(cosmoRouterPrometheusMeterName,
otelmetric.WithInstrumentationVersion(cosmoRouterPrometheusMeterVersion),
)
m := &PromMetricStore{
meter: meter,
logger: logger,
meterProvider: meterProvider,
}
measures, err := createMeasures(meter)
if err != nil {
return nil, err
}
m.measurements = measures
return m, nil
}
func (h *PromMetricStore) MeasureInFlight(ctx context.Context, opts ...otelmetric.AddOption) func() {
if c, ok := h.measurements.upDownCounters[InFlightRequestsUpDownCounter]; ok {
c.Add(ctx, 1, opts...)
}
return func() {
if c, ok := h.measurements.upDownCounters[InFlightRequestsUpDownCounter]; ok {
c.Add(ctx, -1, opts...)
}
}
}
func (h *PromMetricStore) MeasureRequestCount(ctx context.Context, opts ...otelmetric.AddOption) {
if c, ok := h.measurements.counters[RequestCounter]; ok {
c.Add(ctx, 1, opts...)
}
}
func (h *PromMetricStore) MeasureRequestSize(ctx context.Context, contentLength int64, opts ...otelmetric.AddOption) {
if c, ok := h.measurements.counters[RequestContentLengthCounter]; ok {
c.Add(ctx, contentLength, opts...)
}
}
func (h *PromMetricStore) MeasureResponseSize(ctx context.Context, size int64, opts ...otelmetric.AddOption) {
if c, ok := h.measurements.counters[ResponseContentLengthCounter]; ok {
c.Add(ctx, size, opts...)
}
}
func (h *PromMetricStore) MeasureLatency(ctx context.Context, latency float64, opts ...otelmetric.RecordOption) {
if c, ok := h.measurements.histograms[ServerLatencyHistogram]; ok {
c.Record(ctx, latency, opts...)
}
}
func (h *PromMetricStore) MeasureRequestError(ctx context.Context, opts ...otelmetric.AddOption) {
if c, ok := h.measurements.counters[RequestError]; ok {
c.Add(ctx, 1, opts...)
}
}
func (h *PromMetricStore) MeasureOperationPlanningTime(ctx context.Context, planningTime float64, opts ...otelmetric.RecordOption) {
if c, ok := h.measurements.histograms[OperationPlanningTime]; ok {
c.Record(ctx, planningTime, opts...)
}
}
func (h *PromMetricStore) Flush(ctx context.Context) error {
return h.meterProvider.ForceFlush(ctx)
}
func (h *PromMetricStore) Shutdown(ctx context.Context) error {
return h.meterProvider.Shutdown(ctx)
}
// explodeAddInstrument explodes the metric into multiple metrics with different label values in Prometheus.
func explodeAddInstrument(ctx context.Context, sliceAttrs []attribute.KeyValue, collect func(ctx context.Context, opts ...otelmetric.AddOption)) {
for _, attr := range sliceAttrs {
s := attr.Value.AsStringSlice()
// If the slice is empty, we should at least emit the metric without the attribute.
// to not ignore the metric emission.
if len(s) == 0 {
collect(ctx)
continue
}
for _, v := range s {
kv := attribute.KeyValue{
Key: attr.Key,
Value: attribute.StringValue(v),
}
o := []otelmetric.AddOption{
otelmetric.WithAttributeSet(attribute.NewSet(kv)),
}
collect(ctx, o...)
}
}
}
// explodeRecordInstrument explodes the metric into multiple metrics with different label values in Prometheus.
func explodeRecordInstrument(ctx context.Context, sliceAttrs []attribute.KeyValue, collect func(ctx context.Context, opts ...otelmetric.RecordOption)) {
for _, attr := range sliceAttrs {
s := attr.Value.AsStringSlice()
// If the slice is empty, we should at least emit the metric without the attribute.
// to not ignore the metric emission.
if len(s) == 0 {
collect(ctx)
continue
}
for _, v := range s {
kv := attribute.KeyValue{
Key: attr.Key,
Value: attribute.StringValue(v),
}
o := []otelmetric.RecordOption{
otelmetric.WithAttributeSet(attribute.NewSet(kv)),
}
collect(ctx, o...)
}
}
}