bug_bunny 3.0.1 → 3.0.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: 730ae48562650742b536036de0db8b811efa48855790ef08bffc15713867654e
4
- data.tar.gz: c4d16c6b17307831dd9cfc3be7ef2f78ecc52254034ded5187b2a19f00490aa3
3
+ metadata.gz: 42bf10dd1d3e749561b43b298e6dda00f9b6a76a40dce49103d1d094faa39948
4
+ data.tar.gz: 3088ecec60567a95972ed7abc7dff8f6dd9f5019ad6e0a19bb15dcae61e68bb3
5
5
  SHA512:
6
- metadata.gz: 5551a1bff89d318faf4155dead6e042ac991b57a30459efa2bfcad9029191b12391e6e058e275dda39bdaa4ed7cd2413e46ececa048905836009e4294b8d85e2
7
- data.tar.gz: dd6af676b128f77d01c885d1107b773080d3ebd80b0a49477f3f6d4db511f91acdf8d2775281fa4cae529e17161cf7180caf41c983aece183112f6ab93fedf69
6
+ metadata.gz: ccf976738d355512f3effec2d84a94087c62b41b329516df4cb15ca99034385da216592a7b52acec6aa224708b7c5c272f17dde349a8d5fe88f3903e33ff1122
7
+ data.tar.gz: 60252a5667d1178b4ac9de5118dd21cfd5538c12c9f69360988f493ffea50c81e4efec2cc08f50570fc9e76c9a57d9bb08c1ef6d75aef4a6b25f995fc765b512
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.0.2] - 2026-02-12
4
+
5
+ ### 🚀 Features
6
+ * **Automatic Parameter Wrapping:** `BugBunny::Resource` now automatically wraps the payload inside a root key derived from the model name (e.g., `Manager::Service` -> `{ service: { ... } }`). This mimics Rails form behavior and enables the use of `params.require(:service)` in controllers.
7
+ * Added `self.param_key = '...'` to `BugBunny::Resource` to allow custom root keys.
8
+ * **Declarative Error Handling:** Added Rails-like `rescue_from` DSL to `BugBunny::Controller`. You can now register exception handlers at the class level without overriding methods manually.
9
+ ```ruby
10
+ rescue_from ActiveRecord::RecordNotFound do |e|
11
+ render status: :not_found, json: { error: e.message }
12
+ end
13
+ ```
14
+
15
+ ### 🐛 Bug Fixes
16
+ * **RPC Timeouts on Crash:** Fixed a critical issue where the Client would hang until timeout (`BugBunny::RequestTimeout`) if the Consumer crashed or the route was not found.
17
+ * The Consumer now catches `NameError` (Route not found) and returns a **501 Not Implemented**.
18
+ * The Consumer catches unhandled `StandardError` (App crash) and returns a **500 Internal Server Error**.
19
+ * Ensures a reply is ALWAYS sent to the caller, preventing blocking processes.
20
+
21
+ ### 🛠 Improvements
22
+ * **Controller:** Refactored `BugBunny::Controller` to include a default safety net that catches unhandled errors and logs them properly before returning a 500 status.
23
+
3
24
  ## [3.0.1] - 2026-02-10
4
25
 
5
26
  ### 🚀 Features: RESTful Architecture
data/README.md CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  **BugBunny** es un framework RPC para Ruby on Rails sobre **RabbitMQ**.
4
4
 
5
- Su filosofía es **"Active Record over AMQP"**. Abstrae la complejidad de colas y exchanges transformando patrones de mensajería en una arquitectura **RESTful simulada**.
6
-
7
- A diferencia de otros clientes de RabbitMQ, BugBunny viaja con **Verbos HTTP** (`GET`, `POST`, `PUT`, `DELETE`) inyectados en los headers AMQP. Esto permite construir una API semántica donde un **Router Inteligente** despacha los mensajes a controladores Rails estándar.
5
+ Su filosofía es **"Active Record over AMQP"**. Transforma la complejidad de la mensajería asíncrona en una arquitectura **RESTful simulada**. Los mensajes viajan con Verbos HTTP (`GET`, `POST`, `PUT`, `DELETE`) inyectados en los headers AMQP, permitiendo que un **Router Inteligente** despache las peticiones a controladores Rails estándar.
8
6
 
