chronos-ruby 0.4.0.pre.1 → 0.6.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +33 -9
  4. data/contracts/event-v1.schema.json +25 -11
  5. data/contracts/sidekiq-job-v1.schema.json +21 -0
  6. data/docs/adr/ADR-013-legacy-rails-notifications.md +27 -0
  7. data/docs/adr/ADR-014-sidekiq-envelope-context.md +27 -0
  8. data/docs/architecture.md +12 -1
  9. data/docs/compatibility.md +12 -6
  10. data/docs/configuration.md +8 -2
  11. data/docs/data-collected.md +14 -2
  12. data/docs/modules/rails-legacy.md +60 -0
  13. data/docs/modules/sidekiq-legacy.md +23 -0
  14. data/docs/modules/telemetry-events.md +9 -0
  15. data/docs/performance.md +27 -3
  16. data/docs/privacy-lgpd.md +12 -3
  17. data/docs/troubleshooting.md +17 -1
  18. data/lib/chronos/agent.rb +58 -1
  19. data/lib/chronos/application/capture_telemetry.rb +37 -0
  20. data/lib/chronos/application/remote_configuration.rb +1 -1
  21. data/lib/chronos/configuration/snapshot.rb +53 -0
  22. data/lib/chronos/configuration/validation.rb +122 -0
  23. data/lib/chronos/configuration.rb +15 -153
  24. data/lib/chronos/core/telemetry_event.rb +106 -0
  25. data/lib/chronos/integrations/job_payload.rb +72 -0
  26. data/lib/chronos/integrations/rack/middleware.rb +5 -1
  27. data/lib/chronos/integrations/sidekiq.rb +258 -0
  28. data/lib/chronos/integrations.rb +1 -1
  29. data/lib/chronos/internal/worker_pool.rb +19 -2
  30. data/lib/chronos/rails/installer.rb +70 -0
  31. data/lib/chronos/rails/notifications_subscriber.rb +203 -0
  32. data/lib/chronos/rails/railtie.rb +21 -0
  33. data/lib/chronos/rails.rb +16 -0
  34. data/lib/chronos/sidekiq.rb +12 -0
  35. data/lib/chronos/version.rb +1 -1
  36. data/lib/chronos.rb +34 -1
  37. data/lib/generators/chronos/install/install_generator.rb +25 -0
  38. data/lib/generators/chronos/install/templates/chronos.rb +20 -0
  39. metadata +21 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ab10ffdc0cff4274241465904b18e4bceb4fa230ea9fddb4b8be419c08e3463
4
- data.tar.gz: 2d6d778012088192efed14e0ab64958716c8a6b4c3e697127c52573dc4dcd47b
3
+ metadata.gz: 5ad7bc09dd58b0476b65687d0b7c695bdc5e82c898f882fc82ed2e87eb352b79
4
+ data.tar.gz: 527383d76a4432bc16f48d2a476d7c2398d66f5b891bbe666d1e0ceba9dc9959
5
5
  SHA512:
6
- metadata.gz: a8bb789313b2b7d360b6a8c08e5b5cf731aa1325912be7b560a1869b3d8717e4e779610e92591c1a2c436f5d14cd39e28f1e5db837d954def1db232788284d2d
7
- data.tar.gz: 93a311d6905d83dee031a88cb5887854a32e60d26eceb0dc0446a8244c77a2c9fa763089bc5ec5fa25d265cb5881c3775dafe6361b1e84a1c6044aa89df220ee
6
+ metadata.gz: 24bf5ed37de64c34a82fd0b98f3e1323cac14c2392fa0d6a01dbcd24d48a69a52100a6127a79b2567314bcf2c464b354fee226ab19bba08ea384bfb3ec8a12f9
7
+ data.tar.gz: 1696953512893c9d3eecc23d361b35c5434df7dda25e33a1fe6321b5c25ae4d9e03a2a843bd76e9c327130be2cba2a6c7a9766d70a6f068007b24959aa3cfdc0
data/CHANGELOG.md CHANGED
@@ -6,11 +6,33 @@ All notable changes are documented here. The project follows Semantic Versioning
6
6
 
7
7
  ### Fixed
8
8
 
9
+ - `WorkerPool#flush` no longer returns before a worker marks a just-popped event as active;
9
10
  - release publishing now updates RubyGems to a Ruby 2.6-compatible version that supports `GEM_HOST_API_KEY`.
10
11
  - Updated the legacy development toolchain to non-vulnerable Rake and RuboCop versions.
11
12
  - legacy CI now resolves Bundler 1.17.3 through `Gem.bin_path` on RubyGems versions that do not support the `_version_` executable selector.
12
13
  - documentation verification now reads source and Markdown files explicitly as UTF-8 on legacy container locales.
13
14
 
