diff --git a/internal/server/evaluation/evaluation.go b/internal/server/evaluation/evaluation.go index 418a5843d4..438a3e12f0 100644 --- a/internal/server/evaluation/evaluation.go +++ b/internal/server/evaluation/evaluation.go @@ -37,15 +37,10 @@ func (s *Server) Variant(ctx context.Context, v *rpcevaluation.EvaluationRequest fliptotel.AttributeFlag.String(v.FlagKey), fliptotel.AttributeEntityID.String(v.EntityId), fliptotel.AttributeRequestID.String(v.RequestId), - } - - if ver != nil { - spanAttrs = append(spanAttrs, - fliptotel.AttributeMatch.Bool(ver.Match), - fliptotel.AttributeValue.String(ver.VariantKey), - fliptotel.AttributeReason.String(ver.Reason.String()), - fliptotel.AttributeSegment.String(ver.SegmentKey), - ) + fliptotel.AttributeMatch.Bool(ver.Match), + fliptotel.AttributeValue.String(ver.VariantKey), + fliptotel.AttributeReason.String(ver.Reason.String()), + fliptotel.AttributeSegment.String(ver.SegmentKey), } // add otel attributes to span @@ -101,7 +96,25 @@ func (s *Server) Boolean(ctx context.Context, r *rpcevaluation.EvaluationRequest return nil, errs.ErrInvalidf("flag type %s invalid", flag.Type) } - return s.boolean(ctx, flag, r) + ber, err := s.boolean(ctx, flag, r) + if err != nil { + return nil, err + } + + spanAttrs := []attribute.KeyValue{ + fliptotel.AttributeNamespace.String(r.NamespaceKey), + fliptotel.AttributeFlag.String(r.FlagKey), + fliptotel.AttributeEntityID.String(r.EntityId), + fliptotel.AttributeRequestID.String(r.RequestId), + fliptotel.AttributeValue.Bool(ber.Value), + fliptotel.AttributeReason.String(ber.Reason.String()), + } + + // add otel attributes to span + span := trace.SpanFromContext(ctx) + span.SetAttributes(spanAttrs...) + + return ber, nil } func (s *Server) boolean(ctx context.Context, flag *flipt.Flag, r *rpcevaluation.EvaluationRequest) (*rpcevaluation.BooleanEvaluationResponse, error) { @@ -110,7 +123,7 @@ func (s *Server) boolean(ctx context.Context, flag *flipt.Flag, r *rpcevaluation return nil, err } - resp, err := s.evaluator.booleanMatch(r, flag.Enabled, rollouts) + resp, err := s.evaluator.booleanMatch(ctx, r, flag.Enabled, rollouts) if err != nil { return nil, err } diff --git a/internal/server/evaluation/evaluator.go b/internal/server/evaluation/evaluator.go index 4874810400..381440b2c4 100644 --- a/internal/server/evaluation/evaluator.go +++ b/internal/server/evaluation/evaluator.go @@ -61,6 +61,7 @@ func (e *Evaluator) Evaluate(ctx context.Context, flag *flipt.Flag, r *flipt.Eva metrics.AttributeSegment.String(resp.SegmentKey), metrics.AttributeReason.String(resp.Reason.String()), metrics.AttributeValue.String(resp.Value), + metrics.AttributeType.String("variant"), ), ), ) @@ -192,8 +193,45 @@ func (e *Evaluator) Evaluate(ctx context.Context, flag *flipt.Flag, r *flipt.Eva return resp, nil } -func (e *Evaluator) booleanMatch(r *rpcevaluation.EvaluationRequest, flagValue bool, rollouts []*storage.EvaluationRollout) (*rpcevaluation.BooleanEvaluationResponse, error) { - resp := &rpcevaluation.BooleanEvaluationResponse{} +func (e *Evaluator) booleanMatch(ctx context.Context, r *rpcevaluation.EvaluationRequest, flagValue bool, rollouts []*storage.EvaluationRollout) (resp *rpcevaluation.BooleanEvaluationResponse, err error) { + res := &rpcevaluation.BooleanEvaluationResponse{} + + var ( + startTime = time.Now().UTC() + namespaceAttr = metrics.AttributeNamespace.String(r.NamespaceKey) + flagAttr = metrics.AttributeFlag.String(r.FlagKey) + ) + + metrics.EvaluationsTotal.Add(ctx, 1, metric.WithAttributeSet(attribute.NewSet(namespaceAttr, flagAttr))) + + defer func() { + if err == nil { + metrics.EvaluationResultsTotal.Add(ctx, 1, + metric.WithAttributeSet( + attribute.NewSet( + namespaceAttr, + flagAttr, + metrics.AttributeValue.Bool(resp.Value), + metrics.AttributeReason.String(resp.Reason.String()), + metrics.AttributeType.String("boolean"), + ), + ), + ) + } else { + metrics.EvaluationErrorsTotal.Add(ctx, 1, metric.WithAttributeSet(attribute.NewSet(namespaceAttr, flagAttr))) + } + + metrics.EvaluationLatency.Record( + ctx, + float64(time.Since(startTime).Nanoseconds())/1e6, + metric.WithAttributeSet( + attribute.NewSet( + namespaceAttr, + flagAttr, + ), + ), + ) + }() var lastRank int32 @@ -212,11 +250,11 @@ func (e *Evaluator) booleanMatch(r *rpcevaluation.EvaluationRequest, flagValue b // if this case does not hold, fall through to the next rollout. if normalizedValue < rollout.Threshold.Percentage { - resp.Value = rollout.Threshold.Value - resp.Reason = rpcevaluation.EvaluationReason_MATCH_EVALUATION_REASON + res.Value = rollout.Threshold.Value + res.Reason = rpcevaluation.EvaluationReason_MATCH_EVALUATION_REASON e.logger.Debug("threshold based matched", zap.Int("rank", int(rollout.Rank)), zap.String("rollout_type", "threshold")) - return resp, nil + return res, nil } } else if rollout.Segment != nil { matched, err := e.matchConstraints(r.Context, rollout.Segment.Constraints, rollout.Segment.MatchType) @@ -229,21 +267,21 @@ func (e *Evaluator) booleanMatch(r *rpcevaluation.EvaluationRequest, flagValue b continue } - resp.Value = rollout.Segment.Value - resp.Reason = rpcevaluation.EvaluationReason_MATCH_EVALUATION_REASON + res.Value = rollout.Segment.Value + res.Reason = rpcevaluation.EvaluationReason_MATCH_EVALUATION_REASON e.logger.Debug("segment based matched", zap.Int("rank", int(rollout.Rank)), zap.String("segment", rollout.Segment.Key)) - return resp, nil + return res, nil } } // If we have exhausted all rollouts and we still don't have a match, return the default value. - resp.Reason = rpcevaluation.EvaluationReason_DEFAULT_EVALUATION_REASON - resp.Value = flagValue + res.Reason = rpcevaluation.EvaluationReason_DEFAULT_EVALUATION_REASON + res.Value = flagValue e.logger.Debug("default rollout matched", zap.Bool("value", flagValue)) - return resp, nil + return res, nil } // matchConstraints is a utility function that will return if all or any constraints have matched for a segment depending diff --git a/internal/server/metrics/metrics.go b/internal/server/metrics/metrics.go index e95d20c36c..e13658a2dc 100644 --- a/internal/server/metrics/metrics.go +++ b/internal/server/metrics/metrics.go @@ -61,4 +61,5 @@ var ( AttributeReason = attribute.Key("reason") AttributeValue = attribute.Key("value") AttributeNamespace = attribute.Key("namespace") + AttributeType = attribute.Key("type") )