Class: Datadog::OpenFeature::Hooks::FlagEvalEVPHook

Inherits:
Object
  • Object
show all
Includes:
OpenFeature::SDK::Hooks::Hook
Defined in:
lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb

Overview

EVP flagevaluation hook — structural copy of FlagEvalMetricsHook for the new EVP path.

This hook does ONLY cheap capture + non-blocking enqueue on the caller's eval thread. All aggregation (canonical key, tier routing, cap enforcement) is performed in the background by the FlagEvaluation::Writer.

OTel non-regression: hooks/flag_eval_metrics_hook.rb and metrics/flag_eval_metrics.rb stay on the OTel path. This hook is registered as a provider hook so it receives the SDK-final EvaluationDetails after hook failures and type validation.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(writer) ⇒ FlagEvalEVPHook

Returns a new instance of FlagEvalEVPHook.



27
28
29
# File 'lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb', line 27

def initialize(writer)
  @writer = writer
end

Class Method Details

.available?Boolean

Returns true if the OpenFeature SDK supports hooks (>= 0.5.0)

Returns:

  • (Boolean)


23
24
25
# File 'lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb', line 23

def self.available?
  !!defined?(::OpenFeature::SDK::Hooks::Hook)
end

Instance Method Details

#finally(hook_context:, evaluation_details:, **_opts) ⇒ Object

finally covers success, error, AND default paths (not just After). Cheap extraction only — no aggregation on the caller thread.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb', line 33

def finally(hook_context:, evaluation_details:, **_opts)
  writer = @writer
  return unless writer

  # Eval-time stamped by the provider at eval-entry time; fall back to hook-fire time.
  # Metadata key: 'dd.eval.timestamp_ms' (int, ms since epoch) — stamped at eval entry.
   = evaluation_details.
  eval_time_ms = .is_a?(Hash) ? ["dd.eval.timestamp_ms"] : nil
  eval_time_ms ||= (Core::Utils::Time.now.to_f * 1000).to_i

  # observe_full_evaluation_data travels on the event metadata (stamped from
  # the UFC the evaluation ran against), never read from live config.
  observe_full_evaluation_data = .is_a?(Hash) ? [Ext::METADATA_OBSERVE_FULL_EVALUATION_DATA] : nil
  observe_full_evaluation_data = false unless observe_full_evaluation_data == true

  attrs = if observe_full_evaluation_data
    extract_attributes(hook_context.evaluation_context)
  end

  # Raw messages may contain high-cardinality data, so only error codes enter EVP.
  reason = evaluation_details.reason.to_s
  runtime_default = runtime_default_reason?(reason)
  error_code = evaluation_details.error_code
  error_code = Ext::GENERAL if reason == Ext::ERROR && error_code.to_s.empty?

  writer.enqueue(
    flag_key: hook_context.flag_key,
    variant: runtime_default ? nil : evaluation_details.variant,
    allocation_key: runtime_default ? nil : extract_allocation_key(evaluation_details),
    error_message: error_code,
    runtime_default: runtime_default,
    targeting_key: extract_targeting_key(hook_context.evaluation_context),
    eval_time_ms: eval_time_ms,
    attrs: attrs,
    observe_full_evaluation_data: observe_full_evaluation_data,
  )
end