15
+ ## [0.6.0.pre.1] - 2026-07-20
16
+
17
+ ### Added
18
+
19
+ - optional Sidekiq 4/5 client and server middleware loaded through `chronos/sidekiq`;
20
+ - versioned trace/request context propagation beside, never inside, public job arguments;
21
+ - Sidekiq class, queue, JID, retry count, duration, calculable latency, bounded arguments, tags, status, and error telemetry;
22
+ - shared per-job exception deduplication for nested Sidekiq and Active Job capture paths;
23
+ - Sidekiq payload contract, unit/integration tests, executable example, benchmark, module documentation, and ADR-014.
24
+
25
+ ### Changed
26
+
27
+ - version advanced to `0.6.0.pre.1`;
28
+ - the public facade exposes bounded propagation context for optional process-boundary integrations.
29
+
30
+ ### Known limitations
31
+
32
+ - Sidekiq 4/5 remains `Best effort` until dedicated real-gem matrix jobs pass;
33
+ - Active Job context propagation, Resque, and Delayed Job remain subsequent version 0.6 increments;
34
+ - Sidekiq argument capture is automatic, though strictly bounded and sanitized.
35
+
14
36
  ## [0.4.0.pre.1] - 2026-07-20
15
37
 
16
38
  ### Added
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Chronos Ruby
2
2
 
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.
3
+ Chronos Ruby is the framework-independent client for sending Ruby application errors and bounded telemetry to Chronos. Version 0.6 starts legacy worker support with Sidekiq 4/5 middleware, cross-process trace context, bounded job telemetry, and exception deduplication on top of the Rails, Rack, privacy, and resilience foundations from earlier releases.
4
4
 
5
5
  ## What the gem collects
6
6
 
7
- For each exception, version 0.4 can collect:
7
+ For each exception, version 0.6 can collect:
8
8
 
9
9
  - exception class, message, structured backtrace, and chained causes;
10
10
  - timestamp, severity, tags, and an optional fingerprint;
@@ -22,7 +22,7 @@ Chronos Ruby does not inspect environment variables, request bodies, cookies, HT
22
22
 
23
23
  ## Supported Ruby and Rails versions
24
24
 
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.
25
+ Version 0.x targets Ruby 2.2.10 through Ruby 2.6. Version 0.5 provides best-effort Rails 4.2 through 5.2 integration through public framework APIs and feature detection. All supported combinations must pass dedicated CI before being listed as supported.
26
26
 
27
27
  See [Compatibility](docs/compatibility.md).
28
28
 
@@ -31,7 +31,7 @@ See [Compatibility](docs/compatibility.md).
31
31
  The current public build is a pre-release. Add its exact version to the application's `Gemfile`:
32
32
 
33
33
  ```ruby
34
- gem "chronos-ruby", "0.4.0.pre.1"
34
+ gem "chronos-ruby", "0.6.0.pre.1"
35
35
  ```
36
36
 
37
37
  Install with a Bundler version compatible with the application. For the oldest supported runtime:
@@ -49,7 +49,19 @@ gem install chronos-ruby --pre
49
49
 
50
50
  ## Rails installation
51
51
 
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.
52
+ Version 0.5 exposes Rails support explicitly, keeping Rails and ActiveSupport out of plain Ruby applications:
53
+
54
+ ```ruby
55
+ gem "chronos-ruby", "0.6.0.pre.1", :require => "chronos/rails"
56
+ ```
57
+
58
+ Generate the initializer with:
59
+
60
+ ```bash
61
+ rails generate chronos:install
62
+ ```
63
+
64
+ The Railtie installs the Rack middleware and notification subscribers idempotently. Automatic integration is disabled in test and console by default and can be controlled with `rails_enabled`, `rails_capture_in_test`, `rails_capture_in_console`, and `rails_capture_user_agent`. See [Rails 4.2 and 5.2 integration](docs/modules/rails-legacy.md).
53
65
 
54
66
  ## Minimum configuration
55
67
 
@@ -156,11 +168,20 @@ Local exception-specific ignore callbacks are not available in version 0.4. The
156
168
 
157
169
  ## Performance monitoring
158
170
 
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.
171
+ Version 0.5 emits bounded `request`, `query`, `job`, and `cache` events from public Rails notifications. SQL text and binds, cache keys and values, job arguments, mail content and recipients, and request or response bodies are never copied. Aggregation, percentiles, query fingerprints, and external HTTP monitoring are not implemented in this version.
160
172
 
161
173
  ## Sidekiq and Active Job
162
174
 
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.
175
+ Version `0.6.0.pre.1` adds optional Sidekiq 4/5 middleware:
176
+
177
+ ```ruby
178
+ gem "sidekiq", "~> 5.0"
179
+ gem "chronos-ruby", "0.6.0.pre.1", :require => "chronos/sidekiq"
180
+ ```
181
+
182
+ The client middleware propagates only trace/request identifiers in a versioned Sidekiq-envelope field and never changes worker arguments. The server records class, queue, JID, retry count, duration, calculable queue latency, bounded arguments/tags, status, and error class. Values pass through the shared sanitizer before delivery. Failed jobs are notified once and the original exception is re-raised. See [Sidekiq 4/5 legacy integration](docs/modules/sidekiq-legacy.md).
183
+
184
+ The Rails subscriber continues to record basic Active Job class, queue, and duration. Full Active Job context propagation plus optional Resque and Delayed Job adapters are subsequent version 0.6 increments.
164
185
 
165
186
  ## Deploy tracking
166
187
 
@@ -203,6 +224,7 @@ The code follows hexagonal boundaries:
203
224
  - `Chronos::Ports` defines delivery behavior;
204
225
  - `Chronos::Adapters` implements Net::HTTP delivery and thread-local context;
205
226
  - `Chronos::Integrations::Rack` implements optional automatic Rack capture;
227
+ - `Chronos::Rails` implements the optional Railtie, installer, generator, and public-notification adapters;
206
228
  - `Chronos::Internal` owns bounded queueing, workers, and defensive logging.
