exis_ray 0.5.4 → 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 +4 -4
- data/.gemini/docs/TELEMETRY_GUIDELINES.md +32 -0
- data/CHANGELOG.md +13 -0
- data/CLAUDE.md +1 -0
- data/MANIFEST.md +15 -1
- data/README.md +35 -0
- data/lib/exis_ray/bug_bunny/consumer_tracing.rb +59 -0
- data/lib/exis_ray/bug_bunny/publisher_tracing.rb +41 -0
- data/lib/exis_ray/http_middleware.rb +6 -12
- data/lib/exis_ray/json_formatter.rb +21 -4
- data/lib/exis_ray/sidekiq/server_middleware.rb +4 -5
- data/lib/exis_ray/tracer.rb +14 -0
- data/lib/exis_ray/version.rb +1 -1
- data/lib/exis_ray.rb +18 -0
- data/spec/exis_ray/json_formatter_spec.rb +44 -5
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d66d71924fff9cf19c12e42d411963948739da61e6b223db1444c16633a3158e
|
|
4
|
+
data.tar.gz: 26c599899ecf1f87388c73e1b4a183370e0902893c09e40c24c7796c973c0d96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60c34361234b638c3758b3b51d38933efcbf5db19fb8e1a9365f53f1e21bcfc42e680ceb37755c792663e7146826af2453d862b7ae526481870f65518a84b311
|
|
7
|
+
data.tar.gz: e3cb2ffc12e45d5bdd6f3c1d1a864bc9884b7709c531fa2780923834f0ca4d4766c75148c6afd15aac4d6a6dbf716a151042620474127b544b779fa86ec9c62b
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# OpenTelemetry Implementation Guidelines
|
|
2
|
+
|
|
3
|
+
This document provides expert guidance for aligning code and logs with OpenTelemetry (OTel) standards. Follow these rules for all new instrumentation.
|
|
4
|
+
|
|
5
|
+
## 1. Log Data Model
|
|
6
|
+
- **Body:** Use `body` for the primary log message.
|
|
7
|
+
- **Attributes:** Metadata must be flat Key-Value pairs.
|
|
8
|
+
- **Severity:** `level` maps to `severity_text`.
|
|
9
|
+
|
|
10
|
+
## 2. Semantic Conventions
|
|
11
|
+
When adding attributes, check the official OTel [Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/).
|
|
12
|
+
|
|
13
|
+
### HTTP Attributes
|
|
14
|
+
- `http.request.method`: GET, POST, etc.
|
|
15
|
+
- `http.response.status_code`: 200, 404, etc.
|
|
16
|
+
- `url.path`: The request path.
|
|
17
|
+
- `user_agent.original`: The raw User-Agent string.
|
|
18
|
+
|
|
19
|
+
### Database Attributes
|
|
20
|
+
- `db.system`: postgresql, redis, etc.
|
|
21
|
+
- `db.operation`: select, update, etc.
|
|
22
|
+
- `db.collection.name`: table or collection name.
|
|
23
|
+
|
|
24
|
+
## 3. Metrics & Units
|
|
25
|
+
- Always use the lowest common denominator for units (seconds, bytes).
|
|
26
|
+
- Use `_s` suffix for time duration.
|
|
27
|
+
- Avoid compound strings like `"10ms"`. Use `duration_s=0.01`.
|
|
28
|
+
|
|
29
|
+
## 4. Distributed Tracing
|
|
30
|
+
- **TraceID:** 16-byte array, represented as a 32-char lowercase hex string.
|
|
31
|
+
- **SpanID:** 8-byte array, represented as a 16-char lowercase hex string.
|
|
32
|
+
- **Context Propagation:** Follow W3C Trace Context (traceparent) when possible, or maintain AWS X-Ray compatibility as per project requirements.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
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
|
+
|
|
7
|
+
## [0.5.5] - 2026-03-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Deep Security Filtering:** The `JsonFormatter` now recursively filters sensitive data within `Array` and nested `Hash` structures.
|
|
11
|
+
- **OTel Semantic Mapping:** Added formal mapping of ExisRay fields to OpenTelemetry Semantic Conventions in `MANIFEST.md`.
|
|
12
|
+
- **Test Hardening:** Expanded test suite to cover deep filtering and ensure correct numeric type-casting in logs.
|
|
13
|
+
|
|
1
14
|
## [0.5.4] - 2026-03-24
|
|
2
15
|
|
|
3
16
|
### Changed
|
data/CLAUDE.md
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
## Execution Rules
|
|
33
33
|
- **No Lograge:** Do not suggest or re-add the Lograge dependency.
|
|
34
34
|
- **Pure Data Logging:** Internal logs use KV strings (`component=exis_ray event=...`).
|
|
35
|
+
- **OTel Compliance:** Always follow the guidelines in `.gemini/docs/TELEMETRY_GUIDELINES.md` and alignment in `MANIFEST.md`. Use OTel Semantic Conventions for new attributes.
|
|
35
36
|
- **Resilience:** All logging operations must be wrapped in `rescue StandardError`.
|
|
36
37
|
- **Automatic Fields:** NEVER manually log `time`, `level`, `service`, `source`, `root_id`, `correlation_id`, `sidekiq_job` or `task`. These are handled by the library.
|
|
37
38
|
- **Source Values:** Valid values for `source` field: `http`, `sidekiq`, `task`, `system`.
|
data/MANIFEST.md
CHANGED
|
@@ -26,10 +26,24 @@ Cada línea de log debe llevar los metadatos necesarios para identificar su orig
|
|
|
26
26
|
* `source`: El punto de entrada de la ejecución (`http`, `sidekiq`, `task`, `system`).
|
|
27
27
|
|
|
28
28
|
### 3. Convenciones de Naming & Tipos
|
|
29
|
-
- **Naming:** Las llaves (keys) deben usar siempre `snake_case`.
|
|
29
|
+
- **Naming:** Las llaves (keys) deben usar siempre `snake_case`. Para campos estándar, se prefiere seguir las **OpenTelemetry Semantic Conventions**.
|
|
30
30
|
- **Valores Numéricos:** Deben emitirse como números reales (sin sufijos de texto) para permitir que el motor de logs realice el casting automático.
|
|
31
31
|
- **Formato:** Pares `key=value` en una sola línea estructurada.
|
|
32
32
|
|
|
33
|
+
## 🔭 Alineación con OpenTelemetry
|
|
34
|
+
|
|
35
|
+
Para garantizar la interoperabilidad, `exis_ray` sigue el **OpenTelemetry Log Data Model**. Siempre que sea posible, los campos deben mapearse a las convenciones semánticas oficiales:
|
|
36
|
+
|
|
37
|
+
| Campo ExisRay | OTel Semantic Convention | Descripción |
|
|
38
|
+
| :--- | :--- | :--- |
|
|
39
|
+
| `body` | `body` | El contenido principal del log. |
|
|
40
|
+
| `level` | `severity_text` | Nivel de importancia. |
|
|
41
|
+
| `duration_s` | `duration` (en segundos) | Tiempo de ejecución. |
|
|
42
|
+
| `method` | `http.request.method` | Método HTTP. |
|
|
43
|
+
| `status` | `http.response.status_code` | Código de respuesta. |
|
|
44
|
+
| `path` | `url.path` | Ruta del request. |
|
|
45
|
+
| `user_id` | `user.id` | Identificador del usuario. |
|
|
46
|
+
|
|
33
47
|
## 🏗 Infraestructura de Datos (Automática)
|
|
34
48
|
|
|
35
49
|
Para evitar logs redundantes y pesados, **NUNCA** incluyas manualmente las siguientes llaves en tus mensajes de log. La capa de infraestructura (`exis_ray`) las inyecta automáticamente en el nivel raíz del JSON:
|
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.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
|
@@ -163,17 +163,34 @@ module ExisRay
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
# Filtra recursivamente un Hash que contenga claves sensibles.
|
|
166
|
+
# Maneja valores anidados de tipo Hash o Array.
|
|
166
167
|
#
|
|
167
168
|
# @param hash [Hash]
|
|
168
169
|
# @return [Hash]
|
|
169
170
|
def filter_sensitive_hash(hash)
|
|
170
171
|
hash.each_with_object({}) do |(k, v), result|
|
|
171
|
-
result[k] =
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
result[k] = case v
|
|
173
|
+
when Hash then filter_sensitive_hash(v)
|
|
174
|
+
when Array then filter_sensitive_array(k, v)
|
|
175
|
+
else cast_value(k, v)
|
|
175
176
|
end
|
|
176
177
|
end
|
|
177
178
|
end
|
|
179
|
+
|
|
180
|
+
# Filtra recursivamente los elementos de un Array, propagando la clave padre
|
|
181
|
+
# para que el filtrado de claves sensibles se aplique a hashes anidados.
|
|
182
|
+
#
|
|
183
|
+
# @param key [String, Symbol] Clave padre (para filtrado si el array no contiene hashes).
|
|
184
|
+
# @param array [Array]
|
|
185
|
+
# @return [Array]
|
|
186
|
+
def filter_sensitive_array(key, array)
|
|
187
|
+
array.map do |element|
|
|
188
|
+
case element
|
|
189
|
+
when Hash then filter_sensitive_hash(element)
|
|
190
|
+
when Array then filter_sensitive_array(key, element)
|
|
191
|
+
else cast_value(key, element)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
178
195
|
end
|
|
179
196
|
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
|
|
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.
|
|
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
|
|
data/lib/exis_ray/tracer.rb
CHANGED
|
@@ -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
|
#
|
data/lib/exis_ray/version.rb
CHANGED
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)
|
|
@@ -92,11 +92,10 @@ RSpec.describe ExisRay::JsonFormatter do
|
|
|
92
92
|
expect(result).to include(
|
|
93
93
|
"component" => "orders",
|
|
94
94
|
"event" => "invoice_generated",
|
|
95
|
-
"duration_ms" =>
|
|
96
|
-
"retries" =>
|
|
95
|
+
"duration_ms" => 42.5,
|
|
96
|
+
"retries" => 0
|
|
97
97
|
)
|
|
98
98
|
end
|
|
99
|
-
end
|
|
100
99
|
|
|
101
100
|
it "cae a body si el string parece kv pero no produce ningún par" do
|
|
102
101
|
result = call("key=")
|
|
@@ -156,10 +155,10 @@ RSpec.describe ExisRay::JsonFormatter do
|
|
|
156
155
|
end
|
|
157
156
|
|
|
158
157
|
describe "#parse_kv_string (privado)" do
|
|
159
|
-
it "retorna un hash con claves symbol" do
|
|
158
|
+
it "retorna un hash con claves symbol y valores casteados" do
|
|
160
159
|
result = formatter.send(:parse_kv_string, "a=1 b=2")
|
|
161
160
|
|
|
162
|
-
expect(result).to eq({ a:
|
|
161
|
+
expect(result).to eq({ a: 1, b: 2 })
|
|
163
162
|
end
|
|
164
163
|
|
|
165
164
|
it "desenvuelve las comillas dobles de los valores" do
|
|
@@ -168,4 +167,44 @@ RSpec.describe ExisRay::JsonFormatter do
|
|
|
168
167
|
expect(result).to eq({ msg: "hello world" })
|
|
169
168
|
end
|
|
170
169
|
end
|
|
170
|
+
|
|
171
|
+
describe "#filter_sensitive_hash (privado)" do
|
|
172
|
+
it "filtra claves sensibles en el nivel raíz" do
|
|
173
|
+
result = formatter.send(:filter_sensitive_hash, { user: "gabriel", password: "secret" })
|
|
174
|
+
|
|
175
|
+
expect(result[:user]).to eq("gabriel")
|
|
176
|
+
expect(result[:password]).to eq("[FILTERED]")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "filtra claves sensibles en hashes anidados" do
|
|
180
|
+
result = formatter.send(:filter_sensitive_hash, {
|
|
181
|
+
db: { host: "localhost", token: "abc123" }
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
expect(result[:db][:host]).to eq("localhost")
|
|
185
|
+
expect(result[:db][:token]).to eq("[FILTERED]")
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "filtra claves sensibles dentro de arrays de hashes" do
|
|
189
|
+
result = formatter.send(:filter_sensitive_hash, {
|
|
190
|
+
users: [{ name: "gabriel", password: "secret" }, { name: "ana", password: "other" }]
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
expect(result[:users][0][:name]).to eq("gabriel")
|
|
194
|
+
expect(result[:users][0][:password]).to eq("[FILTERED]")
|
|
195
|
+
expect(result[:users][1][:password]).to eq("[FILTERED]")
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "filtra claves sensibles en arrays primitivos cuando la clave padre es sensible" do
|
|
199
|
+
result = formatter.send(:filter_sensitive_hash, { tokens: ["abc", "def"] })
|
|
200
|
+
|
|
201
|
+
expect(result[:tokens]).to eq(["[FILTERED]", "[FILTERED]"])
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "no altera valores no sensibles en arrays" do
|
|
205
|
+
result = formatter.send(:filter_sensitive_hash, { tags: ["admin", "active"] })
|
|
206
|
+
|
|
207
|
+
expect(result[:tags]).to eq(["admin", "active"])
|
|
208
|
+
end
|
|
209
|
+
end
|
|
171
210
|
end
|
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.
|
|
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-
|
|
11
|
+
date: 2026-03-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -45,6 +45,7 @@ executables: []
|
|
|
45
45
|
extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
|
47
47
|
files:
|
|
48
|
+
- ".gemini/docs/TELEMETRY_GUIDELINES.md"
|
|
48
49
|
- CHANGELOG.md
|
|
49
50
|
- CLAUDE.md
|
|
50
51
|
- LICENSE.txt
|
|
@@ -53,6 +54,8 @@ files:
|
|
|
53
54
|
- Rakefile
|
|
54
55
|
- lib/exis_ray.rb
|
|
55
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
|
|
56
59
|
- lib/exis_ray/configuration.rb
|
|
57
60
|
- lib/exis_ray/current.rb
|
|
58
61
|
- lib/exis_ray/faraday_middleware.rb
|