9
7
  ---
10
8
 
@@ -22,291 +20,262 @@ Ejecuta el bundle:
22
20
  bundle install
23
21
  ```
24
22
 
25
- Corre el instalador para generar la configuración inicial:
23
+ Genera los archivos de configuración iniciales:
26
24
 
27
25
  ```bash
28
26
  rails g bug_bunny:install
29
27
  ```
30
28
 
29
+ Esto creará:
30
+ 1. `config/initializers/bug_bunny.rb`
31
+ 2. `app/rabbit/controllers/`
32
+
31
33
  ---
32
34
 
33
35
  ## ⚙️ Configuración
34
36
 
35
- Configura tus credenciales y el Pool de conexiones en el inicializador `config/initializers/bug_bunny.rb`.
37
+ ### 1. Inicializador y Logging
38
+
39
+ BugBunny separa los logs de la aplicación (Requests) de los logs del driver (Heartbeats/Frames) para mantener la consola limpia.
36
40
 
37
41
  ```ruby
42
+ # config/initializers/bug_bunny.rb
43
+
38
44
  BugBunny.configure do |config|
45
+ # --- Credenciales ---
39
46
  config.host = ENV.fetch('RABBITMQ_HOST', 'localhost')
40
47
  config.username = ENV.fetch('RABBITMQ_USER', 'guest')
41
48
  config.password = ENV.fetch('RABBITMQ_PASS', 'guest')
42
49
  config.vhost = ENV.fetch('RABBITMQ_VHOST', '/')
43
50
 
44
- # Timeouts y Recuperación
45
- config.rpc_timeout = 10 # Segundos a esperar respuesta síncrona
46
- config.network_recovery_interval = 5
51
+ # --- Timeouts ---
52
+ config.rpc_timeout = 10 # Timeout para esperar respuesta (Síncrono)
53
+ config.network_recovery_interval = 5 # Segundos para reintentar conexión
54
+
55
+ # --- Logging (Niveles recomendados) ---
56
+ # Logger de BugBunny: Muestra tus requests (INFO)
57
+ config.logger = Logger.new(STDOUT)
58
+ config.logger.level = Logger::INFO
59
+
60
+ # Logger de Bunny (Driver): Silencia el ruido de bajo nivel (WARN)
61
+ config.bunny_logger = Logger.new(STDOUT)
62
+ config.bunny_logger.level = Logger::WARN
47
63
  end
64
+ ```
65
+
66
+ ### 2. Connection Pool (Crítico) 🧵
67
+
68
+ Para entornos concurrentes como **Puma** o **Sidekiq**, es **obligatorio** definir un Pool de conexiones global. BugBunny no gestiona hilos automáticamente sin esta configuración.
48
69
 
49
- # ⚠️ CRÍTICO: Definimos el Pool Global
50
- # Es vital usar ConnectionPool para garantizar la seguridad en entornos
51
- # multi-hilo como Puma o Sidekiq.
70
+ ```ruby
71
+ # config/initializers/bug_bunny.rb
72
+
73
+ # Define el pool global (ajusta el tamaño según tus hilos de Puma/Sidekiq)
52
74
  BUG_BUNNY_POOL = ConnectionPool.new(size: ENV.fetch('RAILS_MAX_THREADS', 5).to_i, timeout: 5) do
53
75
  BugBunny.create_connection
54
76
  end
55
77
 
56
- # Inyectamos el pool por defecto a los recursos
78
+ # Inyecta el pool a los recursos para que lo usen automáticamente
57
79
  BugBunny::Resource.connection_pool = BUG_BUNNY_POOL