207
229
 
208
230
  The core has no dependency on Rails, Rack, Sidekiq, or ActiveSupport. See [Architecture](docs/architecture.md).
@@ -244,7 +266,7 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
244
266
 
245
267
  ## Benchmark
246
268
 
247
- Run the version 0.4 benchmarks with:
269
+ Run the version 0.6 benchmarks with:
248
270
 
249
271
  ```bash
250
272
  bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
@@ -253,13 +275,15 @@ bundle _1.17.3_ exec ruby benchmarks/filtering.rb
253
275
  bundle _1.17.3_ exec ruby benchmarks/queue.rb
254
276
  bundle _1.17.3_ exec ruby benchmarks/retry_backlog.rb
255
277
  bundle _1.17.3_ exec ruby benchmarks/request_overhead.rb
278
+ bundle _1.17.3_ exec ruby benchmarks/rails_notifications.rb
279
+ bundle _1.17.3_ exec ruby benchmarks/sidekiq_middleware.rb
256
280
  ```
257
281
 
258
282
  Results depend on runtime, hardware, and payload. No performance comparison is claimed until repeatable measurements are published.
259
283
 
260
284
  ## Migration from Airbrake
261
285
 
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.
286
+ An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.6 does not claim API compatibility or automatic replacement.
263
287
 
264
288
  ## Local development
265
289
 
@@ -20,7 +20,7 @@
20
20
  "properties": {
21
21
  "schema_version": {"const": "1.0"},
22
22
  "event_id": {"type": "string", "minLength": 1},
23
- "event_type": {"enum": ["exception"]},
23
+ "event_type": {"enum": ["exception", "request", "query", "job", "cache"]},
24
24
  "occurred_at": {"type": "string"},
25
25
  "sent_at": {"type": "string"},
26
26
  "project_key": {"type": "string", "minLength": 1},
@@ -36,16 +36,30 @@
36
36
  },
37
37
  "runtime": {"type": "object"},
38
38
  "context": {"type": "object"},
39
- "payload": {
40
- "type": "object",
41
- "required": ["exception", "severity"],
42
- "properties": {
43
- "exception": {
44
- "type": "object",
45
- "required": ["class", "message", "backtrace", "causes"]
46
- },
47
- "severity": {"type": "string"}
39
+ "payload": {"type": "object"}
40
+ },
41
+ "allOf": [
42
+ {
43
+ "if": {
44
+ "required": ["event_type"],
45
+ "properties": {
46
+ "event_type": {"const": "exception"}
47
+ }
48
+ },
49
+ "then": {
50
+ "properties": {
51
+ "payload": {
52
+ "required": ["exception", "severity"],
53
+ "properties": {
54
+ "exception": {
55
+ "type": "object",
56
+ "required": ["class", "message", "backtrace", "causes"]
57
+ },
58
+ "severity": {"type": "string"}
59
+ }
60
+ }
61
+ }
48
62
  }
49
63
  }
50
- }
64
+ ]
51
65
  }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://chronos.example/schemas/sidekiq-job-v1.schema.json",
