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 +4 -4
- data/.claude/commands/release.md +64 -0
- data/.claude/skills/opentelemetry/SKILL.md +61 -0
- data/.claude/skills/rails-expert/SKILL.md +85 -0
- data/.claude/skills/readme-writer/SKILL.md +84 -0
- data/.claude/skills/rubocop-omakase/SKILL.md +114 -0
- data/.claude/skills/yard/SKILL.md +77 -0
- data/CHANGELOG.md +10 -0
- data/CLAUDE.md +158 -38
- data/lib/exis_ray/json_formatter.rb +16 -2
- data/lib/exis_ray/version.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f17193f5df75197d569fbe74d8b27e55b116e861bdb819f1e92526a1da79869e
|
|
4
|
+
data.tar.gz: e8e45998cb83236c3bb7cc3a981995ccd36764fdb6fbc1465e796a7966e3b354
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
###
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- **
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
#
|
data/lib/exis_ray/version.rb
CHANGED
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.
|
|
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
|