58
80
  ```
59
81
 
60
82
  ---
61
83
 
62
- ## 🚀 Modo Resource (ORM / Active Record)
84
+ ## 🚀 Modo Resource (ORM / Cliente)
63
85
 
64
- Define modelos que actúan como proxies de recursos remotos. BugBunny separa la **Lógica de Transporte** (RabbitMQ) de la **Lógica de Aplicación** (Controladores).
86
+ Define modelos que actúan como proxies de recursos remotos. BugBunny se encarga de serializar, "wrappear" parámetros y enviar el verbo correcto.
65
87
 
66
88
  ### Definición del Modelo
67
89
 
68
90
  ```ruby
69
- class RemoteUser < BugBunny::Resource
91
+ # app/models/manager/service.rb
92
+ class Manager::Service < BugBunny::Resource
70
93
  # 1. Configuración de Transporte
71
- self.exchange = 'app.topic'
72
- self.exchange_type = 'topic'
73
-
94
+ self.exchange = 'box_cluster_manager'
95
+ self.exchange_type = 'direct'
96
+
74
97
  # 2. Configuración Lógica (Routing)
75
- # Define el nombre del recurso. Se usa para:
76
- # - Routing Key automática: 'users' (Topic)
77
- # - URL Base: 'users'
78
- self.resource_name = 'users'
98
+ # Define la URL base y la routing key por defecto.
99
+ self.resource_name = 'services'
79
100
 
80
- # Nota: BugBunny es Schema-less. No necesitas definir atributos.
81
- # Soporta acceso dinámico: user.Name, user.email, etc.
101
+ # 3. Wrapping de Parámetros (Opcional)
102
+ # Por defecto usa el nombre del modelo sin módulo (Manager::Service -> 'service').
103
+ # Puedes forzarlo con:
104
+ # self.param_key = 'docker_service'
82
105
  end
83
106
  ```
84
107
 
85
- ### Consumiendo el Servicio (CRUD RESTful)
108
+ ### CRUD RESTful
86
109
 
87
- BugBunny traduce automáticamente las llamadas de Ruby a peticiones HTTP simuladas.
110
+ Las operaciones de Ruby se traducen a verbos HTTP sobre AMQP.
88
111
 
89
112
  ```ruby