4
+ "title": "Chronos Sidekiq job payload v1",
5
+ "type": "object",
6
+ "required": ["kind", "class", "queue", "duration_ms", "status"],
7
+ "properties": {
8
+ "kind": {"const": "sidekiq"},
9
+ "class": {"type": "string"},
10
+ "queue": {"type": "string"},
11
+ "jid": {"type": "string"},
12
+ "duration_ms": {"type": "number", "minimum": 0},
13
+ "queue_latency_ms": {"type": ["number", "null"], "minimum": 0},
14
+ "retry_count": {"type": "integer", "minimum": 0},
15
+ "arguments": {"type": "array", "maxItems": 20},
16
+ "arguments_truncated": {"type": "boolean"},
17
+ "tags": {"type": "array", "maxItems": 20},
18
+ "status": {"enum": ["completed", "failed"]},
19
+ "error_class": {"type": "string"}
20
+ }
21
+ }
@@ -0,0 +1,27 @@
1
+ # ADR-013 — Legacy Rails integration through public notifications
2
+
3
+ ## Status
4
+
5
+ Accepted for version 0.5.
6
+
7
+ ## Context
8
+
9
+ Rails 4.2 and 5.2 predate Zeitwerk as a universal loader and expose different optional components. Automatic capture must not couple the Ruby core to Rails, duplicate middleware or subscribers during reload, or collect high-risk framework payloads.
10
+
11
+ ## Decision
12
+
13
+ Load Rails integration explicitly through `chronos/rails`. Use a small Railtie initializer after application configuration, an idempotent installer, and public `ActiveSupport::Notifications.subscribe` callbacks. Detect constants and methods before using optional components. Emit request, query, job, and cache envelopes through the same sanitization and delivery pipeline as exceptions.
14
+
15
+ Allowlist metadata per notification. Never copy SQL text, binds, cache keys, mail bodies, job arguments, request bodies, response bodies, cookies, or authorization headers. Deduplicate controller and Rack exception hooks within request context.
16
+
17
+ ## Alternatives
18
+
19
+ ActiveSupport method patching and controller monkey patches were rejected because public notifications cover the required lifecycle with less version risk. Modeling timings as exception breadcrumbs was rejected because successful requests and SQL need independently deliverable metrics. Requiring Rails from the core was rejected because plain Ruby applications must remain framework-independent.
20
+
21
+ ## Positive consequences
22
+
23
+ The core remains independent, installation is reload-safe, optional components degrade cleanly, and Rails telemetry retains existing privacy and outage boundaries.
24
+
25
+ ## Negative consequences
26
+
27
+ Legacy notification payloads are less uniform than modern tracing APIs. Individual events add delivery volume until aggregation arrives in version 0.7. Synthetic Rails 4.2 controller exceptions may not retain the original exception class when only the public tuple is available.
@@ -0,0 +1,27 @@
1
+ # ADR-014 — Sidekiq context in the job envelope
2
+
3
+ ## Status
4
+
5
+ Accepted for version 0.6.
6
+
7
+ ## Context
8
+
9
+ Sidekiq clients and servers usually run in different processes. Chronos needs correlation without changing the worker's public arguments, opening per-job resources, or allowing nested Active Job and Sidekiq hooks to duplicate the same exception.
10
+
11
+ ## Decision
12
+
13
+ Use Sidekiq 4/5 public client and server middleware. Store a versioned `chronos` object beside `args`, containing only enqueue time, trace ID, and request ID. Never append metadata to `args`. On the server, establish a job execution context with a shared exception-deduplication map, emit bounded job telemetry, notify failures through `notify_once`, and re-raise the original exception. Do not install a global Sidekiq error handler.
14
+
15
+ Limit job arguments before the common sanitizer and serializer: 20 top-level arguments, 20 items per collection, depth four, and 512 bytes per string. Sanitization remains mandatory before queueing, retry, backlog, or transport.
16
+
17
+ ## Alternatives
18
+
19
+ Appending a context argument was rejected because it changes worker arity and application-visible data. A process-global context was rejected because concurrent jobs would leak correlation. A global error handler plus server middleware was rejected because it creates duplicate capture paths. Per-job delivery threads or Redis connections were rejected because lifecycle and resource ownership belong to Sidekiq and the existing bounded agent.
20
+
21
+ ## Positive consequences
22
+
23
+ Worker signatures remain compatible, cross-process correlation is bounded, nested integrations share deduplication, and Sidekiq retains retry semantics.
24
+
25
+ ## Negative consequences
26
+
27
+ The Sidekiq envelope grows slightly, only two context identifiers propagate, and automatic argument collection still requires application privacy review.
data/docs/architecture.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Chronos Ruby 0.4 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
3
+ Chronos Ruby 0.6 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
4
4
 
5
5
  ```mermaid
6
6
  flowchart TB
@@ -14,6 +14,11 @@ flowchart TB
14
14
  Rack[Integrations / Rack middleware] --> Facade
15
15
  Facade --> Context[Ports / ContextStore]
16
16
  Context --> ThreadLocal[Adapters / ThreadLocalContextStore]
17
+ Rails[Chronos Rails / Railtie and subscribers] --> Facade
18
+ Rails --> Notifications[ActiveSupport Notifications]
19
+ Sidekiq[Integrations / Sidekiq client and server middleware] --> Facade
20
+ Sidekiq --> Envelope[Versioned job-envelope context]
21
+ Application --> Telemetry[Core / TelemetryEvent]
17
22
  ```
18
23
 
19
24
  ## Boundaries
@@ -24,6 +29,8 @@ flowchart TB
24
29
  - Adapters contain Net::HTTP, TLS, and thread-local context behavior.
25
30
  - Internal contains private concurrency and diagnostic mechanisms.
26
31
  - Integrations contain optional framework entry points and never enter the domain boundary.
32
+ - `Chronos::Rails` contains the optional Railtie, installer, generator, and public-notification adapters.
33
+ - `Chronos::Integrations::Sidekiq` contains optional Sidekiq 4/5 middleware and bounded job normalization.
27
34
 
28
35
  The `Chronos` module is a thin facade. Rails, Rack, ActiveSupport, Sidekiq, and job libraries must not be required by the core.
29
36
 
@@ -35,6 +42,10 @@ The Rack middleware establishes a context-store scope, adds a bounded request br
35
42
 
36
43
  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.
37
44
 
45
+ Rails timings become immutable `TelemetryEvent` values. `CaptureTelemetry` applies the remote/local event policy, `TelemetrySerializer` sanitizes the allowlisted payload, and the resulting `SerializedEvent` enters the same delivery pipeline. Rails classes are loaded only through `chronos/rails`; the core never requires Rails or ActiveSupport.
46
+
47
+ Sidekiq client middleware writes a versioned, allowlisted trace/request context beside the job's public arguments. Server middleware restores a job scope, limits arguments and tags, emits one job telemetry event, and routes a failure through the existing notice pipeline before re-raising it. Sidekiq retains queue, retry, thread, and connection lifecycle ownership.
48
+
38
49
  ## Failure policy
39
50
 
40
51
  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.
