Class: ExisRay::Tracer

Inherits:
ActiveSupport::CurrentAttributes
  • Object
show all
Defined in:
lib/exis_ray/tracer.rb

Overview

Gestiona el contexto de trazabilidad distribuida (Distributed Tracing). Utiliza ‘ActiveSupport::CurrentAttributes` para mantener el estado de la petición actual de forma segura entre hilos (thread-safe).

Esta clase parsea headers de AWS X-Ray y genera nuevos headers para la propagación.

Class Method Summary collapse

Class Method Details

.correlation_idString

Genera un ID de correlación compuesto, útil para logs y auditoría.

Examples:

ExisRay::Tracer.correlation_id #=> "Wispro-HTTP;1-5759...-..."

Returns:

  • (String)

    Cadena compuesta “ServiceName;RootID”.



32
33
34
# File 'lib/exis_ray/tracer.rb', line 32

def self.correlation_id
  "#{service_name};#{root_id}"
end

.current_duration_msInteger

Calcula el tiempo transcurrido en milisegundos desde el inicio de la request.

Returns:

  • (Integer)

    Duración en ms.



67
68
69
# File 'lib/exis_ray/tracer.rb', line 67

def self.current_duration_ms
  (current_duration_s * 1000).round
end

.current_duration_sFloat

Calcula el tiempo transcurrido en segundos desde el inicio de la request. Cumple con el estándar Wispro-Observability-Spec (v1).

Returns:

  • (Float)

    Duración en segundos.



89
90
91
92
93
# File 'lib/exis_ray/tracer.rb', line 89

def self.current_duration_s
  return 0.0 unless created_at

  (Process.clock_gettime(Process::CLOCK_MONOTONIC) - created_at).round(4)
end

.format_duration(seconds) ⇒ String

Formatea una duración en segundos a un string legible por humanos.

  • Menos de 1s → “7.2ms”

  • Entre 1-60s → “1.25s”

  • 60s o más → “2 minutes 5 seconds” (via ActiveSupport::Duration)

Parameters:

  • seconds (Float)

    Duración en segundos.

Returns:

  • (String)


102
103
104
105
106
107
108
109
110
# File 'lib/exis_ray/tracer.rb', line 102

def self.format_duration(seconds)
  if seconds < 1.0
    "#{(seconds * 1000).round(1)}ms"
  elsif seconds < 60.0
    "#{seconds.round(3)}s"
  else
    ActiveSupport::Duration.build(seconds.round).inspect
  end
end

.generate_trace_headerString

Construye el header de trazabilidad para enviar al siguiente servicio.

Returns:

  • (String)

    Header formateado: “Root=…;Self=…;CalledFrom=…;TotalTimeSoFar=…ms”



115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/exis_ray/tracer.rb', line 115

def self.generate_trace_header
  safe_root = if root_id.present?
                root_id.start_with?("Root=") ? root_id : "Root=#{root_id}"
              else
                generate_new_root
              end

  total_acc_time = (total_time_so_far || 0) + current_duration_ms

  # Nuevo ID para el span actual
  my_new_id = "1-#{Time.now.to_i.to_s(16)}-#{clean_request_id}"

  "#{safe_root};Self=#{my_new_id};CalledFrom=#{service_name};TotalTimeSoFar=#{total_acc_time}ms"
end

.hydrate(trace_id:, source:) ⇒ void

This method returns an undefined value.

Hidrata el Tracer con un trace header entrante y registra el inicio del contexto. Centraliza la inicialización de contexto de trazabilidad para todos los entrypoints (HTTP, Sidekiq, BugBunny, etc.), eliminando duplicación entre middlewares.

Parameters:

  • trace_id (String)

    El header de traza entrante (formato AWS X-Ray).

  • source (String)

    El entrypoint de ejecución (‘http’, ‘sidekiq’, ‘task’, ‘system’).



78
79
80
81
82
83
# File 'lib/exis_ray/tracer.rb', line 78

def self.hydrate(trace_id:, source:)
  self.created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  self.source     = source
  self.trace_id   = trace_id
  parse_trace_id
end

.parse_trace_idvoid

This method returns an undefined value.

Parsea el string de trazabilidad recibido y popula los atributos individuales. Maneja el formato estándar de AWS: “Root=…;Self=…;CalledFrom=…;TotalTimeSoFar=…”



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/exis_ray/tracer.rb', line 40

def self.parse_trace_id
  return unless trace_id.present?

  self.trace_id = generate_new_root(trace_id) if trace_id.exclude?("Root")

  data = {}
  trace_id.split(";").each do |part|
    key_value = part.split("=", 2)
    next unless key_value.size == 2

    data[key_value[0]] = key_value[1]
  end

  self.root_id     = data["Root"]
  self.self_id     = data["Self"]
  self.called_from = data["CalledFrom"]

  self.total_time_so_far = if data["TotalTimeSoFar"]
                             data["TotalTimeSoFar"].gsub(/ms$/i, "").to_i
                           else
                             0
                           end
end

.service_nameString

Devuelve el nombre de la aplicación en snake_case (ej: “cold_storage_service”). Se utiliza como identificador global del servicio en logs y trazabilidad.

Returns:

  • (String)


22
23
24
# File 'lib/exis_ray/tracer.rb', line 22

def self.service_name
  @service_name ||= (defined?(Rails) ? Rails.application.class.module_parent_name.underscore : "app")
end