exis_ray 0.5.8 → 0.5.9

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: 327a3ed9c5558bba99acba81d3a3b328a271c16f4725c88a435f08ea8b1526a8
4
- data.tar.gz: 661cf580101b617b50b565ec342f8a09d8da5a4b8c4d8c11163ea5000236a64e
3
+ metadata.gz: f17193f5df75197d569fbe74d8b27e55b116e861bdb819f1e92526a1da79869e
4
+ data.tar.gz: e8e45998cb83236c3bb7cc3a981995ccd36764fdb6fbc1465e796a7966e3b354
5
5
  SHA512:
6
- metadata.gz: d23f08bf5951c9efa611c09a2ba3654ae8763b5bff0e4706ae68f540f52f3668b0c8b6f9bb4c076ba9ad6aff05df50de40f3d0c2e1b69ebd0aaafed6dcdafce2
7
- data.tar.gz: 9514884ededf873b309345eee8d6e217c6b16b49ba194b55d68825f8a544d907da6d6d624a620223653249da21c12a91de1efe235bb4e39ea5f97c95be15d11d
6
+ metadata.gz: 39999ff8baa584c7dd35a9b090feca1c410eedffcec72aba429ab65fe3d983c17517dff97363f4209c303d36252e3f46e1ad569ab908f2362c677064f43b54dc
7
+ data.tar.gz: d2b8d38f83a0bccdda315c5148d90a8d0b93821424fdb11b106ea322bcea09e3537d42c371a5cd266aa0c63a0e7f73c2d6285f302c7e444cfbf59f98c9451909
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: release
3
+ description: Prepare a new ExisRay release — bump version, update CHANGELOG, commit, push to main, and create git tag.
4
+ argument-hint: <version> (e.g. 0.5.9)
5
+ allowed-tools: [Read, Edit, Write, Bash, Glob, Grep]
6
+ ---
7
+
8
+ # Release ExisRay $ARGUMENTS
9
+
10
+ Seguí estos pasos para preparar y publicar la versión `$ARGUMENTS`.
11
+
12
+ ## 1. Verificar cambios pendientes
13
+
14
+ Revisá qué cambios hay desde el último release:
15
+
16
+ ```bash
17
+ git log --oneline $(git describe --tags --abbrev=0)..HEAD
18
+ git diff --stat
19
+ ```
20
+
21
+ ## 2. Verificar que los tests pasan
22
+
23
+ ```bash
24
+ bundle exec rspec
25
+ ```
26
+
27
+ Si hay fallos, detenerse y resolverlos antes de continuar.
28
+
29
+ ## 3. Bump de versión
30
+
31
+ Editá `lib/exis_ray/version.rb`:
32
+ ```ruby
33
+ VERSION = "$ARGUMENTS"
34
+ ```
35
+
36
+ ## 4. Actualizar CHANGELOG.md
37
+
38
+ Agregá una nueva entrada al tope del CHANGELOG con el formato:
39
+
40
+ ```markdown
41
+ ## [$ARGUMENTS] - FECHA_HOY
42
+
43
+ ### Added / Changed / Fixed / Removed
44
+ - **Componente:** Descripción del cambio.
45
+ ```
46
+
47
+ Basate en los commits desde el último tag para identificar qué va en cada sección.
48
+
49
+ ## 5. Commit
50
+
51
+ ```bash
52
+ git add lib/exis_ray/version.rb CHANGELOG.md [otros archivos modificados]
53
+ git commit -m "chore: bump version to $ARGUMENTS and update CHANGELOG"
54
+ ```
55
+
56
+ ## 6. Push a main y tag
57
+
58
+ ```bash
59
+ git push origin HEAD:main
60
+ git tag v$ARGUMENTS
61
+ git push origin v$ARGUMENTS
62
+ ```
63
+
64
+ El tag dispara el build automático en RubyGems vía CI.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: opentelemetry
3
+ description: This skill should be used when adding new log fields, designing new telemetry events, or reviewing field naming in this gem. Activates when the user mentions "otel", "opentelemetry", "semantic conventions", "field naming", "span", "trace", or asks about how to name a new log field.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # OpenTelemetry Alignment — ExisRay
8
+
9
+ ExisRay sigue el **OpenTelemetry Log Data Model**. Cuando se agregan campos nuevos, priorizar los nombres de OTel Semantic Conventions antes de inventar nombres propios.
10
+
11
+ ## Mapeo de Campos ExisRay → OTel
12
+
13
+ | Campo ExisRay | OTel Semantic Convention | Tipo |
14
+ | :------------- | :-------------------------- | :----- |
15
+ | `body` | `body` | String |
16
+ | `level` | `severity_text` | String |
17
+ | `duration_s` | `duration` (en segundos) | Float |
18
+ | `method` | `http.request.method` | String |
19
+ | `status` | `http.response.status_code` | Integer|
20
+ | `path` | `url.path` | String |
21
+ | `user_id` | `user.id` | String/Integer |
22
+
23
+ ## Convenciones de Naming
24
+
25
+ - **`snake_case`** siempre para keys
26
+ - **Unidades en la key, número en el valor:**
27
+ - `duration_s=1.25` (Float) — nunca `duration="1.25s"`
28
+ - `duration_ms=42` (Integer)
29
+ - `record_count=500` (Integer)
30
+ - `memory_mb=128` (Integer)
31
+ - **`_human`** para representación legible opcional: `duration_human="2 minutes"`
32
+
33
+ ## Campos Reservados
34
+
35
+ No usar estos nombres para otros propósitos — tienen semántica fija en ExisRay:
36
+
37
+ `time`, `level`, `service`, `root_id`, `trace_id`, `source`, `correlation_id`,
38
+ `user_id`, `isp_id`, `sidekiq_job`, `task`, `body`, `tags`
39
+
40
+ ## Log Body vs Campos Estructurados
41
+
42
+ - **Texto libre** → va en `body` (OTel log body)
43
+ - **Datos estructurados** → elevar al nivel raíz via KV string o Hash
44
+
45
+ ```ruby
46
+ # Correcto — datos estructurados al nivel raíz
47
+ logger.info "component=sync event=complete status=success duration_s=1.25"
48
+ # → {"component":"sync","event":"complete","status":"success","duration_s":1.25}
49
+
50
+ # Correcto — texto libre va en body
51
+ logger.info "Esto es un mensaje de texto libre"
52
+ # → {"body":"Esto es un mensaje de texto libre"}
53
+ ```
54
+
55
+ ## Trace Context (AWS X-Ray compatible)
56
+
57
+ ExisRay usa el formato AWS X-Ray pero es compatible con OTel trace context:
58
+
59
+ - `root_id` → equivalente a `trace_id` en OTel (solo el ID raíz, sin prefijos)
60
+ - `trace_id` → header completo X-Ray: `Root=1-...;Self=...;CalledFrom=...`
61
+ - `source` → entrypoint de ejecución (no tiene equivalente directo en OTel)
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: rails-expert
3
+ description: This skill should be used when writing or reviewing any Rails integration code in this gem — Railtie, middleware, CurrentAttributes, initializers, ActiveSupport notifications, or compatibility across Rails 6/7/8. Activates when the user mentions "railtie", "middleware", "initializer", "after_initialize", "current_attributes", "cache_classes", "enable_reloading", "log_subscriber", "tagged_logging", or asks about Rails compatibility.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Rails Expert — ExisRay Context
8
+
9
+ Este skill cubre las integraciones de ExisRay con Rails y las reglas de compatibilidad entre versiones.
10
+
11
+ ## Compatibilidad de Versiones
12
+
13
+ ### Rails 7.1+ vs 6/7.0
14
+
15
+ **Reloading:**
16
+ ```ruby
17
+ # Correcto — compatible con Rails 6, 7 y 8
18
+ def cache_classes?
19
+ config = Rails.application.config
20
+ if config.respond_to?(:enable_reloading)
21
+ !config.enable_reloading # Rails 7.1+ (semántica inversa)
22
+ else
23
+ config.cache_classes # Rails 6/7.0
24
+ end
25
+ end
26
+ ```
27
+
28
+ **Notifications (ActiveSupport):**
29
+ ```ruby
30
+ # Correcto — siempre usar respond_to? guard
31
+ if notifier.respond_to?(:all_listeners_for)
32
+ notifier.all_listeners_for(pattern) # Rails 7.1+
33
+ else
34
+ notifier.listeners_for(pattern) # Rails 6/7.0
35
+ end
36
+ ```
37
+
38
+ ## Railtie — Orden de Inicialización
39
+
40
+ ExisRay usa tres puntos de inicialización con propósitos distintos:
41
+
42
+ ```
43
+ 1. initializer "exis_ray.configure_middleware"
44
+ → Inserta HttpMiddleware en el stack Rack
45
+ → Corre antes de que la app arranque
46
+
47
+ 2. initializer "exis_ray.configure_logging", after: :load_config_initializers
48
+ → Lee config/initializers/exis_ray.rb antes de decidir la estrategia
49
+ → Configura log_tags para modo texto
50
+
51
+ 3. config.after_initialize
52
+ → Todo lo demás: JsonFormatter, LogSubscriber, BugBunny, Sidekiq, ActiveResource
53
+ → Garantiza que TODAS las gemas están cargadas (defined?(::BugBunny) funciona)
54
+ ```
55
+
56
+ **Regla crítica:** Cualquier integración condicional (`defined?(::GemaExterna)`) DEBE ir en `after_initialize`, no en `initializer`. De lo contrario la condición puede evaluarse antes de que la gema esté cargada.
57
+
58
+ ## CurrentAttributes
59
+
60
+ `ExisRay::Tracer` extiende `ActiveSupport::CurrentAttributes` — es thread-local y se resetea automáticamente al final de cada request HTTP (via `ActionDispatch::Executor`).
61
+
62
+ Para contextos no-HTTP (Sidekiq, BugBunny, Rake), siempre hacer reset manual en `ensure`:
63
+ ```ruby
64
+ ensure
65
+ ExisRay::Tracer.reset rescue nil
66
+ end
67
+ ```
68
+
69
+ ## LogSubscriber
70
+
71
+ `ExisRay::LogSubscriber` usa `ActiveSupport::Notifications`. Solo se instala con `json_logs: true`.
72
+
73
+ ```ruby
74
+ ExisRay::LogSubscriber.install!
75
+ # Internamente: subscribe a process_action.action_controller
76
+ # y desuscribe los subscribers por defecto de Rails
77
+ ```
78
+
79
+ ## TaggedLogging (modo texto)
80
+
81
+ En modo texto (`json_logs: false`), el contexto se inyecta via `log_tags`:
82
+ ```ruby
83
+ app.config.log_tags << proc { ExisRay::Tracer.trace_id.presence || ExisRay::Tracer.root_id.presence }
84
+ ```
85
+ Solo funciona dentro del ciclo de un HTTP request. Procesos background (Sidekiq, BugBunny) no tienen este contexto en modo texto.
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: readme-writer
3
+ description: This skill should be used when updating README.md or CHANGELOG.md after adding a new feature, fixing a bug, or releasing a version. Activates when the user mentions "readme", "changelog", "documentation", "release notes", "document this feature", or asks to update the docs.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # README & CHANGELOG Writer — ExisRay
8
+
9
+ ## README.md — Estructura y Estilo
10
+
11
+ El README de ExisRay sigue esta estructura fija. No reorganizar secciones existentes — solo agregar o actualizar.
12
+
13
+ ### Tono y Estilo
14
+ - Conciso y técnico — el lector es un dev Rails que quiere integrar la gema rápido
15
+ - Ejemplos de código reales, no abstractos
16
+ - Mostrar el output (JSON) cuando sea relevante para entender el valor
17
+ - Español para texto corrido, inglés para nombres de clases/métodos/campos
18
+
19
+ ### Secciones del README
20
+ 1. **Badges + descripción una línea**
21
+ 2. **Features** — lista bullets con las capacidades principales
22
+ 3. **Installation** — Gemfile + bundle
23
+ 4. **Configuration** — initializer con todos los options documentados
24
+ 5. **Usage por integración** — una subsección por integración (HTTP, Sidekiq, BugBunny, etc.)
25
+ 6. **Advanced** — subclassing Current, Reporter, LogSubscriber
26
+
27
+ ### Cómo documentar una nueva integración
28
+
29
+ ```markdown
30
+ ## E. NombreIntegración
31
+
32
+ Descripción en 1-2 oraciones de qué hace y por qué es útil.
33
+
34
+ ### Setup
35
+
36
+ \`\`\`ruby
37
+ # Código mínimo para activar la integración
38
+ \`\`\`
39
+
40
+ ### Output
41
+
42
+ \`\`\`json
43
+ {
44
+ "time": "...",
45
+ "root_id": "...",
46
+ "component": "...",
47
+ "event": "..."
48
+ }
49
+ \`\`\`
50
+ ```
51
+
52
+ ---
53
+
54
+ ## CHANGELOG.md — Formato
55
+
56
+ Seguir [Keep a Changelog](https://keepachangelog.com/). Cada entrada en orden cronológico inverso.
57
+
58
+ ### Estructura de una entrada
59
+
60
+ ```markdown
61
+ ## [X.Y.Z] - YYYY-MM-DD
62
+
63
+ ### Added
64
+ - **NombreFeature:** Descripción clara de qué se agregó y por qué es útil.
65
+ Mencionar el componente afectado y el beneficio para el usuario.
66
+
67
+ ### Changed
68
+ - **NombreComponente:** Qué cambió y por qué (motivación técnica o de UX).
69
+
70
+ ### Fixed
71
+ - **Descripción del bug:** Qué fallaba, en qué condición, cómo se resolvió.
72
+
73
+ ### Removed
74
+ - **NombreComponente:** Qué se eliminó y qué lo reemplaza (si aplica).
75
+ ```
76
+
77
+ ### Reglas
78
+ - Una entrada por item significativo — no agrupar cosas no relacionadas
79
+ - Mencionar el nombre del componente en **negrita** al inicio
80
+ - Describir el beneficio para el usuario, no solo el cambio técnico
81
+ - Si hay breaking change, marcarlo explícitamente: `⚠️ Breaking:`
82
+ - Versiones de patch (0.5.x): bugs y fixes pequeños
83
+ - Versiones minor (0.x.0): features nuevas, integraciones
84
+ - Versiones major (x.0.0): breaking changes
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: rubocop-omakase
3
+ description: This skill should be used when writing or reviewing Ruby code in this gem to ensure it follows RuboCop Rails Omakase conventions. Activates when writing new methods, refactoring existing code, or when the user mentions "rubocop", "style", "convention", "cop", or "omakase".
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # RuboCop Rails Omakase — ExisRay
8
+
9
+ Todo código nuevo o modificado debe seguir las convenciones de `rubocop-rails-omakase`. Solo aplicar a código que se está escribiendo o modificando — nunca sugerir fixes en código existente no tocado.
10
+
11
+ ## Reglas Principales
12
+
13
+ **Strings:**
14
+ ```ruby
15
+ # Frozen string literal obligatorio en todos los archivos
16
+ # frozen_string_literal: true
17
+
18
+ # Comillas dobles para strings
19
+ "hola mundo" # correcto
20
+ 'hola mundo' # incorrecto
21
+ ```
22
+
23
+ **Métodos:**
24
+ ```ruby
25
+ # Sin paréntesis en definición si no hay argumentos
26
+ def reset
27
+ # ...
28
+ end
29
+
30
+ # Con paréntesis cuando hay argumentos
31
+ def hydrate(trace_id:, source:)
32
+ # ...
33
+ end
34
+ ```
35
+
36
+ **Bloques:**
37
+ ```ruby
38
+ # { } para bloques de una línea
39
+ [1, 2].map { |n| n * 2 }
40
+
41
+ # do/end para bloques multilínea
42
+ [1, 2].each do |n|
43
+ puts n
44
+ puts n * 2
45
+ end
46
+ ```
47
+
48
+ **Condicionales:**
49
+ ```ruby
50
+ # Guard clauses para retornos tempranos
51
+ def process(value)
52
+ return unless value.present?
53
+ return "[FILTERED]" if sensitive?(value)
54
+
55
+ do_something(value)
56
+ end
57
+
58
+ # Ternario solo cuando es genuinamente simple
59
+ result = condition ? "yes" : "no"
60
+ ```
61
+
62
+ **Espaciado:**
63
+ ```ruby
64
+ # Línea en blanco después de definición de clase/módulo
65
+ class MyClass
66
+ # línea en blanco aquí si hay métodos
67
+
68
+ def my_method
69
+ end
70
+ end
71
+ ```
72
+
73
+ **Hash:**
74
+ ```ruby
75
+ # Ruby 3.1+ shorthand cuando key == variable
76
+ { name:, value: } # en lugar de { name: name, value: value }
77
+
78
+ # Rocket syntax solo para keys no-símbolo
79
+ { "string-key" => value }
80
+ { symbol_key: value }
81
+ ```
82
+
83
+ ## Patterns Comunes en ExisRay
84
+
85
+ ```ruby
86
+ # Módulos con solo métodos de clase — usar module_function o class << self
87
+ module ExisRay
88
+ module TaskMonitor
89
+ class << self
90
+ def run(task_name)
91
+ # ...
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ # Rescue en métodos que no deben interrumpir el flujo
98
+ def setup_context
99
+ do_risky_thing
100
+ rescue StandardError
101
+ # silenciar
102
+ end
103
+
104
+ # Lambdas con -> para bloques cortos
105
+ transformer = ->(value) { value.to_s.downcase }
106
+ ```
107
+
108
+ ## Verificación
109
+
110
+ Antes de proponer código, mentalmente verificar:
111
+ 1. `# frozen_string_literal: true` presente
112
+ 2. Strings con comillas dobles
113
+ 3. Guard clauses en lugar de if anidados
114
+ 4. `rescue StandardError` en operaciones de logging/tracing
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: yard
3
+ description: This skill should be used when writing or reviewing Ruby documentation in this gem. Activates when adding new methods, classes, or modules, or when the user mentions "yard", "documentation", "docstring", "@param", "@return", "@example", or asks how to document something.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # YARD Documentation — ExisRay Style
8
+
9
+ Toda clase, módulo y método público de ExisRay debe estar documentado con YARD. Los métodos privados solo se documentan si la lógica no es autoevidente.
10
+
11
+ ## Estructura de una Clase/Módulo
12
+
13
+ ```ruby
14
+ # Descripción en una línea.
15
+ #
16
+ # Párrafo de contexto opcional explicando el propósito y comportamiento general.
17
+ # Mencionar dependencias importantes o patrones de uso.
18
+ #
19
+ # @example Uso básico
20
+ # ExisRay::Tracer.hydrate(trace_id: header, source: 'http')
21
+ class ExisRay::Tracer < ActiveSupport::CurrentAttributes
22
+ ```
23
+
24
+ ## Métodos de Clase
25
+
26
+ ```ruby
27
+ # Descripción concisa del método en una línea.
28
+ #
29
+ # Párrafo opcional con detalles de comportamiento, casos edge, o efectos secundarios.
30
+ #
31
+ # @param trace_id [String] El header de traza entrante (formato AWS X-Ray).
32
+ # @param source [String] El entrypoint de ejecución ('http', 'sidekiq', 'task', 'system').
33
+ # @return [void]
34
+ def self.hydrate(trace_id:, source:)
35
+ ```
36
+
37
+ ## Tipos Comunes en ExisRay
38
+
39
+ | Tipo YARD | Cuándo usarlo |
40
+ |:----------|:-------------|
41
+ | `[String]` | Strings simples |
42
+ | `[String, nil]` | Puede ser nil |
43
+ | `[Boolean]` | true/false |
44
+ | `[Hash]` | Hash genérico |
45
+ | `[Hash{Symbol => Object}]` | Hash con tipos de keys/values |
46
+ | `[Class, nil]` | Clase o nil (para current_class, reporter_class) |
47
+ | `[void]` | Métodos sin valor de retorno significativo |
48
+ | `[Float]` | Duraciones en segundos |
49
+ | `[Integer]` | Duraciones en ms, counts |
50
+
51
+ ## Atributos con attr_accessor
52
+
53
+ ```ruby
54
+ # @!attribute [rw] trace_header
55
+ # @return [String] La key del header HTTP (formato Rack) para el Trace ID entrante.
56
+ # Por defecto es 'HTTP_X_AMZN_TRACE_ID'.
57
+ attr_accessor :trace_header
58
+ ```
59
+
60
+ ## Métodos Privados
61
+
62
+ Solo documentar si la lógica no es obvia:
63
+ ```ruby
64
+ # Limpia el Request ID para cumplir con los 24 caracteres hex de AWS X-Ray.
65
+ #
66
+ # @api private
67
+ # @return [String]
68
+ def self.clean_request_id
69
+ ```
70
+
71
+ ## Reglas
72
+
73
+ - Primera línea: siempre una oración completa terminada en punto
74
+ - `@example` cuando el uso no es obvio desde la firma
75
+ - `@api private` para métodos que usan `private_class_method`
76
+ - No documentar código que no fue modificado
77
+ - No agregar `@author` ni `@since` — no es el estilo del proyecto
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.5.9] - 2026-03-31
2
+
3
+ ### Fixed
4
+ - **JSON Object Coercion:** `JsonFormatter` ahora intenta parsear como JSON cualquier valor string que comience con `{` o `[` al procesar KV strings. Esto permite que campos como `queue_opts` y `exchange_opts` emitidos por BugBunny como JSON compacto se serialicen como objetos reales en lugar de strings escapados.
5
+
6
+ ### Added
7
+ - **CLAUDE.md:** Documentación completa del proyecto actualizada a v0.5.9: arquitectura, componentes, métodos clave, campos auto-inyectados, reglas de ejecución y guía de testing en consola.
8
+ - **`.claude/skills/`:** Skills de proyecto para rails-expert, YARD, OpenTelemetry, RuboCop Omakase y README writer — disponibles automáticamente para cualquier dev que use Claude Code en este repo.
9
+ - **`.claude/commands/release`:** Comando `/release` para automatizar el flujo de versioning.
10
+
1
11
  ## [0.5.8] - 2026-03-31
