ez_logs_agent 0.1.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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +57 -0
- data/CONFIGURATION.md +752 -0
- data/FAQ.md +574 -0
- data/LICENSE.txt +21 -0
- data/QUICKSTART.md +390 -0
- data/README.md +1021 -0
- data/RELEASING.md +55 -0
- data/Rakefile +8 -0
- data/lib/ez_logs_agent/actor.rb +57 -0
- data/lib/ez_logs_agent/actor_validator.rb +51 -0
- data/lib/ez_logs_agent/buffer.rb +83 -0
- data/lib/ez_logs_agent/capturers/active_job_capturer.rb +270 -0
- data/lib/ez_logs_agent/capturers/database_capturer.rb +467 -0
- data/lib/ez_logs_agent/capturers/job_capturer.rb +238 -0
- data/lib/ez_logs_agent/configuration.rb +186 -0
- data/lib/ez_logs_agent/configuration_validator.rb +139 -0
- data/lib/ez_logs_agent/correlation.rb +40 -0
- data/lib/ez_logs_agent/event_builder.rb +281 -0
- data/lib/ez_logs_agent/flush_scheduler.rb +99 -0
- data/lib/ez_logs_agent/logger.rb +62 -0
- data/lib/ez_logs_agent/middleware/http_request.rb +1094 -0
- data/lib/ez_logs_agent/railtie.rb +353 -0
- data/lib/ez_logs_agent/resource_extractor.rb +172 -0
- data/lib/ez_logs_agent/retry_sender.rb +120 -0
- data/lib/ez_logs_agent/transport.rb +91 -0
- data/lib/ez_logs_agent/version.rb +5 -0
- data/lib/ez_logs_agent.rb +42 -0
- data/lib/generators/ez_logs_agent/install/install_generator.rb +94 -0
- data/lib/generators/ez_logs_agent/install/templates/ez_logs_agent.rb.tt +128 -0
- data/lib/tasks/ez_logs_agent.rake +110 -0
- data/script/publish-to-public.sh +113 -0
- data/sig/ez_logs_agent.rbs +4 -0
- metadata +178 -0
data/CONFIGURATION.md
ADDED
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
# EZLogs Agent — Configuration Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for all configuration options.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Required Settings](#required-settings)
|
|
10
|
+
- [Event Capture Toggles](#event-capture-toggles)
|
|
11
|
+
- [Exclusion Lists](#exclusion-lists)
|
|
12
|
+
- [Display Names](#display-names)
|
|
13
|
+
- [Actor Context](#actor-context)
|
|
14
|
+
- [Transport Settings](#transport-settings)
|
|
15
|
+
- [Logging](#logging)
|
|
16
|
+
- [Environment Variables](#environment-variables)
|
|
17
|
+
- [Validation](#validation)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Required Settings
|
|
22
|
+
|
|
23
|
+
### `server_url`
|
|
24
|
+
|
|
25
|
+
**Type:** String
|
|
26
|
+
**Required:** Yes
|
|
27
|
+
**Default:** None
|
|
28
|
+
|
|
29
|
+
The URL of your EZLogs server where events will be sent.
|
|
30
|
+
|
|
31
|
+
**Example:**
|
|
32
|
+
```ruby
|
|
33
|
+
config.server_url = "https://your-ezlogs-server.com"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Validation:**
|
|
37
|
+
- Must be present
|
|
38
|
+
- Must start with `http://` or `https://`
|
|
39
|
+
- Must be a valid URL format
|
|
40
|
+
|
|
41
|
+
**Common values:**
|
|
42
|
+
- Production: `https://your-ezlogs-server.com`
|
|
43
|
+
- Staging: `https://staging.your-ezlogs-server.com`
|
|
44
|
+
- Development: `http://localhost:3000`
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### `project_token`
|
|
49
|
+
|
|
50
|
+
**Type:** String
|
|
51
|
+
**Required:** Yes (for authentication)
|
|
52
|
+
**Default:** None
|
|
53
|
+
|
|
54
|
+
Your API key from the EZLogs dashboard. This is sent as a Bearer token in the `Authorization` header.
|
|
55
|
+
|
|
56
|
+
**Example:**
|
|
57
|
+
```ruby
|
|
58
|
+
config.project_token = "ezl_abc123xyz..."
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Best practice:** Use environment variables
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
config.project_token = ENV['EZLOGS_API_KEY']
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Validation:**
|
|
68
|
+
- Should be present (warning if missing)
|
|
69
|
+
- Never logged or exposed in error messages
|
|
70
|
+
- Sent over HTTPS only
|
|
71
|
+
|
|
72
|
+
**Where to get it:**
|
|
73
|
+
1. Log into your EZLogs dashboard
|
|
74
|
+
2. Go to Settings → API Keys
|
|
75
|
+
3. Create a new key or copy an existing one
|
|
76
|
+
4. Keys start with `ezl_` prefix
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Event Capture Toggles
|
|
81
|
+
|
|
82
|
+
### `capture_http`
|
|
83
|
+
|
|
84
|
+
**Type:** Boolean
|
|
85
|
+
**Required:** No
|
|
86
|
+
**Default:** `true`
|
|
87
|
+
|
|
88
|
+
Enable or disable HTTP request capture.
|
|
89
|
+
|
|
90
|
+
**Example:**
|
|
91
|
+
```ruby
|
|
92
|
+
config.capture_http = true
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**When enabled, captures:**
|
|
96
|
+
- HTTP method, path, status code, duration
|
|
97
|
+
- Controller and action name (Rails apps)
|
|
98
|
+
- GraphQL operation name and type (queries, mutations, subscriptions)
|
|
99
|
+
- Request correlation ID
|
|
100
|
+
|
|
101
|
+
**When to disable:**
|
|
102
|
+
- API-only apps where HTTP requests aren't meaningful
|
|
103
|
+
- Reducing noise in write-heavy applications
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### `capture_jobs`
|
|
108
|
+
|
|
109
|
+
**Type:** Boolean
|
|
110
|
+
**Required:** No
|
|
111
|
+
**Default:** `true`
|
|
112
|
+
|
|
113
|
+
Enable or disable background job capture.
|
|
114
|
+
|
|
115
|
+
**Example:**
|
|
116
|
+
```ruby
|
|
117
|
+
config.capture_jobs = true
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**When enabled, captures:**
|
|
121
|
+
- Sidekiq job executions
|
|
122
|
+
- ActiveJob executions (any backend)
|
|
123
|
+
- Job class, queue, duration
|
|
124
|
+
- Success/failure status with error messages
|
|
125
|
+
|
|
126
|
+
**When to disable:**
|
|
127
|
+
- Apps without background jobs
|
|
128
|
+
- Reducing noise from high-frequency jobs
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### `capture_database`
|
|
133
|
+
|
|
134
|
+
**Type:** Boolean
|
|
135
|
+
**Required:** No
|
|
136
|
+
**Default:** `true`
|
|
137
|
+
|
|
138
|
+
Enable or disable database change capture.
|
|
139
|
+
|
|
140
|
+
**Example:**
|
|
141
|
+
```ruby
|
|
142
|
+
config.capture_database = true
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**When enabled, captures:**
|
|
146
|
+
- ActiveRecord create, update, destroy operations
|
|
147
|
+
- Model class, record ID, operation type
|
|
148
|
+
- For updates: meaningful attribute changes
|
|
149
|
+
|
|
150
|
+
**When to disable:**
|
|
151
|
+
- Write-heavy applications with excessive database activity
|
|
152
|
+
- Apps where database changes aren't meaningful to business logic
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Exclusion Lists
|
|
157
|
+
|
|
158
|
+
All exclusion lists are **additive** — they add to built-in defaults, not replace them.
|
|
159
|
+
|
|
160
|
+
### `excluded_paths`
|
|
161
|
+
|
|
162
|
+
**Type:** Array of Strings
|
|
163
|
+
**Required:** No
|
|
164
|
+
**Default:** `[]` (uses built-in defaults only)
|
|
165
|
+
|
|
166
|
+
Additional HTTP paths to exclude from capture.
|
|
167
|
+
|
|
168
|
+
**Example:**
|
|
169
|
+
```ruby
|
|
170
|
+
config.excluded_paths = ["/admin*", "/internal*", "/api/internal*"]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Pattern matching:**
|
|
174
|
+
- Use `*` suffix for prefix matching
|
|
175
|
+
- `/admin*` matches `/admin`, `/admin/users`, `/admin/anything`
|
|
176
|
+
- Exact matches without `*` are supported but rarely needed
|
|
177
|
+
|
|
178
|
+
**Built-in exclusions (automatically excluded):**
|
|
179
|
+
- `/rails/active_storage*` — File uploads/downloads
|
|
180
|
+
- `/assets*`, `/packs*`, `/vite*` — Static assets
|
|
181
|
+
- `/health*`, `/up` — Health check endpoints
|
|
182
|
+
- `/favicon.ico` — Browser requests
|
|
183
|
+
|
|
184
|
+
**Common additions:**
|
|
185
|
+
- `/admin*` — Admin panel requests
|
|
186
|
+
- `/internal*` — Internal API endpoints
|
|
187
|
+
- `/debug*` — Debugging tools
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### `excluded_tables`
|
|
192
|
+
|
|
193
|
+
**Type:** Array of Strings
|
|
194
|
+
**Required:** No
|
|
195
|
+
**Default:** `[]` (uses built-in defaults only)
|
|
196
|
+
|
|
197
|
+
Additional database tables to exclude from capture.
|
|
198
|
+
|
|
199
|
+
**Example:**
|
|
200
|
+
```ruby
|
|
201
|
+
config.excluded_tables = ["audit_logs", "versions", "paper_trail_versions"]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Table names:**
|
|
205
|
+
- Use exact table name (lowercase, plural)
|
|
206
|
+
- No prefix matching — must match exactly
|
|
207
|
+
|
|
208
|
+
**Built-in exclusions (automatically excluded):**
|
|
209
|
+
- `sessions` — Session store updates
|
|
210
|
+
- `schema_migrations`, `ar_internal_metadata` — Rails internals
|
|
211
|
+
- `active_storage_*` — ActiveStorage tables
|
|
212
|
+
- `solid_queue_*`, `solid_cache_*`, `solid_cable_*` — Solid* gem internals
|
|
213
|
+
|
|
214
|
+
**Common additions:**
|
|
215
|
+
- `audit_logs`, `versions`, `paper_trail_versions` — Audit trail gems
|
|
216
|
+
- `delayed_jobs` — Delayed::Job queue table
|
|
217
|
+
- `que_jobs` — Que job queue table
|
|
218
|
+
- Internal tables specific to your app
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### `excluded_job_classes`
|
|
223
|
+
|
|
224
|
+
**Type:** Array of Strings
|
|
225
|
+
**Required:** No
|
|
226
|
+
**Default:** `[]` (uses built-in defaults only)
|
|
227
|
+
|
|
228
|
+
Additional job classes to exclude from capture.
|
|
229
|
+
|
|
230
|
+
**Example:**
|
|
231
|
+
```ruby
|
|
232
|
+
config.excluded_job_classes = [
|
|
233
|
+
"MyApp::HealthCheckJob",
|
|
234
|
+
"MyApp::MetricsJob",
|
|
235
|
+
"MyApp::HeartbeatJob"
|
|
236
|
+
]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Class names:**
|
|
240
|
+
- Use full class name including module namespaces
|
|
241
|
+
- `"MyApp::SomeJob"` not `"SomeJob"`
|
|
242
|
+
|
|
243
|
+
**Built-in exclusions (automatically excluded):**
|
|
244
|
+
- `SidekiqAlive::Worker` — Sidekiq health check
|
|
245
|
+
- `SolidQueue::CleanupJob` — SolidQueue maintenance
|
|
246
|
+
- `SolidQueue::RecurringJob` — SolidQueue scheduler
|
|
247
|
+
|
|
248
|
+
**Common additions:**
|
|
249
|
+
- Health check jobs
|
|
250
|
+
- Metrics collection jobs
|
|
251
|
+
- Heartbeat/ping jobs
|
|
252
|
+
- Internal maintenance jobs
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Display Names
|
|
257
|
+
|
|
258
|
+
### `display_name_for`
|
|
259
|
+
|
|
260
|
+
**Type:** Hash (String → Symbol)
|
|
261
|
+
**Required:** No
|
|
262
|
+
**Default:** `{}` (uses fallback strategy for all models)
|
|
263
|
+
|
|
264
|
+
Configure how to display human-readable names for database records.
|
|
265
|
+
|
|
266
|
+
**Example:**
|
|
267
|
+
```ruby
|
|
268
|
+
config.display_name_for = {
|
|
269
|
+
"User" => :email,
|
|
270
|
+
"Product" => :name,
|
|
271
|
+
"Order" => :number,
|
|
272
|
+
"Company" => :name
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Key:** Model class name (String)
|
|
277
|
+
**Value:** Attribute name (Symbol)
|
|
278
|
+
|
|
279
|
+
### How It Works
|
|
280
|
+
|
|
281
|
+
When a database callback fires, the agent resolves a display name:
|
|
282
|
+
|
|
283
|
+
1. **If configured** for the model → use that attribute
|
|
284
|
+
2. **Otherwise, try defaults** → `name`, `title`, `number` (in that order)
|
|
285
|
+
3. **If nothing found** → fall back to `#id`
|
|
286
|
+
|
|
287
|
+
### Examples
|
|
288
|
+
|
|
289
|
+
**Before configuration:**
|
|
290
|
+
```
|
|
291
|
+
User created #123
|
|
292
|
+
Product updated #456
|
|
293
|
+
Order deleted #789
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**After configuration:**
|
|
297
|
+
```
|
|
298
|
+
User created 'jessica@example.com'
|
|
299
|
+
Product updated 'Premium Widget'
|
|
300
|
+
Order deleted '#ORD-2025-0789'
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Important Constraints
|
|
304
|
+
|
|
305
|
+
**✅ Use direct attributes only:**
|
|
306
|
+
```ruby
|
|
307
|
+
# GOOD
|
|
308
|
+
config.display_name_for = { "User" => :email }
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**❌ Never use associations:**
|
|
312
|
+
```ruby
|
|
313
|
+
# BAD - Triggers database query
|
|
314
|
+
config.display_name_for = { "Order" => :customer_email }
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Why?** Associations trigger additional database queries, violating the agent's non-blocking guarantee.
|
|
318
|
+
|
|
319
|
+
**Performance:** Display names are resolved at capture time using only data already loaded in memory.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Actor Context
|
|
324
|
+
|
|
325
|
+
### `actor_from_request`
|
|
326
|
+
|
|
327
|
+
**Type:** Lambda/Proc
|
|
328
|
+
**Required:** No
|
|
329
|
+
**Default:** `nil` (actor tracking disabled)
|
|
330
|
+
|
|
331
|
+
Configure how to extract the "who" (actor) from HTTP requests.
|
|
332
|
+
|
|
333
|
+
**Example:**
|
|
334
|
+
```ruby
|
|
335
|
+
config.actor_from_request = ->(env, controller) {
|
|
336
|
+
return nil unless controller.respond_to?(:current_user)
|
|
337
|
+
user = controller.current_user
|
|
338
|
+
return nil unless user
|
|
339
|
+
|
|
340
|
+
{
|
|
341
|
+
id: user.id.to_s,
|
|
342
|
+
label: user.email
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Parameters
|
|
348
|
+
|
|
349
|
+
**`env`** (Hash)
|
|
350
|
+
- Rack environment hash
|
|
351
|
+
- Always present
|
|
352
|
+
- Contains request headers, session, etc.
|
|
353
|
+
|
|
354
|
+
**`controller`** (Object or nil)
|
|
355
|
+
- Rails controller instance
|
|
356
|
+
- `nil` if controller not available (e.g., Rack apps, API-only mode)
|
|
357
|
+
|
|
358
|
+
### Return Value
|
|
359
|
+
|
|
360
|
+
Return one of:
|
|
361
|
+
|
|
362
|
+
**Actor hash:**
|
|
363
|
+
```ruby
|
|
364
|
+
{
|
|
365
|
+
id: "123", # Required: stable identifier
|
|
366
|
+
label: "user@email.com" # Optional: human-readable display
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Nil (actor unknown):**
|
|
371
|
+
```ruby
|
|
372
|
+
nil
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Schema
|
|
376
|
+
|
|
377
|
+
| Field | Type | Required | Description |
|
|
378
|
+
|-------|------|----------|-------------|
|
|
379
|
+
| `id` | String | Yes | Stable identifier (e.g., user ID). Never changes. |
|
|
380
|
+
| `label` | String | No | Human-readable display (e.g., email). Can change. |
|
|
381
|
+
|
|
382
|
+
### Examples
|
|
383
|
+
|
|
384
|
+
**Devise:**
|
|
385
|
+
```ruby
|
|
386
|
+
config.actor_from_request = ->(env, controller) {
|
|
387
|
+
return nil unless controller.respond_to?(:current_user)
|
|
388
|
+
user = controller.current_user
|
|
389
|
+
return nil unless user
|
|
390
|
+
|
|
391
|
+
{ id: user.id.to_s, label: user.email }
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Clearance:**
|
|
396
|
+
```ruby
|
|
397
|
+
config.actor_from_request = ->(env, controller) {
|
|
398
|
+
return nil unless controller.respond_to?(:current_user)
|
|
399
|
+
user = controller.current_user
|
|
400
|
+
return nil unless user
|
|
401
|
+
|
|
402
|
+
{ id: user.id.to_s, label: user.email }
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Custom session-based auth:**
|
|
407
|
+
```ruby
|
|
408
|
+
config.actor_from_request = ->(env, controller) {
|
|
409
|
+
user_id = env["rack.session"]&.dig("user_id")
|
|
410
|
+
return nil unless user_id
|
|
411
|
+
|
|
412
|
+
# Only if you have fast caching
|
|
413
|
+
user = User.find_by(id: user_id)
|
|
414
|
+
return nil unless user
|
|
415
|
+
|
|
416
|
+
{ id: user.id.to_s, label: user.email }
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**API token auth:**
|
|
421
|
+
```ruby
|
|
422
|
+
config.actor_from_request = ->(env, controller) {
|
|
423
|
+
token = env["HTTP_AUTHORIZATION"]&.split(" ")&.last
|
|
424
|
+
return nil unless token
|
|
425
|
+
|
|
426
|
+
# Lookup user by API token
|
|
427
|
+
user = User.find_by(api_token: token)
|
|
428
|
+
return nil unless user
|
|
429
|
+
|
|
430
|
+
{ id: user.id.to_s, label: user.email }
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Design Philosophy
|
|
435
|
+
|
|
436
|
+
**Actor extraction is opt-in, not automatic.**
|
|
437
|
+
|
|
438
|
+
This prevents:
|
|
439
|
+
- Incorrect attribution (admin impersonating another user)
|
|
440
|
+
- Wrong actors (service accounts, background jobs)
|
|
441
|
+
- Silent failures (custom auth systems)
|
|
442
|
+
|
|
443
|
+
**When actor is unknown, events are captured with `actor: null`.**
|
|
444
|
+
|
|
445
|
+
**Design principle:** Missing data is acceptable; wrong data is not.
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Transport Settings
|
|
450
|
+
|
|
451
|
+
### `buffer_size`
|
|
452
|
+
|
|
453
|
+
**Type:** Integer
|
|
454
|
+
**Required:** No
|
|
455
|
+
**Default:** `10000`
|
|
456
|
+
|
|
457
|
+
Maximum number of events to buffer in memory before oldest events are dropped.
|
|
458
|
+
|
|
459
|
+
**Example:**
|
|
460
|
+
```ruby
|
|
461
|
+
config.buffer_size = 10000
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Memory usage:**
|
|
465
|
+
- Default (10000 events) ≈ 1MB - 2MB
|
|
466
|
+
- 5000 events ≈ 500KB - 1MB
|
|
467
|
+
- 20000 events ≈ 2MB - 4MB
|
|
468
|
+
|
|
469
|
+
**When to adjust:**
|
|
470
|
+
- **Increase to 20000** if you see "Buffer full, dropping events" warnings
|
|
471
|
+
- **Decrease to 5000** if memory usage is a concern (low-volume apps)
|
|
472
|
+
- **Default (10000) is optimized** for high-volume applications with many background jobs
|
|
473
|
+
|
|
474
|
+
**Buffer behavior:**
|
|
475
|
+
- Circular buffer (oldest events dropped when full)
|
|
476
|
+
- Thread-safe
|
|
477
|
+
- Non-blocking
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
### `send_interval`
|
|
482
|
+
|
|
483
|
+
**Type:** Integer (seconds)
|
|
484
|
+
**Required:** No
|
|
485
|
+
**Default:** `3`
|
|
486
|
+
|
|
487
|
+
How often (in seconds) to flush the buffer and send events to the server.
|
|
488
|
+
|
|
489
|
+
**Example:**
|
|
490
|
+
```ruby
|
|
491
|
+
config.send_interval = 3
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
**Trade-offs:**
|
|
495
|
+
|
|
496
|
+
| Value | Latency | Network Usage |
|
|
497
|
+
|-------|---------|---------------|
|
|
498
|
+
| 1s | Lower (more real-time) | Higher (more requests) |
|
|
499
|
+
| 3s | Balanced (default) | Balanced |
|
|
500
|
+
| 5s | Slightly higher latency | Lower requests |
|
|
501
|
+
| 10s | Higher (more delay) | Lower (fewer requests) |
|
|
502
|
+
|
|
503
|
+
**When to adjust:**
|
|
504
|
+
- **Decrease to 1-2s** for more real-time updates
|
|
505
|
+
- **Increase to 5-10s** to reduce network traffic for low-volume apps
|
|
506
|
+
- **Default (3s) is optimized** for high-volume applications with good throughput
|
|
507
|
+
|
|
508
|
+
**Note:** Events are also sent when buffer is full, regardless of interval.
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
### `retry_attempts`
|
|
513
|
+
|
|
514
|
+
**Type:** Integer
|
|
515
|
+
**Required:** No
|
|
516
|
+
**Default:** `3`
|
|
517
|
+
|
|
518
|
+
Number of retry attempts for failed sends (with exponential backoff).
|
|
519
|
+
|
|
520
|
+
**Example:**
|
|
521
|
+
```ruby
|
|
522
|
+
config.retry_attempts = 3
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**Retry schedule (default):**
|
|
526
|
+
1. First attempt: immediate
|
|
527
|
+
2. Retry 1: after 1 second
|
|
528
|
+
3. Retry 2: after 2 seconds
|
|
529
|
+
4. Retry 3: after 4 seconds
|
|
530
|
+
5. Give up
|
|
531
|
+
|
|
532
|
+
**When to adjust:**
|
|
533
|
+
- **Increase (4-5)** if network is unreliable
|
|
534
|
+
- **Decrease (1-2)** if you prefer to drop events quickly
|
|
535
|
+
- **Set to 0** to disable retries (not recommended)
|
|
536
|
+
|
|
537
|
+
**After max retries:**
|
|
538
|
+
- Events are dropped
|
|
539
|
+
- Warning is logged
|
|
540
|
+
- Application continues normally
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Logging
|
|
545
|
+
|
|
546
|
+
### `log_level`
|
|
547
|
+
|
|
548
|
+
**Type:** Symbol
|
|
549
|
+
**Required:** No
|
|
550
|
+
**Default:** `:info`
|
|
551
|
+
|
|
552
|
+
Agent log verbosity level.
|
|
553
|
+
|
|
554
|
+
**Example:**
|
|
555
|
+
```ruby
|
|
556
|
+
config.log_level = :info
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Options:**
|
|
560
|
+
|
|
561
|
+
| Level | What's Logged |
|
|
562
|
+
|-------|---------------|
|
|
563
|
+
| `:debug` | Everything (capture events, buffer state, send attempts) |
|
|
564
|
+
| `:info` | Initialization, sends, warnings (default) |
|
|
565
|
+
| `:warn` | Warnings and errors only |
|
|
566
|
+
| `:error` | Errors only |
|
|
567
|
+
|
|
568
|
+
**Debug output example:**
|
|
569
|
+
```
|
|
570
|
+
[EzLogsAgent] Captured HTTP event: GET /users (200, 45ms)
|
|
571
|
+
[EzLogsAgent] Captured Database event: User#create (id: 123)
|
|
572
|
+
[EzLogsAgent] Buffer: 12 events
|
|
573
|
+
[EzLogsAgent] Sending batch of 12 events...
|
|
574
|
+
[EzLogsAgent] Batch sent successfully (HTTP 200, 120ms)
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Info output example (default):**
|
|
578
|
+
```
|
|
579
|
+
[EzLogsAgent] Agent initialized successfully
|
|
580
|
+
[EzLogsAgent] Sending batch of 12 events...
|
|
581
|
+
[EzLogsAgent] Batch sent successfully (HTTP 200)
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**When to use debug:**
|
|
585
|
+
- Troubleshooting missing events
|
|
586
|
+
- Verifying capture is working
|
|
587
|
+
- Debugging correlation issues
|
|
588
|
+
|
|
589
|
+
**Note:** Debug level can be verbose in high-traffic applications.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## Environment Variables
|
|
594
|
+
|
|
595
|
+
While not directly supported as a configuration option, you can use environment variables for any setting:
|
|
596
|
+
|
|
597
|
+
### Recommended Pattern
|
|
598
|
+
|
|
599
|
+
```ruby
|
|
600
|
+
EzLogsAgent.configure do |config|
|
|
601
|
+
config.server_url = ENV.fetch('EZLOGS_SERVER_URL')
|
|
602
|
+
config.project_token = ENV.fetch('EZLOGS_API_KEY')
|
|
603
|
+
|
|
604
|
+
config.capture_http = ENV.fetch('EZLOGS_CAPTURE_HTTP', 'true') == 'true'
|
|
605
|
+
config.capture_jobs = ENV.fetch('EZLOGS_CAPTURE_JOBS', 'true') == 'true'
|
|
606
|
+
config.capture_database = ENV.fetch('EZLOGS_CAPTURE_DATABASE', 'true') == 'true'
|
|
607
|
+
|
|
608
|
+
config.buffer_size = ENV.fetch('EZLOGS_BUFFER_SIZE', '10000').to_i
|
|
609
|
+
config.send_interval = ENV.fetch('EZLOGS_SEND_INTERVAL', '3').to_i
|
|
610
|
+
config.retry_attempts = ENV.fetch('EZLOGS_RETRY_ATTEMPTS', '3').to_i
|
|
611
|
+
|
|
612
|
+
config.log_level = ENV.fetch('EZLOGS_LOG_LEVEL', 'info').to_sym
|
|
613
|
+
end
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Example `.env` File
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
EZLOGS_SERVER_URL=https://your-ezlogs-server.com
|
|
620
|
+
EZLOGS_API_KEY=ezl_your_api_key_here
|
|
621
|
+
|
|
622
|
+
# Optional
|
|
623
|
+
EZLOGS_CAPTURE_HTTP=true
|
|
624
|
+
EZLOGS_CAPTURE_JOBS=true
|
|
625
|
+
EZLOGS_CAPTURE_DATABASE=true
|
|
626
|
+
EZLOGS_BUFFER_SIZE=10000
|
|
627
|
+
EZLOGS_SEND_INTERVAL=3
|
|
628
|
+
EZLOGS_RETRY_ATTEMPTS=3
|
|
629
|
+
EZLOGS_LOG_LEVEL=info
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Validation
|
|
635
|
+
|
|
636
|
+
Configuration is validated when Rails boots. Errors and warnings are logged.
|
|
637
|
+
|
|
638
|
+
### Error Messages
|
|
639
|
+
|
|
640
|
+
**Missing server_url:**
|
|
641
|
+
```
|
|
642
|
+
[Railtie] Configuration validation failed:
|
|
643
|
+
- server_url is required. Set it in config/initializers/ez_logs_agent.rb
|
|
644
|
+
[Railtie] Agent initialization skipped. Please fix configuration errors.
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
**Invalid server_url:**
|
|
648
|
+
```
|
|
649
|
+
[Railtie] Configuration validation failed:
|
|
650
|
+
- server_url must start with http:// or https://
|
|
651
|
+
[Railtie] Agent initialization skipped. Please fix configuration errors.
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Warning Messages
|
|
655
|
+
|
|
656
|
+
**Missing project_token:**
|
|
657
|
+
```
|
|
658
|
+
[Railtie] Configuration warnings:
|
|
659
|
+
- project_token is not set. Authentication may fail if the server requires it.
|
|
660
|
+
[Railtie] Agent will attempt to initialize without authentication.
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Validation Rules
|
|
664
|
+
|
|
665
|
+
| Setting | Validation |
|
|
666
|
+
|---------|-----------|
|
|
667
|
+
| `server_url` | Must be present, must start with `http://` or `https://` |
|
|
668
|
+
| `project_token` | Warning if missing (not required but recommended) |
|
|
669
|
+
| `buffer_size` | Must be positive integer |
|
|
670
|
+
| `send_interval` | Must be positive integer |
|
|
671
|
+
| `retry_attempts` | Must be non-negative integer |
|
|
672
|
+
| `log_level` | Must be one of: `:debug`, `:info`, `:warn`, `:error` |
|
|
673
|
+
|
|
674
|
+
**If validation fails:**
|
|
675
|
+
- Agent initialization is skipped
|
|
676
|
+
- Application starts normally (non-blocking)
|
|
677
|
+
- Fix errors and restart Rails
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## Complete Example
|
|
682
|
+
|
|
683
|
+
Here's a fully configured example with all options:
|
|
684
|
+
|
|
685
|
+
```ruby
|
|
686
|
+
EzLogsAgent.configure do |config|
|
|
687
|
+
# ==========================================
|
|
688
|
+
# Required
|
|
689
|
+
# ==========================================
|
|
690
|
+
config.server_url = ENV.fetch('EZLOGS_SERVER_URL')
|
|
691
|
+
config.project_token = ENV.fetch('EZLOGS_API_KEY')
|
|
692
|
+
|
|
693
|
+
# ==========================================
|
|
694
|
+
# Event Capture
|
|
695
|
+
# ==========================================
|
|
696
|
+
config.capture_http = true
|
|
697
|
+
config.capture_jobs = true
|
|
698
|
+
config.capture_database = true
|
|
699
|
+
|
|
700
|
+
# ==========================================
|
|
701
|
+
# Exclusions
|
|
702
|
+
# ==========================================
|
|
703
|
+
config.excluded_paths = ["/admin*", "/internal*"]
|
|
704
|
+
config.excluded_tables = ["audit_logs", "versions"]
|
|
705
|
+
config.excluded_job_classes = ["MyApp::HealthCheckJob"]
|
|
706
|
+
|
|
707
|
+
# ==========================================
|
|
708
|
+
# Display Names
|
|
709
|
+
# ==========================================
|
|
710
|
+
config.display_name_for = {
|
|
711
|
+
"User" => :email,
|
|
712
|
+
"Product" => :name,
|
|
713
|
+
"Order" => :number
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
# ==========================================
|
|
717
|
+
# Actor Context
|
|
718
|
+
# ==========================================
|
|
719
|
+
config.actor_from_request = ->(env, controller) {
|
|
720
|
+
return nil unless controller.respond_to?(:current_user)
|
|
721
|
+
user = controller.current_user
|
|
722
|
+
return nil unless user
|
|
723
|
+
|
|
724
|
+
{ id: user.id.to_s, label: user.email }
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
# ==========================================
|
|
728
|
+
# Transport
|
|
729
|
+
# ==========================================
|
|
730
|
+
config.buffer_size = 10000
|
|
731
|
+
config.send_interval = 3
|
|
732
|
+
config.retry_attempts = 3
|
|
733
|
+
|
|
734
|
+
# ==========================================
|
|
735
|
+
# Logging
|
|
736
|
+
# ==========================================
|
|
737
|
+
config.log_level = :info
|
|
738
|
+
end
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
## See Also
|
|
744
|
+
|
|
745
|
+
- [QUICKSTART.md](QUICKSTART.md) — Getting started guide
|
|
746
|
+
- [README.md](README.md) — Full documentation
|
|
747
|
+
- [FAQ.md](FAQ.md) — Frequently asked questions
|
|
748
|
+
- [Troubleshooting](README.md#troubleshooting) — Common issues and solutions
|
|
749
|
+
|
|
750
|
+
---
|
|
751
|
+
|
|
752
|
+
**Questions?** [Open an issue](https://github.com/your-org/ez_logs/issues) or email support@ezlogs.com
|