90
- # --- READ COLLECTION (Index) ---
91
- # Envia: GET users?active=true
92
- # Routing Key: "users"
93
- users = RemoteUser.where(active: true)
94
-
95
- # --- READ MEMBER (Show) ---
96
- # Envia: GET users/123
97
- # Routing Key: "users"
98
- user = RemoteUser.find(123)
99
- puts user.email
100
-
101
- # --- CREATE ---
102
- # Envia: POST users
103
- # Routing Key: "users"
104
- # Body: { "email": "test@test.com", "role": "admin" }
105
- user = RemoteUser.create(email: "test@test.com", role: "admin")
106
-
107
- # --- UPDATE ---
108
- # Envia: PUT users/123
109
- # Routing Key: "users"
110
- user.update(email: "edit@test.com")
111
- # Dirty Tracking: Solo se envían los campos modificados.
112
-
113
- # --- DESTROY ---
114
- # Envia: DELETE users/123
115
- # Routing Key: "users"
116
- user.destroy
113
+ # --- LEER (GET) ---
114
+ # Envia: GET services
115
+ # Routing Key: "services"
116
+ services = Manager::Service.all
117
+
118
+ # Envia: GET services/123
119
+ service = Manager::Service.find('123')
120
+
121
+ # --- CREAR (POST) ---
122
+ # Envia: POST services
123
+ # Body: { "service": { "name": "nginx", "replicas": 3 } }
124
+ # Nota: Envuelve los params automáticamente en la clave 'service'.
125
+ svc = Manager::Service.create(name: 'nginx', replicas: 3)
126
+
127
+ # --- ACTUALIZAR (PUT) ---
128
+ # Envia: PUT services/123
129
+ # Body: { "service": { "replicas": 5 } }
130
+ svc.update(replicas: 5)
131
+
132
+ # --- ELIMINAR (DELETE) ---
133
+ # Envia: DELETE services/123
134
+ svc.destroy
117
135
  ```
118
136
 
119
- ### Estrategias de Routing
120
-
121
- Tienes 3 formas de controlar la `routing_key` hacia donde se envían los mensajes:
137
+ ### Contexto Dinámico (`.with`)
122
138
 
123
- | Nivel | Método | Descripción | Ejemplo Config |
124
- | :--- | :--- | :--- | :--- |
125
- | **1. Dinámico** | `resource_name` | (Por defecto) Usa el nombre del recurso. | `self.resource_name = 'users'` -> Key `users` |
126
- | **2. Estático** | `routing_key` | Fuerza TODO a una sola cola. | `self.routing_key = 'cola_manager'` |
127
- | **3. Temporal** | `.with(...)` | Override solo para esa petición. | `User.with(routing_key: 'urgent').create` |
128
-
129
- ---
130
-
131
- ## 🔌 Modo Publisher (Cliente Manual)
132
-
133
- Si no necesitas mapear un recurso o quieres enviar mensajes crudos, utiliza `BugBunny::Client`. Soporta semántica REST pasando el argumento `method:`.
134
-
135
- ### 1. Instanciar el Cliente
136
-
137
- ```ruby
138
- client = BugBunny::Client.new(pool: BUG_BUNNY_POOL) do |conn|
139
- # Puedes inyectar middlewares aquí
140
- conn.use BugBunny::Middleware::JsonResponse
141
- end
142
- ```
143
-
144
- ### 2. Request (RPC Síncrono)
145
-
146
- Envía el mensaje, **bloquea el hilo** y espera la respuesta JSON. Ideal para obtener datos.
139
+ Puedes cambiar la configuración (Routing Key, Exchange) para una operación específica sin afectar al modelo global. El contexto se mantiene durante el ciclo de vida del objeto.
147
140
 
148
141
  ```ruby
149
- # GET (Leer)
150
- response = client.request('users/123', method: :get)
151
- puts response['body']
142
+ # La instancia nace sabiendo que pertenece a la routing_key 'urgent'
143
+ svc = Manager::Service.with(routing_key: 'urgent').new(name: 'redis')
152
144
 
153
- # POST (Crear / Ejecutar)
154
- response = client.request('math/calc', method: :post, body: { a: 10, b: 20 })
145
+ # ... lógica de negocio ...
155
146
 
156
- # PUT (Actualizar)
157
- client.request('users/123', method: :put, body: { active: true })
158
-
159
- # DELETE (Borrar)
160
- client.request('users/123', method: :delete)
147
+ # Al guardar, BugBunny recuerda el contexto y envía a 'urgent'
148
+ svc.save
149
+ # Log: [BugBunny] [POST] '/services' | Routing Key: 'urgent'
161
150
  ```
162
151
 
163
- ### 3. Publish (Asíncrono / Fire-and-Forget)
152
+ ---
164
153
 
165
- Envía el mensaje y retorna inmediatamente. No espera respuesta. Por defecto usa `method: :post` si no se especifica.
154
+ ## 📡 Modo Servidor (Worker & Router)
166
155
 
167
- ```ruby
168
- # Enviar log o evento
169
- client.publish('logs/error', method: :post, body: { msg: 'Disk full' })
170
- ```
156
+ BugBunny incluye un **Router Inteligente** que despacha mensajes a controladores basándose en el Verbo y el Path, imitando a Rails.
171
157
 
172
- ### 4. Configuración Avanzada (Bloques)
158
+ ### 1. El Controlador (`app/rabbit/controllers/`)
173
159
 
174
- Puedes usar un bloque para configurar opciones de bajo nivel de AMQP (prioridad, expiración, headers, app_id).
160
+ Hereda de `BugBunny::Controller`. Tienes acceso a `params`, `before_action` y `rescue_from`.
175
161
 
176
162
  ```ruby
