exis_ray 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 10abd6a535e59a6d883a669567f45450fdbf694163699c2d5fb3cab4436a6ba9
4
+ data.tar.gz: c64fa72c61bc02921264714ec334ceaa8aee37b6507ebc42e9936f1f2eca86b5
5
+ SHA512:
6
+ metadata.gz: c3bda3764d45ce3de886ed7a48d79df083c62bd48e84ac42e832f77bd736d35d333a723167809b8db6d1aceb21a6b9dd3c9ae42d7b359b9a6b4bb6c78da0b5c1
7
+ data.tar.gz: 8e7b43e3fea6235d93365f89efa180a870785cbfb3329a380563aee4f69334c16d8703599104e12c746c5b06a1ee8af527bbccac19595e492ff172f0b85a3390
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-12-23
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 gabriel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # ExisRay
2
+
3
+ **ExisRay** is a robust observability framework designed for Ruby on Rails microservices. It unifies **Distributed Tracing** (AWS X-Ray compatible), **Business Context Propagation**, and **Error Reporting** into a single, cohesive gem.
4
+
5
+ It acts as the backbone of your architecture, ensuring that every request, background task (Sidekiq/Cron), log line, and external API call carries the necessary context to debug issues across a distributed system.
6
+
7
+ ## 馃殌 Features
8
+
9
+ * **Distributed Tracing:** Automatically parses, generates, and propagates Trace headers (compatible with AWS ALB `X-Amzn-Trace-Id`).
10
+ * **Unified Logging:** Injects the global `Root ID` into every Rails log line automatically, making Kibana/CloudWatch filtering effortless.
11
+ * **Context Management:** Thread-safe storage for business identity (`User`, `ISP`, `CorrelationId`) with automatic cleanup.
12
+ * **Error Reporting:** A wrapper for Sentry (Legacy & Modern SDKs) that enriches errors with the full trace and business context.
13
+ * **Sidekiq Integration:** Automatic context propagation (User/ISP/Trace) between the Enqueuer and the Worker.
14
+ * **Task Monitor:** A specialized monitor for Rake/Cron tasks to initialize traces where no HTTP request exists.
15
+ * **HTTP Clients:** Automatically patches `ActiveResource` and provides middleware for `Faraday`.
16
+
17
+ ---
18
+
19
+ ## 馃摝 Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'exis_ray'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ ```bash
30
+ $ bundle install
31
+ ```
32
+
33
+ ---
34
+
35
+ ## 鈿欙笍 Configuration
36
+
37
+ Create an initializer to configure the behavior. This is crucial to link ExisRay with your specific application logic.
38
+
39
+ **File:** `config/initializers/exis_ray.rb`
40
+
41
+ ```ruby
42
+ ExisRay.configure do |config|
43
+ # 1. Trace Header (Incoming)
44
+ # The HTTP header used to read the Trace ID from the Load Balancer (Rack format).
45
+ # Default: 'HTTP_X_AMZN_TRACE_ID' (AWS Standard).
46
+ config.trace_header = 'HTTP_X_WP_TRACE_ID'
47
+
48
+ # 2. Propagation Header (Outgoing)
49
+ # The header sent to downstream services via ActiveResource/Faraday.
50
+ config.propagation_trace_header = 'X-Wp-Trace-Id'
51
+
52
+ # 3. Dynamic Classes (Required)
53
+ # Link your app's specific classes to the gem.
54
+ # We use Strings to avoid "uninitialized constant" errors during boot.
55
+ config.current_class = 'Current' # Your Context Model
56
+ config.reporter_class = 'Choto' # Your Sentry Wrapper
57
+ end
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 馃摉 Implementation Guide
63
+
64
+ ### 1. Define Business Context (`Current`)
65
+
66
+ Inherit from `ExisRay::Current` to manage your global state. This class handles thread-safety and ensures data is wiped after every request.
67
+
68
+ **File:** `app/models/current.rb`
69
+
70
+ ```ruby
71
+ class Current < ExisRay::Current
72
+ # Add app-specific attributes here
73
+ attribute :billing_cycle, :permissions
74
+
75
+ # ExisRay provides: user_id, isp_id, correlation_id
76
+ end
77
+ ```
78
+
79
+ ### 2. Define Error Reporter (`Reporter`)
80
+
81
+ Inherit from `ExisRay::Reporter` to standardize error handling. This wrapper automatically attaches the `Trace ID`, `User`, `ISP`, and `Tags` to every Sentry event.
82
+
83
+ **File:** `app/models/choto.rb`
84
+
85
+ ```ruby
86
+ class Choto < ExisRay::Reporter
87
+ # Optional hook to add service-specific context
88
+ def self.build_custom_context
89
+ if ExisRay.current_class.respond_to?(:olt)
90
+ add_tags(olt_id: ExisRay.current_class.olt&.id)
91
+ end
92
+ end
93
+ end
94
+ ```
95
+
96
+ ### 3. Hydrate Context (Controller)
97
+
98
+ In your `ApplicationController`, verify the incoming request and set the context. ExisRay handles the Trace ID automatically, you just handle the Business Logic.
99
+
100
+ **File:** `app/controllers/application_controller.rb`
101
+
102
+ ```ruby
103
+ before_action :set_exis_ray_context
104
+
105
+ def set_exis_ray_context
106
+ # 1. User Context (e.g., from Devise)
107
+ Current.user = current_user if current_user
108
+
109
+ # 2. ISP Context (e.g., from Headers)
110
+ Current.isp_id = request.headers['X-Isp-Id']
111
+
112
+ # Note: Setting these automatically prepares headers for ActiveResource
113
+ # and tags for Sentry.
114
+ end
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 馃洜 Usage Scenarios
120
+
121
+ ### A. Automatic Sidekiq Integration
122
+
123
+ If `Sidekiq` is present, ExisRay automatically configures Client and Server middlewares. **No code changes are required in your workers.**
124
+
125
+ **How it works:**
126
+ 1. **Enqueue:** When you call `Worker.perform_async`, the current `Trace ID` and `Current` attributes are injected into the job payload.
127
+ 2. **Process:** When the worker executes, `Current` is hydrated with the original data.
128
+ 3. **Logs:** Sidekiq logs will show the same `[Root=...]` ID as the web request.
129
+
130
+ ```ruby
131
+ # Controller
132
+ def create
133
+ # Trace ID: A, User: 42
134
+ HardWorker.perform_async(100)
135
+ end
136
+
137
+ # Worker
138
+ class HardWorker
139
+ include Sidekiq::Worker
140
+ def perform(amount)
141
+ puts Current.user_id # => 42 (Restored!)
142
+ Rails.logger.info "Processing" # => [Root=A] Processing...
143
+ end
144
+ end
145
+ ```
146
+
147
+ ### B. Background Tasks (Cron/Rake)
148
+
149
+ For Rake tasks or Cron jobs (where no HTTP request exists), use `ExisRay::TaskMonitor`. It generates a fresh `Root ID`.
150
+
151
+ **File:** `lib/tasks/billing.rake`
152
+
153
+ ```ruby
154
+ task generate_invoices: :environment do
155
+ ExisRay::TaskMonitor.run('billing:generate_invoices') do
156
+ # Logs are tagged: [Root=1-65a...bc] [ExisRay] Starting task...
157
+ InvoiceService.process_all
158
+ end
159
+ end
160
+ ```
161
+
162
+ ### C. HTTP Clients
163
+
164
+ ExisRay ensures traceability across microservices.
165
+
166
+ #### ActiveResource (Automatic)
167
+ If `ActiveResource` is detected, ExisRay automatically patches it. All outgoing requests will include:
168
+ * `X-Wp-Trace-Id` (Trace Header)
169
+ * `UserId`, `IspId`, `CorrelationId`
170
+
171
+ #### Faraday (Manual)
172
+ For Faraday, you must explicitly add the middleware:
173
+
174
+ ```ruby
175
+ conn = Faraday.new(url: '[https://api.internal](https://api.internal)') do |f|
176
+ f.use ExisRay::FaradayMiddleware
177
+ f.adapter Faraday.default_adapter
178
+ end
179
+ ```
180
+
181
+ ---
182
+
183
+ ## 馃彈 Architecture
184
+
185
+ * **`ExisRay::Tracer`**: The infrastructure layer. Handles AWS X-Ray format parsing and ID generation.
186
+ * **`ExisRay::Current`**: The business layer. Manages domain identity (`User`, `ISP`).
187
+ * **`ExisRay::Reporter`**: The observability layer. Bridges the gap between your app and Sentry.
188
+ * **`ExisRay::TaskMonitor`**: The entry point for non-HTTP processes.
189
+
190
+ ## License
191
+
192
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: :rubocop
@@ -0,0 +1,39 @@
1
+ module ExisRay
2
+ # M贸dulo dise帽ado para interceptar e instrumentar las peticiones HTTP salientes realizadas con ActiveResource.
3
+ # Utiliza el patr贸n `prepend` para envolver el m茅todo `headers` original sin romper la cadena de herencia.
4
+ #
5
+ # Su funci贸n principal es inyectar autom谩ticamente el header de trazabilidad (Trace ID)
6
+ # en todas las peticiones salientes para mantener la traza distribuida entre microservicios.
7
+ module ActiveResourceInstrumentation
8
+ # Sobrescribe el m茅todo `headers` de ActiveResource para inyectar el Trace ID actual.
9
+ #
10
+ # L贸gica de inyecci贸n:
11
+ # 1. Obtiene los headers definidos originalmente por el modelo o la request.
12
+ # 2. Verifica si existe un contexto de traza activo (Root ID).
13
+ # 3. Si existe, genera el header formateado (AWS/Wispro) y lo fusiona con los headers originales.
14
+ #
15
+ # @return [Hash] Un hash de headers HTTP que incluye el header de trazabilidad si corresponde.
16
+ def headers
17
+ # 1. Obtenemos los headers originales (si los hay)
18
+ original_headers = super
19
+
20
+ # 2. Verificaci贸n Universal:
21
+ # Usamos `root_id` en lugar de `trace_id`.
22
+ # - trace_id: Solo existe si recibimos una petici贸n Web (viene del header entrante).
23
+ # - root_id: Existe SIEMPRE que haya traza (sea Web o sea un Cron generado por TaskMonitor).
24
+ if ExisRay::Tracer.root_id.present?
25
+ # Generamos el string propagable: "Root=...;Parent=...;Sampled=..."
26
+ trace_header_value = ExisRay::Tracer.generate_trace_header
27
+
28
+ # Buscamos la key configurada (ej: 'HTTP_X_AMZN_TRACE_ID' o custom)
29
+ trace_header_key = ExisRay.configuration.trace_header
30
+
31
+ # Retornamos un nuevo hash combinado (merge) para no mutar el original por error
32
+ original_headers.merge(trace_header_key => trace_header_value)
33
+ else
34
+ # Si no hay traza activa, devolvemos los headers tal cual
35
+ original_headers
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ module ExisRay
2
+ # Clase de configuraci贸n global para la gema.
3
+ # Permite personalizar los headers de trazabilidad y definir las clases de la aplicaci贸n host
4
+ # que la gema utilizar谩 para gestionar el contexto (Current) y el reporte de errores (Reporter).
5
+ class Configuration
6
+ # @!attribute [rw] trace_header
7
+ # @return [String] La key del header HTTP (formato Rack) donde se buscar谩 el Trace ID entrante.
8
+ # Por defecto es 'HTTP_X_AMZN_TRACE_ID'.
9
+ attr_accessor :trace_header
10
+
11
+ # @!attribute [rw] propagation_trace_header
12
+ # @return [String] La key del header HTTP que se enviar谩 a otros servicios (formato est谩ndar).
13
+ # Por defecto es 'X-Amzn-Trace-Id'.
14
+ attr_accessor :propagation_trace_header
15
+
16
+ # @!attribute [rw] reporter_class
17
+ # @return [String, Class, nil] El nombre de la clase de la aplicaci贸n host que hereda de {ExisRay::Reporter}.
18
+ # Se recomienda usar un String para evitar problemas de carga (autoloading) durante la inicializaci贸n.
19
+ # @example 'Choto'
20
+ attr_accessor :reporter_class
21
+
22
+ # @!attribute [rw] current_class
23
+ # @return [String, Class, nil] El nombre de la clase de la aplicaci贸n host que hereda de {ExisRay::Current}.
24
+ # Se utiliza para inyectar/leer user_id, isp_id y correlation_id.
25
+ # @example 'Current'
26
+ attr_accessor :current_class
27
+
28
+ # Inicializa la configuraci贸n con valores por defecto compatibles con AWS X-Ray.
29
+ def initialize
30
+ @trace_header = 'HTTP_X_AMZN_TRACE_ID'
31
+ @propagation_trace_header = 'X-Amzn-Trace-Id'
32
+ @reporter_class = 'Reporter'
33
+ @current_class = 'Current'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,112 @@
1
+ require 'active_support/current_attributes'
2
+
3
+ module ExisRay
4
+ # Clase base para la gesti贸n del contexto de negocio (User, ISP, Correlation).
5
+ # Debe ser heredada por la aplicaci贸n host (ej: class Current < ExisRay::Current).
6
+ class Current < ActiveSupport::CurrentAttributes
7
+ attribute :user_id, :isp_id, :correlation_id
8
+
9
+ # Callback nativo de Rails: Se ejecuta autom谩ticamente al llamar a Current.reset
10
+ resets do
11
+ @user = nil
12
+ @isp = nil
13
+
14
+ if defined?(PaperTrail)
15
+ PaperTrail.request.whodunnit = nil
16
+ PaperTrail.request.controller_info = {}
17
+ end
18
+
19
+ if defined?(ActiveResource::Base)
20
+ ActiveResource::Base.headers.delete('UserId')
21
+ ActiveResource::Base.headers.delete('IspId')
22
+ ActiveResource::Base.headers.delete('CorrelationId')
23
+ end
24
+ end
25
+
26
+ # --- Setters con Hooks ---
27
+
28
+ def user_id=(id)
29
+ super
30
+ if defined?(ActiveResource::Base)
31
+ ActiveResource::Base.headers['UserId'] = id.to_s
32
+ end
33
+ if defined?(PaperTrail)
34
+ PaperTrail.request.whodunnit = id
35
+ end
36
+ end
37
+
38
+ def isp_id=(id)
39
+ super
40
+ @isp = nil # Invalida cache
41
+ if defined?(ActiveResource::Base)
42
+ ActiveResource::Base.headers['IspId'] = id.to_s
43
+ end
44
+ end
45
+
46
+ def correlation_id=(id)
47
+ super
48
+
49
+ if defined?(::Session)
50
+ ::Session.request_id = id # Deprecated legacy support
51
+ end
52
+
53
+ if defined?(ActiveResource::Base)
54
+ ActiveResource::Base.headers['CorrelationId'] = id.to_s
55
+ end
56
+
57
+ if defined?(PaperTrail)
58
+ PaperTrail.request.controller_info = { correlation_id: id }
59
+ end
60
+
61
+ # Integraci贸n con el Reporter configurado
62
+ if (reporter = ExisRay.reporter_class) && reporter.respond_to?(:add_tags)
63
+ reporter.add_tags(correlation_id: id)
64
+ end
65
+ end
66
+
67
+ # --- Helpers de Objetos (Lazy Loading) ---
68
+ # Estos m茅todos asumen que la app host tiene modelos ::User e ::Isp
69
+
70
+ def user=(object)
71
+ @user = object
72
+ self.user_id = object&.id
73
+ end
74
+
75
+ def user
76
+ return @user if defined?(@user) && @user
77
+
78
+ if user_id && defined?(::User) && ::User.respond_to?(:find_by)
79
+ @user = ::User.find_by(id: user_id)
80
+ else
81
+ nil
82
+ end
83
+ end
84
+
85
+ def isp=(object)
86
+ @isp = object
87
+ self.isp_id = object&.id
88
+ end
89
+
90
+ def isp
91
+ return @isp if defined?(@isp) && @isp
92
+
93
+ if isp_id && defined?(::Isp) && ::Isp.respond_to?(:find_by)
94
+ @isp = ::Isp.find_by(id: isp_id)
95
+ else
96
+ nil
97
+ end
98
+ end
99
+
100
+ def user?
101
+ user_id.present?
102
+ end
103
+
104
+ def isp?
105
+ isp_id.present?
106
+ end
107
+
108
+ def correlation_id?
109
+ correlation_id.present?
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,19 @@
1
+ require 'faraday'
2
+
3
+ module ExisRay
4
+ # Middleware para Faraday que inyecta el header de trazabilidad saliente.
5
+ class FaradayMiddleware < Faraday::Middleware
6
+ def call(env)
7
+ if ExisRay::Tracer.root_id.present?
8
+ # Generamos el valor de traza
9
+ header_value = ExisRay::Tracer.generate_trace_header
10
+ # Obtenemos la key configurada para propagaci贸n
11
+ header_key = ExisRay.configuration.propagation_trace_header
12
+
13
+ env.request_headers[header_key] = header_value
14
+ end
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,30 @@
1
+ module ExisRay
2
+ # Middleware de Rack para interceptar peticiones HTTP.
3
+ # Inicializa el Tracer y sincroniza el Correlation ID con la clase Current configurada.
4
+ class HttpMiddleware
5
+ def initialize(app)
6
+ @app = app
7
+ @base_service_name = defined?(Rails) ? Rails.application.class.module_parent_name : 'App'
8
+ end
9
+
10
+ def call(env)
11
+ # 1. Hidratar Infraestructura
12
+ ExisRay::Tracer.created_at = Time.now.utc.to_f
13
+ ExisRay::Tracer.service_name = "#{@base_service_name}-HTTP"
14
+
15
+ trace_header_key = ExisRay.configuration.trace_header
16
+
17
+ ExisRay::Tracer.trace_id = env[trace_header_key]
18
+ ExisRay::Tracer.request_id = env['action_dispatch.request_id']
19
+ ExisRay::Tracer.parse_trace_id
20
+
21
+ # 2. Hidratar Negocio
22
+ # Usamos el helper centralizado para obtener la clase Current
23
+ if (curr = ExisRay.current_class) && curr.respond_to?(:correlation_id=) && ExisRay::Tracer.root_id.present?
24
+ curr.correlation_id = ExisRay::Tracer.correlation_id
25
+ end
26
+
27
+ @app.call(env)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ require 'rails/railtie'
2
+
3
+ module ExisRay
4
+ # Integraci贸n autom谩tica con Rails.
5
+ # Carga middlewares HTTP, tags de logs e integraciones (Sidekiq/ActiveResource).
6
+ class Railtie < ::Rails::Railtie
7
+ # 1. Middleware HTTP
8
+ initializer "exis_ray.configure_middleware" do |app|
9
+ require 'exis_ray/http_middleware'
10
+ app.middleware.insert_after ActionDispatch::RequestId, ExisRay::HttpMiddleware
11
+ end
12
+
13
+ # 2. Logs Tags
14
+ initializer "exis_ray.configure_log_tags" do |app|
15
+ app.config.log_tags ||= []
16
+ app.config.log_tags << proc {
17
+ if ExisRay::Tracer.trace_id.present?
18
+ ExisRay::Tracer.trace_id
19
+ elsif ExisRay::Tracer.root_id.present?
20
+ ExisRay::Tracer.root_id
21
+ else
22
+ nil
23
+ end
24
+ }
25
+ end
26
+
27
+ # 3. Integraciones Post-Boot
28
+ config.after_initialize do
29
+ # ActiveResource
30
+ if defined?(ActiveResource::Base)
31
+ require 'exis_ray/active_resource_instrumentation'
32
+ ActiveResource::Base.send(:prepend, ExisRay::ActiveResourceInstrumentation)
33
+ Rails.logger.info "[ExisRay] ActiveResource instrumentado."
34
+ end
35
+
36
+ # Sidekiq
37
+ # Usamos ::Sidekiq para referirnos a la Gema Global y no al m贸dulo local ExisRay::Sidekiq
38
+ if defined?(::Sidekiq)
39
+ require 'exis_ray/sidekiq/client_middleware'
40
+ require 'exis_ray/sidekiq/server_middleware'
41
+
42
+ ::Sidekiq.configure_client do |config|
43
+ config.client_middleware do |chain|
44
+ chain.add ExisRay::Sidekiq::ClientMiddleware
45
+ end
46
+ end
47
+
48
+ ::Sidekiq.configure_server do |config|
49
+ config.client_middleware do |chain|
50
+ chain.add ExisRay::Sidekiq::ClientMiddleware
51
+ end
52
+ config.server_middleware do |chain|
53
+ chain.prepend ExisRay::Sidekiq::ServerMiddleware
54
+ end
55
+ end
56
+ Rails.logger.info "[ExisRay] Sidekiq Middleware integrado."
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,187 @@
1
+ require 'active_support/current_attributes'
2
+
3
+ module ExisRay
4
+ # Clase base h铆brida para reporte de errores.
5
+ # Soporta integraci贸n moderna (Sentry SDK) y legacy (Raven/Session).
6
+ class Reporter < ActiveSupport::CurrentAttributes
7
+ attribute :contexts, :tags, :transaction_name, :fingerprint
8
+
9
+ resets do
10
+ self.contexts = {}
11
+ self.tags = {}
12
+ self.fingerprint = []
13
+ self.transaction_name = nil
14
+
15
+ # Invocamos limpieza legacy si existe.
16
+ # Usamos self.class porque clean_legacy_session! es m茅todo de clase.
17
+ self.class.clean_legacy_session!
18
+ end
19
+
20
+ # --- M茅todos P煤blicos ---
21
+
22
+ def self.report(message, context: {}, tags: {}, fingerprint: [], transaction_name: nil)
23
+ prepare_scope(context, tags, fingerprint, transaction_name)
24
+ if report_to_new_sentry?
25
+ report_to_new_sentry(message)
26
+ else
27
+ report_to_old_sentry(message)
28
+ end
29
+ end
30
+
31
+ def self.exception(excep, context: {}, tags: {}, fingerprint: [], transaction_name: nil)
32
+ prepare_scope(context, tags, fingerprint, transaction_name)
33
+ add_tags(exception: excep.class.to_s)
34
+
35
+ Rails.logger.error(excep) unless Rails.env.production?
36
+
37
+ if report_to_new_sentry?
38
+ exception_to_new_sentry(excep)
39
+ else
40
+ exception_to_old_sentry(excep)
41
+ end
42
+ end
43
+
44
+ # --- Builders de Datos ---
45
+
46
+ def self.add_fingerprint(value)
47
+ current_values = fingerprint || []
48
+ current_values << value
49
+ self.fingerprint = current_values.flatten.compact.uniq
50
+ end
51
+
52
+ def self.add_context(attrs)
53
+ return if attrs.blank?
54
+
55
+ self.contexts = (contexts || {}).merge(attrs.as_json)
56
+ end
57
+
58
+ def self.add_tags(attrs)
59
+ return if attrs.blank?
60
+ self.tags = (tags || {}).merge(attrs.as_json)
61
+ end
62
+
63
+ def self.build_custom_context
64
+ # Hook para subclases
65
+ end
66
+
67
+ # --- L贸gica Legacy ---
68
+ def self.clean_legacy_session!
69
+ return unless defined?(::Session) && ::Session.respond_to?(:clean!)
70
+
71
+ ::Session.clean!
72
+ end
73
+
74
+ def self.session_tag!
75
+ return unless defined?(::Session)
76
+
77
+ ::Session.tags_context ||= {}
78
+
79
+ if fingerprint.present?
80
+ str_fingerprint = fingerprint.flatten.join(',')
81
+ ::Session.tags_context.merge!(fingerprint: str_fingerprint)
82
+ end
83
+ if transaction_name.present?
84
+ ::Session.tags_context.merge!(transaction_name: transaction_name)
85
+ end
86
+ ::Session.tags_context.merge!(tags) if tags.present?
87
+ end
88
+
89
+ def self.session_context!
90
+ return unless contexts.present? && defined?(::Session)
91
+
92
+ ::Session.extra_context ||= {}
93
+ ::Session.extra_context.merge!(contexts)
94
+ end
95
+
96
+ def self.report_to_old_sentry(message)
97
+ session_tag!
98
+ session_context!
99
+ Sentry.send_event(message) if defined?(Sentry)
100
+ end
101
+
102
+ def self.exception_to_old_sentry(exception)
103
+ session_tag!
104
+ session_context!
105
+
106
+ if defined?(Sentry)
107
+ Sentry.populate_context(contexts) if contexts.present?
108
+ Sentry.notify(exception)
109
+ end
110
+ end
111
+
112
+ # --- L贸gica Moderna ---
113
+
114
+ def self.report_to_new_sentry?
115
+ defined?(::NEW_SENTRY) && ::NEW_SENTRY
116
+ end
117
+
118
+ def self.report_to_new_sentry(message)
119
+ send_to_new_sentry { Sentry.capture_message(message, fingerprint: fingerprint) }
120
+ end
121
+
122
+ def self.exception_to_new_sentry(exception)
123
+ send_to_new_sentry { Sentry.capture_exception(exception, level: 'error', fingerprint: fingerprint) }
124
+ end
125
+
126
+ def self.send_to_new_sentry
127
+ return unless defined?(Sentry)
128
+
129
+ Sentry.with_scope do |scope|
130
+ scope.set_transaction_name(transaction_name) if transaction_name.present?
131
+ if contexts.present?
132
+ contexts.each do |key, value|
133
+ val = value.is_a?(Hash) ? value : { value: value }
134
+ scope.set_context(key, val)
135
+ end
136
+ end
137
+ scope.set_tags(tags) if tags.present?
138
+ yield(scope)
139
+ end
140
+ end
141
+
142
+ # --- Inicializaci贸n de Contexto ---
143
+
144
+ private_class_method def self.prepare_scope(context, tags, fingerprint, transaction_name)
145
+ add_context(context)
146
+ add_tags(tags)
147
+ add_fingerprint(fingerprint)
148
+ self.transaction_name = transaction_name if transaction_name.present?
149
+
150
+ build_from_tracer
151
+ build_from_current
152
+ build_custom_context
153
+ end
154
+
155
+ def self.build_from_tracer
156
+ return unless defined?(ExisRay::Tracer)
157
+
158
+ if ExisRay::Tracer.root_id.present?
159
+ add_tags(correlation_id: ExisRay::Tracer.root_id)
160
+ add_context(trace: {
161
+ root_id: ExisRay::Tracer.root_id,
162
+ request_id: ExisRay::Tracer.request_id
163
+ })
164
+ end
165
+ end
166
+
167
+ def self.build_from_current
168
+ klass = ExisRay.current_class
169
+ return unless klass
170
+
171
+ add_tags(user_id: klass.user_id) if klass.respond_to?(:user_id?) && klass.user_id?
172
+ add_tags(isp_id: klass.isp_id) if klass.respond_to?(:isp_id?) && klass.isp_id?
173
+
174
+ if klass.respond_to?(:user) && klass.user.present?
175
+ user_json = klass.user.respond_to?(:as_json) ? klass.user.as_json : { id: klass.user_id }
176
+ add_context(user: user_json)
177
+ end
178
+
179
+ if klass.respond_to?(:isp) && klass.isp.present?
180
+ isp_json = klass.isp.respond_to?(:as_json) ? klass.isp.as_json : { id: klass.isp_id }
181
+ add_context(isp: isp_json)
182
+ end
183
+ end
184
+
185
+ private_class_method :build_from_current, :build_from_tracer
186
+ end
187
+ end
@@ -0,0 +1,31 @@
1
+ module ExisRay
2
+ module Sidekiq
3
+ class ClientMiddleware
4
+ # Intercepta el push del trabajo a Redis.
5
+ # @param worker_class [String, Class] La clase del worker.
6
+ # @param job [Hash] El payload del trabajo (aqu铆 inyectamos datos).
7
+ # @param queue [String] Nombre de la cola.
8
+ # @param redis_pool [Object] Pool de conexi贸n (Legacy v6).
9
+ def call(worker_class, job, queue, redis_pool = nil)
10
+ # Solo inyectamos si hay una traza activa (viniendo de Web o Cron)
11
+ if ExisRay::Tracer.root_id.present?
12
+ # 1. Inyectamos la traza (usamos generate_trace_header para mantener la cadena)
13
+ job['exis_ray_trace'] = ExisRay::Tracer.generate_trace_header
14
+
15
+ # 2. Inyectamos el contexto de negocio (Current)
16
+ # Esto permite saber qu茅 Usuario o ISP dispar贸 el job.
17
+ if ExisRay.current_class.present?
18
+ context = {}
19
+ context[:user_id] = ExisRay.current_class.user_id if ExisRay.current_class.respond_to?(:user_id)
20
+ context[:isp_id] = ExisRay.current_class.isp_id if ExisRay.current_class.respond_to?(:isp_id)
21
+ context[:correlation_id] = ExisRay.current_class.correlation_id if ExisRay.current_class.respond_to?(:correlation_id)
22
+
23
+ job['exis_ray_context'] = context
24
+ end
25
+ end
26
+
27
+ yield
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,103 @@
1
+ module ExisRay
2
+ module Sidekiq
3
+ # Middleware de Servidor para Sidekiq.
4
+ # Se ejecuta alrededor de cada trabajo (job) procesado por un Worker.
5
+ #
6
+ # Su responsabilidad es:
7
+ # 1. Recuperar el Trace ID y contexto (User, ISP) inyectados por el cliente.
8
+ # 2. Configurar el entorno (Tracer, Current, Reporter).
9
+ # 3. Limpiar todo al finalizar para no contaminar el thread (Thread Pooling).
10
+ class ServerMiddleware
11
+ # Intercepta la ejecuci贸n del job.
12
+ #
13
+ # @param worker [Object] La instancia del worker que procesar谩 el job.
14
+ # @param job [Hash] El payload del trabajo (contiene argumentos y metadatos).
15
+ # @param queue [String] El nombre de la cola.
16
+ def call(worker, job, queue)
17
+ # 1. Hidrataci贸n de Infraestructura (Tracer)
18
+ hydrate_tracer(worker, job)
19
+
20
+ # 2. Hidrataci贸n de Negocio (Current Class configurada)
21
+ hydrate_current(job)
22
+
23
+ # 3. Configuraci贸n de Reporte (Sentry/Reporter Class configurada)
24
+ setup_reporter(worker)
25
+
26
+ # 4. Ejecuci贸n con Logs Taggeados
27
+ # Inyectamos el Root ID en los logs de Rails para correlacionarlos con Sidekiq.
28
+ tags = [ExisRay::Tracer.root_id]
29
+
30
+ if Rails.logger.respond_to?(:tagged)
31
+ Rails.logger.tagged(*tags) { yield }
32
+ else
33
+ yield
34
+ end
35
+
36
+ ensure
37
+ # 5. Limpieza Total (Vital en Sidekiq)
38
+ # Sidekiq reutiliza threads. Si no limpiamos, el contexto de un job
39
+ # (ej: usuario actual) podr铆a filtrarse al siguiente job.
40
+ ExisRay::Tracer.reset
41
+
42
+ # Limpieza usando los helpers centralizados (sin hardcodear Current)
43
+ ExisRay.current_class&.reset if ExisRay.current_class.respond_to?(:reset)
44
+ ExisRay.reporter_class&.reset if ExisRay.reporter_class.respond_to?(:reset)
45
+ end
46
+
47
+ private
48
+
49
+ # Configura el Tracer con el ID recibido o genera uno nuevo.
50
+ def hydrate_tracer(worker, job)
51
+ ExisRay::Tracer.created_at = Time.now.utc.to_f
52
+ ExisRay::Tracer.service_name = "Sidekiq-#{worker.class.name}"
53
+
54
+ if job['exis_ray_trace']
55
+ # Continuidad: Usamos la traza que viene del cliente (Web/Cron)
56
+ ExisRay::Tracer.trace_id = job['exis_ray_trace']
57
+ ExisRay::Tracer.parse_trace_id
58
+ else
59
+ # Origen: El job naci贸 aqu铆 (ej: desde consola o trigger externo sin contexto)
60
+ ExisRay::Tracer.root_id = ExisRay::Tracer.send(:generate_new_root)
61
+ end
62
+ end
63
+
64
+ # Hidrata la clase Current configurada con los datos del payload.
65
+ def hydrate_current(job)
66
+ # Obtenemos la clase din谩mica (ej: Current)
67
+ klass = ExisRay.current_class
68
+
69
+ # Salimos si no hay clase configurada o no hay contexto en el job
70
+ return unless klass && job['exis_ray_context']
71
+
72
+ ctx = job['exis_ray_context']
73
+
74
+ # Asignaci贸n segura usando la clase din谩mica
75
+ klass.user_id = ctx['user_id'] if ctx['user_id'] && klass.respond_to?(:user_id=)
76
+ klass.isp_id = ctx['isp_id'] if ctx['isp_id'] && klass.respond_to?(:isp_id=)
77
+
78
+ if ctx['correlation_id'] && klass.respond_to?(:correlation_id=)
79
+ klass.correlation_id = ctx['correlation_id']
80
+ end
81
+ end
82
+
83
+ # Configura tags y nombres de transacci贸n en el Reporter.
84
+ def setup_reporter(worker)
85
+ klass = ExisRay.reporter_class
86
+ return unless klass
87
+
88
+ # Nombre de transacci贸n para Sentry: "Sidekiq/HardWorker"
89
+ if klass.respond_to?(:transaction_name=)
90
+ klass.transaction_name = "Sidekiq/#{worker.class.name}"
91
+ end
92
+
93
+ # Tags adicionales de infraestructura Sidekiq
94
+ if klass.respond_to?(:add_tags)
95
+ klass.add_tags(
96
+ sidekiq_queue: worker.class.get_sidekiq_options['queue'],
97
+ retry_count: worker.respond_to?(:retry_count) ? worker.retry_count : 0
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,55 @@
1
+ module ExisRay
2
+ # Wrapper para monitorear tareas en segundo plano (Rake/Cron).
3
+ module TaskMonitor
4
+ # Ejecuta un bloque dentro de un contexto monitoreado.
5
+ # @param task_name [String] Nombre identificador (ej: 'billing:generate').
6
+ def self.run(task_name)
7
+ setup_tracer(task_name)
8
+
9
+ short_name = task_name.to_s.split(':').last
10
+
11
+ # Configurar Reporter
12
+ if (rep = ExisRay.reporter_class) && rep.respond_to?(:transaction_name=)
13
+ rep.transaction_name = short_name
14
+ rep.add_tags(service: :cron, task: short_name) if rep.respond_to?(:add_tags)
15
+ end
16
+
17
+ # Configurar Current
18
+ if (curr = ExisRay.current_class) && curr.respond_to?(:correlation_id=)
19
+ curr.correlation_id = ExisRay::Tracer.correlation_id
20
+ end
21
+
22
+ # Logs con Root ID
23
+ tags = [ExisRay::Tracer.root_id]
24
+ Rails.logger.tagged(*tags) do
25
+ Rails.logger.info "[ExisRay] Iniciando tarea: #{task_name}"
26
+ yield
27
+ Rails.logger.info "[ExisRay] Finalizada con 茅xito."
28
+ end
29
+
30
+ rescue StandardError => e
31
+ Rails.logger.error "[ExisRay] Fall贸 la tarea #{task_name}: #{e.message}"
32
+ raise e
33
+ ensure
34
+ # Limpieza centralizada
35
+ ExisRay::Tracer.reset
36
+ ExisRay.current_class&.reset if ExisRay.current_class.respond_to?(:reset)
37
+ ExisRay.reporter_class&.reset if ExisRay.reporter_class.respond_to?(:reset)
38
+ end
39
+
40
+ def self.setup_tracer(task_name)
41
+ ExisRay::Tracer.service_name = task_name.to_s.gsub(':', '-').camelize
42
+ ExisRay::Tracer.request_id = SecureRandom.uuid
43
+ ExisRay::Tracer.created_at = Time.now.utc.to_f
44
+
45
+ pod_id = get_pod_identifier
46
+ ExisRay::Tracer.root_id = ExisRay::Tracer.send(:generate_new_root, pod_id)
47
+ end
48
+
49
+ def self.get_pod_identifier
50
+ (ENV['HOSTNAME'] || 'local').split('-').last.to_s
51
+ end
52
+
53
+ private_class_method :get_pod_identifier, :setup_tracer
54
+ end
55
+ end
@@ -0,0 +1,111 @@
1
+ require 'active_support/current_attributes'
2
+ require 'securerandom'
3
+
4
+ module ExisRay
5
+ # Gestiona el contexto de trazabilidad distribuida (Distributed Tracing).
6
+ # Utiliza `ActiveSupport::CurrentAttributes` para mantener el estado de la petici贸n actual
7
+ # de forma segura entre hilos (thread-safe).
8
+ #
9
+ # Esta clase parsea headers de AWS X-Ray y genera nuevos headers para la propagaci贸n.
10
+ #
11
+ # @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html Documentaci贸n de AWS X-Ray
12
+ class Tracer < ActiveSupport::CurrentAttributes
13
+ attribute :trace_id, :request_id, :root_id, :self_id, :called_from, :total_time_so_far, :created_at, :service_name
14
+
15
+ # Devuelve el nombre del servicio actual.
16
+ # Si no se ha definido manualmente, hace fallback al nombre de la aplicaci贸n Rails.
17
+ #
18
+ # @return [String] El nombre del servicio (ej: "Wispro", "Wispro-Worker", "App").
19
+ def self.service_name
20
+ super || (defined?(Rails) ? Rails.application.class.module_parent_name : 'App')
21
+ end
22
+
23
+ # Genera un ID de correlaci贸n compuesto, 煤til para logs y auditor铆a.
24
+ #
25
+ # @example
26
+ # ExisRay::Tracer.correlation_id #=> "Wispro-HTTP;1-5759...-..."
27
+ #
28
+ # @return [String] Cadena compuesta "ServiceName;RootID".
29
+ def self.correlation_id
30
+ "#{service_name};#{root_id}"
31
+ end
32
+
33
+ # Parsea el string de trazabilidad recibido y popula los atributos individuales.
34
+ # Maneja el formato est谩ndar de AWS: "Root=...;Self=...;CalledFrom=...;TotalTimeSoFar=..."
35
+ #
36
+ # @return [void]
37
+ def self.parse_trace_id
38
+ return unless trace_id.present?
39
+
40
+ # Fallback para desarrollo: Si el header no trae Root, generamos uno nuevo.
41
+ self.trace_id = generate_new_root(trace_id) if trace_id.exclude?('Root')
42
+
43
+ # Parseo a Hash
44
+ data = trace_id.split(';').map { |part| part.split('=', 2) }.to_h
45
+
46
+ self.root_id = data['Root']
47
+ self.self_id = data['Self']
48
+ self.called_from = data['CalledFrom']
49
+
50
+ if data['TotalTimeSoFar']
51
+ self.total_time_so_far = data['TotalTimeSoFar'].gsub(/ms$/i, '').to_i
52
+ else
53
+ self.total_time_so_far = 0
54
+ end
55
+ end
56
+
57
+ # Calcula el tiempo transcurrido en milisegundos desde el inicio de la request.
58
+ #
59
+ # @return [Integer] Duraci贸n en ms.
60
+ def self.current_duration_ms
61
+ return 0 unless created_at
62
+ ((Time.now.utc.to_f - created_at) * 1000).round
63
+ end
64
+
65
+ # Construye el header de trazabilidad para enviar al siguiente servicio.
66
+ #
67
+ # @return [String] Header formateado: "Root=...;Self=...;CalledFrom=...;TotalTimeSoFar=...ms"
68
+ def self.generate_trace_header
69
+ safe_root = if root_id.present?
70
+ root_id.start_with?('Root=') ? root_id : "Root=#{root_id}"
71
+ else
72
+ generate_new_root
73
+ end
74
+
75
+ total_acc_time = (total_time_so_far || 0) + current_duration_ms
76
+
77
+ # Nuevo ID para el span actual
78
+ my_new_id = "1-#{Time.now.to_i.to_s(16)}-#{clean_request_id}"
79
+
80
+ "#{safe_root};Self=#{my_new_id};CalledFrom=#{service_name};TotalTimeSoFar=#{total_acc_time}ms"
81
+ end
82
+
83
+ # Genera un nuevo Root ID compatible con AWS X-Ray.
84
+ #
85
+ # @api private
86
+ # @param suffix_id [String, nil] Sufijo opcional (ej: ID del Pod).
87
+ # @return [String] Formato: 1-TimestampHex-RandomHex
88
+ def self.generate_new_root(suffix_id = nil)
89
+ timestamp_hex = Time.now.to_i.to_s(16)
90
+
91
+ if suffix_id.present?
92
+ suffix_hex = suffix_id.to_i.to_s(16).rjust(8, '0')
93
+ unique_part = SecureRandom.hex(8) + suffix_hex
94
+ else
95
+ unique_part = SecureRandom.hex(12)
96
+ end
97
+
98
+ "Root=1-#{timestamp_hex}-#{unique_part}"
99
+ end
100
+
101
+ # Limpia el Request ID para cumplir con los 24 caracteres hex de AWS.
102
+ #
103
+ # @api private
104
+ # @return [String]
105
+ def self.clean_request_id
106
+ (request_id || SecureRandom.hex).delete('-').first(24)
107
+ end
108
+
109
+ private_class_method :clean_request_id, :generate_new_root
110
+ end
111
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExisRay
4
+ # Versi贸n actual de la gema.
5
+ VERSION = "0.1.0"
6
+ end
data/lib/exis_ray.rb ADDED
@@ -0,0 +1,88 @@
1
+ require "exis_ray/version"
2
+
3
+ # Dependencias externas
4
+ # Necesario para 'safe_constantize', 'present?', y 'CurrentAttributes'
5
+ require "active_support"
6
+ require "active_support/core_ext/string/inflections" # Para safe_constantize
7
+ require "active_support/current_attributes"
8
+
9
+ # Componentes internos del Core
10
+ require "exis_ray/configuration"
11
+ require "exis_ray/tracer"
12
+ require "exis_ray/task_monitor"
13
+ require "exis_ray/http_middleware"
14
+ require "exis_ray/current"
15
+ require "exis_ray/reporter"
16
+
17
+ # Integraciones Opcionales
18
+ # Solo cargamos el middleware de Faraday si la gema est谩 presente en el sistema.
19
+ require "exis_ray/faraday_middleware" if defined?(Faraday)
20
+
21
+ # Solo cargamos la instrumentaci贸n si ActiveResource est谩 presente.
22
+ require "exis_ray/active_resource_instrumentation" if defined?(ActiveResource::Base)
23
+
24
+ # Integraci贸n autom谩tica con Rails
25
+ # Solo cargamos el Railtie si la constante Rails est谩 definida.
26
+ require "exis_ray/railtie" if defined?(Rails)
27
+
28
+ # Namespace principal de la gema ExisRay.
29
+ # Contiene la configuraci贸n global y los helpers de resoluci贸n de clases din谩micas.
30
+ module ExisRay
31
+ class Error < StandardError; end
32
+
33
+ class << self
34
+ # @!attribute [w] configuration
35
+ attr_writer :configuration
36
+
37
+ # Accesor para la configuraci贸n global de la gema.
38
+ # Inicializa una nueva instancia de {Configuration} si no existe.
39
+ #
40
+ # @return [ExisRay::Configuration] La instancia de configuraci贸n actual.
41
+ def configuration
42
+ @configuration ||= Configuration.new
43
+ end
44
+
45
+ # Bloque de configuraci贸n para inicializar la gema.
46
+ #
47
+ # @example Configurar en un initializer de Rails
48
+ # ExisRay.configure do |config|
49
+ # config.trace_header = 'HTTP_X_WP_TRACE_ID'
50
+ # config.current_class = 'Current'
51
+ # config.reporter_class = 'Choto'
52
+ # end
53
+ #
54
+ # @yieldparam config [ExisRay::Configuration] El objeto de configuraci贸n.
55
+ def configure
56
+ yield(configuration)
57
+ end
58
+
59
+ # --- Helpers Centralizados de Resoluci贸n de Clases ---
60
+
61
+ # Resuelve y retorna la clase configurada para manejar el contexto de negocio (Current).
62
+ # Convierte el String configurado (ej: 'Current') en la clase real constante.
63
+ #
64
+ # @return [Class, nil] La clase constante (ej: Current) o nil si no se encuentra/configura.
65
+ def current_class
66
+ return nil unless configuration
67
+
68
+ klass_name = configuration.current_class
69
+ return nil unless klass_name.present?
70
+
71
+ # Si es String, lo convertimos a constante de forma segura.
72
+ klass_name.is_a?(String) ? klass_name.safe_constantize : klass_name
73
+ end
74
+
75
+ # Resuelve y retorna la clase configurada para el reporte de errores (Reporter).
76
+ # Convierte el String configurado (ej: 'Choto') en la clase real constante.
77
+ #
78
+ # @return [Class, nil] La clase constante (ej: Choto) o nil si no se encuentra/configura.
79
+ def reporter_class
80
+ return nil unless configuration
81
+
82
+ klass_name = configuration.reporter_class
83
+ return nil unless klass_name.present?
84
+
85
+ klass_name.is_a?(String) ? klass_name.safe_constantize : klass_name
86
+ end
87
+ end
88
+ end
data/sig/exis_ray.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module ExisRay
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exis_ray
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Edera
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-02-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: railties
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ description: Gema que gestiona el contexto de request, logs y propagaci贸n de headers.
42
+ email:
43
+ - gab.edera@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - LICENSE.txt
50
+ - README.md
51
+ - Rakefile
52
+ - lib/exis_ray.rb
53
+ - lib/exis_ray/active_resource_instrumentation.rb
54
+ - lib/exis_ray/configuration.rb
55
+ - lib/exis_ray/current.rb
56
+ - lib/exis_ray/faraday_middleware.rb
57
+ - lib/exis_ray/http_middleware.rb
58
+ - lib/exis_ray/railtie.rb
59
+ - lib/exis_ray/reporter.rb
60
+ - lib/exis_ray/sidekiq/client_middleware.rb
61
+ - lib/exis_ray/sidekiq/server_middleware.rb
62
+ - lib/exis_ray/task_monitor.rb
63
+ - lib/exis_ray/tracer.rb
64
+ - lib/exis_ray/version.rb
65
+ - sig/exis_ray.rbs
66
+ homepage: https://github.com/gedera/exis_ray
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 2.6.0
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.4.19
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Trazabilidad distribuida entre microservicios (AWS X-Ray style).
89
+ test_files: []