@@ -4,14 +4,20 @@ 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 | 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 |
7
+ | 2.2.10 | Rails 4.2 | Best effort | Core CI, Rails 4.2 example, dedicated smoke gate |
8
+ | 2.3.8 | Rails 4.2 / 5.0 | Best effort | Core CI and feature-detection contract; dedicated app gate incomplete |
9
+ | 2.4.10 | Rails 4.2 / 5.0 | Best effort | Core CI and feature-detection contract; dedicated app gate incomplete |
10
+ | 2.5.9 | Rails 5.2 | Best effort | Core CI, Rails 5.2 example, dedicated smoke gate |
11
+ | 2.6.10 | Rails 5.2 | Best effort | Core CI and feature-detection contract; dedicated app gate incomplete |
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.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.
14
+ | Ruby | Sidekiq integration | Status | Evidence |
15
+ |---|---|---|---|
16
+ | 2.2.10–2.6 | Sidekiq 4 / 5 | Best effort | Middleware unit/integration contracts; dedicated real-gem matrix pending |
17
+
18
+ Version 0.5 includes Rails 4.2 and 5.2 applications plus a dedicated matrix, but this document conservatively keeps the combinations at `Best effort` until all release-gate evidence, including fake-server payload validation, is green. Rails 5.0 uses the same feature-detected public APIs but does not yet have its own example application.
19
+
20
+ Version `0.6.0.pre.1` uses the public Sidekiq 4/5 middleware signatures and remains `Best effort` until dedicated jobs exercise both real gem versions across their valid Ruby combinations.
15
21
 
16
22
  Status meanings:
17
23
 
@@ -38,12 +38,16 @@
38
38
  | `circuit_reset_timeout` | Optional | `30.0` | Seconds before one half-open delivery probe |
39
39
  | `remote_configuration` | Optional | `true` | Accepts only the documented bounded remote policy fields |
40
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 |
41
+ | `sampling_rate` | Optional | `1.0` | Local upper bound for event sampling |
42
+ | `enabled_event_types` | Optional | exception, request, query, job, cache | Local allowlist for supported event envelopes |
43
43
  | `max_remote_send_interval` | Optional | `60.0` | Local upper bound for remotely requested send spacing |
44
44
  | `context_store` | Optional | `:thread_local` | `:thread_local` or an object implementing `get`, `set`, `clear`, and `with_context` |
45
45
  | `breadcrumb_capacity` | Optional | `20` | Positive count of newest breadcrumbs retained per execution |
46
46
  | `breadcrumb_max_bytes` | Optional | `2048` | Maximum bytes per normalized breadcrumb; minimum `128` |
47
+ | `rails_enabled` | Optional | `true` | Enables automatic Rails middleware and subscribers |
48
+ | `rails_capture_in_console` | Optional | `false` | Enables automatic integration while Rails console is loaded |
49
+ | `rails_capture_in_test` | Optional | `false` | Enables automatic integration in the Rails test environment |
50
+ | `rails_capture_user_agent` | Optional | `false` | Adds the Rack user agent to request context |
47
51
 
48
52
  ```ruby
49
53
  Chronos.configure do |config|
@@ -61,6 +65,8 @@ Chronos.configure do |config|
61
65
  config.circuit_failure_threshold = 5
62
66
  config.context_store = :thread_local
63
67
  config.breadcrumb_capacity = 20
68
+ config.rails_capture_in_test = false
69
+ config.rails_capture_in_console = false
64
70
  end
65
71
  ```
66
72
 
@@ -1,6 +1,6 @@
1
1
  # Data collected
2
2
 
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.
3
+ Version 0.6 emits exception, request, query, job, and cache events. 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
  |---|---|---|
@@ -21,8 +21,20 @@ Version 0.4 emits exception events submitted manually or captured by the optiona
21
21
  | Rack parameters | Collected only when already parsed | Explicit, Rack, or Action Dispatch parameter hashes |
22
22
  | Response size | `Content-Length` when present | Rack response headers |
23
23
  | Breadcrumbs | Explicit and bounded | Application and Chronos integration |
24
+ | Rails controller/action, status, method, query-free path, normalized route, duration | Collected with Rails integration | `process_action.action_controller` |
25
+ | Sanitized Rails parameters | Collected with Rails integration | Public controller notification payload |
26
+ | View template basename and duration | Collected with Rails integration | `render_template.action_view` |
27
+ | SQL operation name, cached flag, duration | Collected; SQL text omitted | `sql.active_record` |
28
+ | Mailer/action and duration | Collected; message content omitted | `deliver.action_mailer` |
29
+ | Active Job class, queue, duration | Collected when available; arguments omitted | `perform.active_job` |
30
+ | Sidekiq class, queue, JID, retry count, duration, status, error class | Collected with optional middleware | Sidekiq job envelope and local clock |
31
+ | Sidekiq queue latency | Collected when enqueue time is available | Sidekiq or Chronos envelope timestamp |
32
+ | Sidekiq arguments | Collected, sanitized, and bounded | First 20 job arguments; collections/depth/strings limited |
33
+ | Sidekiq tags | Collected and bounded | Job payload or public worker options |
34
+ | Sidekiq trace/request IDs | Propagated when present; trace generated otherwise | Chronos job-envelope metadata |
35
+ | Cache operation, store, hit flag, duration | Collected; key/value omitted | ActiveSupport cache notifications |
24
36
 
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.
37
+ The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, source code, raw SQL, SQL bind values, database rows, cache keys/values, mail bodies/recipients, or installed gems. Sidekiq JIDs and bounded arguments are the explicit version 0.6 exception to the earlier job-ID/argument exclusion.
26
38
 
27
39
  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.
28
40
 