177
- client.publish('jobs/process') do |req|
178
- req.method = :post
179
- req.body = { image_id: 99 }
180
-
181
- # Metadatos AMQP
182
- req.priority = 9 # Alta prioridad (0-9)
183
- req.expiration = '5000' # TTL 5 segundos (ms)
184
- req.app_id = 'web-frontend'
185
- req.headers['X-Trace-Id'] = 'abc-123'
186
- end
187
- ```
163
+ # app/rabbit/controllers/services_controller.rb
164
+ class ServicesController < BugBunny::Controller
165
+ # Callbacks
166
+ before_action :set_service, only: %i[show update destroy]
188
167
 
189
- ### 5. Referencia de Opciones
168
+ # GET services
169
+ def index
170
+ render status: 200, json: DockerService.all
171
+ end
190
172
 
191
- Estas opciones pueden pasarse como argumentos (`client.request(key: val)`) o dentro del bloque (`req.key = val`).
173
+ # POST services
174
+ def create
175
+ # BugBunny wrappea los params automáticamente en el Resource.
176
+ # Aquí los consumimos con seguridad usando Strong Parameters simulados o hash access.
177
+ # params[:service] estará disponible gracias al param_key del Resource.
192
178
 
193
- | Opción / Atributo | Tipo | Descripción | Default |
194
- | :--- | :--- | :--- | :--- |
195
- | `body` | `Hash/String` | El contenido del mensaje. | `nil` |
196
- | `method` | `Symbol` | Verbo HTTP (`:get`, `:post`, `:put`, `:delete`). | `:get` (en request) |
197
- | `exchange` | `String` | Nombre del Exchange destino. | `''` (Default Ex) |
198
- | `routing_key` | `String` | Clave de ruteo. Si falta, usa el `path`. | `path` |
199
- | `headers` | `Hash` | Headers personalizados. | `{}` |
200
- | `timeout` | `Integer` | (Solo RPC) Segundos máx de espera. | Config global |
201
- | `app_id` | `String` | ID de la aplicación origen. | `nil` |
202
- | `priority` | `Integer` | Prioridad del mensaje (0-9). | `0` |
203
- | `expiration` | `String` | TTL del mensaje en ms. | `nil` |
179
+ result = DockerService.create(params[:service])
180
+ render status: 201, json: result
181
+ end
204
182
 
205
- ---
183
+ private
206
184
 
207
- ## 📡 Modo Servidor (El Worker)
185
+ def set_service
186
+ # params[:id] se extrae automágicamente de la URL (Route Param)
187
+ @service = DockerService.find(params[:id])
208
188
 
209
- BugBunny incluye un **Router Inteligente** que funciona igual que el `config/routes.rb` de Rails. Infiere la acción basándose en el **Verbo HTTP** y la estructura de la **URL**.
189
+ unless @service
190
+ render status: 404, json: { error: "Service not found" }
191
+ end
192
+ end
193
+ end
194
+ ```
210
195
 
211
- ### 1. Definir Controladores
196
+ ### 2. Manejo de Errores (`rescue_from`)
212
197
 
213
- Crea tus controladores en `app/rabbit/controllers/`. Heredan de `BugBunny::Controller`.
198
+ Puedes definir un `ApplicationController` base para manejar errores de forma centralizada y declarativa.
214
199
 
