exis_ray 0.7.1 → 0.7.2

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: 91d6964520e83c2088af45357edf601358d2ffefbff4861ef15e1abaf09a36bc
4
- data.tar.gz: d276b0753bd7b956662a3f6e1d61cb51d707a9729496f17b8bd4a3ca1bc7fba4
3
+ metadata.gz: 5dd52ddf30f1d4eaaaad95ed4acde4bd94c8dd4a01b20853f42ddd71887c0876
4
+ data.tar.gz: 329163a0bbbd0473e8da70971822c645de763f6a2c99eb4ccf1c973603cd700e
5
5
  SHA512:
6
- metadata.gz: 4b382e4fcd8171db8771f9f5f8d2238fdf2cd17b2ac4e953c54d06248eab390c321bf37579b954e3965bb7092c0959dcfae999782200ea8cbee487ca6c74b314
7
- data.tar.gz: a6da50e40e16e4560d6c52e6443b36d2c7ffef5263940b73a3696d7411817ef5990cd26bf625141984694b7d2f824b22fdd88ba2e9072762b721b9e1718a3eaf
6
+ metadata.gz: 9eb8bb340dae5b7f423e206ed1fa708caf0b599be0bfc3dd9f1d71209485cf6f2a17a06c9805167cd4988cae20b751f684fda2253d5983efa3c3d9e456e51849
7
+ data.tar.gz: e18cf7eaa0962d39591072eb9241df8c950c0d1d1c1786aaff4d84ebc4acd1b6ff993b280f5bedc6175bbabd56a0a75f0e8f92a50b899051a1942d21e91888fc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.7.2] - 2026-05-11
2
+
3
+ ### Documentación
4
+ - **Warning sobre `ActiveSupport::TaggedLogging` con JSON logging:** `TaggedLogging` antepone tags como texto raw antes del formatter, rompiendo el JSON (`[req_id] {...}`). `JsonFormatter` ya inyecta `request_id`/`trace_id` como campos JSON — usar ambos genera salida inválida. Documentada la config correcta del logger en `production.rb` (README + SKILL FAQ).
5
+ - **Skill improvements (issue #4):** (1) Clarificado el criterio auto-inyectado vs manual — el formatter solo conoce contexto de ejecución (Tracer/Current), por eso `component` y `event` deben aportarse manualmente. (2) Agregados ejemplos concretos de output JSON lado-a-lado con el input KV/Hash. (3) Completada la lista de campos default de `LogSubscriber` (17 campos con tipos y condiciones, antes truncado con "etc."). (4) Documentados los 3 modos de input del formatter (KV / Hash / string libre) con ejemplos de cada uno.
6
+
1
7
  ## [0.7.1] - 2026-04-06
2
8
 
3
9
  ### Fixed
data/README.md CHANGED
@@ -252,22 +252,46 @@ Con `log_format: :json`, `ExisRay::JsonFormatter` reemplaza el formatter de Rail
252
252
  {"time":"2026-04-01T14:30:00Z","level":"INFO","severity_number":9,"service":"wispro_agent","service_version":"1.2.3","deployment_environment":"production","root_id":"1-65f...abc","trace_id":"Root=1-65f...;Self=...","source":"http","user_id":42,"isp_id":10,"component":"exis_ray","event":"http_request","method":"GET","path":"/api/v1/users","http_route":"/api/v1/users","http_status":200,"duration_s":0.0452,"user_agent_original":"Mozilla/5.0","server_address":"api.example.com"}
253
253
  ```
254
254
 
255
- Los mensajes con formato `key=value` se parsean y elevan al root del JSON. Los valores numéricos se castean automáticamente:
255
+ El formatter acepta tres tipos de mensaje. Los tres producen output JSON equivalente; elegí el que sea más legible para tu caso:
256
256
 
257
257
  ```ruby
258
+ # 1. String KV — one-liner rápido, valores numéricos se castean
258
259
  Rails.logger.info "component=billing event=invoice_created invoice_id=123 total=45.50"
259
- # => {"time":"...","level":"INFO","service":"...","root_id":"...","component":"billing","event":"invoice_created","invoice_id":123,"total":45.5}
260
- ```
261
260
 
262
- Los mensajes tipo Hash (usados internamente por `LogSubscriber`) se mergean directamente. Los mensajes de texto libre se asignan a la clave `body`:
261
+ # 2. Hash style payloads complejos o con valores nested
262
+ Rails.logger.info(component: "billing", event: "invoice_created",
263
+ invoice: { id: 123, total: 45.50 })
263
264
 
264
- ```ruby
265
+ # 3. String libre — fallback, va a la clave `body`
265
266
  Rails.logger.info "Algo pasó sin formato KV"
266
267
  # => {"time":"...","level":"INFO","service":"...","body":"Algo pasó sin formato KV"}
267
268
  ```
268
269
 
270
+ Los mensajes Hash también son la forma que usa internamente `LogSubscriber` para emitir el cierre de cada request HTTP.
271
+
272
+ > **Nota:** `component` (módulo de negocio) y `event` (qué pasó) **no** son auto-inyectados — los aporta el call site. El formatter solo conoce el contexto de **ejecución** (quién, de dónde, con qué identidad), no el contexto del **lugar del código** que loguea.
273
+
269
274
  En modo `:text`, ExisRay inyecta el `trace_id` o `root_id` como tag de Rails (`config.log_tags`) y no modifica el formatter.
270
275
 
276
+ ### Configuración del logger en producción
277
+
278
+ Para logs JSON limpios sin códigos ANSI ni texto extra, configurar el logger así:
279
+
280
+ ```ruby
281
+ # config/environments/production.rb
282
+ config.colorize_logging = false
283
+ config.logger = ActiveSupport::Logger.new(STDOUT)
284
+ config.logger.formatter = ExisRay::JsonFormatter
285
+ ```
286
+
287
+ **No usar `ActiveSupport::TaggedLogging`** — agrega tags como texto al inicio de cada línea antes del formatter, lo cual rompe el JSON:
288
+
289
+ ```
290
+ [request_id] {"time":"...","level":"INFO",...} # ← texto antes del JSON (INVALIDO)
291
+ ```
292
+
293
+ `ExisRay::JsonFormatter` ya inyecta los tags (`request_id`, `trace_id`, etc.) como campos JSON, así que `TaggedLogging` es redundante e incompatible.
294
+
271
295
  ### Campos auto-inyectados
272
296
 
273
297
  `JsonFormatter` inyecta estos campos automáticamente en cada línea. **Nunca** los incluyas manualmente en tus logs:
@@ -290,17 +314,29 @@ En modo `:text`, ExisRay inyecta el `trace_id` o `root_id` como tag de Rails (`c
290
314
  | `task` | Solo en procesos TaskMonitor |
291
315
  | `tags` | Solo si hay Rails tagged logging activo |
292
316
 
293
- `LogSubscriber` inyecta además estos campos en los logs de requests HTTP:
294
-
295
- | Campo | Descripción |
296
- |:------|:------------|
297
- | `http_status` | Código HTTP (Integer). Antes `status`, renombrado en v0.6.0 |
298
- | `http_route` | Template de ruta (ej: `/users/:id`). Resolución via `route.defaults` |
299
- | `user_agent_original` | Header `User-Agent` del request |
300
- | `server_address` | Hostname sin puerto del header `Host` |
301
- | `exception.type` | Clase de la excepción (cuando el request falla) |
302
- | `exception.message` | Mensaje de la excepción |
303
- | `exception.stacktrace` | Primeras 20 líneas del backtrace |
317
+ `LogSubscriber` inyecta además estos campos en cada log de cierre de request HTTP. **Nunca duplicarlos** en logs manuales:
318
+
319
+ | Campo | Tipo | Notas |
320
+ |:------|:-----|:------|
321
+ | `component` | String | Siempre `"exis_ray"` |
322
+ | `event` | String | Siempre `"http_request"` |
323
+ | `method` | String | Verbo HTTP |
324
+ | `path` | String | URL concreta del request |
325
+ | `http_route` | String | Template (ej: `/users/:id`). Baja cardinalidad para dashboards |
326
+ | `format` | Symbol/String | `html`, `json`, etc. |
327
+ | `controller` | String | Class name del controller |
328
+ | `action` | String | Nombre del action |
329
+ | `http_status` | Integer | Status HTTP final. Antes `status`, renombrado en v0.6.0 |
330
+ | `duration_s` | Float | Segundos (Rails reporta ms, se convierte), redondeo 4 decimales |
331
+ | `duration_human` | String | Legible: `"42.5ms"`, `"1.25s"`, `"2 minutes 5 seconds"` |
332
+ | `view_runtime_s` | Float\|nil | Solo si Rails lo reporta |
333
+ | `db_runtime_s` | Float\|nil | Solo si ActiveRecord lo reporta |
334
+ | `user_agent_original` | String | Header `User-Agent` |
335
+ | `server_address` | String | Hostname sin puerto del header `Host` |
336
+ | `error_class`, `error_message` | String | Solo en fallo (legacy) |
337
+ | `exception.type`, `exception.message`, `exception.stacktrace` | String | Solo en fallo (OTel; stack limitado a 20 líneas) |
338
+
339
+ Severity del log: `ERROR` si `http_status >= 500`, sino `INFO`.
304
340
 
305
341
  ### Filtrado de claves sensibles
306
342
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module ExisRay
4
4
  # Versión actual de la gema.
5
- VERSION = "0.7.1"
5
+ VERSION = "0.7.2"
6
6
  end
data/skill/SKILL.md CHANGED
@@ -71,8 +71,8 @@ ExisRay unifica trazabilidad distribuida, logging estructurado JSON, contexto de
71
71
  2. `Tracer.parse_trace_id` extrae `root_id`, `self_id`, `called_from`, `total_time_so_far`
72
72
  3. `ExisRay.sync_correlation_id` asigna `Tracer.correlation_id` a `Current.correlation_id`
73
73
  4. Controller ejecuta `before_action` para setear `Current.user_id`, `Current.isp_id`
74
- 5. `JsonFormatter` intercepta cada `Rails.logger.*` e inyecta automaticamente: `time`, `level`, `severity_number`, `service`, `service_version`, `deployment_environment`, `root_id`, `trace_id`, `source`, `user_id`, `isp_id`, `correlation_id`
75
- 6. `LogSubscriber` emite un unico Hash al finalizar el request (method, path, http_status, http_route, duration_s, user_agent_original, server_address, etc.)
74
+ 5. `JsonFormatter` intercepta cada `Rails.logger.*` e inyecta automaticamente: `time`, `level`, `severity_number`, `service`, `service_version`, `deployment_environment`, `root_id`, `trace_id`, `source`, `user_id`, `isp_id`, `correlation_id`. El developer aporta `component` (modulo de negocio) y `event` (que paso); estos NO son auto-inyectados porque dependen del call site, no del contexto de ejecucion.
75
+ 6. `LogSubscriber` emite un unico Hash al finalizar el request con campos default (`component`, `event`, `method`, `path`, `http_route`, `format`, `controller`, `action`, `http_status`, `duration_s`, `duration_human`, `view_runtime_s`, `db_runtime_s`, `user_agent_original`, `server_address`, y en error `error_class`/`error_message`/`exception.*`).
76
76
  7. En llamadas salientes, `FaradayMiddleware`/`ActiveResourceInstrumentation` inyectan `propagation_trace_header` con `Tracer.generate_trace_header`
77
77
  8. Al finalizar, `ActiveSupport::CurrentAttributes` hace reset automatico
78
78
 
@@ -212,6 +212,32 @@ end
212
212
 
213
213
  ### ExisRay::LogSubscriber
214
214
 
215
+ Reemplaza Lograge. Se suscribe a `process_action.action_controller` y emite un Hash por request HTTP. Severity es `ERROR` si `http_status >= 500`, sino `INFO`.
216
+
217
+ **Campos default emitidos** (mergeados al payload JSON; nunca duplicarlos manualmente):
218
+
219
+ | Campo | Tipo | Notas |
220
+ |:------|:-----|:------|
221
+ | `component` | String | Siempre `"exis_ray"` |
222
+ | `event` | String | Siempre `"http_request"` |
223
+ | `method` | String | Verbo HTTP |
224
+ | `path` | String | URL concreta del request |
225
+ | `http_route` | String | Template (ej: `/users/:id`). Baja cardinalidad para dashboards |
226
+ | `format` | Symbol/String | `html`, `json`, etc. |
227
+ | `controller` | String | Class name del controller |
228
+ | `action` | String | Nombre del action |
229
+ | `http_status` | Integer | Status HTTP final |
230
+ | `duration_s` | Float | Segundos (Rails reporta ms, se convierte), redondeo 4 decimales |
231
+ | `duration_human` | String | Legible: `"42.5ms"`, `"1.25s"`, `"2 minutes 5 seconds"` |
232
+ | `view_runtime_s` | Float\|nil | Solo si Rails lo reporta |
233
+ | `db_runtime_s` | Float\|nil | Solo si ActiveRecord lo reporta |
234
+ | `user_agent_original` | String | Header `User-Agent` |
235
+ | `server_address` | String | Hostname sin puerto (de `Host` header) |
236
+ | `error_class`, `error_message` | String | Solo en fallo (legacy) |
237
+ | `exception.type`, `exception.message`, `exception.stacktrace` | String | Solo en fallo (OTel; stack limitado a 20 lineas) |
238
+
239
+ Para inyectar campos extra, sobreescribir `extra_fields`:
240
+
215
241
  ```ruby
216
242
  class MyLogSubscriber < ExisRay::LogSubscriber
217
243
  def self.extra_fields(event)
@@ -227,12 +253,41 @@ ExisRay.configure { |c| c.log_subscriber_class = "MyLogSubscriber" }
227
253
 
228
254
  Se asigna automaticamente a `Rails.logger.formatter` cuando `log_format: :json`. Acepta tres tipos de mensaje:
229
255
 
230
- - **Hash**: merge directo al payload JSON
231
- - **String KV** (`"event=foo bar=baz"`): parsea pares y los eleva al root del JSON
232
- - **String libre**: asigna al campo `body`
256
+ - **Hash**: merge directo al payload JSON. Util para payloads complejos o con valores nested.
257
+ - **String KV** (`"event=foo bar=baz"`): parsea pares y los eleva al root del JSON. Util para one-liners rapidos.
258
+ - **String libre**: asigna al campo `body` (OTel log body).
233
259
 
234
260
  Casteo automatico: integers, floats, objetos JSON (`{...}`, `[...]`). Filtra claves sensibles (`password|secret|token|api_key|auth`) a `[FILTERED]`. Fallback a JSON minimo si el formateo falla.
235
261
 
262
+ #### Criterio auto-inyectado vs manual
263
+
264
+ - **Auto-inyectado** (formatter conoce desde `Tracer`/`Current`): contexto de **ejecucion** — quien hace el request, de donde viene, en que servicio, con que identidad.
265
+ - **Manual** (lo aporta cada `Rails.logger.*`): contexto del **call site** — que modulo (`component`) y que paso (`event`). El formatter no puede saber esto sin recorrer el stack en cada log.
266
+
267
+ Por eso `component` y `event` jamas se auto-inyectan, aunque el estandar Wispro los exija.
268
+
269
+ #### Ejemplos: KV vs Hash producen output equivalente
270
+
271
+ ```ruby
272
+ # KV string — one-liner rapido
273
+ Rails.logger.info("component=billing event=invoice_paid invoice_id=42 total=199.99")
274
+
275
+ # Hash style — payloads complejos / nested
276
+ Rails.logger.info(component: "billing", event: "invoice_paid",
277
+ invoice: { id: 42, total: 199.99 })
278
+
279
+ # String libre — fallback, va a `body`
280
+ Rails.logger.info("usuario hizo click")
281
+ ```
282
+
283
+ #### Output JSON resultante (mismo para KV y Hash del ejemplo)
284
+
285
+ ```json
286
+ {"time":"2026-05-11T09:15:00.123Z","level":"INFO","severity_number":9,"service":"box_radius_manager","service_version":"1.2.3","deployment_environment":"production","root_id":"1-abc","trace_id":"Root=1-abc;Self=...","source":"http","user_id":42,"isp_id":10,"correlation_id":"box_radius_manager;1-abc","component":"billing","event":"invoice_paid","invoice_id":42,"total":199.99}
287
+ ```
288
+
289
+ Los campos hasta `correlation_id` los inyecta el formatter automaticamente. De `component` en adelante son los campos del mensaje del developer.
290
+
236
291
  ### Middlewares de propagacion
237
292
 
238
293
  ```ruby
@@ -290,6 +345,20 @@ Agrega `f.use ExisRay::FaradayMiddleware` al stack de Faraday. Solo inyecta head
290
345
  **P: Puedo usar ExisRay sin JSON logging?**
291
346
  Si. Con `log_format: :text` (default), ExisRay inyecta el `root_id` como tag de Rails via `config.log_tags`. El JsonFormatter y LogSubscriber no se activan.
292
347
 
348
+ **P: Por que no usar `ActiveSupport::TaggedLogging` con JSON logging?**
349
+ `TaggedLogging` agrega tags como texto plano al inicio de cada línea **antes** del formatter, lo cual rompe el JSON:
350
+ ```
351
+ [request_id] {"time":"...","level":"INFO",...} # ← texto antes del JSON
352
+ ```
353
+ `ExisRay::JsonFormatter` ya inyecta los tags (`request_id`, `trace_id`, etc.) como campos JSON. Usar ambos genera JSON inválido.
354
+
355
+ Configuración correcta en production.rb:
356
+ ```ruby
357
+ config.colorize_logging = false
358
+ config.logger = ActiveSupport::Logger.new(STDOUT)
359
+ config.logger.formatter = ExisRay::JsonFormatter
360
+ ```
361
+
293
362
  **P: Que pasa con los logs de Sidekiq (el propio logger de Sidekiq)?**
294
363
  Si `json_logs?` es true, el Railtie asigna `Sidekiq.logger.formatter = ExisRay::JsonFormatter.new`, asi los logs internos de Sidekiq tambien salen en JSON.
295
364
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exis_ray
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Edera
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-07 00:00:00.000000000 Z
11
+ date: 2026-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -87,8 +87,8 @@ licenses:
87
87
  metadata:
88
88
  homepage_uri: https://github.com/gedera/exis_ray
89
89
  source_code_uri: https://github.com/gedera/exis_ray
90
- changelog_uri: https://github.com/gedera/exis_ray/blob/v0.7.1/CHANGELOG.md
91
- documentation_uri: https://github.com/gedera/exis_ray/blob/v0.7.1/skill
90
+ changelog_uri: https://github.com/gedera/exis_ray/blob/v0.7.2/CHANGELOG.md
91
+ documentation_uri: https://github.com/gedera/exis_ray/blob/v0.7.2/skill
92
92
  post_install_message:
93
93
  rdoc_options: []
94
94
  require_paths: