anzen 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/.rubocop.yml +57 -0
- data/CHANGELOG.md +51 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/README.md +321 -0
- data/Rakefile +23 -0
- data/bin/anzen +168 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/lib/anzen/README_API.md +608 -0
- data/lib/anzen/cli.rb +301 -0
- data/lib/anzen/configuration.rb +254 -0
- data/lib/anzen/exceptions.rb +130 -0
- data/lib/anzen/monitor.rb +85 -0
- data/lib/anzen/monitors/call_stack_depth.rb +147 -0
- data/lib/anzen/monitors/memory.rb +228 -0
- data/lib/anzen/monitors/recursion.rb +193 -0
- data/lib/anzen/registry.rb +169 -0
- data/lib/anzen/version.rb +5 -0
- data/lib/anzen.rb +210 -0
- data/sig/anzen.rbs +4 -0
- metadata +74 -0
data/bin/console
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'anzen'
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
require 'irb'
|
|
11
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
# Anzen Ruby API Reference
|
|
2
|
+
|
|
3
|
+
This document provides comprehensive API reference for the Anzen runtime safety protection gem.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Core Module](#core-module)
|
|
8
|
+
- [Configuration](#configuration)
|
|
9
|
+
- [Monitors](#monitors)
|
|
10
|
+
- [Exceptions](#exceptions)
|
|
11
|
+
- [CLI Interface](#cli-interface)
|
|
12
|
+
|
|
13
|
+
## Core Module
|
|
14
|
+
|
|
15
|
+
The `Anzen` module provides the main public API for runtime safety protection.
|
|
16
|
+
|
|
17
|
+
### Setup and Initialization
|
|
18
|
+
|
|
19
|
+
#### `Anzen.setup(config: {})`
|
|
20
|
+
|
|
21
|
+
Initializes the Anzen safety protection system with the specified configuration.
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- `config` (Hash): Configuration hash with the following structure:
|
|
25
|
+
- `enabled_monitors` (Array<String>): Array of monitor names to enable initially
|
|
26
|
+
- `monitors` (Hash): Monitor-specific configuration
|
|
27
|
+
|
|
28
|
+
**Example:**
|
|
29
|
+
```ruby
|
|
30
|
+
Anzen.setup(config: {
|
|
31
|
+
enabled_monitors: ['recursion', 'memory'],
|
|
32
|
+
monitors: {
|
|
33
|
+
recursion: { depth_limit: 1000 },
|
|
34
|
+
memory: { limit_mb: 512, sampling_interval_ms: 100 }
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Raises:**
|
|
40
|
+
- `Anzen::InitializationError`: If Anzen is already initialized
|
|
41
|
+
- `Anzen::ConfigurationError`: If configuration is invalid
|
|
42
|
+
|
|
43
|
+
**Returns:** `nil`
|
|
44
|
+
|
|
45
|
+
### Runtime Control
|
|
46
|
+
|
|
47
|
+
#### `Anzen.enable(name)`
|
|
48
|
+
|
|
49
|
+
Enables a registered monitor by name.
|
|
50
|
+
|
|
51
|
+
**Parameters:**
|
|
52
|
+
- `name` (String): Name of the monitor to enable
|
|
53
|
+
|
|
54
|
+
**Example:**
|
|
55
|
+
```ruby
|
|
56
|
+
Anzen.enable('recursion')
|
|
57
|
+
Anzen.enable('memory')
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Raises:**
|
|
61
|
+
- `Anzen::MonitorNotFoundError`: If monitor is not registered
|
|
62
|
+
|
|
63
|
+
**Returns:** `true` if enabled, `false` if already enabled
|
|
64
|
+
|
|
65
|
+
#### `Anzen.disable(name)`
|
|
66
|
+
|
|
67
|
+
Disables a registered monitor by name.
|
|
68
|
+
|
|
69
|
+
**Parameters:**
|
|
70
|
+
- `name` (String): Name of the monitor to disable
|
|
71
|
+
|
|
72
|
+
**Example:**
|
|
73
|
+
```ruby
|
|
74
|
+
Anzen.disable('memory')
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Raises:**
|
|
78
|
+
- `Anzen::MonitorNotFoundError`: If monitor is not registered
|
|
79
|
+
|
|
80
|
+
**Returns:** `true` if disabled, `false` if already disabled
|
|
81
|
+
|
|
82
|
+
#### `Anzen.check!`
|
|
83
|
+
|
|
84
|
+
Performs safety checks on all enabled monitors. Raises the first violation detected.
|
|
85
|
+
|
|
86
|
+
**Example:**
|
|
87
|
+
```ruby
|
|
88
|
+
begin
|
|
89
|
+
Anzen.check!
|
|
90
|
+
puts "All checks passed"
|
|
91
|
+
rescue Anzen::RecursionLimitExceeded => e
|
|
92
|
+
puts "Recursion violation: #{e.current_depth} > #{e.threshold}"
|
|
93
|
+
rescue Anzen::MemoryLimitExceeded => e
|
|
94
|
+
puts "Memory violation: #{e.current_memory_mb}MB > #{e.memory_limit_mb}MB"
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Raises:**
|
|
99
|
+
- `Anzen::ViolationError`: Subclass for safety violations
|
|
100
|
+
- `Anzen::CheckFailedError`: For infrastructure errors
|
|
101
|
+
|
|
102
|
+
**Returns:** `nil` if all checks pass
|
|
103
|
+
|
|
104
|
+
### Status and Information
|
|
105
|
+
|
|
106
|
+
#### `Anzen.status`
|
|
107
|
+
|
|
108
|
+
Returns the current status of all monitors and the system.
|
|
109
|
+
|
|
110
|
+
**Returns:** Hash with the following structure:
|
|
111
|
+
```ruby
|
|
112
|
+
{
|
|
113
|
+
enabled_monitors: ['recursion', 'memory'],
|
|
114
|
+
setup_time: Time, # When Anzen was initialized
|
|
115
|
+
monitors: {
|
|
116
|
+
'recursion' => {
|
|
117
|
+
status: 'enabled', # or 'disabled'
|
|
118
|
+
thresholds: { depth_limit: 1000 },
|
|
119
|
+
last_check: Time, # or nil
|
|
120
|
+
violations_detected: 0
|
|
121
|
+
},
|
|
122
|
+
'memory' => {
|
|
123
|
+
status: 'enabled',
|
|
124
|
+
thresholds: { limit_mb: 512, sampling_interval_ms: 100 },
|
|
125
|
+
last_check: Time,
|
|
126
|
+
violations_detected: 0
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Example:**
|
|
133
|
+
```ruby
|
|
134
|
+
status = Anzen.status
|
|
135
|
+
puts "Enabled monitors: #{status[:enabled_monitors].join(', ')}"
|
|
136
|
+
puts "Setup time: #{status[:setup_time]}"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Custom Monitor Registration
|
|
140
|
+
|
|
141
|
+
#### `Anzen.register_monitor(monitor_instance)`
|
|
142
|
+
|
|
143
|
+
Registers a custom monitor instance with the system.
|
|
144
|
+
|
|
145
|
+
**Parameters:**
|
|
146
|
+
- `monitor_instance`: Object implementing the Monitor interface
|
|
147
|
+
|
|
148
|
+
**Example:**
|
|
149
|
+
```ruby
|
|
150
|
+
class CustomMonitor
|
|
151
|
+
def name; 'custom'; end
|
|
152
|
+
def enable; @enabled = true; end
|
|
153
|
+
def disable; @enabled = false; end
|
|
154
|
+
def enabled?; @enabled; end
|
|
155
|
+
def check!; # custom safety check; end
|
|
156
|
+
def status; { status: enabled? ? 'enabled' : 'disabled' }; end
|
|
157
|
+
def to_cli; "Custom monitor: #{enabled? ? 'enabled' : 'disabled'}"; end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
Anzen.register_monitor(CustomMonitor.new)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Raises:**
|
|
164
|
+
- `Anzen::InvalidMonitorError`: If monitor doesn't implement required interface
|
|
165
|
+
- `Anzen::MonitorNameConflictError`: If monitor name already registered
|
|
166
|
+
|
|
167
|
+
**Returns:** `true` if registered successfully
|
|
168
|
+
|
|
169
|
+
## Configuration
|
|
170
|
+
|
|
171
|
+
Configuration can be loaded from multiple sources with the following precedence (highest to lowest):
|
|
172
|
+
|
|
173
|
+
1. Programmatic configuration (passed to `Anzen.setup`)
|
|
174
|
+
2. Environment variable `ANZEN_CONFIG` (JSON/YAML)
|
|
175
|
+
3. Configuration file (anzen.yml, anzen.json)
|
|
176
|
+
|
|
177
|
+
### Configuration Schema
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
anzen:
|
|
181
|
+
enabled_monitors:
|
|
182
|
+
- recursion
|
|
183
|
+
- memory
|
|
184
|
+
monitors:
|
|
185
|
+
recursion:
|
|
186
|
+
depth_limit: 1000
|
|
187
|
+
memory:
|
|
188
|
+
limit_mb: 512
|
|
189
|
+
sampling_interval_ms: 100
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Environment Variable Configuration
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
export ANZEN_CONFIG='{
|
|
196
|
+
"enabled_monitors": ["recursion", "memory"],
|
|
197
|
+
"monitors": {
|
|
198
|
+
"recursion": {"depth_limit": 500},
|
|
199
|
+
"memory": {"limit_mb": 1024}
|
|
200
|
+
}
|
|
201
|
+
}'
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### File-based Configuration
|
|
205
|
+
|
|
206
|
+
Create `anzen.yml` or `anzen.json` in your project root:
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
# anzen.yml
|
|
210
|
+
anzen:
|
|
211
|
+
enabled_monitors: [recursion, memory]
|
|
212
|
+
monitors:
|
|
213
|
+
recursion:
|
|
214
|
+
depth_limit: 1000
|
|
215
|
+
memory:
|
|
216
|
+
limit_mb: 512
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Monitors
|
|
220
|
+
|
|
221
|
+
Anzen includes built-in monitors for common safety concerns. All monitors implement the same interface.
|
|
222
|
+
|
|
223
|
+
### Monitor Interface
|
|
224
|
+
|
|
225
|
+
All monitors must implement these methods:
|
|
226
|
+
|
|
227
|
+
- `name` (String): Unique monitor identifier
|
|
228
|
+
- `enable`: Enable the monitor
|
|
229
|
+
- `disable`: Disable the monitor
|
|
230
|
+
- `enabled?` (Boolean): Check if monitor is enabled
|
|
231
|
+
- `check!`: Perform safety check, raise ViolationError if unsafe
|
|
232
|
+
- `status` (Hash): Return monitor status information
|
|
233
|
+
- `to_cli` (String): Return human-readable status for CLI
|
|
234
|
+
|
|
235
|
+
### Built-in Monitors
|
|
236
|
+
|
|
237
|
+
#### RecursionMonitor
|
|
238
|
+
|
|
239
|
+
Detects recursive method calls and raises immediately on first detection.
|
|
240
|
+
|
|
241
|
+
**Configuration:**
|
|
242
|
+
- No thresholds - blocks all recursion
|
|
243
|
+
|
|
244
|
+
**Status Fields:**
|
|
245
|
+
- `status`: 'enabled' or 'disabled'
|
|
246
|
+
- `violations_detected`: Number of recursion violations detected
|
|
247
|
+
|
|
248
|
+
#### CallStackDepthMonitor
|
|
249
|
+
|
|
250
|
+
Limits call stack depth to prevent stack overflow.
|
|
251
|
+
|
|
252
|
+
**Configuration:**
|
|
253
|
+
- `depth_limit` (Integer): Maximum allowed stack depth (default: 1000)
|
|
254
|
+
|
|
255
|
+
**Status Fields:**
|
|
256
|
+
- `status`: 'enabled' or 'disabled'
|
|
257
|
+
- `thresholds`: { depth_limit: Integer }
|
|
258
|
+
- `last_check`: Time of last check or nil
|
|
259
|
+
- `violations_detected`: Number of depth violations
|
|
260
|
+
|
|
261
|
+
#### MemoryMonitor
|
|
262
|
+
|
|
263
|
+
Monitors process memory usage with sampling to prevent OOM conditions.
|
|
264
|
+
|
|
265
|
+
**Configuration:**
|
|
266
|
+
- `limit_mb` (Integer): Memory limit in MB
|
|
267
|
+
- `sampling_interval_ms` (Integer): Minimum time between checks (default: 100)
|
|
268
|
+
|
|
269
|
+
**Status Fields:**
|
|
270
|
+
- `status`: 'enabled' or 'disabled'
|
|
271
|
+
- `thresholds`: { limit_mb: Integer, sampling_interval_ms: Integer }
|
|
272
|
+
- `last_check`: Time of last check or nil
|
|
273
|
+
- `violations_detected`: Number of memory violations
|
|
274
|
+
|
|
275
|
+
## Exceptions
|
|
276
|
+
|
|
277
|
+
Anzen uses a structured exception hierarchy for different types of errors.
|
|
278
|
+
|
|
279
|
+
### Violation Errors
|
|
280
|
+
|
|
281
|
+
Raised when safety violations are detected during `check!` calls.
|
|
282
|
+
|
|
283
|
+
#### `Anzen::ViolationError`
|
|
284
|
+
|
|
285
|
+
Base class for all safety violations. Inherits from `StandardError`.
|
|
286
|
+
|
|
287
|
+
**Attributes:**
|
|
288
|
+
- `monitor_name` (String): Name of the monitor that detected the violation
|
|
289
|
+
|
|
290
|
+
#### `Anzen::RecursionLimitExceeded`
|
|
291
|
+
|
|
292
|
+
Raised when recursion limits are exceeded.
|
|
293
|
+
|
|
294
|
+
**Attributes:**
|
|
295
|
+
- `current_depth` (Integer): Current call stack depth
|
|
296
|
+
- `threshold` (Integer): Configured depth limit
|
|
297
|
+
|
|
298
|
+
**Example:**
|
|
299
|
+
```ruby
|
|
300
|
+
rescue Anzen::RecursionLimitExceeded => e
|
|
301
|
+
puts "Recursion limit exceeded: #{e.current_depth} > #{e.threshold}"
|
|
302
|
+
end
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### `Anzen::MemoryLimitExceeded`
|
|
306
|
+
|
|
307
|
+
Raised when memory limits are exceeded.
|
|
308
|
+
|
|
309
|
+
**Attributes:**
|
|
310
|
+
- `current_memory_mb` (Float): Current memory usage in MB
|
|
311
|
+
- `memory_limit_mb` (Integer): Configured memory limit in MB
|
|
312
|
+
|
|
313
|
+
**Example:**
|
|
314
|
+
```ruby
|
|
315
|
+
rescue Anzen::MemoryLimitExceeded => e
|
|
316
|
+
puts "Memory limit exceeded: #{e.current_memory_mb}MB > #{e.memory_limit_mb}MB"
|
|
317
|
+
end
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Infrastructure Errors
|
|
321
|
+
|
|
322
|
+
Raised for configuration and operational issues.
|
|
323
|
+
|
|
324
|
+
#### `Anzen::CheckFailedError`
|
|
325
|
+
|
|
326
|
+
Raised when a monitor check fails due to infrastructure issues.
|
|
327
|
+
|
|
328
|
+
**Attributes:**
|
|
329
|
+
- `monitor_name` (String): Name of the monitor that failed
|
|
330
|
+
- `reason` (String): Description of the failure
|
|
331
|
+
- `original_error` (Exception): The underlying error that caused the failure
|
|
332
|
+
|
|
333
|
+
#### `Anzen::ConfigurationError`
|
|
334
|
+
|
|
335
|
+
Raised when configuration is invalid.
|
|
336
|
+
|
|
337
|
+
**Attributes:**
|
|
338
|
+
- `field` (String): Configuration field that is invalid
|
|
339
|
+
- `value`: The invalid value
|
|
340
|
+
- `reason` (String): Why the value is invalid
|
|
341
|
+
|
|
342
|
+
#### `Anzen::MonitorNotFoundError`
|
|
343
|
+
|
|
344
|
+
Raised when trying to enable/disable a monitor that doesn't exist.
|
|
345
|
+
|
|
346
|
+
**Attributes:**
|
|
347
|
+
- `monitor_name` (String): Name of the monitor that was not found
|
|
348
|
+
|
|
349
|
+
#### `Anzen::InvalidMonitorError`
|
|
350
|
+
|
|
351
|
+
Raised when registering a monitor that doesn't implement the required interface.
|
|
352
|
+
|
|
353
|
+
**Attributes:**
|
|
354
|
+
- `monitor_class`: The invalid monitor class
|
|
355
|
+
- `missing_methods` (Array<String>): Methods that are missing from the interface
|
|
356
|
+
|
|
357
|
+
#### `Anzen::MonitorNameConflictError`
|
|
358
|
+
|
|
359
|
+
Raised when trying to register a monitor with a name that already exists.
|
|
360
|
+
|
|
361
|
+
**Attributes:**
|
|
362
|
+
- `monitor_name` (String): The conflicting monitor name
|
|
363
|
+
|
|
364
|
+
#### `Anzen::InitializationError`
|
|
365
|
+
|
|
366
|
+
Raised when trying to initialize Anzen multiple times.
|
|
367
|
+
|
|
368
|
+
**Attributes:**
|
|
369
|
+
- `reason` (String): Why initialization failed
|
|
370
|
+
|
|
371
|
+
## CLI Interface
|
|
372
|
+
|
|
373
|
+
The Anzen CLI provides command-line access to system status and configuration.
|
|
374
|
+
|
|
375
|
+
### Commands
|
|
376
|
+
|
|
377
|
+
#### `anzen status [--format json|text]`
|
|
378
|
+
|
|
379
|
+
Displays the current status of all monitors and the system.
|
|
380
|
+
|
|
381
|
+
**Options:**
|
|
382
|
+
- `--format`: Output format ('json' or 'text', default: 'text')
|
|
383
|
+
|
|
384
|
+
**Exit Codes:**
|
|
385
|
+
- 0: Success
|
|
386
|
+
- 1: General error
|
|
387
|
+
- 2: Monitor not found
|
|
388
|
+
- 3: Configuration error
|
|
389
|
+
- 4: Initialization error
|
|
390
|
+
|
|
391
|
+
**Example Output (text):**
|
|
392
|
+
```
|
|
393
|
+
Anzen Safety Protection Status
|
|
394
|
+
========================================
|
|
395
|
+
|
|
396
|
+
Enabled Monitors: recursion, memory
|
|
397
|
+
|
|
398
|
+
Monitor: recursion
|
|
399
|
+
Status: enabled
|
|
400
|
+
Thresholds:
|
|
401
|
+
depth_limit: 1000
|
|
402
|
+
Last Check: never
|
|
403
|
+
Violations Detected: 0
|
|
404
|
+
|
|
405
|
+
Monitor: memory
|
|
406
|
+
Status: enabled
|
|
407
|
+
Thresholds:
|
|
408
|
+
limit_mb: 512
|
|
409
|
+
sampling_interval_ms: 100
|
|
410
|
+
Last Check: never
|
|
411
|
+
Violations Detected: 0
|
|
412
|
+
|
|
413
|
+
Total Violations: 0
|
|
414
|
+
Setup Time: 2025-11-17 17:05:02 EST
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
#### `anzen config [monitor_name] [--format json|text]`
|
|
418
|
+
|
|
419
|
+
Displays configuration information.
|
|
420
|
+
|
|
421
|
+
**Parameters:**
|
|
422
|
+
- `monitor_name` (optional): Show config for specific monitor only
|
|
423
|
+
|
|
424
|
+
**Options:**
|
|
425
|
+
- `--format`: Output format ('json' or 'text', default: 'text')
|
|
426
|
+
|
|
427
|
+
**Example:**
|
|
428
|
+
```bash
|
|
429
|
+
anzen config memory
|
|
430
|
+
anzen config --format json
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
#### `anzen info [--format json|text]`
|
|
434
|
+
|
|
435
|
+
Displays system and gem information.
|
|
436
|
+
|
|
437
|
+
**Options:**
|
|
438
|
+
- `--format`: Output format ('json' or 'text', default: 'text')
|
|
439
|
+
|
|
440
|
+
**Example Output:**
|
|
441
|
+
```
|
|
442
|
+
Anzen Runtime Safety Protection
|
|
443
|
+
Version: 0.1.0
|
|
444
|
+
Ruby Version: 3.2.2
|
|
445
|
+
Platform: x86_64-darwin22
|
|
446
|
+
Process ID: 12345
|
|
447
|
+
Memory Usage: 45.2 MB
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### `anzen help [command]`
|
|
451
|
+
|
|
452
|
+
Displays help information.
|
|
453
|
+
|
|
454
|
+
**Parameters:**
|
|
455
|
+
- `command` (optional): Show help for specific command
|
|
456
|
+
|
|
457
|
+
**Example:**
|
|
458
|
+
```bash
|
|
459
|
+
anzen help
|
|
460
|
+
anzen help status
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Global Options
|
|
464
|
+
|
|
465
|
+
- `--version`: Show version information
|
|
466
|
+
- `--help`: Show help information
|
|
467
|
+
|
|
468
|
+
### Exit Codes
|
|
469
|
+
|
|
470
|
+
All CLI commands return standardized exit codes:
|
|
471
|
+
|
|
472
|
+
- `0`: Success
|
|
473
|
+
- `1`: General error (unexpected exceptions)
|
|
474
|
+
- `2`: Monitor not found
|
|
475
|
+
- `3`: Configuration error
|
|
476
|
+
- `4`: Initialization error (Anzen not set up)
|
|
477
|
+
|
|
478
|
+
## Integration Patterns
|
|
479
|
+
|
|
480
|
+
### Rails Application
|
|
481
|
+
|
|
482
|
+
```ruby
|
|
483
|
+
# config/initializers/anzen.rb
|
|
484
|
+
require 'anzen'
|
|
485
|
+
|
|
486
|
+
Anzen.setup(config: {
|
|
487
|
+
enabled_monitors: ['recursion', 'memory'],
|
|
488
|
+
monitors: {
|
|
489
|
+
recursion: { depth_limit: 1000 },
|
|
490
|
+
memory: { limit_mb: 512 }
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Rack Middleware
|
|
496
|
+
|
|
497
|
+
```ruby
|
|
498
|
+
# config.ru
|
|
499
|
+
require 'anzen'
|
|
500
|
+
|
|
501
|
+
use Anzen::Middleware
|
|
502
|
+
|
|
503
|
+
Anzen.setup(config: {
|
|
504
|
+
enabled_monitors: ['recursion']
|
|
505
|
+
})
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Standalone Ruby Script
|
|
509
|
+
|
|
510
|
+
```ruby
|
|
511
|
+
#!/usr/bin/env ruby
|
|
512
|
+
require 'anzen'
|
|
513
|
+
|
|
514
|
+
Anzen.setup(config: {
|
|
515
|
+
enabled_monitors: ['memory'],
|
|
516
|
+
monitors: {
|
|
517
|
+
memory: { limit_mb: 1024 }
|
|
518
|
+
}
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
# Your application code here
|
|
522
|
+
# Anzen automatically checks safety limits
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Error Handling
|
|
526
|
+
|
|
527
|
+
```ruby
|
|
528
|
+
begin
|
|
529
|
+
# Risky operation
|
|
530
|
+
process_large_dataset(data)
|
|
531
|
+
rescue Anzen::RecursionLimitExceeded => e
|
|
532
|
+
logger.error("Recursion limit exceeded: #{e.current_depth} > #{e.threshold}")
|
|
533
|
+
# Handle gracefully - retry with smaller chunks
|
|
534
|
+
rescue Anzen::MemoryLimitExceeded => e
|
|
535
|
+
logger.error("Memory limit exceeded: #{e.current_memory_mb}MB > #{e.memory_limit_mb}MB")
|
|
536
|
+
# Clean up resources and exit gracefully
|
|
537
|
+
rescue Anzen::CheckFailedError => e
|
|
538
|
+
logger.error("Monitor check failed: #{e.monitor_name} - #{e.reason}")
|
|
539
|
+
# Infrastructure issue - may need to disable monitor
|
|
540
|
+
end
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Custom Monitors
|
|
544
|
+
|
|
545
|
+
```ruby
|
|
546
|
+
class DatabaseConnectionMonitor
|
|
547
|
+
def name; 'database_connections'; end
|
|
548
|
+
|
|
549
|
+
def initialize(config = {})
|
|
550
|
+
@max_connections = config.fetch('max_connections', 100)
|
|
551
|
+
@enabled = false
|
|
552
|
+
@violations = 0
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def enable; @enabled = true; end
|
|
556
|
+
def disable; @enabled = false; end
|
|
557
|
+
def enabled?; @enabled; end
|
|
558
|
+
|
|
559
|
+
def check!
|
|
560
|
+
return unless enabled?
|
|
561
|
+
|
|
562
|
+
current_connections = ActiveRecord::Base.connection_pool.connections.size
|
|
563
|
+
if current_connections > @max_connections
|
|
564
|
+
raise Anzen::ViolationError.new(
|
|
565
|
+
"Database connections exceeded: #{current_connections} > #{@max_connections}",
|
|
566
|
+
monitor_name: name
|
|
567
|
+
)
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def status
|
|
572
|
+
{
|
|
573
|
+
status: enabled? ? 'enabled' : 'disabled',
|
|
574
|
+
thresholds: { max_connections: @max_connections },
|
|
575
|
+
violations_detected: @violations
|
|
576
|
+
}
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def to_cli
|
|
580
|
+
"#{name}: #{enabled? ? 'enabled' : 'disabled'} (max: #{@max_connections})"
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Register the custom monitor
|
|
585
|
+
Anzen.register_monitor(DatabaseConnectionMonitor.new(max_connections: 50))
|
|
586
|
+
Anzen.enable('database_connections')
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
## Performance Considerations
|
|
590
|
+
|
|
591
|
+
- **Sampling Intervals**: Memory monitor uses sampling to reduce overhead
|
|
592
|
+
- **Selective Enabling**: Only enable monitors you need
|
|
593
|
+
- **Check Frequency**: Call `check!` strategically, not on every operation
|
|
594
|
+
- **Error Handling**: Use rescue blocks to handle violations gracefully
|
|
595
|
+
|
|
596
|
+
## Thread Safety
|
|
597
|
+
|
|
598
|
+
Anzen is designed to be thread-safe:
|
|
599
|
+
- Monitor state is protected with mutexes
|
|
600
|
+
- Status queries are atomic
|
|
601
|
+
- Configuration is immutable after setup
|
|
602
|
+
|
|
603
|
+
## Version Compatibility
|
|
604
|
+
|
|
605
|
+
- **Ruby**: 3.0+
|
|
606
|
+
- **Platforms**: Linux, macOS, Windows (with limitations)
|
|
607
|
+
- **Dependencies**: Zero external runtime dependencies</content>
|
|
608
|
+
<parameter name="filePath">/Users/korakot-air/dev/anzen/lib/anzen/README_API.md
|