chronos-ruby 0.2.0.pre.1 → 0.4.0.pre.1
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 +48 -0
- data/README.md +60 -19
- data/contracts/rack-context-v1.schema.json +43 -0
- data/docs/adr/ADR-005-sanitize-before-backlog.md +2 -2
- data/docs/adr/ADR-011-bounded-resilience-and-remote-control.md +27 -0
- data/docs/adr/ADR-012-rack-context-isolation.md +27 -0
- data/docs/architecture.md +15 -6
- data/docs/compatibility.md +6 -6
- data/docs/configuration.md +22 -0
- data/docs/data-collected.md +8 -2
- data/docs/examples/plain-ruby.md +6 -0
- data/docs/modules/async-queue.md +6 -2
- data/docs/modules/notice-pipeline.md +2 -1
- data/docs/modules/rack-context.md +59 -0
- data/docs/modules/remote-configuration.md +54 -0
- data/docs/modules/retry-backlog.md +60 -0
- data/docs/modules/transport.md +5 -3
- data/docs/performance.md +39 -2
- data/docs/privacy-lgpd.md +10 -2
- data/docs/troubleshooting.md +10 -2
- data/lib/chronos/adapters/net_http_transport.rb +21 -3
- data/lib/chronos/adapters/thread_local_context_store.rb +52 -0
- data/lib/chronos/agent.rb +68 -11
- data/lib/chronos/application/capture_exception.rb +20 -14
- data/lib/chronos/application/circuit_breaker.rb +70 -0
- data/lib/chronos/application/delivery_pipeline.rb +248 -0
- data/lib/chronos/application/remote_configuration.rb +173 -0
- data/lib/chronos/application/retry_policy.rb +57 -0
- data/lib/chronos/configuration.rb +128 -20
- data/lib/chronos/core/breadcrumb.rb +126 -0
- data/lib/chronos/core/payload_serializer.rb +4 -2
- data/lib/chronos/integrations/rack/middleware.rb +152 -0
- data/lib/chronos/integrations/rack.rb +12 -0
- data/lib/chronos/integrations.rb +10 -0
- data/lib/chronos/internal/bounded_queue.rb +1 -1
- data/lib/chronos/internal/memory_backlog.rb +65 -0
- data/lib/chronos/internal/worker_pool.rb +5 -6
- data/lib/chronos/ports/context_store.rb +22 -0
- data/lib/chronos/ports/transport.rb +4 -3
- data/lib/chronos/version.rb +1 -1
- data/lib/chronos.rb +26 -1
- metadata +18 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ab10ffdc0cff4274241465904b18e4bceb4fa230ea9fddb4b8be419c08e3463
|
|
4
|
+
data.tar.gz: 2d6d778012088192efed14e0ab64958716c8a6b4c3e697127c52573dc4dcd47b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8bb789313b2b7d360b6a8c08e5b5cf731aa1325912be7b560a1869b3d8717e4e779610e92591c1a2c436f5d14cd39e28f1e5db837d954def1db232788284d2d
|
|
7
|
+
data.tar.gz: 93a311d6905d83dee031a88cb5887854a32e60d26eceb0dc0446a8244c77a2c9fa763089bc5ec5fa25d265cb5881c3775dafe6361b1e84a1c6044aa89df220ee
|
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,54 @@ All notable changes are documented here. The project follows Semantic Versioning
|
|
|
11
11
|
- legacy CI now resolves Bundler 1.17.3 through `Gem.bin_path` on RubyGems versions that do not support the `_version_` executable selector.
|
|
12
12
|
- documentation verification now reads source and Markdown files explicitly as UTF-8 on legacy container locales.
|
|
13
13
|
|
|
14
|
+
## [0.4.0.pre.1] - 2026-07-20
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Rack-protocol middleware for automatic exception capture with propagation of the original failure;
|
|
19
|
+
- request context for method, normalized route, status, duration, request ID, optional user agent, host, path, controller/action, approximate response size, user, and sanitized parameters;
|
|
20
|
+
- selectable context-store port with a thread-local legacy adapter and guaranteed scope cleanup;
|
|
21
|
+
- bounded circular breadcrumbs for custom, log, request, query, external HTTP, cache, and job categories;
|
|
22
|
+
- public `Chronos.with_context` and `Chronos.add_breadcrumb` APIs;
|
|
23
|
+
- Rack context contract, concurrency tests, module documentation, ADR, executable example, and request-overhead benchmark.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- manual notifications now inherit the current execution context and breadcrumbs;
|
|
28
|
+
- version advanced to `0.4.0.pre.1`.
|
|
29
|
+
|
|
30
|
+
### Known limitations
|
|
31
|
+
|
|
32
|
+
- generic route normalization cannot identify every application-specific dynamic segment;
|
|
33
|
+
- thread-local context does not propagate into application-created threads or fibers;
|
|
34
|
+
- deferred exceptions raised only while enumerating a streaming response body are not captured;
|
|
35
|
+
- Rails-specific installation and route discovery remain planned for version 0.5.
|
|
36
|
+
|
|
37
|
+
## [0.3.0.pre.1] - 2026-07-20
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- explicit delivery-state counters for accepted, queued, serialized, sent, retried, dropped, and rejected events;
|
|
42
|
+
- finite exponential retry with bounded jitter and `Retry-After` support;
|
|
43
|
+
- retry classification for network errors, HTTP 408, 429, and 5xx responses;
|
|
44
|
+
- closed, open, and half-open circuit breaker states;
|
|
45
|
+
- fixed-capacity in-memory backlog restricted to sanitized serialized events;
|
|
46
|
+
- bounded remote configuration for sampling, event types, payload limits, exact fingerprint ignores, send interval, and kill switch;
|
|
47
|
+
- resilience contracts, unit tests, module documentation, ADR, executable example, and outage benchmark.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- asynchronous and synchronous delivery now pass through `DeliveryPipeline`;
|
|
52
|
+
- `WorkerPool` delegates delivery policy instead of sending directly through a transport;
|
|
53
|
+
- version advanced to `0.3.0.pre.1`.
|
|
54
|
+
|
|
55
|
+
### Known limitations
|
|
56
|
+
|
|
57
|
+
- backlog is not persisted and is lost when the process exits;
|
|
58
|
+
- backlog draining requires later delivery activity;
|
|
59
|
+
- no dedicated remote-configuration polling endpoint;
|
|
60
|
+
- no automatic Rack, Rails, or job integration.
|
|
61
|
+
|
|
14
62
|
## [0.2.0.pre.1] - 2026-07-19
|
|
15
63
|
|
|
16
64
|
### Added
|
data/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# Chronos Ruby
|
|
2
2
|
|
|
3
|
-
Chronos Ruby is the framework-independent client for sending Ruby application errors to Chronos. Version 0.
|
|
3
|
+
Chronos Ruby is the framework-independent client for sending Ruby application errors to Chronos. Version 0.4 adds automatic Rack exception capture, isolated request context, and bounded breadcrumbs to the privacy and resilience foundations from earlier releases.
|
|
4
4
|
|
|
5
5
|
## What the gem collects
|
|
6
6
|
|
|
7
|
-
For each exception, version 0.
|
|
7
|
+
For each exception, version 0.4 can collect:
|
|
8
8
|
|
|
9
9
|
- exception class, message, structured backtrace, and chained causes;
|
|
10
10
|
- timestamp, severity, tags, and an optional fingerprint;
|
|
11
11
|
- application-supplied context, parameters, session, and user fields;
|
|
12
12
|
- Ruby version, engine, platform, process ID, opaque thread ID, and hostname;
|
|
13
13
|
- application version, environment, and service name.
|
|
14
|
+
- Rack method, normalized route, status, duration, request ID, host, query-free path, optional user agent, controller/action, response size, trace ID, and already-parsed parameters when the middleware is used;
|
|
15
|
+
- bounded breadcrumbs explicitly supplied by the application or integration.
|
|
14
16
|
|
|
15
17
|
See [Data collected](docs/data-collected.md) for the complete field table.
|
|
16
18
|
|
|
@@ -20,7 +22,7 @@ Chronos Ruby does not inspect environment variables, request bodies, cookies, HT
|
|
|
20
22
|
|
|
21
23
|
## Supported Ruby and Rails versions
|
|
22
24
|
|
|
23
|
-
Version 0.x targets Ruby 2.2.10 through Ruby 2.6. Version 0.
|
|
25
|
+
Version 0.x targets Ruby 2.2.10 through Ruby 2.6. Version 0.4 implements the Rack protocol but does not yet declare Rails support. All supported combinations must pass dedicated CI before being listed as supported.
|
|
24
26
|
|
|
25
27
|
See [Compatibility](docs/compatibility.md).
|
|
26
28
|
|
|
@@ -29,7 +31,7 @@ See [Compatibility](docs/compatibility.md).
|
|
|
29
31
|
The current public build is a pre-release. Add its exact version to the application's `Gemfile`:
|
|
30
32
|
|
|
31
33
|
```ruby
|
|
32
|
-
gem "chronos-ruby", "0.
|
|
34
|
+
gem "chronos-ruby", "0.4.0.pre.1"
|
|
33
35
|
```
|
|
34
36
|
|
|
35
37
|
Install with a Bundler version compatible with the application. For the oldest supported runtime:
|
|
@@ -47,7 +49,7 @@ gem install chronos-ruby --pre
|
|
|
47
49
|
|
|
48
50
|
## Rails installation
|
|
49
51
|
|
|
50
|
-
Rails automatic
|
|
52
|
+
Rails automatic installation is not part of version 0.4. A Rails application may install the Rack middleware manually, but this does not constitute declared Rails support. Railtie and generator support are planned for version 0.5.
|
|
51
53
|
|
|
52
54
|
## Minimum configuration
|
|
53
55
|
|
|
@@ -70,7 +72,14 @@ HTTPS verification is enabled by default. HTTP requires explicitly setting `ssl_
|
|
|
70
72
|
|
|
71
73
|
## Automatic capture
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
Rack applications can capture unhandled exceptions automatically and preserve the application error semantics:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
use Chronos::Integrations::Rack::Middleware,
|
|
79
|
+
:include_user_agent => false
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The middleware notifies asynchronously and re-raises the same exception. It never reads the request body, raw query string, cookies, authorization headers, or response body. See [Rack integration and context](docs/modules/rack-context.md).
|
|
74
83
|
|
|
75
84
|
## Manual capture
|
|
76
85
|
|
|
@@ -101,15 +110,25 @@ User data is opt-in and must contain only values your application is allowed to
|
|
|
101
110
|
Chronos.notify(error, :user => {"id" => "customer-42", "role" => "operator"})
|
|
102
111
|
```
|
|
103
112
|
|
|
104
|
-
Version 0.
|
|
113
|
+
Version 0.4 sanitizes this context before delivery and before it can enter retry storage. Data minimization remains the application's responsibility.
|
|
105
114
|
|
|
106
115
|
## Breadcrumbs
|
|
107
116
|
|
|
108
|
-
Breadcrumbs
|
|
117
|
+
Breadcrumbs use a fixed circular buffer scoped to the current execution:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
Chronos.add_breadcrumb(
|
|
121
|
+
:category => "custom",
|
|
122
|
+
:message => "payment started",
|
|
123
|
+
:metadata => {"provider" => "example"}
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
No log, SQL, HTTP, cache, job, request body, or response body payload is collected automatically. Unknown categories become `custom`, and metadata is bounded and sanitized before queueing.
|
|
109
128
|
|
|
110
129
|
## Filters and LGPD
|
|
111
130
|
|
|
112
|
-
Version 0.
|
|
131
|
+
Version 0.4 recursively redacts sensitive keys and detects Bearer tokens, JWTs, e-mail addresses, CPF, CNPJ, and valid payment-card candidates in free text. IPv4 addresses are anonymized by default. Applications can add blocklist matchers, hash selected identifiers, or install custom filters:
|
|
113
132
|
|
|
114
133
|
```ruby
|
|
115
134
|
Chronos.configure do |config|
|
|
@@ -133,19 +152,19 @@ Chronos.configure do |config|
|
|
|
133
152
|
end
|
|
134
153
|
```
|
|
135
154
|
|
|
136
|
-
|
|
155
|
+
Local exception-specific ignore callbacks are not available in version 0.4. The server may provide a bounded list of exact fingerprints to ignore; it cannot provide regular expressions or executable rules.
|
|
137
156
|
|
|
138
157
|
## Performance monitoring
|
|
139
158
|
|
|
140
|
-
Request, SQL, cache, job, and external HTTP monitoring are not implemented in version 0.
|
|
159
|
+
Request timing events, SQL, cache, job, and external HTTP monitoring are not implemented in version 0.4. Rack exception context is bounded, and HTTP delivery runs outside the caller thread when `Chronos.notify` is used.
|
|
141
160
|
|
|
142
161
|
## Sidekiq and Active Job
|
|
143
162
|
|
|
144
|
-
Sidekiq and Active Job integrations are not implemented in version 0.
|
|
163
|
+
Sidekiq and Active Job integrations are not implemented in version 0.4. Calling the manual API from a job is possible, but automatic capture and deduplication are not yet guaranteed.
|
|
145
164
|
|
|
146
165
|
## Deploy tracking
|
|
147
166
|
|
|
148
|
-
Deploy notifications are not implemented in version 0.
|
|
167
|
+
Deploy notifications are not implemented in version 0.4. `app_version` may be included in exception events for release correlation.
|
|
149
168
|
|
|
150
169
|
## Asynchronous queue
|
|
151
170
|
|
|
@@ -156,16 +175,23 @@ flowchart LR
|
|
|
156
175
|
E[Exception] --> N[Notice builder]
|
|
157
176
|
N --> P[Privacy sanitizer]
|
|
158
177
|
P --> S[Safe bounded serializer]
|
|
159
|
-
S -->
|
|
178
|
+
S --> D[Delivery pipeline]
|
|
179
|
+
D --> Q[Bounded queue]
|
|
160
180
|
Q --> W[Fixed worker pool]
|
|
161
|
-
W -->
|
|
181
|
+
W --> R[Retry and circuit breaker]
|
|
182
|
+
R --> H[Net::HTTP transport]
|
|
183
|
+
R --> B[Bounded memory backlog]
|
|
162
184
|
```
|
|
163
185
|
|
|
164
186
|
Use `Chronos.flush(timeout)` to wait for accepted events and `Chronos.close(timeout)` during shutdown. Workers are recreated after a process fork.
|
|
165
187
|
|
|
166
188
|
## Retry and backlog
|
|
167
189
|
|
|
168
|
-
|
|
190
|
+
The resilience layer introduced in version 0.3 retries network errors, HTTP `408`, `429`, and `5xx` responses with exponential backoff, bounded jitter, and a finite attempt count. Other `4xx` responses are permanent and are not retried. A circuit breaker pauses requests after repeated failures, preventing retry storms.
|
|
191
|
+
|
|
192
|
+
After retries are exhausted, the already sanitized `SerializedEvent` may enter a fixed-capacity memory backlog. The backlog drops new items when full, is lost when the process exits, and never writes to disk. A later successful half-open probe drains backlog items as new events arrive.
|
|
193
|
+
|
|
194
|
+
The SaaS may return a JSON policy in the bounded `X-Chronos-Remote-Configuration` response header. Only sampling rate, enabled event types, a lower payload limit, exact ignored fingerprints, send interval, and kill switch are accepted. Remote values cannot change the host, project credentials, TLS, local maximums, code, or regular expressions. See [Retry and backlog](docs/modules/retry-backlog.md) and [Remote configuration](docs/modules/remote-configuration.md).
|
|
169
195
|
|
|
170
196
|
## How it works internally
|
|
171
197
|
|
|
@@ -173,8 +199,10 @@ The code follows hexagonal boundaries:
|
|
|
173
199
|
|
|
174
200
|
- `Chronos::Core` contains immutable notices, sanitization, and safe normalization;
|
|
175
201
|
- `Chronos::Application` coordinates capture;
|
|
202
|
+
- `Chronos::Application::DeliveryPipeline` owns bounded retry and remote policy;
|
|
176
203
|
- `Chronos::Ports` defines delivery behavior;
|
|
177
|
-
- `Chronos::Adapters` implements Net::HTTP delivery;
|
|
204
|
+
- `Chronos::Adapters` implements Net::HTTP delivery and thread-local context;
|
|
205
|
+
- `Chronos::Integrations::Rack` implements optional automatic Rack capture;
|
|
178
206
|
- `Chronos::Internal` owns bounded queueing, workers, and defensive logging.
|
|
179
207
|
|
|
180
208
|
The core has no dependency on Rails, Rack, Sidekiq, or ActiveSupport. See [Architecture](docs/architecture.md).
|
|
@@ -194,6 +222,17 @@ Chronos.configure do |config|
|
|
|
194
222
|
config.workers = 1
|
|
195
223
|
config.timeout = 5.0
|
|
196
224
|
config.open_timeout = 2.0
|
|
225
|
+
config.max_retries = 3
|
|
226
|
+
config.retry_base_interval = 0.5
|
|
227
|
+
config.retry_max_interval = 30.0
|
|
228
|
+
config.retry_jitter = 0.25
|
|
229
|
+
config.backlog_size = 100
|
|
230
|
+
config.circuit_failure_threshold = 5
|
|
231
|
+
config.circuit_reset_timeout = 30.0
|
|
232
|
+
config.remote_configuration = true
|
|
233
|
+
config.context_store = :thread_local
|
|
234
|
+
config.breadcrumb_capacity = 20
|
|
235
|
+
config.breadcrumb_max_bytes = 2048
|
|
197
236
|
end
|
|
198
237
|
```
|
|
199
238
|
|
|
@@ -205,20 +244,22 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
|
|
|
205
244
|
|
|
206
245
|
## Benchmark
|
|
207
246
|
|
|
208
|
-
Run the version 0.
|
|
247
|
+
Run the version 0.4 benchmarks with:
|
|
209
248
|
|
|
210
249
|
```bash
|
|
211
250
|
bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
|
|
212
251
|
bundle _1.17.3_ exec ruby benchmarks/serialization.rb
|
|
213
252
|
bundle _1.17.3_ exec ruby benchmarks/filtering.rb
|
|
214
253
|
bundle _1.17.3_ exec ruby benchmarks/queue.rb
|
|
254
|
+
bundle _1.17.3_ exec ruby benchmarks/retry_backlog.rb
|
|
255
|
+
bundle _1.17.3_ exec ruby benchmarks/request_overhead.rb
|
|
215
256
|
```
|
|
216
257
|
|
|
217
258
|
Results depend on runtime, hardware, and payload. No performance comparison is claimed until repeatable measurements are published.
|
|
218
259
|
|
|
219
260
|
## Migration from Airbrake
|
|
220
261
|
|
|
221
|
-
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.
|
|
262
|
+
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.4 does not claim API compatibility or automatic replacement.
|
|
222
263
|
|
|
223
264
|
## Local development
|
|
224
265
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://chronos.example/schemas/rack-context-v1.schema.json",
|
|
4
|
+
"title": "Chronos Rack exception context v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": true,
|
|
7
|
+
"required": ["request", "trace_id", "breadcrumbs"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"trace_id": {"type": "string", "minLength": 1},
|
|
10
|
+
"request": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"required": ["method", "route", "status", "duration_ms", "request_id", "host", "path"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"method": {"type": "string"},
|
|
16
|
+
"route": {"type": "string"},
|
|
17
|
+
"status": {"type": "integer"},
|
|
18
|
+
"duration_ms": {"type": "number", "minimum": 0},
|
|
19
|
+
"request_id": {"type": ["string", "null"]},
|
|
20
|
+
"user_agent": {"type": ["string", "null"]},
|
|
21
|
+
"host": {"type": "string"},
|
|
22
|
+
"path": {"type": "string"},
|
|
23
|
+
"controller": {"type": ["string", "null"]},
|
|
24
|
+
"action": {"type": ["string", "null"]},
|
|
25
|
+
"response_size": {"type": ["integer", "null"], "minimum": 0}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"breadcrumbs": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"required": ["category", "message", "metadata", "timestamp"],
|
|
34
|
+
"properties": {
|
|
35
|
+
"category": {"enum": ["custom", "log", "request", "query", "external_http", "cache", "job"]},
|
|
36
|
+
"message": {"type": "string"},
|
|
37
|
+
"metadata": {"type": "object"},
|
|
38
|
+
"timestamp": {"type": "string"}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
Accepted
|
|
5
|
+
Accepted and implemented in version 0.3.
|
|
6
6
|
|
|
7
7
|
## Context
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Persisting unsanitized events would increase privacy and credential exposure ris
|
|
|
10
10
|
|
|
11
11
|
## Decision
|
|
12
12
|
|
|
13
|
-
`PayloadSerializer` must run `Sanitizer` before `SafeSerializer`, queueing, transport, or
|
|
13
|
+
`PayloadSerializer` must run `Sanitizer` before `SafeSerializer`, queueing, transport, retry, or backlog. `MemoryBacklog` accepts only `SerializedEvent`. Raw notices remain transient in process memory and never enter delivery storage.
|
|
14
14
|
|
|
15
15
|
## Alternatives
|
|
16
16
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# ADR-011 — Bounded resilience and restricted remote control
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted for version 0.3.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Chronos must tolerate prolonged SaaS outages in legacy applications without unbounded memory, unlimited retries, extra per-event threads, unsanitized storage, or a remote-control channel capable of changing security boundaries.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Place retry orchestration in `DeliveryPipeline`. Use finite exponential backoff with bounded positive jitter, a simple closed/open/half-open circuit breaker, and a fixed-capacity in-memory backlog that accepts only sanitized `SerializedEvent` values. Do not persist events to disk in version 0.3.
|
|
14
|
+
|
|
15
|
+
Accept remote policy only from a size-limited JSON response header after successful authenticated event delivery. Apply a fixed scalar allowlist and local upper bounds. Reject executable content, remote regular expressions, host, credentials, TLS, and capacity changes.
|
|
16
|
+
|
|
17
|
+
## Alternatives
|
|
18
|
+
|
|
19
|
+
Retry inside `NetHttpTransport` was rejected because transport classification and delivery policy would become coupled. An unbounded queue was rejected because endpoint availability would control host memory. Immediate disk persistence was rejected because permissions, rotation, expiry, checksums, corruption, and privacy need an independent design. Arbitrary remote JSON or code callbacks were rejected as an unsafe control plane.
|
|
20
|
+
|
|
21
|
+
## Positive consequences
|
|
22
|
+
|
|
23
|
+
Outage memory remains bounded, retry storms are contained, transport stays replaceable, privacy precedes backlog, and remote operators can reduce or stop later collection without changing endpoint security.
|
|
24
|
+
|
|
25
|
+
## Negative consequences
|
|
26
|
+
|
|
27
|
+
Memory backlog is lost on restart and drains only when later delivery activity occurs. Synchronous notification may wait through retry delays. Process-local sampling is approximate. A response header limits policy size and complexity.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# ADR-012 — Rack capture with selectable execution context
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted for version 0.4.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Automatic Rack capture needs request-scoped state, but the legacy line cannot rely on modern fiber-local APIs. Loading the plain Ruby gem must not force Rack into applications that do not use it. Request and response bodies also carry disproportionate privacy and performance risk.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Implement a Rack-protocol middleware without a runtime Rack dependency. Catch failures from the downstream application call, notify asynchronously, and re-raise the same exception. Never read `rack.input`, raw query strings, cookies, authorization headers, or response body enumeration. Use already-parsed parameter hashes only.
|
|
14
|
+
|
|
15
|
+
Define a `ContextStore` port and use a selectable thread-local adapter by default. Restore nested scopes in `ensure`. Keep breadcrumbs in a fixed circular per-execution buffer and sanitize them with the rest of the event before queueing.
|
|
16
|
+
|
|
17
|
+
## Alternatives
|
|
18
|
+
|
|
19
|
+
Requiring Rack from the core was rejected because it would add dependency and resolution risk to plain Ruby legacy applications. Global or process-wide context was rejected because concurrent requests would leak data. Automatic body parsing and body proxies were deferred because they change I/O behavior and complicate streaming lifecycle ownership.
|
|
20
|
+
|
|
21
|
+
## Positive consequences
|
|
22
|
+
|
|
23
|
+
Plain Ruby loading remains dependency-free, concurrent request context is isolated, middleware failure semantics are preserved, and diagnostic memory is bounded.
|
|
24
|
+
|
|
25
|
+
## Negative consequences
|
|
26
|
+
|
|
27
|
+
Thread-local context does not propagate to application-created threads or fibers. Generic route normalization is less precise than router-specific integration. Exceptions raised only during deferred response-body enumeration are outside version 0.4 capture.
|
data/docs/architecture.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
Chronos Ruby 0.
|
|
3
|
+
Chronos Ruby 0.4 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
|
|
4
4
|
|
|
5
5
|
```mermaid
|
|
6
6
|
flowchart TB
|
|
@@ -8,24 +8,33 @@ flowchart TB
|
|
|
8
8
|
Application --> Core[Core / Notice and serialization]
|
|
9
9
|
Application --> Ports[Ports / Transport contract]
|
|
10
10
|
Ports --> Adapter[Adapters / NetHttpTransport]
|
|
11
|
-
Application -->
|
|
11
|
+
Application --> Delivery[Application / DeliveryPipeline]
|
|
12
|
+
Delivery --> Internal[Internal / BoundedQueue, WorkerPool, and MemoryBacklog]
|
|
12
13
|
Internal --> Ports
|
|
14
|
+
Rack[Integrations / Rack middleware] --> Facade
|
|
15
|
+
Facade --> Context[Ports / ContextStore]
|
|
16
|
+
Context --> ThreadLocal[Adapters / ThreadLocalContextStore]
|
|
13
17
|
```
|
|
14
18
|
|
|
15
19
|
## Boundaries
|
|
16
20
|
|
|
17
21
|
- Domain/Core owns immutable event values and Ruby normalization.
|
|
18
22
|
- Application owns use-case ordering and failure containment.
|
|
19
|
-
- Ports define behavior expected from infrastructure.
|
|
20
|
-
- Adapters contain Net::HTTP and
|
|
23
|
+
- Ports define behavior expected from transport and execution-context infrastructure.
|
|
24
|
+
- Adapters contain Net::HTTP, TLS, and thread-local context behavior.
|
|
21
25
|
- Internal contains private concurrency and diagnostic mechanisms.
|
|
26
|
+
- Integrations contain optional framework entry points and never enter the domain boundary.
|
|
22
27
|
|
|
23
28
|
The `Chronos` module is a thin facade. Rails, Rack, ActiveSupport, Sidekiq, and job libraries must not be required by the core.
|
|
24
29
|
|
|
30
|
+
## Rack capture flow
|
|
31
|
+
|
|
32
|
+
The Rack middleware establishes a context-store scope, adds a bounded request breadcrumb, and calls the downstream application. An unhandled exception is enriched with request duration and status, captured through the normal notice pipeline, then re-raised unchanged. The context-store adapter restores or clears its value in `ensure`. The middleware implements the Rack protocol without requiring the Rack library.
|
|
33
|
+
|
|
25
34
|
## Capture flow
|
|
26
35
|
|
|
27
|
-
An exception becomes an immutable notice. `Sanitizer` removes sensitive values before `SafeSerializer` creates a bounded JSON envelope. Asynchronous capture inserts only that sanitized serialized event into the queue. A fixed worker sends it through
|
|
36
|
+
An exception becomes an immutable notice. `Sanitizer` removes sensitive values before `SafeSerializer` creates a bounded JSON envelope. Asynchronous capture inserts only that sanitized serialized event into the queue. A fixed worker sends it through `DeliveryPipeline`, which applies finite retry, a circuit breaker, and a fixed memory backlog. Synchronous capture bypasses the queue but uses the same privacy and resilience boundaries.
|
|
28
37
|
|
|
29
38
|
## Failure policy
|
|
30
39
|
|
|
31
|
-
Explicit invalid configuration raises `Chronos::ConfigurationError`. Capture, serialization, logger, worker, TLS, network, and
|
|
40
|
+
Explicit invalid configuration raises `Chronos::ConfigurationError`. Capture, serialization, logger, worker, retry, circuit, TLS, network, HTTP, and remote-policy failures do not escape into the host application. They produce `false`, a classified transport result, a bounded state transition, or a bounded logger diagnostic.
|
data/docs/compatibility.md
CHANGED
|
@@ -4,14 +4,14 @@ Chronos Ruby 0.x is the legacy line. Technical compatibility does not make an en
|
|
|
4
4
|
|
|
5
5
|
| Ruby | Rails integration | Status | Evidence |
|
|
6
6
|
|---|---|---|---|
|
|
7
|
-
| 2.2.10 |
|
|
8
|
-
| 2.3.8 |
|
|
9
|
-
| 2.4.10 |
|
|
10
|
-
| 2.5.9 |
|
|
11
|
-
| 2.6.10 |
|
|
7
|
+
| 2.2.10 | Rack protocol in 0.4; no Rails | Best effort | Dedicated legacy core CI and Rack contract tests |
|
|
8
|
+
| 2.3.8 | Rack protocol in 0.4; no Rails | Best effort | Dedicated legacy core CI and Rack contract tests |
|
|
9
|
+
| 2.4.10 | Rack protocol in 0.4; no Rails | Best effort | Dedicated legacy core CI and Rack contract tests |
|
|
10
|
+
| 2.5.9 | Rack protocol in 0.4; no Rails | Best effort | Dedicated legacy core CI and Rack contract tests |
|
|
11
|
+
| 2.6.10 | Rack protocol in 0.4; no Rails | Best effort | Dedicated legacy core CI and Rack contract tests |
|
|
12
12
|
| 2.7 and newer | None in 0.x | Unsupported | Belongs to transitional or modern lines |
|
|
13
13
|
|
|
14
|
-
No Rails version is declared supported by version 0.
|
|
14
|
+
No Rails version is declared supported by version 0.4. The middleware is tested against the Rack protocol shape without adding a Rack runtime dependency. Rails support requires a real example application, Railtie integration tests, and a successful dedicated matrix job.
|
|
15
15
|
|
|
16
16
|
Status meanings:
|
|
17
17
|
|
data/docs/configuration.md
CHANGED
|
@@ -29,6 +29,21 @@
|
|
|
29
29
|
| `hash_keys` | Optional | `[]` | Scalar identifier keys replaced by scoped SHA-256 values |
|
|
30
30
|
| `filters` | Optional | `[]` | Callable application-specific privacy filters |
|
|
31
31
|
| `anonymize_ip` | Recommended | `true` | Replaces the final octet of supplied IPv4 addresses |
|
|
32
|
+
| `max_retries` | Optional | `3` | Maximum retry attempts after the first delivery failure |
|
|
33
|
+
| `retry_base_interval` | Optional | `0.5` | Initial exponential-backoff delay in seconds |
|
|
34
|
+
| `retry_max_interval` | Optional | `30.0` | Maximum local or `Retry-After` delay in seconds |
|
|
35
|
+
| `retry_jitter` | Optional | `0.25` | Random positive jitter fraction between `0.0` and `1.0` |
|
|
36
|
+
| `backlog_size` | Optional | `100` | Maximum sanitized events retained in memory after delivery failure; `0` disables retention |
|
|
37
|
+
| `circuit_failure_threshold` | Optional | `5` | Consecutive retryable failures before opening the circuit |
|
|
38
|
+
| `circuit_reset_timeout` | Optional | `30.0` | Seconds before one half-open delivery probe |
|
|
39
|
+
| `remote_configuration` | Optional | `true` | Accepts only the documented bounded remote policy fields |
|
|
40
|
+
| `remote_config_max_bytes` | Optional | `4096` | Maximum remote policy response-header bytes |
|
|
41
|
+
| `sampling_rate` | Optional | `1.0` | Local upper bound for exception sampling |
|
|
42
|
+
| `enabled_event_types` | Optional | `["exception"]` | Local event-type allowlist; only exception exists in version 0.4 |
|
|
43
|
+
| `max_remote_send_interval` | Optional | `60.0` | Local upper bound for remotely requested send spacing |
|
|
44
|
+
| `context_store` | Optional | `:thread_local` | `:thread_local` or an object implementing `get`, `set`, `clear`, and `with_context` |
|
|
45
|
+
| `breadcrumb_capacity` | Optional | `20` | Positive count of newest breadcrumbs retained per execution |
|
|
46
|
+
| `breadcrumb_max_bytes` | Optional | `2048` | Maximum bytes per normalized breadcrumb; minimum `128` |
|
|
32
47
|
|
|
33
48
|
```ruby
|
|
34
49
|
Chronos.configure do |config|
|
|
@@ -41,9 +56,16 @@ Chronos.configure do |config|
|
|
|
41
56
|
config.workers = 1
|
|
42
57
|
config.blocklist_keys += [:medical_record, /bank_account/i]
|
|
43
58
|
config.hash_keys += [:customer_id]
|
|
59
|
+
config.max_retries = 3
|
|
60
|
+
config.backlog_size = 100
|
|
61
|
+
config.circuit_failure_threshold = 5
|
|
62
|
+
config.context_store = :thread_local
|
|
63
|
+
config.breadcrumb_capacity = 20
|
|
44
64
|
end
|
|
45
65
|
```
|
|
46
66
|
|
|
47
67
|
Privacy matcher collections and filters are copied into the immutable runtime snapshot. Matchers must be String, Symbol, or Regexp values, and every custom filter must respond to `call`.
|
|
48
68
|
|
|
49
69
|
The gem never reads all environment variables automatically. The host application decides which values to pass. See [Privacy and LGPD](privacy-lgpd.md) before adding application context.
|
|
70
|
+
|
|
71
|
+
Remote configuration can only reduce collection within local bounds. It cannot increase `max_payload_size` or `sampling_rate`, enable an unsupported or locally disabled event type, change the endpoint, replace credentials, disable TLS verification, or install executable matching rules.
|
data/docs/data-collected.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Data collected
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
3
|
+
Version 0.4 emits exception events submitted manually or captured by the optional Rack middleware. All fields pass through the privacy sanitizer and bounded safe serializer before queueing, retry storage, or delivery.
|
|
4
4
|
|
|
5
5
|
| Data | Default | Source |
|
|
6
6
|
|---|---|---|
|
|
@@ -15,8 +15,14 @@ Version 0.2 emits only manually submitted exception events. All fields pass thro
|
|
|
15
15
|
| Context, parameters, session, user | Optional and sanitized | Explicit capture arguments |
|
|
16
16
|
| Tags and fingerprint | Optional and sanitized | Explicit capture arguments |
|
|
17
17
|
| IPv4 address in supplied text | Anonymized | Explicit capture arguments |
|
|
18
|
+
| Rack method, normalized route, status, duration, request ID, host, query-free path, trace ID | Collected with Rack middleware | Rack environment and local clock |
|
|
19
|
+
| Rack user agent | Disabled by default | `HTTP_USER_AGENT` when middleware option is enabled |
|
|
20
|
+
| Rack controller/action | Collected when already supplied | Explicit Chronos or Action Dispatch environment values |
|
|
21
|
+
| Rack parameters | Collected only when already parsed | Explicit, Rack, or Action Dispatch parameter hashes |
|
|
22
|
+
| Response size | `Content-Length` when present | Rack response headers |
|
|
23
|
+
| Breadcrumbs | Explicit and bounded | Application and Chronos integration |
|
|
18
24
|
|
|
19
|
-
The gem never collects request bodies, response bodies, cookies, HTTP authorization headers, environment variables in bulk, source code, SQL bind values, database rows, or installed gems in version 0.
|
|
25
|
+
The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, source code, SQL bind values, database rows, or installed gems in version 0.4.
|
|
20
26
|
|
|
21
27
|
The secret `project_key` is an authentication header and is excluded from the JSON payload and logger diagnostics. The envelope field named `project_key` contains the public `project_id` required by the current v1 server contract.
|
|
22
28
|
|
data/docs/examples/plain-ruby.md
CHANGED
|
@@ -17,4 +17,10 @@ For a network-free privacy audit, run:
|
|
|
17
17
|
bundle _1.17.3_ exec ruby examples/plain-ruby/privacy_audit.rb
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
For a network-free outage and bounded-backlog example, run:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bundle _1.17.3_ exec ruby examples/plain-ruby/resilient_delivery.rb
|
|
24
|
+
```
|
|
25
|
+
|
|
20
26
|
The example builds the final event locally and demonstrates key redaction, e-mail and Bearer-token detection, application-specific blocklisting, and identifier hashing.
|
data/docs/modules/async-queue.md
CHANGED
|
@@ -10,8 +10,12 @@ stateDiagram-v2
|
|
|
10
10
|
Accepted --> Queued
|
|
11
11
|
Queued --> Sending
|
|
12
12
|
Sending --> Sent
|
|
13
|
-
Sending -->
|
|
13
|
+
Sending --> Retried: retryable failure
|
|
14
|
+
Retried --> Sending: policy permits
|
|
15
|
+
Retried --> Backlog: attempts exhausted
|
|
16
|
+
Backlog --> Sending: later recovery probe
|
|
17
|
+
Sending --> Rejected: permanent 4xx
|
|
14
18
|
Accepted --> Dropped: queue full
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
Version 0.
|
|
21
|
+
Version 0.3 adds retry and a separate fixed-size memory backlog, but no disk persistence. Only sanitized serialized events enter either structure. Queue statistics expose current size, capacity, accepted events, and dropped events. Delivery diagnostics additionally expose state counters, backlog statistics, and circuit state. Tests cover full queues, waiter wakeup, lazy startup, contained failures, timed shutdown, double close, flush, fork behavior, and prolonged endpoint failure.
|
|
@@ -13,10 +13,11 @@ flowchart LR
|
|
|
13
13
|
Sanitizer --> SafeSerializer
|
|
14
14
|
SafeSerializer --> PayloadSerializer
|
|
15
15
|
PayloadSerializer --> SerializedEvent
|
|
16
|
+
SerializedEvent --> DeliveryPipeline
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
`Notice` is immutable. Parsers are stateless. Raw notice values exist only in process memory. The sanitizer removes sensitive keys and recognized personal data before the safe serializer accepts JSON primitives, bounds nested structures, tolerates invalid encoding, and represents unknown objects without arbitrary application serialization.
|
|
19
20
|
|
|
20
|
-
Version 0.
|
|
21
|
+
Version 0.3 passes the serialized event to `DeliveryPipeline`. The pipeline applies remote sampling and exact fingerprint policy before serialization where possible, then manages queueing, retry, circuit state, and bounded memory backlog without changing transport or queue code.
|
|
21
22
|
|
|
22
23
|
Unit and contract tests verify missing backtraces, cyclic causes, invalid encoding, unsafe objects, payload limits, and the v1 envelope.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Rack integration, execution context, and breadcrumbs
|
|
2
|
+
|
|
3
|
+
Version 0.4 adds automatic capture through `Chronos::Integrations::Rack::Middleware`. The integration implements the Rack protocol directly and does not require Rack when `chronos-ruby` is loaded. Add the middleware after configuring Chronos:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
use Chronos::Integrations::Rack::Middleware,
|
|
7
|
+
:include_user_agent => false
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Capture behavior
|
|
11
|
+
|
|
12
|
+
The middleware catches an exception raised by the downstream application, submits it asynchronously, and re-raises the same exception object. A notification or logger failure cannot replace the application failure. An `ensure` boundary clears request context in every outcome.
|
|
13
|
+
|
|
14
|
+
Captured request fields are method, normalized route, status, duration in milliseconds, request ID, optional user agent, host, query-free path, controller/action when already supplied, approximate response size from `Content-Length`, a generated or supplied trace ID, already-parsed parameters, and application-supplied user context. The final event serializer sanitizes every field before queueing.
|
|
15
|
+
|
|
16
|
+
The middleware never reads `rack.input`, copies `QUERY_STRING`, parses a body, enumerates a response body, or reads cookies and authorization headers. Parameters are collected only from already-materialized hashes at `rack.request.query_hash`, `action_dispatch.request.query_parameters`, `action_dispatch.request.path_parameters`, or the explicit `chronos.parameters` key. User context is opt-in through `chronos.user`.
|
|
17
|
+
|
|
18
|
+
Without an explicit `chronos.route` or `action_dispatch.route_uri_pattern`, numeric and UUID path segments are replaced with `:id`. This is a conservative cardinality guard, not a complete router-aware normalizer.
|
|
19
|
+
|
|
20
|
+
## Context store
|
|
21
|
+
|
|
22
|
+
`Chronos::Ports::ContextStore` defines `get`, `set`, `clear`, and `with_context`. The legacy default, `Chronos::Adapters::ThreadLocalContextStore`, isolates each thread and restores nested scopes in `ensure`. It deliberately does not propagate state into newly created threads or fibers.
|
|
23
|
+
|
|
24
|
+
A custom strategy can be selected at configuration time if it implements the complete port:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
Chronos.configure do |config|
|
|
28
|
+
# credentials omitted
|
|
29
|
+
config.context_store = MyContextStore.new
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Application code can add scoped values. Manual notifications issued inside the block inherit them:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
Chronos.with_context(:user => {"id" => "customer-42"}) do
|
|
37
|
+
Chronos.notify(error)
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Breadcrumbs
|
|
42
|
+
|
|
43
|
+
`Chronos.add_breadcrumb` records an explicit marker in the current execution buffer:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
Chronos.add_breadcrumb(
|
|
47
|
+
:category => "custom",
|
|
48
|
+
:message => "payment authorization started",
|
|
49
|
+
:metadata => {"provider" => "example"}
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Allowed categories are `custom`, `log`, `request`, `query`, `external_http`, `cache`, and `job`; an unknown value becomes `custom`. No log, SQL, body, cache, job, or external HTTP payload is captured automatically in 0.4. Metadata exists only when the application or a later integration explicitly supplies it.
|
|
54
|
+
|
|
55
|
+
The default circular buffer keeps the newest 20 entries. Each breadcrumb is normalized to JSON primitives with bounded depth, collections, strings, nodes, and serialized bytes. The entire exception payload still passes through the privacy sanitizer before it can enter the queue or retry backlog.
|
|
56
|
+
|
|
57
|
+
## Concurrency and limitations
|
|
58
|
+
|
|
59
|
+
Concurrent Rack threads receive distinct user, parameter, breadcrumb, and trace values. Context does not cross threads automatically, so applications that start their own thread must establish a new scope explicitly. Version 0.4 captures exceptions raised during the initial Rack application call; deferred failures raised only while a server enumerates a streaming response body are not intercepted.
|