@@ -0,0 +1,60 @@
1
+ # Rails 4.2 and 5.2 legacy integration
2
+
3
+ Version 0.5 adds an explicit Rails entry point:
4
+
5
+ ```ruby
6
+ require "chronos/rails"
7
+ ```
8
+
9
+ `Chronos::Rails::Railtie` registers one initializer after `load_config_initializers`. It requires no Zeitwerk and delegates to an idempotent installer. The installer uses feature detection, installs `Chronos::Integrations::Rack::Middleware` once per application, and registers notification subscribers once per `ActiveSupport::Notifications` bus.
10
+
11
+ ## Installation generator
12
+
13
+ Run:
14
+
15
+ ```bash
16
+ rails generate chronos:install
17
+ ```
18
+
19
+ The generator creates `config/initializers/chronos.rb`. The template reads only explicitly named `CHRONOS_*` variables, uses `Rails.env`, adopts `Rails.logger` when available, and disables automatic integration in test and console by default. It invokes the idempotent installer as a fallback for applications that disabled Bundler auto-require, so the Railtie and initializer paths cannot create duplicate hooks. It never scans all environment variables or modifies routes and application classes.
20
+
21
+ ## Captured integrations
22
+
23
+ The public `ActiveSupport::Notifications.subscribe` API is used for:
24
+
25
+ | Notification | Chronos event | Allowlisted data |
26
+ |---|---|---|
27
+ | `process_action.action_controller` | `request` | controller, action, status, method, query-free path, normalized route, duration, sanitized parameters |
28
+ | `render_template.action_view` | `request` with `kind=view` | template basename and duration |
29
+ | `sql.active_record` | `query` | operation name, cached flag, and duration |
30
+ | `deliver.action_mailer` | `job` with `kind=mailer` | mailer, action, and duration |
31
+ | `perform.active_job` | `job` | job class, queue, and duration, when Active Job is available |
32
+ | cache read/write/hit notifications | `cache` | operation, store, hit flag, and duration |
33
+
34
+ Raw SQL, binds, cache keys, mail recipients and bodies, job IDs and arguments, request/response bodies, cookies, and authorization headers are not copied. Template identifiers are reduced to their basename. Every event passes through `Sanitizer`, `SafeSerializer`, the bounded queue, retry policy, circuit breaker, and memory backlog.
35
+
36
+ ## Controller exceptions and deduplication
37
+
38
+ When `process_action.action_controller` exposes `exception_object`, the original exception is captured. Rails versions that expose only the public exception tuple produce a bounded synthetic `RuntimeError` containing the supplied message. A request-scoped deduplicator prevents the same error from being reported again by the Rack middleware. The host application continues to receive its original exception semantics.
39
+
40
+ ## Configuration
41
+
42
+ ```ruby
43
+ Chronos.configure do |config|
44
+ # connection settings omitted
45
+ config.rails_enabled = true
46
+ config.rails_capture_in_test = false
47
+ config.rails_capture_in_console = false
48
+ config.rails_capture_user_agent = false
49
+ end
50
+ ```
51
+
52
+ Setting `rails_enabled = false` prevents middleware and subscribers from being installed. Test and console switches affect only automatic Rails integration; explicit manual Chronos calls retain their normal configuration behavior.
53
+
54
+ ## Compatibility evidence
55
+
56
+ The repository contains independent applications under `examples/rails-4.2` and `examples/rails-5.2`. Their smoke scripts execute a successful request, controller exception, SQL query, view, cache access, inline Active Job, Action Mailer, flush, and shutdown. The dedicated legacy Rails workflow is the release gate; a framework/runtime pair must not be labeled `Supported` until that job and fake-server payload validation pass.
57
+
58
+ ## Limits
59
+
60
+ Version 0.5 emits individual bounded timings. It does not aggregate APM metrics, capture SQL text, or calculate query fingerprints. Sidekiq support is a separate optional integration beginning in version 0.6; full Active Job propagation remains a later 0.6 increment.
@@ -0,0 +1,23 @@
1
+ # Sidekiq 4/5 legacy integration
2
+
3
+ Version `0.6.0.pre.1` starts the legacy jobs line with optional Sidekiq 4 and 5 client and server middleware. Load it after declaring Sidekiq:
4
+
5
+ ```ruby
6
+ gem "sidekiq", "~> 5.0"
7
+ gem "chronos-ruby", "0.6.0.pre.1", :require => "chronos/sidekiq"
8
+ ```
9
+
10
+ `chronos/sidekiq` installs middleware through the public `configure_client` and `configure_server` APIs. It does nothing when Sidekiq is unavailable, and the core gem never requires Sidekiq. Installation adds no Chronos thread or Redis/database connection per job; delivery continues through the agent's existing fixed worker pool.
11
+
12
+ The client middleware adds a top-level `chronos` metadata object to the Sidekiq envelope. It never changes the public `args` array. Only `trace_id` and `request_id` propagate, with schema version and enqueue time. The server scopes that context for the worker, measures duration and calculable queue latency, and emits one `job` event.
13
+
14
+ Collected Sidekiq fields are worker class, queue, JID, retry count, duration, queue latency, status, error class, bounded tags, and bounded arguments. At most 20 top-level arguments, 20 values per nested collection, four levels, and 512 bytes per string are traversed. Arguments then pass through the standard sensitive-key/content sanitizer before queueing. Limits reduce exposure and resource use but do not make unnecessary personal data safe to collect.
15
+
16
+ On failure, the server middleware records failed-job telemetry, invokes `notify_once`, and re-raises the identical exception so Sidekiq retains ownership of retries and failure handlers. A shared execution marker prevents a nested Active Job hook and the Sidekiq middleware from sending the same exception twice. Chronos does not install an additional global Sidekiq error handler.
17
+
18
+ Known limitations for this first 0.6 prerelease:
19
+
20
+ - the compatibility contract uses Sidekiq 4/5 public middleware signatures; dedicated real Sidekiq matrix jobs must pass before status becomes `Supported`;
21
+ - Active Job propagation, Resque, and Delayed Job are subsequent 0.6 increments;
22
+ - argument collection is automatic in this prerelease; applications should avoid placing secrets or unnecessary personal data in job arguments;
23
+ - context propagation is limited to trace and request identifiers.
@@ -0,0 +1,9 @@
1
+ # Framework telemetry events
2
+
3
+ `Chronos::Core::TelemetryEvent` and `Chronos::Core::TelemetrySerializer` extend the v1 envelope to `request`, `query`, `job`, and `cache` events. These values exist so framework adapters do not model operational timings as fake exceptions.
4
+
5
+ Telemetry has the same `schema_version`, event ID, timestamps, project, environment, service, runtime, context, sanitization, payload-size limit, idempotency header, asynchronous queue, retry, circuit breaker, and memory backlog used by exception events. Unsupported types are rejected locally.
6
+
7
+ `Chronos.record_event` is the narrow integration entry point. Application code should prefer documented higher-level APIs; its payload is allowlisted by each bundled integration and then sanitized. Remote configuration can reduce or disable any locally enabled telemetry type but cannot enable a type excluded by local configuration.
8
+
9
+ Version 0.5 intentionally sends individual framework events. Aggregation, percentiles, slow-query analysis, and transaction breakdowns remain part of version 0.7.
data/docs/performance.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Performance
2
2
 