215
200
  ```ruby
216
- # app/rabbit/controllers/users_controller.rb
217
- class UsersController < BugBunny::Controller
218
-
219
- # GET users
220
- def index
221
- users = User.where(active: params[:active])
222
- render status: 200, json: users
201
+ # app/rabbit/controllers/application.rb
202
+ class ApplicationController < BugBunny::Controller
203
+ # Manejo específico
204
+ rescue_from ActiveRecord::RecordNotFound do
205
+ render status: :not_found, json: { error: "Resource missing" }
223
206
  end
224
207
 
225
- # GET users/123
226
- def show
227
- user = User.find(params[:id])
228
- render status: 200, json: user
229
- end
230
-
231
- # POST users
232
- def create
233
- user = User.new(params)
234
- if user.save
235
- render status: 201, json: user
236
- else
237
- # Estos errores se propagan como BugBunny::UnprocessableEntity
238
- render status: 422, json: { errors: user.errors }
239
- end
240
- end
241
-
242
- # PUT users/123
243
- def update
244
- # ...
208
+ rescue_from ActiveModel::ValidationError do |e|
209
+ render status: :unprocessable_entity, json: e.model.errors
245
210
  end
246
211
 
247
- # DELETE users/123
248
- def destroy
249
- # ...
212
+ # Catch-all (Red de seguridad)
213
+ rescue_from StandardError do |e|
214
+ BugBunny.configuration.logger.error(e)
215
+ render status: :internal_server_error, json: { error: "Internal Error" }
250
216
  end
251
217
  end
252
218
  ```
253
219
 
254
- ### 2. Tabla de Ruteo (Convención)
220
+ ### 3. Tabla de Ruteo (Convención)
255
221
 
256
- El Router despacha automáticamente según esta tabla:
222
+ El Router infiere la acción automáticamente:
257
223
 
258
- | Header `x-http-method` | Header `type` (URL) | Controlador | Acción |
224
+ | Verbo | URL Pattern | Controlador | Acción |
259
225
  | :--- | :--- | :--- | :--- |
260
- | `GET` | `users` | `UsersController` | `index` |
261
- | `GET` | `users/12` | `UsersController` | `show` |
262
- | `POST` | `users` | `UsersController` | `create` |
263
- | `PUT` | `users/12` | `UsersController` | `update` |
264
- | `DELETE` | `users/12` | `UsersController` | `destroy` |
265
- | `POST` | `users/12/promote` | `UsersController` | `promote` (Custom) |
266
-
267
- ### 3. Ejecutar el Worker
268
-
269
- ```bash
270
- bundle exec rake bug_bunny:work
271
- ```
226
+ | `GET` | `services` | `ServicesController` | `index` |
227
+ | `GET` | `services/12` | `ServicesController` | `show` |
228
+ | `POST` | `services` | `ServicesController` | `create` |
229
+ | `PUT` | `services/12` | `ServicesController` | `update` |
230
+ | `DELETE` | `services/12` | `ServicesController` | `destroy` |
231
+ | `POST` | `services/12/restart` | `ServicesController` | `restart` (Custom) |
272
232
 
273
233
  ---
274
234
 
275
- ## 🏗 Arquitectura REST-over-AMQP
235
+ ## 🔌 Modo Publisher (Cliente Manual)
236
+
237
+ Si necesitas enviar mensajes crudos fuera de la lógica Resource, usa `BugBunny::Client`.
276
238
 
277
- BugBunny desacopla el transporte de la lógica usando headers AMQP estándar.
239
+ ```ruby
240
+ client = BugBunny::Client.new(pool: BUG_BUNNY_POOL)
241
+
242
+ # --- REQUEST (Síncrono / RPC) ---
243
+ # Espera la respuesta. Lanza BugBunny::RequestTimeout si falla.
244
+ response = client.request('services/123/logs',
245
+ method: :get,
246
+ exchange: 'logs_exchange',
247
+ timeout: 5
248
+ )
249
+ puts response['body']
278
250
 
279
- | Concepto | REST (HTTP) | BugBunny (AMQP) |
280
- | :--- | :--- | :--- |
281
- | **Recurso** | `POST /users` | Header `type`: `users` + Header `x-http-method`: `POST` |
282
- | **Parametros** | Query String / Body | Header `type` (Query) + Body (Payload) |
283
- | **Destino** | DNS / IP | Routing Key (ej: `users`) |
284
- | **Status** | HTTP Code (200, 404) | JSON Response `status` |
251
+ # --- PUBLISH (Asíncrono / Fire-and-Forget) ---
252
+ # No espera respuesta.
253
+ client.publish('audit/events',
254
+ method: :post,
255
+ body: { event: 'login', user_id: 1 }
256
+ )
257
+ ```
285
258
 
