chronos-ruby 0.9.0.pre.1 → 0.9.0.pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +13 -5
- data/SECURITY.md +2 -0
- data/docs/adr/ADR-018-pre-1.0-hardening.md +9 -0
- data/docs/compatibility.md +5 -2
- data/docs/configuration.md +5 -1
- data/docs/data-collected.md +3 -2
- data/docs/deprecation-policy.md +7 -0
- data/docs/migration-from-airbrake.md +18 -0
- data/docs/modules/active-job.md +7 -0
- data/docs/modules/ignore-rules.md +11 -0
- data/docs/modules/rails-legacy.md +3 -3
- data/docs/modules/sidekiq-legacy.md +1 -1
- data/docs/performance.md +11 -0
- data/docs/protocol-v1.md +5 -0
- data/docs/release-1.0-readiness.md +18 -0
- data/docs/security-review.md +16 -0
- data/docs/semver.md +11 -0
- data/lib/chronos/agent.rb +10 -1
- data/lib/chronos/application/capture_exception.rb +7 -0
- data/lib/chronos/application/ignore_policy.rb +54 -0
- data/lib/chronos/configuration/validation.rb +16 -1
- data/lib/chronos/configuration.rb +3 -1
- data/lib/chronos/integrations/active_job.rb +119 -0
- data/lib/chronos/rails/installer.rb +7 -0
- data/lib/chronos/rails/notifications_subscriber.rb +29 -1
- data/lib/chronos/rails.rb +1 -0
- data/lib/chronos/version.rb +1 -1
- data/lib/chronos.rb +9 -1
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba6053cbe8155fba7fa99c0adf09db9e0aa38d33314b2355c49f1760233183fe
|
|
4
|
+
data.tar.gz: 138ee6958622e5e03d24863c553066c6368459fe423731950bcfcc63bfb12e02
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58b5255dacdb15e3df4147cceb563e247989fa8cc2e72c23e79166e51c772ffc9ba0e87120c025de5d20cff04be21a7625cafb39d9f47793f82d156bdcb5cf09
|
|
7
|
+
data.tar.gz: 981f49d6f3d12314278456338e975797c27df3f7d93217eb2a8c9df630faa6081a683159b7694e0963cd7f9fa4ed6fae5f095b697a1ea9ce1c3159f388a5b36d
|
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,39 @@ All notable changes are documented here. The project follows Semantic Versioning
|
|
|
12
12
|
- legacy CI now resolves Bundler 1.17.3 through `Gem.bin_path` on RubyGems versions that do not support the `_version_` executable selector.
|
|
13
13
|
- documentation verification now reads source and Markdown files explicitly as UTF-8 on legacy container locales.
|
|
14
14
|
|
|
15
|
+
## [0.9.0.pre.2] - 2026-07-21
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- bounded startup and runtime exception ignore rules through `config.ignore_rules` and `Chronos.ignore_if`;
|
|
20
|
+
- Active Job trace/request propagation without modifying public arguments, plus adapter, job/provider IDs, attempts, status, and failure telemetry;
|
|
21
|
+
- real-gem Sidekiq 4.2/5.2 Docker compatibility jobs;
|
|
22
|
+
- repeatable Rack comparison and fake-endpoint load gates;
|
|
23
|
+
- corrected `script/test_all` so `pipefail` no longer turns installed Rubies into false skips and an empty matrix fails;
|
|
24
|
+
- Airbrake migration, SemVer, deprecation, protocol stability, security review, and 1.0 readiness documentation.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- version advanced to `0.9.0.pre.2` instead of `1.0.0` because the full external legacy matrix has not yet supplied zero-failure evidence;
|
|
29
|
+
- Active Job identifiers are now documented collected fields and propagated context is bounded to trace/request IDs.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- the Sidekiq 5 compatibility smoke now resolves the shared Sidekiq 4/5 fixture inside `examples/` instead of the nonexistent `/app/sidekiq-4` path.
|
|
34
|
+
|
|
35
|
+
### Security
|
|
36
|
+
|
|
37
|
+
- local ignore rules are bounded to 100, contain callback failures, and cannot be installed remotely;
|
|
38
|
+
- fake-endpoint load validation rejects payloads containing the secret authentication key;
|
|
39
|
+
- package signing remains deferred until a trusted key lifecycle exists; protected publishing credentials remain required.
|
|
40
|
+
|
|
41
|
+
### Known limitations
|
|
42
|
+
|
|
43
|
+
- compatibility stays `Best effort` until all new and existing GitHub Actions matrix jobs pass without skips;
|
|
44
|
+
- end-of-life runtimes and framework dependencies retain their upstream security risk;
|
|
45
|
+
- Active Job adapters that bypass standard serialization/execution hooks need dedicated evidence;
|
|
46
|
+
- comparative results are not a performance claim and must be published with controlled environment metadata.
|
|
47
|
+
|
|
15
48
|
## [0.9.0.pre.1] - 2026-07-21
|
|
16
49
|
|
|
17
50
|
### Added
|
data/README.md
CHANGED
|
@@ -33,7 +33,7 @@ See [Compatibility](docs/compatibility.md).
|
|
|
33
33
|
The current public build is a pre-release. Add its exact version to the application's `Gemfile`:
|
|
34
34
|
|
|
35
35
|
```ruby
|
|
36
|
-
gem "chronos-ruby", "0.9.0.pre.
|
|
36
|
+
gem "chronos-ruby", "0.9.0.pre.2"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Install with a Bundler version compatible with the application. For the oldest supported runtime:
|
|
@@ -54,7 +54,7 @@ gem install chronos-ruby --pre
|
|
|
54
54
|
Version 0.5 exposes Rails support explicitly, keeping Rails and ActiveSupport out of plain Ruby applications:
|
|
55
55
|
|
|
56
56
|
```ruby
|
|
57
|
-
gem "chronos-ruby", "0.9.0.pre.
|
|
57
|
+
gem "chronos-ruby", "0.9.0.pre.2", :require => "chronos/rails"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Generate the initializer with:
|
|
@@ -166,7 +166,15 @@ Chronos.configure do |config|
|
|
|
166
166
|
end
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
Version `0.9.0.pre.2` adds bounded local rules after configuration:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
Chronos.ignore_if do |notice|
|
|
173
|
+
notice.exception_class == "SomeExpectedError"
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Rules receive an immutable normalized notice, run before serialization/queueing, and must return exactly `true` to discard. The default limit is 20 and the hard configurable maximum is 100. A failing rule is contained. See [Bounded local ignore rules](docs/modules/ignore-rules.md).
|
|
170
178
|
|
|
171
179
|
## Performance monitoring
|
|
172
180
|
|
|
@@ -193,12 +201,12 @@ Version `0.6.0.pre.1` adds optional Sidekiq 4/5 middleware:
|
|
|
193
201
|
|
|
194
202
|
```ruby
|
|
195
203
|
gem "sidekiq", "~> 5.0"
|
|
196
|
-
gem "chronos-ruby", "0.9.0.pre.
|
|
204
|
+
gem "chronos-ruby", "0.9.0.pre.2", :require => "chronos/sidekiq"
|
|
197
205
|
```
|
|
198
206
|
|
|
199
207
|
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).
|
|
200
208
|
|
|
201
|
-
|
|
209
|
+
When Active Job is available, the Rails integration propagates only bounded trace/request identifiers in a namespaced serialized field without changing job arguments. It records adapter, job/provider IDs, class, queue, attempts, duration, status, and error class, and captures a supplied failure once. See [Active Job legacy integration](docs/modules/active-job.md).
|
|
202
210
|
|
|
203
211
|
## External HTTP, cache, and dependencies
|
|
204
212
|
|
data/SECURITY.md
CHANGED
|
@@ -7,3 +7,5 @@ Never include live project keys, customer payloads, personal data, or production
|
|
|
7
7
|
The Chronos 0.x line provides technical compatibility with end-of-life Ruby versions. It cannot provide security maintenance for the Ruby runtime, OpenSSL, operating system, Rails, or other dependencies used by the host application.
|
|
8
8
|
|
|
9
9
|
Security fixes in the agent will be assessed for backporting to maintained Chronos version lines. No response-time commitment is made before a formal security process is published.
|
|
10
|
+
|
|
11
|
+
The current prerelease review and residual risks are recorded in [docs/security-review.md](docs/security-review.md). Release publishing uses a protected GitHub environment and a scoped RubyGems key. Package signing is deferred until the project has a trusted certificate issuance, rotation, revocation, and offline-key process; do not add a private signing key to the repository or workflow secrets without that process.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# ADR-018: Pre-1.0 hardening gates
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
Keep the release at `0.9.0.pre.2` while any mandatory 1.0 evidence remains external or incomplete. Add bounded local ignore rules, Active Job envelope propagation, real Sidekiq compatibility jobs, repeatable comparative/load benchmarks, and explicit release/security policies.
|
|
6
|
+
|
|
7
|
+
## Consequences
|
|
8
|
+
|
|
9
|
+
The stable API is not promised before the full legacy matrix passes. Active Job gains one namespaced serialized field without changing arguments. Application callbacks remain bounded in count but their execution cost belongs to the application. Package checksums are used until a trusted signing lifecycle is available.
|
data/docs/compatibility.md
CHANGED
|
@@ -13,11 +13,14 @@ Chronos Ruby 0.x is the legacy line. Technical compatibility does not make an en
|
|
|
13
13
|
|
|
14
14
|
| Ruby | Sidekiq integration | Status | Evidence |
|
|
15
15
|
|---|---|---|---|
|
|
16
|
-
| 2.2.10
|
|
16
|
+
| 2.2.10 | Sidekiq 4.2.10 | Best effort | Unit/integration contracts and dedicated real-gem Docker job |
|
|
17
|
+
| 2.5.9 | Sidekiq 5.2.10 | Best effort | Unit/integration contracts and dedicated real-gem Docker job |
|
|
17
18
|
|
|
18
19
|
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
|
|
|
20
|
-
Version `0.
|
|
21
|
+
Version `0.9.0.pre.2` adds dedicated Sidekiq 4.2.10 and 5.2.10 real-gem jobs. Status remains `Best effort` until both external jobs pass on the release candidate.
|
|
22
|
+
|
|
23
|
+
Active Job propagation uses the standard `serialize`, `deserialize`, and `perform_now` extension points with a namespaced bounded field. Rails 4.2/5.2 example jobs and unit contracts provide evidence; the complete external matrix must still pass before stable support is declared.
|
|
21
24
|
|
|
22
25
|
Version `0.7.0.pre.1` keeps the same Ruby/Rails matrix and implements APM aggregation without modern concurrency or SQL-parser dependencies. Its compatibility remains `Best effort` until request/SQL/job aggregate payloads pass the dedicated fake-server gates for every listed runtime.
|
|
23
26
|
|
data/docs/configuration.md
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
| `allowlist_keys` | Optional | `[]` | Explicit key-name exceptions; content detection still runs |
|
|
33
33
|
| `hash_keys` | Optional | `[]` | Scalar identifier keys replaced by scoped SHA-256 values |
|
|
34
34
|
| `filters` | Optional | `[]` | Callable application-specific privacy filters |
|
|
35
|
+
| `ignore_rules` | Optional | `[]` | Callable local exception rules evaluated before serialization |
|
|
36
|
+
| `max_ignore_rules` | Optional | `20` | Maximum retained local rules; range 1–100 |
|
|
35
37
|
| `anonymize_ip` | Recommended | `true` | Replaces the final octet of supplied IPv4 addresses |
|
|
36
38
|
| `max_retries` | Optional | `3` | Maximum retry attempts after the first delivery failure |
|
|
37
39
|
| `retry_base_interval` | Optional | `0.5` | Initial exponential-backoff delay in seconds |
|
|
@@ -83,6 +85,8 @@ Chronos.configure do |config|
|
|
|
83
85
|
config.workers = 1
|
|
84
86
|
config.blocklist_keys += [:medical_record, /bank_account/i]
|
|
85
87
|
config.hash_keys += [:customer_id]
|
|
88
|
+
config.ignore_rules << proc { |notice| notice.exception_class == "SomeExpectedError" }
|
|
89
|
+
config.max_ignore_rules = 20
|
|
86
90
|
config.max_retries = 3
|
|
87
91
|
config.backlog_size = 100
|
|
88
92
|
config.circuit_failure_threshold = 5
|
|
@@ -106,7 +110,7 @@ Chronos.configure do |config|
|
|
|
106
110
|
end
|
|
107
111
|
```
|
|
108
112
|
|
|
109
|
-
Privacy matcher collections and
|
|
113
|
+
Privacy matcher collections, filters, and startup ignore rules are copied into the immutable runtime snapshot. Matchers must be String, Symbol, or Regexp values, and every filter/rule must respond to `call`. Runtime rules added through `Chronos.ignore_if` cannot exceed `max_ignore_rules`.
|
|
110
114
|
|
|
111
115
|
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.
|
|
112
116
|
|
data/docs/data-collected.md
CHANGED
|
@@ -26,7 +26,8 @@ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and agg
|
|
|
26
26
|
| View template basename and duration | Collected with Rails integration | `render_template.action_view` |
|
|
27
27
|
| SQL operation name, cached flag, duration | Collected; SQL text omitted | `sql.active_record` |
|
|
28
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` |
|
|
29
|
+
| Active Job adapter, job/provider IDs, class, queue, attempts, duration, status, error class | Collected when available; arguments omitted | serialization and `perform.active_job` |
|
|
30
|
+
| Active Job trace/request IDs | Propagated in a bounded namespaced field | Current Chronos context |
|
|
30
31
|
| Sidekiq class, queue, JID, retry count, duration, status, error class | Collected with optional middleware | Sidekiq job envelope and local clock |
|
|
31
32
|
| Sidekiq queue latency | Collected when enqueue time is available | Sidekiq or Chronos envelope timestamp |
|
|
32
33
|
| Sidekiq arguments | Collected, sanitized, and bounded | First 20 job arguments; collections/depth/strings limited |
|
|
@@ -48,7 +49,7 @@ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and agg
|
|
|
48
49
|
| Event release, revision, deploy ID, environment, service, region, instance | Present as bounded correlation; values optional | Explicit immutable configuration or normalized deploy payload |
|
|
49
50
|
| Deploy environment, revision, version, repository, actor, deploy ID, service, region, instance | Explicit synchronous deploy API | Application/deployment integration arguments |
|
|
50
51
|
|
|
51
|
-
The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, Git state, source code, raw SQL, SQL bind values, database rows, raw cache keys/values, mail bodies/recipients, gem paths, or lockfile contents. Sidekiq JIDs/arguments, loaded gem names/versions, and deploy fields are documented integration data.
|
|
52
|
+
The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, Git state, source code, raw SQL, SQL bind values, database rows, raw cache keys/values, mail bodies/recipients, Active Job arguments, gem paths, or lockfile contents. Active Job IDs, Sidekiq JIDs/arguments, loaded gem names/versions, and deploy fields are documented integration data.
|
|
52
53
|
|
|
53
54
|
APM dimensions never include user ID, job ID, raw URL, exception message, bind value, or cache key. Normalized routes replace common numeric/UUID segments. Normalized SQL can retain schema, table, and column identifiers; review those identifiers as part of the privacy audit.
|
|
54
55
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Deprecation policy
|
|
2
|
+
|
|
3
|
+
After `1.0.0`, a public API scheduled for removal remains available for at least one minor release and normally six months. The warning, replacement, first deprecated version, and earliest removal version must be documented in the changelog and migration material.
|
|
4
|
+
|
|
5
|
+
Warnings are emitted at most once per process through the configured safe logger and must not include application payloads. Security fixes, behavior that can expose secrets, and upstream runtime incompatibilities may require faster action; the security advisory and changelog must explain the exception. Ruby/Rails support changes are recorded in `docs/compatibility.md` before removal.
|
|
6
|
+
|
|
7
|
+
Prereleases may revise APIs without the full stable window, but every incompatible revision must remain explicit. Version `0.9.0.pre.2` introduces no removal.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Migration from Airbrake
|
|
2
|
+
|
|
3
|
+
Version `0.9.0.pre.2` provides a staged migration path. Run both agents only long enough to compare delivery, then remove Airbrake to avoid duplicate reports and overhead.
|
|
4
|
+
|
|
5
|
+
| Airbrake concept | Chronos equivalent |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `Airbrake.configure` credentials/host | `Chronos.configure` with `project_id`, `project_key`, and `host` |
|
|
8
|
+
| `Airbrake.notify(error, params)` | `Chronos.notify(error, :parameters => params)` |
|
|
9
|
+
| `Airbrake.notify_sync` | `Chronos.notify_sync` |
|
|
10
|
+
| ignore/filter callbacks | `Chronos.ignore_if` and `config.filters` |
|
|
11
|
+
| deploy notification | `Chronos.notify_deploy` |
|
|
12
|
+
| Rack/Rails integration | `require "chronos/rails"` or Chronos Rack middleware |
|
|
13
|
+
|
|
14
|
+
Start with `error_notifications`, Rails capture, and APM enabled in one non-production environment. Configure the same application version and environment used by the old notifier. Compare exception count, class, sanitized context, deploy correlation, request/job metrics, and retry diagnostics. Never copy an Airbrake project key into Chronos.
|
|
15
|
+
|
|
16
|
+
Chronos callbacks receive a normalized immutable `Chronos::Core::Notice`, not an Airbrake notice. Filters operate on each sanitized key/value pair. Translate callback logic explicitly and add tests; do not assume callback argument compatibility. Chronos intentionally omits request/response bodies, raw SQL, binds, cookies, authorization headers, mail content, and raw cache keys.
|
|
17
|
+
|
|
18
|
+
Before removing Airbrake, exercise a synchronous notification, an asynchronous notification followed by `Chronos.flush`, a Rails/Rack failure, a background job failure, and an unavailable endpoint. Confirm that the bounded queue/backlog behavior is acceptable. Roll back by disabling Chronos or restoring the previous dependency; no application exception semantics are changed by the agent.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Active Job legacy integration
|
|
2
|
+
|
|
3
|
+
When `ActiveJob::Base` is available, the Rails installer prepends public serialization/execution hooks. Enqueue serialization adds a namespaced `chronos_context` field with schema version plus at most the trace and request identifiers; each identifier is limited to 128 bytes. Public job arguments are unchanged. Deserialization restores this context only around `perform_now`.
|
|
4
|
+
|
|
5
|
+
The `perform.active_job` subscriber records adapter, job ID, provider job ID, class, queue, execution count, duration, status, and error class. Arguments are omitted. A supplied exception is reported once with bounded job context. The integration never changes retry, acknowledgement, adapter, or original exception behavior.
|
|
6
|
+
|
|
7
|
+
This metadata can identify a workflow or tenant in some applications. Review retention and identifier generation under the project's privacy policy.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Bounded local ignore rules
|
|
2
|
+
|
|
3
|
+
Configure startup rules with `config.ignore_rules` or add a rule to the active agent with `Chronos.ignore_if`. A rule receives an immutable normalized notice and must return exactly `true` to discard it before serialization, queueing, retry, or network work.
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
Chronos.ignore_if do |notice|
|
|
7
|
+
notice.exception_class == "SomeExpectedError"
|
|
8
|
+
end
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
At most `max_ignore_rules` rules are retained (default 20, hard maximum 100). Registration returns `false` when Chronos is not configured, the object is not callable, or the limit is full. A rule failure is contained and treated as no match. Rules run in the capture caller and therefore must be fast, thread-safe, and free of I/O. Remote configuration cannot add executable rules.
|
|
@@ -28,10 +28,10 @@ The public `ActiveSupport::Notifications.subscribe` API is used for:
|
|
|
28
28
|
| `render_template.action_view` | `request` with `kind=view` | template basename and duration |
|
|
29
29
|
| `sql.active_record` | `query` | operation name, cached flag, and duration |
|
|
30
30
|
| `deliver.action_mailer` | `job` with `kind=mailer` | mailer, action, and duration |
|
|
31
|
-
| `perform.active_job` | `job` | job class, queue,
|
|
31
|
+
| `perform.active_job` | `job` | adapter, job/provider IDs, class, queue, attempts, duration, status, and error class |
|
|
32
32
|
| cache read/write/hit notifications | `cache` | operation, backend, namespace, hit/miss, duration, and optional scoped key hash |
|
|
33
33
|
|
|
34
|
-
Raw SQL, binds, cache keys, mail recipients and bodies, job
|
|
34
|
+
Raw SQL, binds, cache keys, mail recipients and bodies, job arguments, request/response bodies, cookies, and authorization headers are not copied. Documented Active Job IDs are collected, while public job arguments remain unchanged and omitted. Template identifiers are reduced to their basename. Every event passes through `Sanitizer`, `SafeSerializer`, the bounded queue, retry policy, circuit breaker, and memory backlog.
|
|
35
35
|
|
|
36
36
|
## Controller exceptions and deduplication
|
|
37
37
|
|
|
@@ -57,4 +57,4 @@ The repository contains independent applications under `examples/rails-4.2` and
|
|
|
57
57
|
|
|
58
58
|
## Limits
|
|
59
59
|
|
|
60
|
-
Version 0.7 aggregates controller, SQL, and job timings into bounded APM batches. SQL literals and binds remain excluded while a bounded normalized query and fingerprint are produced. Sidekiq support remains a separate optional integration
|
|
60
|
+
Version 0.7 aggregates controller, SQL, and job timings into bounded APM batches. SQL literals and binds remain excluded while a bounded normalized query and fingerprint are produced. Sidekiq support remains a separate optional integration. Version `0.9.0.pre.2` provides bounded Active Job trace/request propagation through a namespaced serialized field; adapters that replace the standard Active Job serialization/execution hooks require separate compatibility evidence.
|
|
@@ -4,7 +4,7 @@ Version `0.6.0.pre.1` starts the legacy jobs line with optional Sidekiq 4 and 5
|
|
|
4
4
|
|
|
5
5
|
```ruby
|
|
6
6
|
gem "sidekiq", "~> 5.0"
|
|
7
|
-
gem "chronos-ruby", "0.9.0.pre.
|
|
7
|
+
gem "chronos-ruby", "0.9.0.pre.2", :require => "chronos/sidekiq"
|
|
8
8
|
```
|
|
9
9
|
|
|
10
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.
|
data/docs/performance.md
CHANGED
|
@@ -29,6 +29,17 @@ Current controls:
|
|
|
29
29
|
|
|
30
30
|
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.
|
|
31
31
|
|
|
32
|
+
## Version 0.9.0.pre.2 release gates
|
|
33
|
+
|
|
34
|
+
`benchmarks/comparative.rb` compares the same successful Rack fixture without and with Chronos instrumentation. It performs configurable warmup, at least three samples, and reports median plus median absolute deviation. `benchmarks/fake_endpoint_load.rb` sends asynchronous exception events to a local TCP endpoint, verifies the v1 schema marker, ensures the secret key is absent from every payload, and fails on loss, rejection, invalid payload, or timeout.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ITERATIONS=50000 WARMUP=5000 SAMPLES=7 bundle exec ruby benchmarks/comparative.rb
|
|
38
|
+
ITERATIONS=500 bundle exec ruby benchmarks/fake_endpoint_load.rb
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Results are environment-specific evidence, not a general speed claim. Record CPU, OS, Ruby, gem commit, and environment variables with any published result. Airbrake comparison remains optional and must use a legally compatible, equivalent sanitized payload on the same supported runtime.
|
|
42
|
+
|
|
32
43
|
## Version 0.5 Rails subscriber benchmark
|
|
33
44
|
|
|
34
45
|
Run:
|
data/docs/protocol-v1.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Protocol v1 stability
|
|
2
|
+
|
|
3
|
+
The schemas under `contracts/` are the source of truth for protocol v1. Version `0.9.0.pre.2` keeps `schema_version: "1.0"` and treats every required field, enum value, privacy exclusion, and maximum as a compatibility contract.
|
|
4
|
+
|
|
5
|
+
Compatible changes may add optional bounded fields or new event types accepted by the server. Removing or renaming a field, changing its type/meaning, weakening a bound, or making an optional field required needs a new protocol major schema. Authentication remains outside the JSON payload. Contract tests and `script/verify_docs` must pass before a release.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Version 1.0 readiness
|
|
2
|
+
|
|
3
|
+
Version `0.9.0.pre.2` is a hardening release, not the stable release. The local suite, lint, documentation verifier, build, load test, and comparative benchmark are required evidence. The release can advance to `1.0.0` only after the GitHub Actions matrix is green for every declared legacy Ruby/framework/job combination.
|
|
4
|
+
|
|
5
|
+
| Gate | State in pre.2 |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Ruby pure, Rack, Rails 4.2/5.2 | Implemented; external legacy matrix must pass |
|
|
8
|
+
| Sidekiq 4/5 real gems | Dedicated Docker matrix added; must pass |
|
|
9
|
+
| Active Job metadata/error/context | Implemented and contract-tested |
|
|
10
|
+
| filters, bounded ignore rules, context, breadcrumbs | Implemented and documented |
|
|
11
|
+
| request/SQL/job APM, deploy, retry/backlog, remote configuration | Implemented and contract-tested |
|
|
12
|
+
| payload fixture privacy | Contract-tested |
|
|
13
|
+
| public API/options/protocol review | Documented; final review required |
|
|
14
|
+
| Airbrake migration, SemVer, deprecation, security review | Added in pre.2 |
|
|
15
|
+
| fake endpoint load and repeatable comparison | Executable gates added |
|
|
16
|
+
| package signing | Not currently feasible; protected secret plus checksum is the interim control |
|
|
17
|
+
|
|
18
|
+
Do not change compatibility status from `Best effort` or create a `v1.0.0` tag until all external jobs and the dependency audit pass without skips.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Security review for 0.9.0.pre.2
|
|
2
|
+
|
|
3
|
+
Review date: 2026-07-21. Scope: capture, serialization, transport, remote configuration, framework/job integrations, release workflow, examples, and fixtures.
|
|
4
|
+
|
|
5
|
+
Verified by contracts and implementation review:
|
|
6
|
+
|
|
7
|
+
- the secret project key is sent only in an authentication header and never in the event body;
|
|
8
|
+
- sanitization and payload limits run before queueing, retry, backlog, or transport;
|
|
9
|
+
- queue, workers, retries, delays, circuit state, backlog, breadcrumbs, APM groups, dependencies, ignore rules, and propagated identifiers are bounded;
|
|
10
|
+
- TLS verification is default and plain HTTP requires explicit `ssl_verify = false`;
|
|
11
|
+
- remote configuration is size-limited, allowlisted, and cannot replace credentials/host or install executable rules;
|
|
12
|
+
- integrations contain agent failures and do not collect bodies, authorization, raw SQL/binds, mail content, or raw cache keys;
|
|
13
|
+
- Active Job propagation uses a namespaced v1 field containing only bounded trace/request identifiers and does not alter job arguments;
|
|
14
|
+
- fixture privacy is enforced by contract tests and dependency advisories are checked by the security workflow.
|
|
15
|
+
|
|
16
|
+
Residual risks: supported Ruby/Rails versions are end-of-life; in-memory backlog is lost at exit; application filters/ignore rules execute application code; project identifiers and documented job IDs may be personal data in some deployments; package signing is not enabled because no trusted certificate/key lifecycle exists. Release artifacts should use protected environments and published SHA-256 checksums until signing can be operated safely.
|
data/docs/semver.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Semantic Versioning policy
|
|
2
|
+
|
|
3
|
+
Chronos follows Semantic Versioning for the gem API and the independently versioned event protocol.
|
|
4
|
+
|
|
5
|
+
- Before `1.0.0`, incompatible changes may occur only in a documented prerelease and must appear in the changelog and migration notes.
|
|
6
|
+
- From `1.0.0`, patch releases contain compatible fixes, minor releases add compatible capability, and major releases may remove or change public behavior.
|
|
7
|
+
- Public API includes documented `Chronos` methods, configuration options, supported require paths, integration entry points, and documented return/error behavior.
|
|
8
|
+
- Internal constants and files explicitly described as internal are not public API.
|
|
9
|
+
- The JSON `schema_version` is independent of the gem version. Additive optional v1 fields are compatible; removing, renaming, or changing required v1 fields requires a new protocol major version.
|
|
10
|
+
|
|
11
|
+
A release is not promoted to stable while its supported matrix or mandatory release gates are incomplete.
|
data/lib/chronos/agent.rb
CHANGED
|
@@ -50,6 +50,10 @@ module Chronos
|
|
|
50
50
|
@capture.call_sync(exception, context_for_capture(context))
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def ignore_if(&block)
|
|
54
|
+
@ignore_policy.add(&block)
|
|
55
|
+
end
|
|
56
|
+
|
|
53
57
|
def with_context(context = {}, &block)
|
|
54
58
|
@context_store.with_context(context, &block)
|
|
55
59
|
end
|
|
@@ -186,7 +190,12 @@ module Chronos
|
|
|
186
190
|
private
|
|
187
191
|
|
|
188
192
|
def initialize_capture(options)
|
|
189
|
-
@
|
|
193
|
+
@ignore_policy = options[:ignore_policy] || Application::IgnorePolicy.new(
|
|
194
|
+
@config.ignore_rules, @config.max_ignore_rules, @logger
|
|
195
|
+
)
|
|
196
|
+
@capture = options[:capture] || Application::CaptureException.new(
|
|
197
|
+
@config, @delivery_pipeline, @logger, :ignore_policy => @ignore_policy
|
|
198
|
+
)
|
|
190
199
|
@telemetry = options[:telemetry] || Application::CaptureTelemetry.new(@config, @delivery_pipeline, @logger)
|
|
191
200
|
end
|
|
192
201
|
|
|
@@ -18,6 +18,7 @@ module Chronos
|
|
|
18
18
|
@delivery_pipeline = delivery_pipeline
|
|
19
19
|
@logger = logger || Internal::SafeLogger.new(config.logger)
|
|
20
20
|
@notice_builder = options[:notice_builder] || Core::NoticeBuilder.new(config)
|
|
21
|
+
@ignore_policy = options[:ignore_policy]
|
|
21
22
|
@serializer = options[:serializer] || Core::PayloadSerializer.new(
|
|
22
23
|
config,
|
|
23
24
|
nil,
|
|
@@ -29,6 +30,7 @@ module Chronos
|
|
|
29
30
|
return false unless capture_enabled?
|
|
30
31
|
|
|
31
32
|
notice = build_notice(exception, context)
|
|
33
|
+
return false if ignored?(notice)
|
|
32
34
|
return false unless @delivery_pipeline.capture_allowed?("exception", notice.fingerprint)
|
|
33
35
|
|
|
34
36
|
@delivery_pipeline.enqueue(@serializer.call(notice))
|
|
@@ -41,6 +43,7 @@ module Chronos
|
|
|
41
43
|
return false unless capture_enabled?
|
|
42
44
|
|
|
43
45
|
notice = build_notice(exception, context)
|
|
46
|
+
return false if ignored?(notice)
|
|
44
47
|
return false unless @delivery_pipeline.capture_allowed?("exception", notice.fingerprint)
|
|
45
48
|
|
|
46
49
|
@delivery_pipeline.deliver_sync(@serializer.call(notice))
|
|
@@ -59,6 +62,10 @@ module Chronos
|
|
|
59
62
|
@notice_builder.call(exception, context)
|
|
60
63
|
end
|
|
61
64
|
|
|
65
|
+
def ignored?(notice)
|
|
66
|
+
@ignore_policy && @ignore_policy.ignored?(notice)
|
|
67
|
+
end
|
|
68
|
+
|
|
62
69
|
def diagnose(error)
|
|
63
70
|
@logger.warn("Chronos capture failed: #{error.class}")
|
|
64
71
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Chronos
|
|
2
|
+
module Application
|
|
3
|
+
# Stores and evaluates bounded application-owned exception ignore rules.
|
|
4
|
+
#
|
|
5
|
+
# @responsibility Decide whether a normalized Notice should be discarded locally.
|
|
6
|
+
# @motivation Expected application failures should not consume queue or network capacity.
|
|
7
|
+
# @limits Rules inspect only the bounded Notice API and cannot be installed remotely.
|
|
8
|
+
# @collaborators CaptureException and application-provided callables.
|
|
9
|
+
# @thread_safety Rule registration and snapshots are protected by a mutex.
|
|
10
|
+
# @compatibility Ruby 2.2.10 through Ruby 2.6.
|
|
11
|
+
# @example
|
|
12
|
+
# policy.add { |notice| notice.exception_class == "ExpectedError" }
|
|
13
|
+
# @errors A failing rule is contained, logged, and treated as not matching.
|
|
14
|
+
# @performance The number of evaluated rules is capped by configuration.
|
|
15
|
+
class IgnorePolicy
|
|
16
|
+
def initialize(rules, limit, logger)
|
|
17
|
+
@rules = Array(rules).dup
|
|
18
|
+
@limit = limit
|
|
19
|
+
@logger = logger
|
|
20
|
+
@mutex = Mutex.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add(rule = nil, &block)
|
|
24
|
+
candidate = rule || block
|
|
25
|
+
return false unless candidate.respond_to?(:call)
|
|
26
|
+
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
return false if @rules.length >= @limit
|
|
29
|
+
|
|
30
|
+
@rules << candidate
|
|
31
|
+
end
|
|
32
|
+
true
|
|
33
|
+
rescue StandardError
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ignored?(notice)
|
|
38
|
+
rules = @mutex.synchronize { @rules.dup }
|
|
39
|
+
rules.any? { |rule| safely_matches?(rule, notice) }
|
|
40
|
+
rescue StandardError
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def safely_matches?(rule, notice)
|
|
47
|
+
rule.call(notice) == true
|
|
48
|
+
rescue StandardError => error
|
|
49
|
+
@logger.warn("Chronos ignore rule failed: #{error.class}")
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -9,7 +9,7 @@ module Chronos
|
|
|
9
9
|
# @thread_safety Validation reads one configuration instance without shared state.
|
|
10
10
|
# @compatibility Ruby 2.2.10 through Ruby 2.6.
|
|
11
11
|
# @errors Invalid values become messages; malformed URLs are contained.
|
|
12
|
-
module ConfigurationValidation
|
|
12
|
+
module ConfigurationValidation # rubocop:disable Metrics/ModuleLength
|
|
13
13
|
private
|
|
14
14
|
|
|
15
15
|
def resilience_errors
|
|
@@ -60,6 +60,7 @@ module Chronos
|
|
|
60
60
|
errors.concat(matcher_errors("allowlist_keys", allowlist_keys))
|
|
61
61
|
errors.concat(matcher_errors("hash_keys", hash_keys))
|
|
62
62
|
errors.concat(filter_errors)
|
|
63
|
+
errors.concat(ignore_rule_errors)
|
|
63
64
|
errors.concat(anonymization_errors)
|
|
64
65
|
errors
|
|
65
66
|
end
|
|
@@ -99,6 +100,20 @@ module Chronos
|
|
|
99
100
|
[true, false].include?(anonymize_ip) ? [] : ["anonymize_ip must be true or false"]
|
|
100
101
|
end
|
|
101
102
|
|
|
103
|
+
def ignore_rule_errors
|
|
104
|
+
errors = []
|
|
105
|
+
unless ignore_rules.is_a?(Array) && ignore_rules.all? { |rule| rule.respond_to?(:call) }
|
|
106
|
+
errors << "ignore_rules must contain only callable objects"
|
|
107
|
+
end
|
|
108
|
+
unless positive_integer?(max_ignore_rules) && max_ignore_rules <= 100
|
|
109
|
+
errors << "max_ignore_rules must be an integer between 1 and 100"
|
|
110
|
+
end
|
|
111
|
+
if ignore_rules.is_a?(Array) && max_ignore_rules.is_a?(Integer) && ignore_rules.length > max_ignore_rules
|
|
112
|
+
errors << "ignore_rules cannot exceed max_ignore_rules"
|
|
113
|
+
end
|
|
114
|
+
errors
|
|
115
|
+
end
|
|
116
|
+
|
|
102
117
|
def matcher_errors(name, values)
|
|
103
118
|
return [] unless values.is_a?(Array)
|
|
104
119
|
return [] if values.all? { |value| value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Regexp) }
|
|
@@ -37,7 +37,7 @@ module Chronos
|
|
|
37
37
|
:queue_size, :workers, :enabled, :error_notifications,
|
|
38
38
|
:ignored_environments, :proxy, :ssl_verify, :user_agent,
|
|
39
39
|
:max_payload_size, :gzip, :blocklist_keys, :allowlist_keys,
|
|
40
|
-
:filters, :hash_keys, :anonymize_ip, :max_retries,
|
|
40
|
+
:filters, :hash_keys, :anonymize_ip, :ignore_rules, :max_ignore_rules, :max_retries,
|
|
41
41
|
:retry_base_interval, :retry_max_interval, :retry_jitter,
|
|
42
42
|
:backlog_size, :circuit_failure_threshold, :circuit_reset_timeout,
|
|
43
43
|
:remote_configuration, :remote_config_max_bytes, :sampling_rate,
|
|
@@ -162,6 +162,8 @@ module Chronos
|
|
|
162
162
|
@filters = []
|
|
163
163
|
@hash_keys = []
|
|
164
164
|
@anonymize_ip = true
|
|
165
|
+
@ignore_rules = []
|
|
166
|
+
@max_ignore_rules = 20
|
|
165
167
|
end
|
|
166
168
|
|
|
167
169
|
def initialize_resilience_defaults
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
|
|
3
|
+
module Chronos
|
|
4
|
+
module Integrations
|
|
5
|
+
# Propagates bounded Chronos context through Active Job serialization.
|
|
6
|
+
#
|
|
7
|
+
# @responsibility Add trace/request identifiers to the job envelope and restore them while performing.
|
|
8
|
+
# @motivation Active Job adapters commonly cross process boundaries where thread-local context is lost.
|
|
9
|
+
# @limits It changes only a namespaced serialization field, never public job arguments or adapter behavior.
|
|
10
|
+
# @collaborators ActiveJob::Base serialization hooks and the Chronos facade.
|
|
11
|
+
# @thread_safety Installation is mutex-protected and each job owns its restored context.
|
|
12
|
+
# @compatibility Active Job shipped with Rails 4.2 through Rails 5.2.
|
|
13
|
+
# @example
|
|
14
|
+
# Chronos::Integrations::ActiveJob.install(ActiveJob::Base)
|
|
15
|
+
# @errors Context failures are contained so enqueue and perform semantics remain unchanged.
|
|
16
|
+
# @performance Adds at most two strings of 128 bytes to a serialized job.
|
|
17
|
+
module ActiveJob
|
|
18
|
+
CONTEXT_KEY = "chronos_context".freeze
|
|
19
|
+
SCHEMA_VERSION = "1.0".freeze
|
|
20
|
+
MAX_IDENTIFIER_BYTES = 128
|
|
21
|
+
|
|
22
|
+
@mutex = Mutex.new
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
def install(base = nil, notifier = Chronos)
|
|
26
|
+
target = base || (::ActiveJob::Base if defined?(::ActiveJob::Base))
|
|
27
|
+
return false unless target && target.respond_to?(:prepend)
|
|
28
|
+
|
|
29
|
+
@mutex.synchronize do
|
|
30
|
+
return false if target.ancestors.include?(JobExtensions)
|
|
31
|
+
|
|
32
|
+
JobExtensions.notifier = notifier
|
|
33
|
+
target.send(:prepend, JobExtensions)
|
|
34
|
+
end
|
|
35
|
+
true
|
|
36
|
+
rescue StandardError
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def envelope(notifier)
|
|
41
|
+
source = notifier.respond_to?(:propagation_context) ? notifier.propagation_context : {}
|
|
42
|
+
source = {} unless source.is_a?(Hash)
|
|
43
|
+
context = %w(trace_id request_id).each_with_object({}) do |key, result|
|
|
44
|
+
value = source[key] || source[key.to_sym]
|
|
45
|
+
result[key] = bounded(value) unless value.to_s.empty?
|
|
46
|
+
end
|
|
47
|
+
context["trace_id"] = SecureRandom.uuid if context["trace_id"].to_s.empty?
|
|
48
|
+
{"schema_version" => SCHEMA_VERSION, "context" => context}
|
|
49
|
+
rescue StandardError
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def context(value)
|
|
54
|
+
return {} unless value.is_a?(Hash)
|
|
55
|
+
return {} unless (value["schema_version"] || value[:schema_version]).to_s == SCHEMA_VERSION
|
|
56
|
+
|
|
57
|
+
source = value["context"] || value[:context]
|
|
58
|
+
return {} unless source.is_a?(Hash)
|
|
59
|
+
|
|
60
|
+
%w(trace_id request_id).each_with_object({}) do |key, result|
|
|
61
|
+
candidate = source[key] || source[key.to_sym]
|
|
62
|
+
result[key] = bounded(candidate) unless candidate.to_s.empty?
|
|
63
|
+
end
|
|
64
|
+
rescue StandardError
|
|
65
|
+
{}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def bounded(value)
|
|
71
|
+
string = value.to_s
|
|
72
|
+
return string if string.bytesize <= MAX_IDENTIFIER_BYTES
|
|
73
|
+
|
|
74
|
+
string.byteslice(0, MAX_IDENTIFIER_BYTES)
|
|
75
|
+
rescue StandardError
|
|
76
|
+
""
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Active Job instance hooks installed through Module#prepend.
|
|
81
|
+
module JobExtensions
|
|
82
|
+
class << self
|
|
83
|
+
attr_accessor :notifier
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def serialize(*arguments)
|
|
87
|
+
data = super
|
|
88
|
+
begin
|
|
89
|
+
envelope = Chronos::Integrations::ActiveJob.envelope(JobExtensions.notifier || Chronos)
|
|
90
|
+
data[CONTEXT_KEY] = envelope if data.is_a?(Hash) && envelope
|
|
91
|
+
rescue StandardError
|
|
92
|
+
nil
|
|
93
|
+
end
|
|
94
|
+
data
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def deserialize(job_data)
|
|
98
|
+
result = super
|
|
99
|
+
begin
|
|
100
|
+
@chronos_context = Chronos::Integrations::ActiveJob.context(
|
|
101
|
+
job_data.is_a?(Hash) ? (job_data[CONTEXT_KEY] || job_data[CONTEXT_KEY.to_sym]) : nil
|
|
102
|
+
)
|
|
103
|
+
rescue StandardError
|
|
104
|
+
@chronos_context = {}
|
|
105
|
+
end
|
|
106
|
+
result
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def perform_now(*arguments, &block)
|
|
110
|
+
context = @chronos_context || {}
|
|
111
|
+
notifier = JobExtensions.notifier || Chronos
|
|
112
|
+
return super if context.empty? || !notifier.respond_to?(:with_context)
|
|
113
|
+
|
|
114
|
+
notifier.with_context(:context => context) { super }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -33,6 +33,7 @@ module Chronos
|
|
|
33
33
|
return false if self.class.applications[application.object_id]
|
|
34
34
|
|
|
35
35
|
install_middleware(application, options)
|
|
36
|
+
install_active_job
|
|
36
37
|
@subscriber.install
|
|
37
38
|
self.class.applications[application.object_id] = true
|
|
38
39
|
end
|
|
@@ -58,6 +59,12 @@ module Chronos
|
|
|
58
59
|
true
|
|
59
60
|
end
|
|
60
61
|
|
|
62
|
+
def install_active_job
|
|
63
|
+
return false unless defined?(::ActiveJob::Base)
|
|
64
|
+
|
|
65
|
+
Chronos::Integrations::ActiveJob.install(::ActiveJob::Base, @notifier)
|
|
66
|
+
end
|
|
67
|
+
|
|
61
68
|
def environment
|
|
62
69
|
defined?(::Rails) && ::Rails.respond_to?(:env) ? ::Rails.env.to_s : nil
|
|
63
70
|
end
|
|
@@ -144,11 +144,39 @@ module Chronos
|
|
|
144
144
|
|
|
145
145
|
def active_job(payload, duration)
|
|
146
146
|
job = value(payload, :job)
|
|
147
|
+
exception = job_exception(payload)
|
|
147
148
|
data = {
|
|
148
149
|
"kind" => "active_job", "class" => safe_class_name(job),
|
|
149
|
-
"
|
|
150
|
+
"adapter" => active_job_adapter(job), "job_id" => safe_job_value(job, :job_id),
|
|
151
|
+
"provider_job_id" => safe_job_value(job, :provider_job_id),
|
|
152
|
+
"queue" => safe_job_value(job, :queue_name), "attempts" => job_attempts(job),
|
|
153
|
+
"duration_ms" => duration, "status" => exception ? "failed" : "completed"
|
|
150
154
|
}
|
|
155
|
+
data["error_class"] = exception.class.name.to_s if exception
|
|
151
156
|
@notifier.record_event("job", data)
|
|
157
|
+
@notifier.notify_once(exception, :context => {"job" => data}) if exception
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def job_exception(payload)
|
|
161
|
+
exception = value(payload, :exception_object)
|
|
162
|
+
details = value(payload, :exception)
|
|
163
|
+
exception ||= RuntimeError.new(Array(details).last.to_s) if details
|
|
164
|
+
exception
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def active_job_adapter(job)
|
|
168
|
+
adapter = job.respond_to?(:queue_adapter) ? job.queue_adapter : nil
|
|
169
|
+
adapter = adapter.class if adapter && !adapter.is_a?(Class)
|
|
170
|
+
adapter ? adapter.name.to_s.split("::").last.to_s.sub(/Adapter$/, "") : ""
|
|
171
|
+
rescue StandardError
|
|
172
|
+
""
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def job_attempts(job)
|
|
176
|
+
value = job.respond_to?(:executions) ? job.executions : 0
|
|
177
|
+
value.is_a?(Numeric) ? value.to_i : 0
|
|
178
|
+
rescue StandardError
|
|
179
|
+
0
|
|
152
180
|
end
|
|
153
181
|
|
|
154
182
|
def cache(name, payload, duration)
|
data/lib/chronos/rails.rb
CHANGED
data/lib/chronos/version.rb
CHANGED
data/lib/chronos.rb
CHANGED
|
@@ -33,6 +33,7 @@ require "chronos/core/breadcrumb"
|
|
|
33
33
|
require "chronos/application/retry_policy"
|
|
34
34
|
require "chronos/application/circuit_breaker"
|
|
35
35
|
require "chronos/application/remote_configuration"
|
|
36
|
+
require "chronos/application/ignore_policy"
|
|
36
37
|
require "chronos/application/delivery_pipeline"
|
|
37
38
|
require "chronos/application/capture_exception"
|
|
38
39
|
require "chronos/application/apm_error_classifier"
|
|
@@ -60,7 +61,7 @@ require "chronos/integrations/rack/middleware"
|
|
|
60
61
|
# config.host = "https://chronos.example.com"
|
|
61
62
|
# end
|
|
62
63
|
# Chronos.notify(RuntimeError.new("failed"))
|
|
63
|
-
module Chronos
|
|
64
|
+
module Chronos # rubocop:disable Metrics/ModuleLength
|
|
64
65
|
extend ObservabilityFacade
|
|
65
66
|
|
|
66
67
|
@mutex = Mutex.new
|
|
@@ -144,6 +145,13 @@ module Chronos
|
|
|
144
145
|
false
|
|
145
146
|
end
|
|
146
147
|
|
|
148
|
+
def ignore_if(&block)
|
|
149
|
+
agent = current_agent
|
|
150
|
+
agent ? agent.ignore_if(&block) : false
|
|
151
|
+
rescue StandardError
|
|
152
|
+
false
|
|
153
|
+
end
|
|
154
|
+
|
|
147
155
|
def rails_integration_options(environment = nil, console = false)
|
|
148
156
|
agent = current_agent
|
|
149
157
|
agent ? agent.rails_integration_options(environment, console) : {:enabled => false}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chronos-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.0.pre.
|
|
4
|
+
version: 0.9.0.pre.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Antonio Jefferson
|
|
@@ -105,11 +105,15 @@ files:
|
|
|
105
105
|
- docs/adr/ADR-015-bounded-apm-aggregation.md
|
|
106
106
|
- docs/adr/ADR-016-explicit-observability-integrations.md
|
|
107
107
|
- docs/adr/ADR-017-deploy-events-and-correlation.md
|
|
108
|
+
- docs/adr/ADR-018-pre-1.0-hardening.md
|
|
108
109
|
- docs/architecture.md
|
|
109
110
|
- docs/compatibility.md
|
|
110
111
|
- docs/configuration.md
|
|
111
112
|
- docs/data-collected.md
|
|
113
|
+
- docs/deprecation-policy.md
|
|
112
114
|
- docs/examples/plain-ruby.md
|
|
115
|
+
- docs/migration-from-airbrake.md
|
|
116
|
+
- docs/modules/active-job.md
|
|
113
117
|
- docs/modules/apm-aggregation.md
|
|
114
118
|
- docs/modules/async-queue.md
|
|
115
119
|
- docs/modules/cache-observability.md
|
|
@@ -117,6 +121,7 @@ files:
|
|
|
117
121
|
- docs/modules/dependencies.md
|
|
118
122
|
- docs/modules/deploy-tracking.md
|
|
119
123
|
- docs/modules/external-http.md
|
|
124
|
+
- docs/modules/ignore-rules.md
|
|
120
125
|
- docs/modules/notice-pipeline.md
|
|
121
126
|
- docs/modules/rack-context.md
|
|
122
127
|
- docs/modules/rails-legacy.md
|
|
@@ -129,6 +134,10 @@ files:
|
|
|
129
134
|
- docs/modules/transport.md
|
|
130
135
|
- docs/performance.md
|
|
131
136
|
- docs/privacy-lgpd.md
|
|
137
|
+
- docs/protocol-v1.md
|
|
138
|
+
- docs/release-1.0-readiness.md
|
|
139
|
+
- docs/security-review.md
|
|
140
|
+
- docs/semver.md
|
|
132
141
|
- docs/troubleshooting.md
|
|
133
142
|
- lib/chronos.rb
|
|
134
143
|
- lib/chronos/adapters.rb
|
|
@@ -143,6 +152,7 @@ files:
|
|
|
143
152
|
- lib/chronos/application/circuit_breaker.rb
|
|
144
153
|
- lib/chronos/application/delivery_pipeline.rb
|
|
145
154
|
- lib/chronos/application/dependency_reporter.rb
|
|
155
|
+
- lib/chronos/application/ignore_policy.rb
|
|
146
156
|
- lib/chronos/application/remote_configuration.rb
|
|
147
157
|
- lib/chronos/application/retry_policy.rb
|
|
148
158
|
- lib/chronos/capistrano.rb
|
|
@@ -169,6 +179,7 @@ files:
|
|
|
169
179
|
- lib/chronos/core/telemetry_event.rb
|
|
170
180
|
- lib/chronos/errors.rb
|
|
171
181
|
- lib/chronos/integrations.rb
|
|
182
|
+
- lib/chronos/integrations/active_job.rb
|
|
172
183
|
- lib/chronos/integrations/capistrano.rb
|
|
173
184
|
- lib/chronos/integrations/job_payload.rb
|
|
174
185
|
- lib/chronos/integrations/net_http.rb
|