3
- Performance is a functional requirement, but version 0.4 makes no unverified speed claim.
3
+ Performance is a functional requirement, but version 0.6 makes no unverified speed claim.
4
4
 
5
5
  Current controls:
6
6
 
@@ -15,11 +15,25 @@ Current controls:
15
15
  - retry count, delay, jitter, and `Retry-After` are capped;
16
16
  - the circuit breaker suppresses requests during sustained failure;
17
17
  - retry backlog capacity is fixed and may be disabled;
18
- - shutdown and flush have caller-controlled timeouts.
18
+ - shutdown and flush have caller-controlled timeouts;
19
19
  - request context and breadcrumbs have fixed structural and byte limits;
20
20
  - Rack middleware never consumes request or response bodies.
21
+ - Rails subscribers copy only small allowlisted field sets and never copy raw SQL or job arguments.
22
+ - Sidekiq middleware creates no per-job thread or connection and bounds arguments, collections, nesting, strings, and tags before telemetry capture.
21
23
 
22
- Run the scripts under `benchmarks/` and record Ruby version, operating system, CPU, warmup, iteration count, median, and dispersion before publishing results. `benchmarks/filtering.rb` measures privacy filtering, `benchmarks/retry_backlog.rb` measures fixed-memory outage behavior, and `benchmarks/request_overhead.rb` compares a successful direct Rack-protocol call with the middleware path.
24
+ Run the scripts under `benchmarks/` and record Ruby version, operating system, CPU, warmup, iteration count, median, and dispersion before publishing results. `benchmarks/filtering.rb` measures privacy filtering, `benchmarks/retry_backlog.rb` measures fixed-memory outage behavior, `benchmarks/request_overhead.rb` compares Rack-protocol calls, and `benchmarks/rails_notifications.rb` isolates subscriber normalization overhead.
25
+
26
+ ## Version 0.5 Rails subscriber benchmark
27
+
28
+ Run:
29
+
30
+ ```bash
31
+ ITERATIONS=100000 bundle _1.17.3_ exec ruby benchmarks/rails_notifications.rb
32
+ ```
33
+
34
+ This synthetic benchmark invokes the SQL notification normalizer with a no-op notifier. It verifies that raw SQL and binds are not copied and measures local allowlist normalization only; it does not include Rails dispatch, serialization, queueing, or network delivery.
35
+
36
+ A local diagnostic run on 2026-07-20 used Ruby 2.2.10 on macOS arm64 and 10,000 iterations. It completed in 0.041232 seconds, or approximately 4.123 microseconds per normalization. This single run has no warmup, median, or dispersion and is not a production performance claim.
23
37
 
24
38
  ## Version 0.2 development measurement
25
39
 