286
259
  ---
287
260
 
288
- ## 🛠 Middlewares
261
+ ## 🏗 Arquitectura REST-over-AMQP
289
262
 
290
- BugBunny usa una pila de middlewares para procesar peticiones y respuestas, permitiendo logging, manejo de errores y transformación de datos.
263
+ BugBunny desacopla el transporte de la lógica usando headers estándar.
291
264
 
292
- ```ruby
293
- # Configuración global en el Resource
294
- BugBunny::Resource.client_middleware do |conn|
295
- # 1. Lanza excepciones Ruby para errores 4xx/5xx
296
- conn.use BugBunny::Middleware::RaiseError
265
+ 1. **Semántica:** El mensaje lleva headers `type` (URL) y `x-http-method` (Verbo).
266
+ 2. **Ruteo:** El consumidor lee estos headers y ejecuta el controlador correspondiente.
267
+ 3. **Parametros:** `params` unifica:
268
+ * **Route Params:** `services/123` -> `params[:id] = 123`
269
+ * **Query Params:** `services?force=true` -> `params[:force] = true`
270
+ * **Body:** Payload JSON fusionado en el hash.
297
271
 
298
- # 2. Parsea JSON a HashWithIndifferentAccess
299
- conn.use BugBunny::Middleware::JsonResponse
300
- end
301
- ```
272
+ ### Logs Estructurados
302
273
 
303
- ### Excepciones Soportadas
274
+ Facilita el debugging mostrando claramente qué recurso se está tocando y por dónde viaja.
304
275
 
305
- * `BugBunny::BadRequest` (400)
306
- * `BugBunny::NotFound` (404)
307
- * `BugBunny::RequestTimeout` (408)
308
- * `BugBunny::UnprocessableEntity` (422) - Incluye errores de validación.
309
- * `BugBunny::InternalServerError` (500)
276
+ ```text
277
+ [BugBunny] [POST] '/services' | Exchange: 'cluster' (Type: direct) | Routing Key: 'node-1'
278
+ ```
310
279
 
311
280
  ---
312
281
 
@@ -26,6 +26,10 @@ module BugBunny
26
26
  # @return [Logger] Instancia del logger para depuración (default: Logger a STDOUT).
27
27
  attr_accessor :logger
28
28
 
29
+ # @return [Logger] Logger específico para el driver Bunny (Conexión, Heartbeats, Frames).
30
+ # Se recomienda nivel WARN para evitar ruido.
31
+ attr_accessor :bunny_logger
32
+
29
33
  # @return [Boolean] Si `true`, Bunny intentará reconectar automáticamente ante fallos de red (default: true).
30
34
  attr_accessor :automatically_recover
31
35
 
@@ -58,7 +62,13 @@ module BugBunny
58
62
 
59
63
  # Inicializa la configuración con valores por defecto seguros.
60
64
  def initialize
65
+ # Logger de la Aplicación (BugBunny) -> INFO (Ves tus requests)
61
66
  @logger = Logger.new(STDOUT)
67
+ @logger.level = Logger::INFO
68
+
69
+ # Logger del Driver (Bunny) -> WARN (Oculta el ruido de "handle_frame")
70
+ @bunny_logger = Logger.new(STDOUT)
71
+ @bunny_logger.level = Logger::WARN
62
72
  @automatically_recover = true
63
73
  @network_recovery_interval = 5
64
74
  @connection_timeout = 10