exis_ray 0.10.0 → 0.11.1
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/AGENTS.md +238 -0
- data/CHANGELOG.md +15 -0
- data/CLAUDE.md +9 -183
- data/README.md +36 -6
- data/docs/config/configuracion.md +144 -0
- data/docs/interface/interface.md +120 -0
- data/docs/test/testing.md +156 -0
- data/lib/exis_ray/configuration.rb +21 -0
- data/lib/exis_ray/log_subscriber.rb +13 -7
- data/lib/exis_ray/task_monitor.rb +21 -4
- data/lib/exis_ray/version.rb +1 -1
- data/skill/SKILL.md +15 -8
- data/skills.yml +6 -24
- data/spec/exis_ray/configuration_spec.rb +22 -0
- data/spec/exis_ray/log_subscriber_spec.rb +43 -0
- data/spec/exis_ray/task_monitor_spec.rb +15 -0
- metadata +8 -5
- data/.gemini/docs/TELEMETRY_GUIDELINES.md +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 833391532fe44ad6a4014ade7f96f43b8f24b32af0c80764d064a2222c8aca59
|
|
4
|
+
data.tar.gz: 0f49d7c49dcaec8ab49ce992798bc79404c1a457c9531207d807faadaae3a1a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0b580fddcadbe0cfc165d7c3e601c184fe31f504cd68be2cddf44b7a0fb661d398c1170534532115a5cb0b18c1c591a749e08868fd0b767d07b966b0a3c1e39
|
|
7
|
+
data.tar.gz: e29e918ecec0e4e7dd155270978bd8c63bda07d447215811bcd7b6901f36b7748fb9a3cd7a2fbe18938c572620e95a9f6305cc3b8805d06011b9f49911499b79
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# ExisRay — Project Intelligence
|
|
2
|
+
|
|
3
|
+
> Fuente de verdad del repositorio. Reglas, convenciones, estructura, entorno y
|
|
4
|
+
> arquitectura viven acá. `CLAUDE.md` queda reservado para notas específicas de
|
|
5
|
+
> Claude Code. Si una regla aplica a cualquier agente, vive en este archivo.
|
|
6
|
+
|
|
7
|
+
## ¿Qué es ExisRay?
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
El estándar de logging que implementa está definido en `skill/SKILL.md` (API, arquitectura, reglas generales) y `skill/references/standard.md` (Data First, mapeo OpenTelemetry, ciclo de vida). Son la fuente de verdad — cualquier duda sobre formato, campos, o semántica de niveles se resuelve ahí.
|
|
12
|
+
|
|
13
|
+
## Documentación
|
|
14
|
+
|
|
15
|
+
- **Para humanos**: `docs/` + `README.md`. Ver README para índice.
|
|
16
|
+
- **Para agentes AI**: `skill/SKILL.md` + `skill/references/`. Es la skill empaquetada que otros proyectos consumen via `wispro-agent sync`.
|
|
17
|
+
- **Nunca referenciar `skill/` desde `docs/` o `README.md`** — son audiencias distintas.
|
|
18
|
+
|
|
19
|
+
## Mapa de conocimiento (cómo leer la doc de este repo)
|
|
20
|
+
|
|
21
|
+
- **Tu conocimiento = la UNIÓN de este repo + sus asociados.** No termina en el `docs/<capa>/` local: incluye la doc de los servicios/gemas de `skills.yml`. Un flujo que cruza servicios (e2e) **no vive como doc estática** en ningún repo — se compone on-demand recorriendo el grafo (RFC-021): seguí las anclas hasta los repos asociados y unificá.
|
|
22
|
+
- **Entrá por** [`skill/SKILL.md`](skill/SKILL.md) — índice de agente; resume el contrato y linkea el detalle.
|
|
23
|
+
- **Cobertura de capas de este repo** (gema de observabilidad, sin DB, instrumenta al host):
|
|
24
|
+
|
|
25
|
+
| capa | RFC | estado | artefacto / motivo |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| comportamiento | RFC-007 | **presente** | [`docs/behavior/behavior.md`](docs/behavior/behavior.md) — parcial incremental |
|
|
28
|
+
| glosario | RFC-009 | **presente** | [`docs/glossary/glossary.md`](docs/glossary/glossary.md) — sembrado, acreta |
|
|
29
|
+
| test | RFC-013 | **presente** | [`docs/test/testing.md`](docs/test/testing.md) — piloto RFC-013 |
|
|
30
|
+
| configuración | RFC-012 | **presente** | [`docs/config/configuracion.md`](docs/config/configuracion.md) — inventario base; enriquecimiento §f pendiente |
|
|
31
|
+
| datos | RFC-002 | `n/a` | gema sin DB (sin `db/schema.rb`) |
|
|
32
|
+
| operaciones | RFC-003 | `n/a` | no expone superficie propia (HTTP/CLI/eventos) — instrumenta al host |
|
|
33
|
+
| dependencias consumidas | RFC-018 | `n/a` | inyecta hooks de tracing, no consume servicios |
|
|
34
|
+
| eventos | RFC-005 | `n/a` | no produce eventos propios; propaga trace en mensajes ajenos |
|
|
35
|
+
| interfaz | RFC-004 | **presente** | [`docs/interface/interface.md`](docs/interface/interface.md) — superficie pública consumer-facing |
|
|
36
|
+
| topología | RFC-006 | **pendiente** | deps + adapters Sidekiq/BugBunny/Faraday/ActiveResource |
|
|
37
|
+
| release | RFC-014 | **pendiente** | `version.rb` + `CHANGELOG.md` + `.github/workflows/release.yml` (tag `v*` → RubyGems) |
|
|
38
|
+
| errores | RFC-020 | **pendiente** | excepciones de validación de config en el Railtie |
|
|
39
|
+
|
|
40
|
+
- **Navegar una ancla cross-repo:** tomá la key de servicio en `skills.yml` (`services.<dep>.repo`) → ese repo es checkout hermano local o alcanzable por GitHub MCP. La doc de los asociados ES parte de tu conocimiento accesible.
|
|
41
|
+
|
|
42
|
+
## Convenciones del framework
|
|
43
|
+
|
|
44
|
+
- Este repo **consume skills del framework** declaradas en `skills.yml` (manifiesto raíz). Ese archivo enumera los MCPs y las skills que el repo trae al contexto del agente.
|
|
45
|
+
- Las skills sincronizadas en `.agents/skills/` traen **conocimiento de dependencias**. **Leer la skill de una dependencia ANTES de responder sobre ella.**
|
|
46
|
+
- El sync de skills lo hace el CLI: `wispro-agent sync`.
|
|
47
|
+
|
|
48
|
+
## Knowledge Base
|
|
49
|
+
|
|
50
|
+
- Las skills en `.agents/skills/` incluyen conocimiento de dependencias.
|
|
51
|
+
- Leer la skill de una dependencia ANTES de responder sobre ella.
|
|
52
|
+
- Rebuild / sincronización: `wispro-agent sync`.
|
|
53
|
+
|
|
54
|
+
### Entorno
|
|
55
|
+
|
|
56
|
+
- Versión de Ruby: leer `.ruby-version`
|
|
57
|
+
- Versión de Rails y gemas: leer `Gemfile.lock`
|
|
58
|
+
- Gestor de Ruby: chruby (no usar rvm ni rbenv)
|
|
59
|
+
- Package manager: Bundler
|
|
60
|
+
|
|
61
|
+
### RuboCop
|
|
62
|
+
|
|
63
|
+
- Usamos rubocop-rails-omakase como base.
|
|
64
|
+
- Correr `bundle exec rubocop -a` antes de commitear.
|
|
65
|
+
- No deshabilitar cops sin justificación en el PR.
|
|
66
|
+
|
|
67
|
+
### YARD
|
|
68
|
+
|
|
69
|
+
- Documentación incremental: si tocás un método, documentalo con YARD.
|
|
70
|
+
- Consultar la skill `yard` para tags y tipos correctos.
|
|
71
|
+
- Verificar cobertura: `bundle exec yard stats --list-undoc`
|
|
72
|
+
|
|
73
|
+
### Testing
|
|
74
|
+
|
|
75
|
+
- Framework: RSpec
|
|
76
|
+
- Correr: `bundle exec rspec`
|
|
77
|
+
- Todo código nuevo debe tener tests.
|
|
78
|
+
|
|
79
|
+
### Releases o Nuevas versiones
|
|
80
|
+
|
|
81
|
+
- Usar `/gem-release` para publicar nuevas versiones.
|
|
82
|
+
- El GitHub Action publica a RubyGems automáticamente al pushear un tag `v*`.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Arquitectura & Componentes
|
|
87
|
+
|
|
88
|
+
### Core
|
|
89
|
+
|
|
90
|
+
- **`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`.
|
|
91
|
+
- **`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.
|
|
92
|
+
- **`ExisRay::Current`** — Contexto de negocio (user_id, isp_id, correlation_id). Clase abstracta — la app host la subclasifica.
|
|
93
|
+
- **`ExisRay::Reporter`** — Wrapper de Sentry. Clase abstracta — la app host la subclasifica.
|
|
94
|
+
- **`ExisRay::Configuration`** — Configuración global con defaults para AWS X-Ray.
|
|
95
|
+
|
|
96
|
+
### Integraciones HTTP
|
|
97
|
+
|
|
98
|
+
- **`ExisRay::HttpMiddleware`** — Rack middleware. Hidrata el Tracer con el header entrante. Se inserta automáticamente después de `ActionDispatch::RequestId`.
|
|
99
|
+
- **`ExisRay::LogSubscriber`** — Logger nativo de requests HTTP. Reemplaza Lograge. Solo activo con `json_logs: true`.
|
|
100
|
+
- **`ExisRay::FaradayMiddleware`** — Inyecta `propagation_trace_header` en requests salientes via Faraday.
|
|
101
|
+
- **`ExisRay::ActiveResourceInstrumentation`** — Ídem para ActiveResource.
|
|
102
|
+
|
|
103
|
+
### Integraciones Sidekiq
|
|
104
|
+
|
|
105
|
+
- **`ExisRay::Sidekiq::ClientMiddleware`** — Inyecta `exis_ray_trace` en el payload del job antes de encolarlo.
|
|
106
|
+
- **`ExisRay::Sidekiq::ServerMiddleware`** — Hidrata el Tracer al inicio de cada job. Genera root_id nuevo si el job no trae trace.
|
|
107
|
+
|
|
108
|
+
### Integraciones BugBunny (RabbitMQ)
|
|
109
|
+
|
|
110
|
+
- **`ExisRay::BugBunny::PublisherTracing`** — Middleware para `BugBunny::Client`/`BugBunny::Resource`. Inyecta `propagation_trace_header` en cada mensaje publicado.
|
|
111
|
+
- **`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.
|
|
112
|
+
- **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.
|
|
113
|
+
|
|
114
|
+
### Procesos en Background
|
|
115
|
+
|
|
116
|
+
- **`ExisRay::TaskMonitor`** — Lifecycle manager para Rake/Cron. Genera root_id, loguea `task_started`/`task_finished` con `duration_s` y `status`.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Métodos Clave
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
# Hidratar el Tracer (HTTP, Sidekiq, BugBunny consumer)
|
|
124
|
+
ExisRay::Tracer.hydrate(trace_id: header_string, source: 'http')
|
|
125
|
+
|
|
126
|
+
# Sincronizar correlation_id al Current configurado
|
|
127
|
+
ExisRay.sync_correlation_id
|
|
128
|
+
|
|
129
|
+
# Generar header de propagación para el siguiente servicio
|
|
130
|
+
ExisRay::Tracer.generate_trace_header
|
|
131
|
+
# => "Root=1-abc123-...;Self=...;CalledFrom=wispro_agent;TotalTimeSoFar=42ms"
|
|
132
|
+
|
|
133
|
+
# Acceder a la configuración
|
|
134
|
+
ExisRay.configuration.propagation_trace_header # => 'X-Amzn-Trace-Id'
|
|
135
|
+
ExisRay.configuration.json_logs? # => true/false
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Campos Auto-Inyectados
|
|
141
|
+
|
|
142
|
+
`JsonFormatter` inyecta estos campos automáticamente. **Nunca** incluirlos manualmente en logs:
|
|
143
|
+
|
|
144
|
+
| Campo | Condición |
|
|
145
|
+
|:------|:----------|
|
|
146
|
+
| `time` | Siempre |
|
|
147
|
+
| `level` | Siempre |
|
|
148
|
+
| `severity_number` | Siempre (OTel: DEBUG=5, INFO=9, WARN=13, ERROR=17, FATAL=21) |
|
|
149
|
+
| `service` | Siempre |
|
|
150
|
+
| `service_version` | Siempre (de `config.version` o `config.x.version`) |
|
|
151
|
+
| `deployment_environment` | Siempre (de `Rails.env`) |
|
|
152
|
+
| `request_id` | Cuando `Tracer.request_id` está presente (independiente de root_id) |
|
|
153
|
+
| `root_id` | Cuando hay trace context activo |
|
|
154
|
+
| `trace_id` | Cuando hay trace context activo |
|
|
155
|
+
| `source` | Cuando hay trace context activo (HTTP siempre genera root fresco si no llega header) |
|
|
156
|
+
| `correlation_id` | Cuando `Current.correlation_id` está presente |
|
|
157
|
+
| `user_id` | Cuando `Current.user_id` está presente |
|
|
158
|
+
| `isp_id` | Cuando `Current.isp_id` está presente |
|
|
159
|
+
| `Current.log_fields` (cualquier key) | Si la subclass overrideó el hook (default `{}`) |
|
|
160
|
+
| `sidekiq_job` | Solo en procesos Sidekiq |
|
|
161
|
+
| `task` | Solo en procesos TaskMonitor |
|
|
162
|
+
| `tags` | Solo si hay Rails tagged logging activo |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Reglas de Ejecución
|
|
167
|
+
|
|
168
|
+
### Logging
|
|
169
|
+
|
|
170
|
+
- Todo log interno usa KV strings: `component=exis_ray event=algo`
|
|
171
|
+
- `component` siempre en `snake_case`
|
|
172
|
+
- DEBUG siempre en block form: `logger.debug { "k=#{v}" }`
|
|
173
|
+
- Nunca `Kernel#warn` ni `$stderr`
|
|
174
|
+
- Toda operación de logging envuelta en `rescue StandardError`
|
|
175
|
+
- Duraciones con `Process.clock_gettime(Process::CLOCK_MONOTONIC)`, nunca `Time.now`
|
|
176
|
+
|
|
177
|
+
### Seguridad
|
|
178
|
+
|
|
179
|
+
- Claves sensibles (`password|pass|passwd|secret|token|api_key|auth`) → `[FILTERED]`
|
|
180
|
+
- Nunca loguear PII. Solo `user_id`, `isp_id`
|
|
181
|
+
|
|
182
|
+
### Source válidos
|
|
183
|
+
|
|
184
|
+
`http` | `sidekiq` | `task` | `system`
|
|
185
|
+
|
|
186
|
+
### Propagación de headers
|
|
187
|
+
|
|
188
|
+
- **Entrante HTTP:** `trace_header` (formato Rack: `HTTP_X_AMZN_TRACE_ID`) — solo en `HttpMiddleware`
|
|
189
|
+
- **Saliente (todos los transportes):** `propagation_trace_header` (formato HTTP: `X-Amzn-Trace-Id`)
|
|
190
|
+
|
|
191
|
+
### Prohibiciones
|
|
192
|
+
|
|
193
|
+
- No Lograge — reemplazado por `LogSubscriber`
|
|
194
|
+
- No loguear manualmente: `time`, `level`, `service`, `source`, `root_id`, `trace_id`, `correlation_id`, `sidekiq_job`, `task`
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Compatibilidad Rails
|
|
199
|
+
|
|
200
|
+
- **Reloading:** Usar `cache_classes?` helper (verifica `respond_to?(:enable_reloading)`) para Rails 7.1+
|
|
201
|
+
- **Notifications:** Rails 7.1+ usa `all_listeners_for`, Rails 6/7.0 usa `listeners_for` — siempre usar `respond_to?` guards
|
|
202
|
+
- **Soporte:** Rails 6, 7 y 8
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Integración Automática (Railtie)
|
|
207
|
+
|
|
208
|
+
El `Railtie` en `after_initialize` detecta y auto-instrumenta sin intervención del desarrollador:
|
|
209
|
+
|
|
210
|
+
| Gema detectada | Qué hace |
|
|
211
|
+
|:---------------|:---------|
|
|
212
|
+
| `BugBunny` | Registra `ConsumerTracingMiddleware` y los hooks RPC (`PublisherTracing` va en el cliente, manual) |
|
|
213
|
+
| `Sidekiq` | Registra client + server middleware |
|
|
214
|
+
| `ActiveResource` | Prepend de `ActiveResourceInstrumentation` |
|
|
215
|
+
| `Faraday` | Disponible como middleware opcional |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Configuración Mínima
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
# config/initializers/exis_ray.rb
|
|
223
|
+
ExisRay.configure do |config|
|
|
224
|
+
config.log_format = :json # :text por defecto
|
|
225
|
+
config.trace_header = 'HTTP_X_AMZN_TRACE_ID'
|
|
226
|
+
config.propagation_trace_header = 'X-Amzn-Trace-Id'
|
|
227
|
+
config.current_class = 'Current'
|
|
228
|
+
config.reporter_class = 'Reporter'
|
|
229
|
+
config.emit_legacy_exception_keys = true # default true; pasar a false cuando consumers usen exception.*
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# BugBunny publisher — debe agregarse manualmente al cliente
|
|
233
|
+
BugBunny::Client.new(pool: pool) do |stack|
|
|
234
|
+
stack.use ExisRay::BugBunny::PublisherTracing
|
|
235
|
+
end
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [0.11.1] - 2026-06-29
|
|
2
|
+
|
|
3
|
+
### Documentación
|
|
4
|
+
- **Capa `config` (RFC-012)**: nuevo `docs/config/configuracion.md` — inventario base (10 opciones de `ExisRay.configure` + `HOSTNAME` + inyecciones del Railtie al host §i + gemas configuradas §j) y enriquecimiento semántico §f (categoría · failure-mode · side-effect · scope-override · business-reason por var, 11/11), §g ramificadores y §h threading. — @gedera
|
|
5
|
+
- **Capa `interface` (RFC-004)**: nuevo `docs/interface/interface.md` — superficie Ruby pública consumer-facing (símbolo · tipo · firma · nota), cerrando la coexistencia transitoria del contrato embebido en `skill/SKILL.md`. — @gedera
|
|
6
|
+
- **Capa `test` (RFC-013)**: `docs/test/testing.md` — piloto del artefacto de test (suites RSpec, fixtures, coverage); status RFC-013 `accepted`. — @gedera
|
|
7
|
+
- **Mapa de conocimiento (RFC-008 r2)** en `AGENTS.md`: tabla de cobertura de las 12 capas (presente / n/a / pendiente). `skill/SKILL.md` y `README.md` recompuestos indexando las capas nuevas; SKILL re-anclado a v0.11.1. Sin cambios de código (`lib/` intacto). — @gedera
|
|
8
|
+
|
|
9
|
+
## [0.11.0] - 2026-05-26
|
|
10
|
+
|
|
11
|
+
### Nuevas funcionalidades
|
|
12
|
+
- **`config.emit_legacy_exception_keys`** (#15): nuevo flag de `Configuration` (default `true`) que controla la emisión de las keys legacy `error_class`/`error_message` junto a `exception.type`/`exception.message`. Bajado a `false`, `ExisRay::LogSubscriber` (logs HTTP) y `ExisRay::TaskMonitor` (logs de tasks) emiten solo las keys OTel v1.0 (`exception.*`), reduciendo bytes y ruido downstream cuando los consumers (dashboards, alertas, queries) ya migraron. Mantiene `true` como default durante la ventana de transición — se flipea a `false` en v0.12.0 y se remueve el flag en v1.0 cuando los legacy se eliminen definitivamente.
|
|
13
|
+
- **`url.path` (OTel v1.0) + `config.emit_legacy_path_key`** (#15): `ExisRay::LogSubscriber` empieza a emitir `url.path` (nombre canónico OTel) en todos los logs HTTP. El alias legacy `path` se sigue emitiendo durante la ventana de transición y queda gateado por `config.emit_legacy_path_key` (default `true`). Cuando los consumers (queries, dashboards, alertas) hayan migrado a `url.path`, bajar el flag a `false` para suprimir el alias. Se flipea a `false` en v0.12.0 y se remueve el flag en v1.0.
|
|
14
|
+
- **Doc: `url.path` vs `http_route` no se deduplican** (#15): el caso reportado de "valores idénticos en endpoints estáticos" no es un bug — `url.path` es la URL **concreta** (alta cardinalidad), `http_route` es el **template** (baja cardinalidad). Cumplen roles distintos en dashboards/queries downstream; siguen emitiéndose siempre los dos. Documentado en `README.md` y `skill/SKILL.md` (subsección "Migración OTel — `path` → `url.path`" + nota explicativa de la coexistencia semántica).
|
|
15
|
+
|
|
1
16
|
## [0.10.0] - 2026-05-25
|
|
2
17
|
|
|
3
18
|
### Documentación
|
data/CLAUDE.md
CHANGED
|
@@ -1,189 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Claude Code Notes
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Fuente de verdad del repositorio
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Las reglas, convenciones, estructura y contexto del proyecto viven en `AGENTS.md`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Leer `AGENTS.md` antes de hacer cambios.
|
|
8
|
+
- Tratar `AGENTS.md` como fuente de verdad para identidad, estructura, documentación, convenciones del framework, entorno, y arquitectura.
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Propósito de este archivo
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
- **Para agentes AI**: `skill/SKILL.md` + `skill/references/`. Es la skill empaquetada que otros proyectos consumen via `skill-manager sync`.
|
|
13
|
-
- **Nunca referenciar `skill/` desde `docs/` o `README.md`** — son audiencias distintas.
|
|
12
|
+
`CLAUDE.md` queda reservado para instrucciones o notas específicas de Claude Code.
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
- Leer la skill de una dependencia ANTES de responder sobre ella.
|
|
18
|
-
- Rebuild: `ruby .agents/skills/skill-manager/scripts/sync.rb`
|
|
19
|
-
|
|
20
|
-
### Entorno
|
|
21
|
-
- Versión de Ruby: leer `.ruby-version`
|
|
22
|
-
- Versión de Rails y gemas: leer `Gemfile.lock`
|
|
23
|
-
- Gestor de Ruby: chruby (no usar rvm ni rbenv)
|
|
24
|
-
- Package manager: Bundler
|
|
25
|
-
|
|
26
|
-
### RuboCop
|
|
27
|
-
- Usamos rubocop-rails-omakase como base.
|
|
28
|
-
- Correr `bundle exec rubocop -a` antes de commitear.
|
|
29
|
-
- No deshabilitar cops sin justificación en el PR.
|
|
30
|
-
|
|
31
|
-
### YARD
|
|
32
|
-
- Documentación incremental: si tocás un método, documentalo con YARD.
|
|
33
|
-
- Consultar la skill `yard` para tags y tipos correctos.
|
|
34
|
-
- Verificar cobertura: `bundle exec yard stats --list-undoc`
|
|
35
|
-
|
|
36
|
-
### Testing
|
|
37
|
-
- Framework: RSpec
|
|
38
|
-
- Correr: `bundle exec rspec`
|
|
39
|
-
- Todo código nuevo debe tener tests.
|
|
40
|
-
|
|
41
|
-
### Releases o Nuevas versiones
|
|
42
|
-
- Usar `/gem-release` para publicar nuevas versiones.
|
|
43
|
-
- El GitHub Action publica a RubyGems automáticamente al pushear un tag `v*`.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Arquitectura & Componentes
|
|
48
|
-
|
|
49
|
-
### Core
|
|
50
|
-
- **`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`.
|
|
51
|
-
- **`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.
|
|
52
|
-
- **`ExisRay::Current`** — Contexto de negocio (user_id, isp_id, correlation_id). Clase abstracta — la app host la subclasifica.
|
|
53
|
-
- **`ExisRay::Reporter`** — Wrapper de Sentry. Clase abstracta — la app host la subclasifica.
|
|
54
|
-
- **`ExisRay::Configuration`** — Configuración global con defaults para AWS X-Ray.
|
|
55
|
-
|
|
56
|
-
### Integraciones HTTP
|
|
57
|
-
- **`ExisRay::HttpMiddleware`** — Rack middleware. Hidrata el Tracer con el header entrante. Se inserta automáticamente después de `ActionDispatch::RequestId`.
|
|
58
|
-
- **`ExisRay::LogSubscriber`** — Logger nativo de requests HTTP. Reemplaza Lograge. Solo activo con `json_logs: true`.
|
|
59
|
-
- **`ExisRay::FaradayMiddleware`** — Inyecta `propagation_trace_header` en requests salientes via Faraday.
|
|
60
|
-
- **`ExisRay::ActiveResourceInstrumentation`** — Ídem para ActiveResource.
|
|
61
|
-
|
|
62
|
-
### Integraciones Sidekiq
|
|
63
|
-
- **`ExisRay::Sidekiq::ClientMiddleware`** — Inyecta `exis_ray_trace` en el payload del job antes de encolarlo.
|
|
64
|
-
- **`ExisRay::Sidekiq::ServerMiddleware`** — Hidrata el Tracer al inicio de cada job. Genera root_id nuevo si el job no trae trace.
|
|
65
|
-
|
|
66
|
-
### Integraciones BugBunny (RabbitMQ)
|
|
67
|
-
- **`ExisRay::BugBunny::PublisherTracing`** — Middleware para `BugBunny::Client`/`BugBunny::Resource`. Inyecta `propagation_trace_header` en cada mensaje publicado.
|
|
68
|
-
- **`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.
|
|
69
|
-
- **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.
|
|
70
|
-
|
|
71
|
-
### Procesos en Background
|
|
72
|
-
- **`ExisRay::TaskMonitor`** — Lifecycle manager para Rake/Cron. Genera root_id, loguea `task_started`/`task_finished` con `duration_s` y `status`.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Métodos Clave
|
|
77
|
-
|
|
78
|
-
```ruby
|
|
79
|
-
# Hidratar el Tracer (HTTP, Sidekiq, BugBunny consumer)
|
|
80
|
-
ExisRay::Tracer.hydrate(trace_id: header_string, source: 'http')
|
|
81
|
-
|
|
82
|
-
# Sincronizar correlation_id al Current configurado
|
|
83
|
-
ExisRay.sync_correlation_id
|
|
84
|
-
|
|
85
|
-
# Generar header de propagación para el siguiente servicio
|
|
86
|
-
ExisRay::Tracer.generate_trace_header
|
|
87
|
-
# => "Root=1-abc123-...;Self=...;CalledFrom=wispro_agent;TotalTimeSoFar=42ms"
|
|
88
|
-
|
|
89
|
-
# Acceder a la configuración
|
|
90
|
-
ExisRay.configuration.propagation_trace_header # => 'X-Amzn-Trace-Id'
|
|
91
|
-
ExisRay.configuration.json_logs? # => true/false
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## Campos Auto-Inyectados
|
|
97
|
-
|
|
98
|
-
`JsonFormatter` inyecta estos campos automáticamente. **Nunca** incluirlos manualmente en logs:
|
|
99
|
-
|
|
100
|
-
| Campo | Condición |
|
|
101
|
-
|:------|:----------|
|
|
102
|
-
| `time` | Siempre |
|
|
103
|
-
| `level` | Siempre |
|
|
104
|
-
| `severity_number` | Siempre (OTel: DEBUG=5, INFO=9, WARN=13, ERROR=17, FATAL=21) |
|
|
105
|
-
| `service` | Siempre |
|
|
106
|
-
| `service_version` | Siempre (de `config.version` o `config.x.version`) |
|
|
107
|
-
| `deployment_environment` | Siempre (de `Rails.env`) |
|
|
108
|
-
| `request_id` | Cuando `Tracer.request_id` está presente (independiente de root_id) |
|
|
109
|
-
| `root_id` | Cuando hay trace context activo |
|
|
110
|
-
| `trace_id` | Cuando hay trace context activo |
|
|
111
|
-
| `source` | Cuando hay trace context activo (HTTP siempre genera root fresco si no llega header) |
|
|
112
|
-
| `correlation_id` | Cuando `Current.correlation_id` está presente |
|
|
113
|
-
| `user_id` | Cuando `Current.user_id` está presente |
|
|
114
|
-
| `isp_id` | Cuando `Current.isp_id` está presente |
|
|
115
|
-
| `Current.log_fields` (cualquier key) | Si la subclass overrideó el hook (default `{}`) |
|
|
116
|
-
| `sidekiq_job` | Solo en procesos Sidekiq |
|
|
117
|
-
| `task` | Solo en procesos TaskMonitor |
|
|
118
|
-
| `tags` | Solo si hay Rails tagged logging activo |
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Reglas de Ejecución
|
|
123
|
-
|
|
124
|
-
### Logging
|
|
125
|
-
- Todo log interno usa KV strings: `component=exis_ray event=algo`
|
|
126
|
-
- `component` siempre en `snake_case`
|
|
127
|
-
- DEBUG siempre en block form: `logger.debug { "k=#{v}" }`
|
|
128
|
-
- Nunca `Kernel#warn` ni `$stderr`
|
|
129
|
-
- Toda operación de logging envuelta en `rescue StandardError`
|
|
130
|
-
- Duraciones con `Process.clock_gettime(Process::CLOCK_MONOTONIC)`, nunca `Time.now`
|
|
131
|
-
|
|
132
|
-
### Seguridad
|
|
133
|
-
- Claves sensibles (`password|pass|passwd|secret|token|api_key|auth`) → `[FILTERED]`
|
|
134
|
-
- Nunca loguear PII. Solo `user_id`, `isp_id`
|
|
135
|
-
|
|
136
|
-
### Source válidos
|
|
137
|
-
`http` | `sidekiq` | `task` | `system`
|
|
138
|
-
|
|
139
|
-
### Propagación de headers
|
|
140
|
-
- **Entrante HTTP:** `trace_header` (formato Rack: `HTTP_X_AMZN_TRACE_ID`) — solo en `HttpMiddleware`
|
|
141
|
-
- **Saliente (todos los transportes):** `propagation_trace_header` (formato HTTP: `X-Amzn-Trace-Id`)
|
|
142
|
-
|
|
143
|
-
### Prohibiciones
|
|
144
|
-
- No Lograge — reemplazado por `LogSubscriber`
|
|
145
|
-
- No loguear manualmente: `time`, `level`, `service`, `source`, `root_id`, `trace_id`, `correlation_id`, `sidekiq_job`, `task`
|
|
146
|
-
- No referenciar `.gemini/` — obsoleto
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## Compatibilidad Rails
|
|
151
|
-
|
|
152
|
-
- **Reloading:** Usar `cache_classes?` helper (verifica `respond_to?(:enable_reloading)`) para Rails 7.1+
|
|
153
|
-
- **Notifications:** Rails 7.1+ usa `all_listeners_for`, Rails 6/7.0 usa `listeners_for` — siempre usar `respond_to?` guards
|
|
154
|
-
- **Soporte:** Rails 6, 7 y 8
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Integración Automática (Railtie)
|
|
159
|
-
|
|
160
|
-
El `Railtie` en `after_initialize` detecta y auto-instrumenta sin intervención del desarrollador:
|
|
161
|
-
|
|
162
|
-
| Gema detectada | Qué hace |
|
|
163
|
-
|:---------------|:---------|
|
|
164
|
-
| `BugBunny` | Registra `PublisherTracing` no — ese va en el cliente. Registra `ConsumerTracingMiddleware` y los hooks RPC |
|
|
165
|
-
| `Sidekiq` | Registra client + server middleware |
|
|
166
|
-
| `ActiveResource` | Prepend de `ActiveResourceInstrumentation` |
|
|
167
|
-
| `Faraday` | Disponible como middleware opcional |
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## Configuración Mínima
|
|
172
|
-
|
|
173
|
-
```ruby
|
|
174
|
-
# config/initializers/exis_ray.rb
|
|
175
|
-
ExisRay.configure do |config|
|
|
176
|
-
config.log_format = :json # :text por defecto
|
|
177
|
-
config.trace_header = 'HTTP_X_AMZN_TRACE_ID'
|
|
178
|
-
config.propagation_trace_header = 'X-Amzn-Trace-Id'
|
|
179
|
-
config.current_class = 'Current'
|
|
180
|
-
config.reporter_class = 'Reporter'
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
# BugBunny publisher — debe agregarse manualmente al cliente
|
|
184
|
-
BugBunny::Client.new(pool: pool) do |stack|
|
|
185
|
-
stack.use ExisRay::BugBunny::PublisherTracing
|
|
186
|
-
end
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
---
|
|
14
|
+
- No duplicar aquí reglas generales del repositorio.
|
|
15
|
+
- Si una regla aplica a cualquier agente, moverla a `AGENTS.md`.
|
data/README.md
CHANGED
|
@@ -64,10 +64,13 @@ Artefactos de detalle (RFC-008 — el contrato y el significado viven acá; este
|
|
|
64
64
|
|:-----|:----------|:-------|
|
|
65
65
|
| Comportamiento | [`docs/behavior/behavior.md`](docs/behavior/behavior.md) — secuencias de hidratación de trace por entrypoint y emisión en logs | parcial, incremental por PR |
|
|
66
66
|
| Glosario | [`docs/glossary/glossary.md`](docs/glossary/glossary.md) — lenguaje ubicuo (`root_id`, `trace_id`, `source`, `request_id`, `entrypoint`, ...) | sembrado inicial, acreta |
|
|
67
|
-
|
|
|
68
|
-
|
|
|
67
|
+
| Configuración | [`docs/config/configuracion.md`](docs/config/configuracion.md) — opciones de `ExisRay.configure`, `HOSTNAME` e inyecciones del Railtie al host | inventario base + enriquecimiento §f |
|
|
68
|
+
| Test | [`docs/test/testing.md`](docs/test/testing.md) — suites RSpec, fixtures y coverage | piloto RFC-013 |
|
|
69
|
+
| Interfaz | [`docs/interface/interface.md`](docs/interface/interface.md) — superficie Ruby pública (símbolo · tipo · firma) | completa (consumer-facing) |
|
|
70
|
+
| Datos · Operaciones · Eventos · Consumidas | — | n/a (gema sin DB, no expone superficie propia ni consume servicios) |
|
|
71
|
+
| Topología · Release · Errores | — | pendiente (ver mapa de cobertura en `AGENTS.md`) |
|
|
69
72
|
|
|
70
|
-
> **
|
|
73
|
+
> **Nota:** las secciones _Cómo funciona_, _Campos auto-inyectados_ y _Referencia del Tracer_ de este README son material humano de onboarding; el contrato formal de la superficie pública vive en [`docs/interface`](docs/interface/interface.md), el significado y las secuencias en `docs/glossary` y `docs/behavior`.
|
|
71
74
|
|
|
72
75
|
## Instalación
|
|
73
76
|
|
|
@@ -370,8 +373,9 @@ config.logger.formatter = ExisRay::JsonFormatter
|
|
|
370
373
|
| `component` | String | Siempre `"exis_ray"` |
|
|
371
374
|
| `event` | String | Siempre `"http_request"` |
|
|
372
375
|
| `method` | String | Verbo HTTP |
|
|
373
|
-
| `path` | String | URL concreta del request |
|
|
374
|
-
| `
|
|
376
|
+
| `url.path` | String | URL concreta del request (OTel v1.0). Siempre presente |
|
|
377
|
+
| `path` | String | Alias legacy de `url.path`. Solo si `config.emit_legacy_path_key` (default `true`, deprecado) |
|
|
378
|
+
| `http_route` | String | Template (ej: `/users/:id`). Baja cardinalidad para dashboards. En endpoints sin params coincide en valor con `url.path` — la dupe es semánticamente esperada (concrete vs template) |
|
|
375
379
|
| `format` | Symbol/String | `html`, `json`, etc. |
|
|
376
380
|
| `controller` | String | Class name del controller |
|
|
377
381
|
| `action` | String | Nombre del action |
|
|
@@ -382,11 +386,37 @@ config.logger.formatter = ExisRay::JsonFormatter
|
|
|
382
386
|
| `db_runtime_s` | Float\|nil | Solo si ActiveRecord lo reporta |
|
|
383
387
|
| `user_agent_original` | String | Header `User-Agent` |
|
|
384
388
|
| `server_address` | String | Hostname sin puerto del header `Host` |
|
|
385
|
-
| `error_class`, `error_message` | String | Solo en fallo (
|
|
389
|
+
| `error_class`, `error_message` | String | Solo en fallo y si `config.emit_legacy_exception_keys` (default `true`, deprecadas) |
|
|
386
390
|
| `exception.type`, `exception.message`, `exception.stacktrace` | String | Solo en fallo (OTel; stack limitado a 20 líneas) |
|
|
387
391
|
|
|
388
392
|
Severity del log: `ERROR` si `http_status >= 500`, sino `INFO`.
|
|
389
393
|
|
|
394
|
+
### Migración OTel — `error_class`/`error_message` → `exception.*`
|
|
395
|
+
|
|
396
|
+
Durante la ventana de transición OTel v1.0, `LogSubscriber` y `TaskMonitor` emiten ambos sets de keys en cada log de error. Cuando todos los consumers (dashboards, queries, alertas) hayan migrado a `exception.type`/`exception.message`/`exception.stacktrace`, desactivar los legacy:
|
|
397
|
+
|
|
398
|
+
```ruby
|
|
399
|
+
ExisRay.configure do |c|
|
|
400
|
+
c.emit_legacy_exception_keys = false # default: true
|
|
401
|
+
end
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Roadmap: default `false` en v0.12.0; flag y legacy keys removidos en v1.0.
|
|
405
|
+
|
|
406
|
+
### Migración OTel — `path` → `url.path`
|
|
407
|
+
|
|
408
|
+
`LogSubscriber` emite `url.path` (nombre OTel v1.0) en todos los logs HTTP. Durante la ventana de transición emite también `path` (alias legacy Wispro). Cuando las queries/dashboards/alertas migren a `url.path`, desactivar el legacy:
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
ExisRay.configure do |c|
|
|
412
|
+
c.emit_legacy_path_key = false # default: true
|
|
413
|
+
end
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Roadmap: default `false` en v0.12.0; flag y key `path` removidos en v1.0.
|
|
417
|
+
|
|
418
|
+
> **Sobre `url.path` vs `http_route`:** son semánticamente distintos — `url.path` es la URL **concreta** (alta cardinalidad, ej. `/users/42`), `http_route` es el **template** matcheado (baja cardinalidad, ej. `/users/:id`). En endpoints sin params coinciden en valor (ej. `/up`), pero ambos se siguen emitiendo porque cumplen roles distintos en dashboards/queries downstream. La dupe en valor es esperada, no es un bug.
|
|
419
|
+
|
|
390
420
|
### Filtrado de claves sensibles
|
|
391
421
|
|
|
392
422
|
Las claves que matcheen `/password|pass|passwd|secret|token|api_key|auth/i` se reemplazan automáticamente por `[FILTERED]`, tanto en strings KV como en Hashes (incluyendo anidados).
|