exis_ray 0.4.1 → 0.5.0
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/CHANGELOG.md +19 -0
- data/CLAUDE.md +27 -0
- data/lib/exis_ray/json_formatter.rb +30 -2
- data/lib/exis_ray/log_subscriber.rb +23 -10
- data/lib/exis_ray/railtie.rb +1 -1
- data/lib/exis_ray/task_monitor.rb +8 -2
- data/lib/exis_ray/tracer.rb +10 -2
- data/lib/exis_ray/version.rb +1 -1
- data/lib/exis_ray.rb +3 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c916d765b57e909cd3320e1670623536907d648c360039cbb5cada88b88a86fd
|
|
4
|
+
data.tar.gz: cab31ec104f79b58ecbd2ad8a8758f55f767e217f09c2880a3b911972a743112
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b428640a4df2e1b64aa798568769c0d9902e503aa59fb14f7d2f34948115883f35e436097504de34a879eeb63890bbb031dc68e9f2adb672fa24c04c6064a47c
|
|
7
|
+
data.tar.gz: aa9c8bb741789384b66d1a98b9e5d5008d9e5eeea3802ecde39264c08c8ed95be8114285005c03dfb11d3506c2d4efb65c59336bc582d8b9cc8ccb1cf3bdb577
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [0.5.0] - 2026-03-23
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
- **Wispro-Observability-Spec (v1) Compliance:** Refactored telemetry across all execution layers (`TaskMonitor` and HTTP `LogSubscriber`).
|
|
5
|
+
- **Unit Standard:** Duration metrics now use `_s` suffix (Float) as the source of truth (e.g., `duration_s`, `view_runtime_s`, `db_runtime_s`).
|
|
6
|
+
- **Human Readability:** Added `duration_human` to both HTTP requests and Background Tasks.
|
|
7
|
+
- **Task Lifecycle:** The closing log now always uses `event=task_finished` with `status`, `duration_s`, and `duration_human` fields.
|
|
8
|
+
- **Error Consistency:** Failed tasks now include `error_class` and `error_message` fields in the JSON log.
|
|
9
|
+
- **Tracer Accuracy:** Improved `Tracer` with `current_duration_s` using monotonic clock precision.
|
|
10
|
+
|
|
11
|
+
## [0.4.2] - 2026-03-23
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **Full compliance with "Gabriel's Global Standards" for Logging:**
|
|
15
|
+
- **Sensitive Data Filtering:** The KV parser and Hash merger now automatically filter sensitive keys (`password`, `token`, `api_key`, `auth`, etc.) replacing values with `[FILTERED]`.
|
|
16
|
+
- **Standardized HTTP Logs:** Added `component=exis_ray` and `event=http_request` to all automatic Rails request logs.
|
|
17
|
+
- **Dynamic Log Levels:** `LogSubscriber` now uses `ERROR` level for 5xx status codes and `INFO` for the rest.
|
|
18
|
+
- **Performance:** Switched to block form for internal `DEBUG` logs in `Railtie` to avoid unnecessary string interpolation.
|
|
19
|
+
|
|
1
20
|
## [0.4.1] - 2026-03-23
|
|
2
21
|
|
|
3
22
|
### Changed
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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.
|
|
8
|
+
- `ExisRay::LogSubscriber`: Native HTTP logger since v0.4.0. Replaces Lograge.
|
|
9
|
+
|
|
10
|
+
## Technical Knowledge & Compatibility
|
|
11
|
+
|
|
12
|
+
### Rails Compatibility (6, 7, 8)
|
|
13
|
+
- **Reloading:** Use `cache_classes?` helper (checks `respond_to?(:enable_reloading)`) to avoid deprecation warnings in Rails 7.1+.
|
|
14
|
+
- **Notifications:** Rails 7.1+ uses `all_listeners_for`, while 6/7.0 uses `listeners_for`. Always use `respond_to?` guards when manipulating subscribers.
|
|
15
|
+
|
|
16
|
+
### Distributed Tracing (AWS X-Ray)
|
|
17
|
+
- **Propagation:** Use `propagation_trace_header` (standard HTTP format) for outgoing requests.
|
|
18
|
+
- **Parsing:** `trace_header` (Rack format) is ONLY for incoming request parsing.
|
|
19
|
+
|
|
20
|
+
### Security & Privacy
|
|
21
|
+
- **Sentry Context:** Default `sentry_user_context` and `sentry_isp_context` to `{ id: }` only. Never send full objects to avoid leaking sensitive fields (PII/Tokens).
|
|
22
|
+
- **Masking:** `JsonFormatter` automatically filters `password`, `token`, `api_key`, `auth`, `secret`.
|
|
23
|
+
|
|
24
|
+
## Execution Rules
|
|
25
|
+
- **No Lograge:** Do not suggest or re-add the Lograge dependency.
|
|
26
|
+
- **Pure Data Logging:** Gem internal logs must use KV strings (`component=exis_ray event=...`).
|
|
27
|
+
- **Resilience:** All logging operations must be wrapped in `rescue StandardError` to ensure the host application never crashes due to a telemetry failure.
|
|
@@ -25,6 +25,9 @@ module ExisRay
|
|
|
25
25
|
# Extrae pares key=value de un string. Soporta valores sin espacios o entre comillas dobles.
|
|
26
26
|
KV_PARSE_RE = /(\w+)=("(?:[^"\\]|\\.)*"|\S+)/
|
|
27
27
|
|
|
28
|
+
# Claves sensibles que deben filtrarse automáticamente según el estándar de Gabriel.
|
|
29
|
+
SENSITIVE_KEYS = /password|pass|passwd|secret|token|api_key|auth/i
|
|
30
|
+
|
|
28
31
|
# Procesa un mensaje de log y lo formatea como una cadena estructurada en JSON.
|
|
29
32
|
#
|
|
30
33
|
# @param severity [String] El nivel de severidad del log (ej. "INFO", "ERROR", "DEBUG").
|
|
@@ -103,7 +106,8 @@ module ExisRay
|
|
|
103
106
|
# @return [void]
|
|
104
107
|
def process_message(payload, msg)
|
|
105
108
|
if msg.is_a?(Hash)
|
|
106
|
-
|
|
109
|
+
# Filtramos las claves del Hash antes del merge
|
|
110
|
+
payload.merge!(filter_sensitive_hash(msg))
|
|
107
111
|
elsif msg.is_a?(String) && kv_string?(msg)
|
|
108
112
|
parsed = parse_kv_string(msg)
|
|
109
113
|
parsed.empty? ? payload[:message] = msg : payload.merge!(parsed)
|
|
@@ -129,9 +133,33 @@ module ExisRay
|
|
|
129
133
|
def parse_kv_string(str)
|
|
130
134
|
result = {}
|
|
131
135
|
str.scan(KV_PARSE_RE) do |key, value|
|
|
132
|
-
|
|
136
|
+
val = value.start_with?('"') ? (value[1..-2] || "").gsub('\\"', '"') : value
|
|
137
|
+
result[key.to_sym] = filter_sensitive_value(key, val)
|
|
133
138
|
end
|
|
134
139
|
result
|
|
135
140
|
end
|
|
141
|
+
|
|
142
|
+
# Filtra un valor si la clave se considera sensible.
|
|
143
|
+
#
|
|
144
|
+
# @param key [String, Symbol]
|
|
145
|
+
# @param value [Object]
|
|
146
|
+
# @return [Object]
|
|
147
|
+
def filter_sensitive_value(key, value)
|
|
148
|
+
key.to_s.match?(SENSITIVE_KEYS) ? "[FILTERED]" : value
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Filtra recursivamente un Hash que contenga claves sensibles.
|
|
152
|
+
#
|
|
153
|
+
# @param hash [Hash]
|
|
154
|
+
# @return [Hash]
|
|
155
|
+
def filter_sensitive_hash(hash)
|
|
156
|
+
hash.each_with_object({}) do |(k, v), result|
|
|
157
|
+
result[k] = if v.is_a?(Hash)
|
|
158
|
+
filter_sensitive_hash(v)
|
|
159
|
+
else
|
|
160
|
+
filter_sensitive_value(k, v)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
136
164
|
end
|
|
137
165
|
end
|
|
@@ -32,7 +32,12 @@ module ExisRay
|
|
|
32
32
|
# @return [void]
|
|
33
33
|
def process_action(event)
|
|
34
34
|
payload = build_payload(event)
|
|
35
|
-
|
|
35
|
+
# Usamos el nivel ERROR si el status es 5xx, cumpliendo el estándar de Gabriel.
|
|
36
|
+
if payload[:status] && payload[:status] >= 500
|
|
37
|
+
logger.error(payload)
|
|
38
|
+
else
|
|
39
|
+
logger.info(payload)
|
|
40
|
+
end
|
|
36
41
|
rescue StandardError
|
|
37
42
|
# El logger nunca debe interrumpir el flujo del request.
|
|
38
43
|
end
|
|
@@ -64,16 +69,24 @@ module ExisRay
|
|
|
64
69
|
payload = event.payload
|
|
65
70
|
status = payload[:status] || exception_status(payload[:exception])
|
|
66
71
|
|
|
72
|
+
# Convertimos milisegundos (Rails default) a segundos (Estandar Wispro)
|
|
73
|
+
duration_s = (event.duration / 1000.0).round(4)
|
|
74
|
+
view_s = payload[:view_runtime] ? (payload[:view_runtime] / 1000.0).round(4) : nil
|
|
75
|
+
db_s = payload[:db_runtime] ? (payload[:db_runtime] / 1000.0).round(4) : nil
|
|
76
|
+
|
|
67
77
|
data = {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
component: "exis_ray",
|
|
79
|
+
event: "http_request",
|
|
80
|
+
method: payload[:method],
|
|
81
|
+
path: payload[:path],
|
|
82
|
+
format: payload[:format],
|
|
83
|
+
controller: payload[:controller],
|
|
84
|
+
action: payload[:action],
|
|
85
|
+
status: status,
|
|
86
|
+
duration_s: duration_s,
|
|
87
|
+
duration_human: ActiveSupport::Duration.build(duration_s).inspect,
|
|
88
|
+
view_runtime_s: view_s,
|
|
89
|
+
db_runtime_s: db_s
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
data.merge!(self.class.extra_fields(event))
|
data/lib/exis_ray/railtie.rb
CHANGED
|
@@ -34,9 +34,15 @@ module ExisRay
|
|
|
34
34
|
# Bloque de ejecución con o sin tags dependiendo de la configuración
|
|
35
35
|
execute_with_optional_tags { yield }
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
duration_s = ExisRay::Tracer.current_duration_s
|
|
38
|
+
human_time = ActiveSupport::Duration.build(duration_s).inspect
|
|
39
|
+
|
|
40
|
+
log_event(:info, "component=exis_ray event=task_finished task=#{task_name} status=success duration_s=#{duration_s} duration_human=\"#{human_time}\"")
|
|
38
41
|
rescue StandardError => e
|
|
39
|
-
|
|
42
|
+
duration_s = ExisRay::Tracer.current_duration_s
|
|
43
|
+
human_time = ActiveSupport::Duration.build(duration_s).inspect
|
|
44
|
+
|
|
45
|
+
log_event(:error, "component=exis_ray event=task_finished task=#{task_name} status=failed duration_s=#{duration_s} duration_human=\"#{human_time}\" error_class=#{e.class} error_message=#{e.message.inspect}")
|
|
40
46
|
raise e
|
|
41
47
|
ensure
|
|
42
48
|
# Limpieza centralizada obligatoria para evitar filtraciones de memoria o contexto
|
data/lib/exis_ray/tracer.rb
CHANGED
|
@@ -58,8 +58,16 @@ module ExisRay
|
|
|
58
58
|
#
|
|
59
59
|
# @return [Integer] Duración en ms.
|
|
60
60
|
def self.current_duration_ms
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
(current_duration_s * 1000).round
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Calcula el tiempo transcurrido en segundos desde el inicio de la request.
|
|
65
|
+
# Cumple con el estándar Wispro-Observability-Spec (v1).
|
|
66
|
+
#
|
|
67
|
+
# @return [Float] Duración en segundos.
|
|
68
|
+
def self.current_duration_s
|
|
69
|
+
return 0.0 unless created_at
|
|
70
|
+
(Process.clock_gettime(Process::CLOCK_MONOTONIC) - created_at).round(4)
|
|
63
71
|
end
|
|
64
72
|
|
|
65
73
|
# Construye el header de trazabilidad para enviar al siguiente servicio.
|
data/lib/exis_ray/version.rb
CHANGED
data/lib/exis_ray.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require "exis_ray/version"
|
|
2
2
|
|
|
3
3
|
# Dependencias externas
|
|
4
|
-
# Necesario para 'safe_constantize', 'present?', y '
|
|
4
|
+
# Necesario para 'safe_constantize', 'present?', 'CurrentAttributes' y 'Duration'
|
|
5
5
|
require "active_support"
|
|
6
6
|
require "active_support/core_ext/string/inflections" # Para safe_constantize
|
|
7
7
|
require "active_support/current_attributes"
|
|
8
|
+
require "active_support/duration"
|
|
9
|
+
require "active_support/core_ext/numeric/time" # Para .seconds, .minutes, etc.
|
|
8
10
|
|
|
9
11
|
# Componentes internos del Core
|
|
10
12
|
require "exis_ray/configuration"
|
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.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gabriel Edera
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -46,6 +46,7 @@ extensions: []
|
|
|
46
46
|
extra_rdoc_files: []
|
|
47
47
|
files:
|
|
48
48
|
- CHANGELOG.md
|
|
49
|
+
- CLAUDE.md
|
|
49
50
|
- LICENSE.txt
|
|
50
51
|
- README.md
|
|
51
52
|
- Rakefile
|