exis_ray 0.5.5 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a79e0a93c6efad500f943dad46daa5d429f133d8144db2fd1ce1e32e2b07ac68
4
- data.tar.gz: 243915edbd6405a89656457b1371b34971efa5f377def03093d4a024ca73b661
3
+ metadata.gz: d66d71924fff9cf19c12e42d411963948739da61e6b223db1444c16633a3158e
4
+ data.tar.gz: 26c599899ecf1f87388c73e1b4a183370e0902893c09e40c24c7796c973c0d96
5
5
  SHA512:
6
- metadata.gz: 6877511e716b0fb3ec6fe55a4542b7129163da596344ec1b98f8042d9bdedd9b514c2fe80ec78af474a4d18d5fd9b70bacd84669d3c467ecd2c69904555a6d94
7
- data.tar.gz: cc0356850bfd66654bd241e3aa2654c5114ea2e3a6fbac91eab248e5b4cb5f10affd2ccfdf62065b5f70cbe6f6d362018572299cb307d365db20cd7d2e7c03b7
6
+ metadata.gz: 60c34361234b638c3758b3b51d38933efcbf5db19fb8e1a9365f53f1e21bcfc42e680ceb37755c792663e7146826af2453d862b7ae526481870f65518a84b311
7
+ data.tar.gz: e3cb2ffc12e45d5bdd6f3c1d1a864bc9884b7709c531fa2780923834f0ca4d4766c75148c6afd15aac4d6a6dbf716a151042620474127b544b779fa86ec9c62b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.5.6] - 2026-03-26
2
+
3
+ ### Added
4
+ - **BugBunny Integration:** Added `ExisRay::BugBunny::PublisherTracing` middleware for `BugBunny::Client` and `BugBunny::Resource`. Injects the active trace context into the `x-trace-id` AMQP header on every published message.
5
+ - **BugBunny Integration:** Added `ExisRay::BugBunny::ConsumerTracing` concern for `BugBunny::Controller`. Restores the ExisRay trace context from `x-trace-id` header via `around_action`, enabling end-to-end distributed tracing across RabbitMQ message boundaries.
6
+
1
7
  ## [0.5.5] - 2026-03-24
2
8
 
3
9
  ### Added
data/README.md CHANGED
@@ -14,6 +14,7 @@ It acts as the backbone of your architecture, ensuring that every request, backg
14
14
  * **Sidekiq Integration:** Automatic context propagation (User/ISP/Trace) and log formatting between the Enqueuer and the Worker.
15
15
  * **Task Monitor:** A specialized monitor for Rake/Cron tasks to initialize traces and format logs where no HTTP request exists.
16
16
  * **HTTP Clients:** Automatically patches `ActiveResource` and provides middleware for `Faraday`.
17
+ * **BugBunny Integration:** Propagates the trace context across RabbitMQ message boundaries via a publisher middleware and a consumer concern.
17
18
 
18
19
  ---
19
20
 
@@ -200,6 +201,40 @@ conn = Faraday.new(url: "[https://api.internal](https://api.internal)") do |f|
200
201
  end
201
202
  ```
202
203
 
204
+ ### E. BugBunny (RabbitMQ)
205
+
206
+ If your app publishes messages via [BugBunny](https://github.com/gedera/bug_bunny), ExisRay can propagate the active trace context through the `x-trace-id` AMQP header, and restore it on the consumer side so every log line during message processing shares the same `root_id` as the original HTTP request.
207
+
208
+ #### Publisher — inject the trace header
209
+
210
+ Add `ExisRay::BugBunny::PublisherTracing` to your client middleware stack. Works with both `BugBunny::Client` and `BugBunny::Resource`.
211
+
212
+ ```ruby
213
+ client = BugBunny::Client.new(pool: connection_pool) do |stack|
214
+ stack.use ExisRay::BugBunny::PublisherTracing
215
+ stack.use BugBunny::Middleware::JsonResponse
216
+ end
217
+ ```
218
+
219
+ If no trace context is active (e.g. a standalone script), the middleware does nothing.
220
+
221
+ #### Consumer — restore the trace context
222
+
223
+ Include `ExisRay::BugBunny::ConsumerTracing` in your base controller. It registers an `around_action` that reads `x-trace-id` from the message headers and hydrates `ExisRay::Tracer` before your action runs. The context is always reset in `ensure`, preventing leaks between messages.
224
+
225
+ ```ruby
226
+ class ApplicationController < BugBunny::Controller
227
+ include ExisRay::BugBunny::ConsumerTracing
228
+ end
229
+ ```
230
+
231
+ **How it works end-to-end:**
232
+ 1. **Publish:** The publisher middleware injects `x-trace-id` into the AMQP headers of every outgoing message.
233
+ 2. **Consume:** The consumer concern reads that header, restores the trace context, and sets `source=system`.
234
+ 3. **Logs:** Every log line emitted during the controller action carries the original `root_id` and `correlation_id` from the upstream HTTP request.
235
+
236
+ If a message arrives without `x-trace-id` (published without ExisRay), the concern is a no-op.
237
+
203
238
  ---
204
239
 
205
240
  ## 📋 Advanced Logging Guide
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module ExisRay
6
+ module BugBunny
7
+ # Concern para BugBunny::Controller que restaura el trace context de ExisRay
8
+ # a partir del header 'x-trace-id' inyectado por el publicador.
9
+ #
10
+ # Permite que todos los logs emitidos durante el procesamiento de un mensaje
11
+ # incluyan el mismo root_id y correlation_id que el request HTTP original,
12
+ # logrando trazabilidad distribuida de punta a punta.
13
+ #
14
+ # El contexto se limpia siempre en `ensure`, garantizando que un error en el
15
+ # controller no contamine el procesamiento del siguiente mensaje.
16
+ #
17
+ # @example Incluir en el ApplicationController de BugBunny
18
+ # class ApplicationController < BugBunny::Controller
19
+ # include ExisRay::BugBunny::ConsumerTracing
20
+ # end
21
+ module ConsumerTracing
22
+ extend ActiveSupport::Concern
23
+
24
+ # Header AMQP desde el cual se lee el trace context propagado.
25
+ TRACE_HEADER = 'x-trace-id'
26
+
27
+ included do
28
+ around_action :exis_ray_trace_context
29
+ end
30
+
31
+ private
32
+
33
+ # Restaura el contexto de trazabilidad y lo limpia al finalizar,
34
+ # independientemente de si el controller levantó una excepción.
35
+ #
36
+ # @yieldreturn [void]
37
+ def exis_ray_trace_context
38
+ setup_exis_ray_context
39
+ yield
40
+ ensure
41
+ ExisRay::Tracer.reset rescue nil
42
+ end
43
+
44
+ # Hidrata el Tracer con el header de traza recibido en el mensaje.
45
+ # Si el header no está presente (mensaje sin trace context), no hace nada.
46
+ #
47
+ # @return [void]
48
+ def setup_exis_ray_context
49
+ trace_header = headers[TRACE_HEADER]
50
+ return unless trace_header.present?
51
+
52
+ ExisRay::Tracer.hydrate(trace_id: trace_header, source: 'system')
53
+ ExisRay.sync_correlation_id
54
+ rescue StandardError
55
+ # El tracing nunca debe interrumpir el procesamiento del mensaje.
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExisRay
4
+ module BugBunny
5
+ # Middleware de publicación para BugBunny::Client.
6
+ #
7
+ # Inyecta el header de trazabilidad de ExisRay en cada mensaje publicado,
8
+ # permitiendo la propagación del trace context a los servicios consumidores.
9
+ # Esto hace posible correlacionar un request HTTP original con todos los mensajes
10
+ # y logs que genera en el ecosistema de microservicios.
11
+ #
12
+ # @example Configuración en el cliente
13
+ # client = BugBunny::Client.new(pool: pool) do |stack|
14
+ # stack.use ExisRay::BugBunny::PublisherTracing
15
+ # end
16
+ #
17
+ # @example Configuración en un BugBunny::Resource
18
+ # class UserResource < BugBunny::Resource
19
+ # middleware do |stack|
20
+ # stack.use ExisRay::BugBunny::PublisherTracing
21
+ # end
22
+ # end
23
+ class PublisherTracing < ::BugBunny::Middleware::Base
24
+ # Header AMQP personalizado para propagar el trace context entre servicios.
25
+ TRACE_HEADER = 'x-trace-id'
26
+
27
+ # Inyecta el header de traza antes de publicar el mensaje.
28
+ # Solo actúa si hay un trace context activo (root_id presente).
29
+ #
30
+ # @param env [BugBunny::Request] El objeto request del mensaje saliente.
31
+ # @return [void]
32
+ def on_request(env)
33
+ return unless ExisRay::Tracer.root_id.present?
34
+
35
+ env.headers[TRACE_HEADER] = ExisRay::Tracer.generate_trace_header
36
+ rescue StandardError
37
+ # La propagación de tracing nunca debe interrumpir la publicación de mensajes.
38
+ end
39
+ end
40
+ end
41
+ end
@@ -8,20 +8,14 @@ module ExisRay
8
8
 
9
9
  def call(env)
10
10
  # 1. Hidratar Infraestructura
11
- ExisRay::Tracer.created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
12
- ExisRay::Tracer.source = "http"
13
-
14
- trace_header_key = ExisRay.configuration.trace_header
15
-
16
- ExisRay::Tracer.trace_id = env[trace_header_key]
17
- ExisRay::Tracer.request_id = env['action_dispatch.request_id']
18
- ExisRay::Tracer.parse_trace_id
11
+ ExisRay::Tracer.hydrate(
12
+ trace_id: env[ExisRay.configuration.trace_header],
13
+ source: "http"
14
+ )
15
+ ExisRay::Tracer.request_id = env['action_dispatch.request_id']
19
16
 
20
17
  # 2. Hidratar Negocio
21
- # Usamos el helper centralizado para obtener la clase Current
22
- if (curr = ExisRay.current_class) && curr.respond_to?(:correlation_id=) && ExisRay::Tracer.root_id.present?
23
- curr.correlation_id = ExisRay::Tracer.correlation_id
24
- end
18
+ ExisRay.sync_correlation_id
25
19
 
26
20
  @app.call(env)
27
21
  end
@@ -46,17 +46,16 @@ module ExisRay
46
46
  # @param job [Hash] Payload de Sidekiq.
47
47
  # @return [void]
48
48
  def hydrate_tracer(worker, job)
49
- ExisRay::Tracer.created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
50
49
  ExisRay::Tracer.sidekiq_job = worker.class.name
51
- ExisRay::Tracer.source = "sidekiq"
52
50
 
53
51
  if job["exis_ray_trace"]
54
52
  # Continuidad: Usamos la traza propagada desde el cliente (Web/Cron)
55
- ExisRay::Tracer.trace_id = job["exis_ray_trace"]
56
- ExisRay::Tracer.parse_trace_id
53
+ ExisRay::Tracer.hydrate(trace_id: job["exis_ray_trace"], source: "sidekiq")
57
54
  else
58
55
  # Origen: El job nació directamente aquí sin contexto previo
59
- ExisRay::Tracer.root_id = ExisRay::Tracer.send(:generate_new_root)
56
+ ExisRay::Tracer.created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
57
+ ExisRay::Tracer.source = "sidekiq"
58
+ ExisRay::Tracer.root_id = ExisRay::Tracer.send(:generate_new_root)
60
59
  end
61
60
  end
62
61
 
@@ -61,6 +61,20 @@ module ExisRay
61
61
  (current_duration_s * 1000).round
62
62
  end
63
63
 
64
+ # Hidrata el Tracer con un trace header entrante y registra el inicio del contexto.
65
+ # Centraliza la inicialización de contexto de trazabilidad para todos los entrypoints
66
+ # (HTTP, Sidekiq, BugBunny, etc.), eliminando duplicación entre middlewares.
67
+ #
68
+ # @param trace_id [String] El header de traza entrante (formato AWS X-Ray).
69
+ # @param source [String] El entrypoint de ejecución ('http', 'sidekiq', 'task', 'system').
70
+ # @return [void]
71
+ def self.hydrate(trace_id:, source:)
72
+ self.created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
73
+ self.source = source
74
+ self.trace_id = trace_id
75
+ parse_trace_id
76
+ end
77
+
64
78
  # Calcula el tiempo transcurrido en segundos desde el inicio de la request.
65
79
  # Cumple con el estándar Wispro-Observability-Spec (v1).
66
80
  #
@@ -2,5 +2,5 @@
2
2
 
3
3
  module ExisRay
4
4
  # Versión actual de la gema.
5
- VERSION = "0.5.5"
5
+ VERSION = "0.5.6"
6
6
  end
data/lib/exis_ray.rb CHANGED
@@ -24,6 +24,13 @@ require "exis_ray/faraday_middleware" if defined?(Faraday)
24
24
  # Solo cargamos la instrumentación si ActiveResource está presente.
25
25
  require "exis_ray/active_resource_instrumentation" if defined?(ActiveResource::Base)
26
26
 
27
+ # Integraciones BugBunny: publisher middleware y consumer concern.
28
+ # Solo se cargan si la gema BugBunny está presente.
29
+ if defined?(::BugBunny)
30
+ require "exis_ray/bug_bunny/publisher_tracing"
31
+ require "exis_ray/bug_bunny/consumer_tracing"
32
+ end
33
+
27
34
  # Integración automática con Rails
28
35
  # Solo cargamos el Railtie si la constante Rails está definida.
29
36
  require "exis_ray/railtie" if defined?(Rails)
@@ -96,6 +103,17 @@ module ExisRay
96
103
  end
97
104
  end
98
105
 
106
+ # Sincroniza el correlation_id del Tracer en la clase Current configurada.
107
+ # Debe llamarse después de hidratar el Tracer (post `hydrate` o `parse_trace_id`).
108
+ #
109
+ # @return [void]
110
+ def sync_correlation_id
111
+ curr = current_class
112
+ return unless curr&.respond_to?(:correlation_id=) && Tracer.root_id.present?
113
+
114
+ curr.correlation_id = Tracer.correlation_id
115
+ end
116
+
99
117
  private
100
118
 
101
119
  def resolve_class(klass_name)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exis_ray
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Edera
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-24 00:00:00.000000000 Z
11
+ date: 2026-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -54,6 +54,8 @@ files:
54
54
  - Rakefile
55
55
  - lib/exis_ray.rb
56
56
  - lib/exis_ray/active_resource_instrumentation.rb
57
+ - lib/exis_ray/bug_bunny/consumer_tracing.rb
58
+ - lib/exis_ray/bug_bunny/publisher_tracing.rb
57
59
  - lib/exis_ray/configuration.rb
58
60
  - lib/exis_ray/current.rb
59
61
  - lib/exis_ray/faraday_middleware.rb