@@ -63,3 +77,13 @@ ITERATIONS=100000 bundle _1.17.3_ exec ruby benchmarks/request_overhead.rb
63
77
  ```
64
78
 
65
79
  A local diagnostic run on 2026-07-20 used Ruby 2.2.10 on macOS arm64 and 10,000 successful Rack-protocol calls. The direct application calls took 0.013272 seconds and middleware calls took 0.539758 seconds, for an estimated 52.649 microseconds of middleware work per request. The fixture uses no network, error capture, or response-body enumeration. This is a single development measurement, not a production latency claim; controlled warmup, median, and dispersion remain required before publishing a performance claim.
80
+
81
+ ## Version 0.6 Sidekiq middleware benchmark
82
+
83
+ Run:
84
+
85
+ ```bash
86
+ ITERATIONS=100000 bundle _1.17.3_ exec ruby benchmarks/sidekiq_middleware.rb
87
+ ```
88
+
89
+ The benchmark executes client context injection and successful server normalization with a no-op notifier. It includes bounded argument handling and clock reads, but excludes Sidekiq/Redis, serialization, queueing, and network delivery. Record a controlled result before making a performance claim.
data/docs/privacy-lgpd.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Privacy and LGPD
2
2
 
3
- Version 0.4 sanitizes every exception event before JSON serialization, queueing, retry backlog, or transport. This reduces accidental exposure, but the host application remains responsible for lawful purpose, minimization, access control, retention, and responses to data-subject requests.
3
+ Version 0.6 sanitizes exception and framework telemetry before JSON serialization, queueing, retry backlog, or transport. This reduces accidental exposure, but the host application remains responsible for lawful purpose, minimization, access control, retention, and responses to data-subject requests.
4
4
 
5
5
  ## Default policy
6
6
 
@@ -13,9 +13,18 @@ Version 0.4 sanitizes every exception event before JSON serialization, queueing,
13
13
  | Payment-card candidates that pass the Luhn check | Replaced with `[FILTERED_CARD]` |
14
14
  | IPv4 addresses | Last octet replaced with `0` |
15
15
  | Unknown Ruby objects | Represented by class name without calling application serialization |
16
- | Request/response bodies, raw query strings, cookies, authorization headers, SQL binds, environment variables | Never collected automatically in version 0.4 |
16
+ | Request/response bodies, raw query strings, cookies, authorization headers, raw SQL/binds, cache values, mail bodies, environment variables | Never collected automatically |
17
+ | Sidekiq arguments | Collected automatically, limited before sanitization, then redacted by the common policy |
17
18
 
18
- The retry backlog exists only in process memory, accepts only `SerializedEvent`, has a fixed capacity, and disappears on process exit. Version 0.4 does not persist telemetry to disk.
19
+ The retry backlog exists only in process memory, accepts only `SerializedEvent`, has a fixed capacity, and disappears on process exit. Version 0.6 does not persist telemetry to disk.
20
+
21
+ ## Rails telemetry
22
+
23
+ Rails subscribers use per-notification allowlists. SQL events retain only the operation name, cached flag, and duration; cache events omit key and value; mailer events omit addresses and content; Active Job events omit job ID and arguments; view identifiers are reduced to basenames. Controller parameters are sanitized by the normal payload pipeline before queueing.
24
+
25
+ ## Sidekiq jobs
26
+
27
+ The optional Sidekiq middleware is the only version 0.6 integration that automatically reads job arguments. It traverses at most 20 top-level arguments, 20 items per nested collection, four levels, and 512 bytes per string. These structural limits run before the common key and content sanitizer. Trace propagation contains only trace and request identifiers. Do not place credentials or unnecessary personal, health, or financial data in job arguments; configure application-specific blocklist keys and audit representative synthetic payloads before enabling production delivery.
19
28
 
20
29
  ## Rack context and breadcrumbs
21
30
 
@@ -14,7 +14,23 @@ Check DNS, TLS certificates, credentials, HTTP status, proxy configuration, and
14
14
 
15
15
  ## Rack exception is not captured
16
16
 
17
- Confirm that `Chronos.configure` runs before the middleware handles requests and that the middleware wraps the application component that raises. Version 0.4 captures exceptions raised by the initial downstream Rack call; an exception raised later while a server enumerates a streaming response body is outside this release. The original exception is always re-raised, so the server log should still show it.
17
+ Confirm that `Chronos.configure` runs before the middleware handles requests and that the middleware wraps the application component that raises. Version 0.5 captures exceptions raised by the initial downstream Rack call; an exception raised later while a server enumerates a streaming response body is outside this release. The original exception is always re-raised, so the server log should still show it.
18
+
19
+ ## Rails middleware or telemetry is missing
20
+
21
+ Require `chronos/rails` from the generated initializer and confirm it runs before requests. Automatic integration is disabled by default in Rails test and console; set `rails_capture_in_test` or `rails_capture_in_console` explicitly when validating those environments. `rails_enabled = false` disables all automatic Rails hooks. Active Job is subscribed only when its constant is available during installation.
22
+
23
+ ## Rails telemetry contains no SQL or cache key
24
+
25
+ This is intentional. Version 0.5 records SQL operation name/duration and cache operation/store/hit status without raw statements, binds, keys, or values. These omissions are privacy and cardinality boundaries, not capture failures.
26
+
27
+ ## Sidekiq telemetry is missing
28
+
29
+ Require `chronos/sidekiq` after Sidekiq is available and configure Chronos before jobs run. The entry point uses Sidekiq's public client/server middleware configuration and remains optional. Requiring only `chronos` or `chronos/rails` does not load Sidekiq. A job already enqueued before client middleware installation may lack propagated trace context, but server timing and failure capture can still run.
30
+
31
+ ## A failed Sidekiq job appears twice
32
+
33
+ The bundled middleware does not install a global error handler and uses `notify_once` inside a shared job scope. Check for application-installed Chronos calls or third-party global handlers outside that scope. Keep a single bundled server middleware entry in the Sidekiq chain.
18
34
 
19
35
  ## Context appears missing
20
36