2
12
 
3
13
  ### Added
data/CLAUDE.md CHANGED
@@ -1,38 +1,158 @@
1
- # ExisRay Project Memory
2
-
3
- ## Architecture & Components
4
- - `ExisRay::Tracer`: Distributed tracing (AWS X-Ray). Uses `CurrentAttributes` for thread-safety.
5
- - `ExisRay::Current`: Business context (User, ISP). Abstract base class for host app subclassing.
6
- - `ExisRay::Reporter`: Sentry wrapper. Abstract base class for host app subclassing.
7
- - `ExisRay::JsonFormatter`: Core engine. Handles Hash, KV strings, and free-text with automatic masking and **Type Casting**.
8
- - `ExisRay::LogSubscriber`: Native HTTP logger since v0.4.0. Replaces Lograge.
9
- - `ExisRay::TaskMonitor`: Lifecycle manager for non-HTTP processes.
10
-
11
- ## Technical Knowledge & Compatibility
12
-
13
- ### Wispro Observability Spec (v1)
14
- - **Manifest:** All logging MUST follow the rules defined in `MANIFEST.md`.
15
- - **Metrics:** Always use `_s` suffix for durations (Float) and `count` for volumes (Integer). Never include units in values.
16
- - **Type-Awareness:** `JsonFormatter` automatically casts numeric KV values to Float/Integer. Emit raw numbers in KV strings.
17
- - **Automatic Fields:** Do not manually log `time`, `level`, `service`, `source`, `root_id`, `correlation_id`, `sidekiq_job` or `task`. These are handled by the library.
18
-
19
- ### Rails Compatibility (6, 7, 8)
20
- - **Reloading:** Use `cache_classes?` helper (checks `respond_to?(:enable_reloading)`) to avoid deprecation warnings in Rails 7.1+.
21
- - **Notifications:** Rails 7.1+ uses `all_listeners_for`, while 6/7.0 uses `listeners_for`. Always use `respond_to?` guards when manipulating subscribers.
22
-
23
- ### Distributed Tracing (AWS X-Ray)
24
- - **Propagation:** Use `propagation_trace_header` (standard HTTP format) for outgoing requests.
25
- - **Parsing:** `trace_header` (Rack format) is ONLY for incoming request parsing.
26
-
27
- ## Security & Privacy
28
- - **Sensitive Key Filtering:** `JsonFormatter` auto-filters keys matching `password|pass|passwd|secret|token|api_key|auth` replaced with `[FILTERED]`.
29
- - **Header Injection Prevention:** `ExisRay::Current` sanitizes values via `sanitize_header_value` before storing user/ISP identity.
30
- - **No PII in Logs:** Never log raw user data. Only IDs (`user_id`, `isp_id`) are permitted.
31
-
32
- ## Execution Rules
33
- - **No Lograge:** Do not suggest or re-add the Lograge dependency.
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.
36
- - **Resilience:** All logging operations must be wrapped in `rescue StandardError`.
37
- - **Automatic Fields:** NEVER manually log `time`, `level`, `service`, `source`, `root_id`, `correlation_id`, `sidekiq_job` or `task`. These are handled by the library.
38
- - **Source Values:** Valid values for `source` field: `http`, `sidekiq`, `task`, `system`.
1
+ # ExisRay Project Intelligence
2
+
3
+ ## Qué es esta gema
4
+
5
+ ExisRay es la capa de observabilidad y trazabilidad distribuida del ecosistema Wispro. Se integra con Rails para emitir logs estructurados en JSON, propagar trace context entre servicios (HTTP, Sidekiq, RabbitMQ), y mantener identidad de negocio (user_id, isp_id, correlation_id) en cada línea de log.
6
+
7
+ El estándar de logging que implementa está definido en `MANIFEST.md`. Ese documento es la fuente de verdad — cualquier duda sobre formato, campos, o semántica de niveles se resuelve ahí.
8
+
9
+ ---
10
+
11
+ ## Arquitectura & Componentes
12
+
13
+ ### Core
14
+ - **`ExisRay::Tracer`** Contexto de trazabilidad distribuida (AWS X-Ray). Extiende `ActiveSupport::CurrentAttributes` para thread-safety. Campos: `trace_id`, `root_id`, `self_id`, `source`, `created_at`, `request_id`, `sidekiq_job`, `task`.
15
+ - **`ExisRay::JsonFormatter`** Formateador global. Acepta Hash, KV strings (`key=value`) y texto libre. Inyecta automáticamente el contexto del Tracer y del Current en cada línea. Castea valores numéricos y filtra claves sensibles.
16
+ - **`ExisRay::Current`** Contexto de negocio (user_id, isp_id, correlation_id). Clase abstracta la app host la subclasifica.
17
+ - **`ExisRay::Reporter`** Wrapper de Sentry. Clase abstracta la app host la subclasifica.
18
+ - **`ExisRay::Configuration`** — Configuración global con defaults para AWS X-Ray.
19
+
20
+ ### Integraciones HTTP
21
+ - **`ExisRay::HttpMiddleware`** Rack middleware. Hidrata el Tracer con el header entrante. Se inserta automáticamente después de `ActionDispatch::RequestId`.
22
+ - **`ExisRay::LogSubscriber`** — Logger nativo de requests HTTP. Reemplaza Lograge. Solo activo con `json_logs: true`.
23
+ - **`ExisRay::FaradayMiddleware`** Inyecta `propagation_trace_header` en requests salientes via Faraday.
24
+ - **`ExisRay::ActiveResourceInstrumentation`** Ídem para ActiveResource.
25
+
26
+ ### Integraciones Sidekiq
27
+ - **`ExisRay::Sidekiq::ClientMiddleware`** Inyecta `exis_ray_trace` en el payload del job antes de encolarlo.
28
+ - **`ExisRay::Sidekiq::ServerMiddleware`** Hidrata el Tracer al inicio de cada job. Genera root_id nuevo si el job no trae trace.
29
+
30
+ ### Integraciones BugBunny (RabbitMQ)
31
+ - **`ExisRay::BugBunny::PublisherTracing`** — Middleware para `BugBunny::Client`/`BugBunny::Resource`. Inyecta `propagation_trace_header` en cada mensaje publicado.
32
+ - **`ExisRay::BugBunny::ConsumerTracingMiddleware`** — Consumer middleware. Corre antes de todos los logs internos de BugBunny. Hidrata el Tracer desde el header AMQP entrante o genera root_id nuevo.
33
+ - **Railtie hooks** `rpc_reply_headers` inyecta el trace actualizado en el reply del consumer. `on_rpc_reply` hidrata el Tracer en el thread del publisher al recibir la respuesta.
34
+
35
+ ### Procesos en Background
36
+ - **`ExisRay::TaskMonitor`** Lifecycle manager para Rake/Cron. Genera root_id, loguea `task_started`/`task_finished` con `duration_s` y `status`.
37
+
38
+ ---
39
+
40
+ ## Métodos Clave
41
+
42
+ ```ruby
43
+ # Hidratar el Tracer (HTTP, Sidekiq, BugBunny consumer)
44
+ ExisRay::Tracer.hydrate(trace_id: header_string, source: 'http')
45
+
46
+ # Sincronizar correlation_id al Current configurado
47
+ ExisRay.sync_correlation_id
48
+
49
+ # Generar header de propagación para el siguiente servicio
50
+ ExisRay::Tracer.generate_trace_header
51
+ # => "Root=1-abc123-...;Self=...;CalledFrom=wispro_agent;TotalTimeSoFar=42ms"
52
+
53
+ # Acceder a la configuración
54
+ ExisRay.configuration.propagation_trace_header # => 'X-Amzn-Trace-Id'
55
+ ExisRay.configuration.json_logs? # => true/false
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Campos Auto-Inyectados
61
+
62
+ `JsonFormatter` inyecta estos campos automáticamente. **Nunca** incluirlos manualmente en logs:
63
+
64
+ | Campo | Condición |
65
+ |:------|:----------|
66
+ | `time` | Siempre |
67
+ | `level` | Siempre |
68
+ | `service` | Siempre |
69
+ | `root_id` | Cuando hay trace context activo |
70
+ | `trace_id` | Cuando hay trace context activo |
71
+ | `source` | Cuando hay trace context activo |
72
+ | `correlation_id` | Cuando `Current.correlation_id` está presente |
73
+ | `user_id` | Cuando `Current.user_id` está presente |
74
+ | `isp_id` | Cuando `Current.isp_id` está presente |
75
+ | `sidekiq_job` | Solo en procesos Sidekiq |
76
+ | `task` | Solo en procesos TaskMonitor |
77
+ | `tags` | Solo si hay Rails tagged logging activo |
78
+
79
+ ---
80
+
81
+ ## Reglas de Ejecución
82
+
83
+ ### Logging
84
+ - Todo log interno usa KV strings: `component=exis_ray event=algo`
85
+ - `component` siempre en `snake_case`
86
+ - DEBUG siempre en block form: `logger.debug { "k=#{v}" }`
87
+ - Nunca `Kernel#warn` ni `$stderr`
88
+ - Toda operación de logging envuelta en `rescue StandardError`
89
+ - Duraciones con `Process.clock_gettime(Process::CLOCK_MONOTONIC)`, nunca `Time.now`
90
+
91
+ ### Seguridad
92
+ - Claves sensibles (`password|pass|passwd|secret|token|api_key|auth`) → `[FILTERED]`
93
+ - Nunca loguear PII. Solo `user_id`, `isp_id`
94
+
95
+ ### Source válidos
96
+ `http` | `sidekiq` | `task` | `system`
97
+
98
+ ### Propagación de headers
99
+ - **Entrante HTTP:** `trace_header` (formato Rack: `HTTP_X_AMZN_TRACE_ID`) — solo en `HttpMiddleware`
100
+ - **Saliente (todos los transportes):** `propagation_trace_header` (formato HTTP: `X-Amzn-Trace-Id`)
101
+
102
+ ### Prohibiciones
103
+ - No Lograge — reemplazado por `LogSubscriber`
104
+ - No loguear manualmente: `time`, `level`, `service`, `source`, `root_id`, `trace_id`, `correlation_id`, `sidekiq_job`, `task`
105
+ - No referenciar `.gemini/` — obsoleto
106
+
107
+ ---
108
+
109
+ ## Compatibilidad Rails
110
+
111
+ - **Reloading:** Usar `cache_classes?` helper (verifica `respond_to?(:enable_reloading)`) para Rails 7.1+
112
+ - **Notifications:** Rails 7.1+ usa `all_listeners_for`, Rails 6/7.0 usa `listeners_for` — siempre usar `respond_to?` guards
113
+ - **Soporte:** Rails 6, 7 y 8
114
+
115
+ ---
116
+
117
+ ## Integración Automática (Railtie)
118
+
119
+ El `Railtie` en `after_initialize` detecta y auto-instrumenta sin intervención del desarrollador:
120
+
121
+ | Gema detectada | Qué hace |
122
+ |:---------------|:---------|
123
+ | `BugBunny` | Registra `PublisherTracing` no — ese va en el cliente. Registra `ConsumerTracingMiddleware` y los hooks RPC |
124
+ | `Sidekiq` | Registra client + server middleware |
125
+ | `ActiveResource` | Prepend de `ActiveResourceInstrumentation` |
126
+ | `Faraday` | Disponible como middleware opcional |
127
+
128
+ ---
129
+
130
+ ## Configuración Mínima
131
+
132
+ ```ruby
133
+ # config/initializers/exis_ray.rb
134
+ ExisRay.configure do |config|
135
+ config.log_format = :json # :text por defecto
136
+ config.trace_header = 'HTTP_X_AMZN_TRACE_ID'
137
+ config.propagation_trace_header = 'X-Amzn-Trace-Id'
138
+ config.current_class = 'Current'
139
+ config.reporter_class = 'Reporter'
140
+ end
141
+
142
+ # BugBunny publisher — debe agregarse manualmente al cliente
143
+ BugBunny::Client.new(pool: pool) do |stack|
144
+ stack.use ExisRay::BugBunny::PublisherTracing
145
+ end
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Testing en Consola
151
+
152
+ ```ruby
153
+ # Inicializar trace context para probar desde rails console
154
+ ExisRay::Tracer.hydrate(
155
+ trace_id: "Root=1-#{Time.now.to_i.to_s(16)}-#{SecureRandom.hex(12)}",
156
+ source: 'system'
157
+ )
158
+ ```
@@ -148,7 +148,7 @@ module ExisRay
148
148
  end
149
149
 
150
150
  # Filtra un valor si la clave se considera sensible.
151
- # Si no es sensible, intenta castear el valor a número si corresponde.
151
+ # Si no es sensible, intenta castear el valor a número o a objeto JSON si corresponde.
152
152
  #
153
153
  # @param key [String, Symbol]
154
154
  # @param value [Object]
@@ -159,10 +159,24 @@ module ExisRay
159
159
  case value
160
160
  when /\A\d+\z/ then value.to_i
161
161
  when /\A\d+\.\d+\z/ then value.to_f
162
- else value
162
+ else try_parse_json(value)
163
163
  end
164
164
  end
165
165
 
166
+ # Intenta parsear un string como JSON si parece un objeto o array.
167
+ # Permite que valores como queue_opts={"exclusive":false} se emitan como
168
+ # objetos JSON en lugar de strings escapados.
169
+ #
170
+ # @param value [Object]
171
+ # @return [Object] El objeto parseado o el valor original si no es JSON válido.
172
+ def try_parse_json(value)
173
+ return value unless value.is_a?(String) && (value.start_with?("{") || value.start_with?("["))
174
+
175
+ JSON.parse(value)
176
+ rescue JSON::ParserError
177
+ value
178
+ end
179
+
166
180
  # Filtra recursivamente un Hash que contenga claves sensibles.
167
181
  # Maneja valores anidados de tipo Hash o Array.
168
182
  #
@@ -2,5 +2,5 @@
2
2
 
3
3
  module ExisRay
4
4
  # Versión actual de la gema.
5
- VERSION = "0.5.8"
5
+ VERSION = "0.5.9"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exis_ray
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Edera
@@ -45,6 +45,12 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".claude/commands/release.md"
49
+ - ".claude/skills/opentelemetry/SKILL.md"
50
+ - ".claude/skills/rails-expert/SKILL.md"
51
+ - ".claude/skills/readme-writer/SKILL.md"
52
+ - ".claude/skills/rubocop-omakase/SKILL.md"
53
+ - ".claude/skills/yard/SKILL.md"
48
54
  - ".gemini/docs/TELEMETRY_GUIDELINES.md"
49
55
  - CHANGELOG.md
50
56
  - CLAUDE.md