chronos-ruby 0.8.0.pre.1 → 0.9.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +30 -8
- data/contracts/deploy-v1.schema.json +26 -0
- data/contracts/event-v1.schema.json +15 -1
- data/docs/adr/ADR-017-deploy-events-and-correlation.md +25 -0
- data/docs/architecture.md +7 -1
- data/docs/compatibility.md +2 -0
- data/docs/configuration.md +13 -2
- data/docs/data-collected.md +4 -2
- data/docs/examples/plain-ruby.md +7 -1
- data/docs/modules/configuration.md +2 -0
- data/docs/modules/dependencies.md +2 -2
- data/docs/modules/deploy-tracking.md +86 -0
- data/docs/modules/sidekiq-legacy.md +1 -1
- data/docs/modules/telemetry-events.md +3 -1
- data/docs/performance.md +15 -1
- data/docs/privacy-lgpd.md +11 -2
- data/docs/troubleshooting.md +12 -0
- data/lib/chronos/agent.rb +20 -1
- data/lib/chronos/application/capture_telemetry.rb +17 -0
- data/lib/chronos/application/dependency_reporter.rb +12 -1
- data/lib/chronos/application/remote_configuration.rb +1 -1
- data/lib/chronos/capistrano.rb +4 -0
- data/lib/chronos/configuration/apm_validation.rb +11 -0
- data/lib/chronos/configuration.rb +11 -2
- data/lib/chronos/core/correlation_context.rb +65 -0
- data/lib/chronos/core/deploy_normalizer.rb +102 -0
- data/lib/chronos/core/payload_serializer.rb +4 -1
- data/lib/chronos/core/telemetry_event.rb +31 -4
- data/lib/chronos/integrations/capistrano.rb +61 -0
- data/lib/chronos/observability_facade.rb +9 -2
- data/lib/chronos/version.rb +1 -1
- data/lib/chronos.rb +2 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 34b5d9d1193017931d129dc366922b276d9b85ced0321e78d3f4a5b1a8c13e40
|
|
4
|
+
data.tar.gz: 72277b74f51825118ce6cb2dda83652f035bbfef3cb56c18b7ee98cc3e6d1d54
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c6b44a856bbb8975c6a4dc9aba5d9bcd2cf9a9d44456c19fe373860872491ff310f11a2a1b4c7588306ae2795caf4af8cf008e38c10bcdbe0322fca9a803ece
|
|
7
|
+
data.tar.gz: c4ff3f4da09810013c0e4d352bb18cd04c0a652c160816465d4b088490d5eea5f722c326ab8a6dcc9806f7b74c050ee0c77e8c6a4c604ddf650cb08fc66a3fd4
|
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,31 @@ 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.1] - 2026-07-21
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- synchronous `Chronos.notify_deploy` API with bounded environment, revision, version, repository, actor, deploy ID, service, region, and instance fields;
|
|
20
|
+
- fixed bounded release/deploy correlation on every new exception and telemetry envelope;
|
|
21
|
+
- explicit configuration for `revision`, `deploy_id`, `region`, and `instance_id`;
|
|
22
|
+
- automatic deploy IDs and credential removal from common HTTP/SCP repository references;
|
|
23
|
+
- optional idempotent Capistrano post-publish task loaded through `chronos/capistrano`;
|
|
24
|
+
- manual, Kamal-command, and GitHub Actions deployment examples;
|
|
25
|
+
- deploy/correlation contracts, tests, benchmark, module documentation, and ADR-017.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- dependency inventory can be refreshed once after a successful deploy notification with the new release;
|
|
30
|
+
- deploy events use their normalized environment/service/release values in the common envelope;
|
|
31
|
+
- version advanced to `0.9.0.pre.1`.
|
|
32
|
+
|
|
33
|
+
### Known limitations
|
|
34
|
+
|
|
35
|
+
- `notify_deploy` does not mutate the immutable correlation of an already running agent;
|
|
36
|
+
- Kamal support is command/documentation based rather than a plugin;
|
|
37
|
+
- Capistrano must load its task DSL before the optional Chronos entry point;
|
|
38
|
+
- deployment policy must decide whether a `false` notification result blocks publication.
|
|
39
|
+
|
|
15
40
|
## [0.8.0.pre.1] - 2026-07-20
|
|
16
41
|
|
|
17
42
|
### 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 and bounded telemetry to Chronos. Version 0.
|
|
3
|
+
Chronos Ruby is the framework-independent client for sending Ruby application errors and bounded telemetry to Chronos. Version 0.9 adds synchronous deploy tracking and bounded release correlation across every event.
|
|
4
4
|
|
|
5
5
|
## What the gem collects
|
|
6
6
|
|
|
7
|
-
Version 0.
|
|
7
|
+
Version 0.9 can collect:
|
|
8
8
|
|
|
9
9
|
- exception class, message, structured backtrace, and chained causes;
|
|
10
10
|
- timestamp, severity, tags, and an optional fingerprint;
|
|
@@ -13,6 +13,8 @@ Version 0.8 can collect:
|
|
|
13
13
|
- application version, environment, and service name.
|
|
14
14
|
- Rack method, normalized route, status, duration, request ID, host, query-free path, optional user agent, controller/action, response size, trace ID, and already-parsed parameters when the middleware is used;
|
|
15
15
|
- bounded breadcrumbs explicitly supplied by the application or integration.
|
|
16
|
+
- release, revision, deploy ID, environment, service, region, and instance correlation;
|
|
17
|
+
- explicit bounded deployment metadata supplied through the public API or integration.
|
|
16
18
|
|
|
17
19
|
See [Data collected](docs/data-collected.md) for the complete field table.
|
|
18
20
|
|
|
@@ -31,7 +33,7 @@ See [Compatibility](docs/compatibility.md).
|
|
|
31
33
|
The current public build is a pre-release. Add its exact version to the application's `Gemfile`:
|
|
32
34
|
|
|
33
35
|
```ruby
|
|
34
|
-
gem "chronos-ruby", "0.
|
|
36
|
+
gem "chronos-ruby", "0.9.0.pre.1"
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
Install with a Bundler version compatible with the application. For the oldest supported runtime:
|
|
@@ -52,7 +54,7 @@ gem install chronos-ruby --pre
|
|
|
52
54
|
Version 0.5 exposes Rails support explicitly, keeping Rails and ActiveSupport out of plain Ruby applications:
|
|
53
55
|
|
|
54
56
|
```ruby
|
|
55
|
-
gem "chronos-ruby", "0.
|
|
57
|
+
gem "chronos-ruby", "0.9.0.pre.1", :require => "chronos/rails"
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
Generate the initializer with:
|
|
@@ -191,7 +193,7 @@ Version `0.6.0.pre.1` adds optional Sidekiq 4/5 middleware:
|
|
|
191
193
|
|
|
192
194
|
```ruby
|
|
193
195
|
gem "sidekiq", "~> 5.0"
|
|
194
|
-
gem "chronos-ruby", "0.
|
|
196
|
+
gem "chronos-ruby", "0.9.0.pre.1", :require => "chronos/sidekiq"
|
|
195
197
|
```
|
|
196
198
|
|
|
197
199
|
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).
|
|
@@ -220,7 +222,21 @@ Rails cache telemetry omits raw keys by default. Set `cache_key_mode = :sha256`
|
|
|
220
222
|
|
|
221
223
|
## Deploy tracking
|
|
222
224
|
|
|
223
|
-
|
|
225
|
+
Version 0.9 sends deployment metadata synchronously and adds a bounded correlation block to every event:
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
Chronos.notify_deploy(
|
|
229
|
+
:environment => "production",
|
|
230
|
+
:revision => ENV["GIT_SHA"],
|
|
231
|
+
:version => ENV["APP_VERSION"],
|
|
232
|
+
:repository => "owner/repository",
|
|
233
|
+
:actor => ENV["DEPLOY_USER"]
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Configure `app_version`, `revision`, `deploy_id`, `environment`, `service_name`, `region`, and `instance_id` in each newly deployed process so subsequent telemetry carries the same release identity. The gem never scans environment variables or Git automatically.
|
|
238
|
+
|
|
239
|
+
Optional Capistrano support loads through `chronos/capistrano`. Manual, Kamal-command, and GitHub Actions examples share the explicit deploy command under `examples/deploy/`. See [Deploy tracking and release correlation](docs/modules/deploy-tracking.md).
|
|
224
240
|
|
|
225
241
|
## Asynchronous queue
|
|
226
242
|
|
|
@@ -296,6 +312,11 @@ Chronos.configure do |config|
|
|
|
296
312
|
config.external_http_enabled = false
|
|
297
313
|
config.cache_key_mode = :none
|
|
298
314
|
config.dependency_reporting = true
|
|
315
|
+
config.app_version = ENV["APP_VERSION"]
|
|
316
|
+
config.revision = ENV["GIT_SHA"]
|
|
317
|
+
config.deploy_id = ENV["DEPLOY_ID"]
|
|
318
|
+
config.region = ENV["REGION"]
|
|
319
|
+
config.instance_id = ENV["INSTANCE_ID"]
|
|
299
320
|
end
|
|
300
321
|
```
|
|
301
322
|
|
|
@@ -307,7 +328,7 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
|
|
|
307
328
|
|
|
308
329
|
## Benchmark
|
|
309
330
|
|
|
310
|
-
Run the version 0.
|
|
331
|
+
Run the version 0.9 benchmarks with:
|
|
311
332
|
|
|
312
333
|
```bash
|
|
313
334
|
bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
|
|
@@ -320,13 +341,14 @@ bundle _1.17.3_ exec ruby benchmarks/rails_notifications.rb
|
|
|
320
341
|
bundle _1.17.3_ exec ruby benchmarks/sidekiq_middleware.rb
|
|
321
342
|
bundle _1.17.3_ exec ruby benchmarks/apm_aggregation.rb
|
|
322
343
|
bundle _1.17.3_ exec ruby benchmarks/external_http.rb
|
|
344
|
+
bundle _1.17.3_ exec ruby benchmarks/correlation.rb
|
|
323
345
|
```
|
|
324
346
|
|
|
325
347
|
Results depend on runtime, hardware, and payload. No performance comparison is claimed until repeatable measurements are published.
|
|
326
348
|
|
|
327
349
|
## Migration from Airbrake
|
|
328
350
|
|
|
329
|
-
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.
|
|
351
|
+
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.9 does not claim API compatibility or automatic replacement.
|
|
330
352
|
|
|
331
353
|
## Local development
|
|
332
354
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://chronos.example/schemas/deploy-v1.schema.json",
|
|
4
|
+
"title": "Chronos deploy payload v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"deploy_id", "environment", "revision", "version", "repository",
|
|
9
|
+
"actor", "service", "region", "instance"
|
|
10
|
+
],
|
|
11
|
+
"properties": {
|
|
12
|
+
"deploy_id": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
13
|
+
"environment": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
14
|
+
"revision": {"type": ["string", "null"], "maxLength": 128},
|
|
15
|
+
"version": {"type": ["string", "null"], "maxLength": 128},
|
|
16
|
+
"repository": {"type": ["string", "null"], "maxLength": 512},
|
|
17
|
+
"actor": {"type": ["string", "null"], "maxLength": 128},
|
|
18
|
+
"service": {"type": ["string", "null"], "maxLength": 128},
|
|
19
|
+
"region": {"type": ["string", "null"], "maxLength": 128},
|
|
20
|
+
"instance": {"type": ["string", "null"], "maxLength": 128}
|
|
21
|
+
},
|
|
22
|
+
"anyOf": [
|
|
23
|
+
{"properties": {"revision": {"type": "string", "minLength": 1}}},
|
|
24
|
+
{"properties": {"version": {"type": "string", "minLength": 1}}}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -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", "request", "query", "job", "cache", "external_http", "dependencies", "metric_batch"]},
|
|
23
|
+
"event_type": {"enum": ["exception", "request", "query", "job", "cache", "external_http", "dependencies", "deploy", "metric_batch"]},
|
|
24
24
|
"occurred_at": {"type": "string"},
|
|
25
25
|
"sent_at": {"type": "string"},
|
|
26
26
|
"project_key": {"type": "string", "minLength": 1},
|
|
@@ -34,6 +34,20 @@
|
|
|
34
34
|
"instance_id": {"type": ["string", "null"]}
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
+
"correlation": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["release", "revision", "deploy_id", "environment", "service", "region", "instance"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"release": {"type": ["string", "null"], "maxLength": 128},
|
|
43
|
+
"revision": {"type": ["string", "null"], "maxLength": 128},
|
|
44
|
+
"deploy_id": {"type": ["string", "null"], "maxLength": 128},
|
|
45
|
+
"environment": {"type": ["string", "null"], "maxLength": 128},
|
|
46
|
+
"service": {"type": ["string", "null"], "maxLength": 128},
|
|
47
|
+
"region": {"type": ["string", "null"], "maxLength": 128},
|
|
48
|
+
"instance": {"type": ["string", "null"], "maxLength": 128}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
37
51
|
"runtime": {"type": "object"},
|
|
38
52
|
"context": {"type": "object"},
|
|
39
53
|
"payload": {"type": "object"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# ADR-017 — Synchronous deploy events and immutable release correlation
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted for version 0.9.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The SaaS needs stable release dimensions to compare error and performance behavior before and after deployments. Deployment tools are short-lived processes, while application telemetry is long-lived and concurrent. Implicit Git, lockfile, or environment discovery would make behavior surprising and could expose credentials.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Add a versioned `deploy` telemetry payload delivered synchronously through the existing transport policy. Require an environment and either revision or version, generate a deploy ID when omitted, bound every field, and remove credentials from common repository URL forms.
|
|
14
|
+
|
|
15
|
+
Emit a `correlation` object on every new v1 envelope with release, revision, deploy ID, environment, service, region, and instance. Keep the schema property optional so payloads from earlier 0.x clients remain valid. Populate it from the immutable configuration snapshot for application events and from normalized deploy fields for deploy events. Do not mutate a running agent's correlation after notification.
|
|
16
|
+
|
|
17
|
+
Refresh the bounded dependency inventory after a successful deploy notification. Integrate Capistrano through its task DSL without a runtime dependency. Support Kamal by documented command and GitHub Actions through an example workflow.
|
|
18
|
+
|
|
19
|
+
## Alternatives
|
|
20
|
+
|
|
21
|
+
Reading Git state, all environment variables, or deployment manifests automatically was rejected for determinism and privacy. Asynchronous-only deploy delivery was rejected because short-lived commands can exit before queue drain. Mutating global release state after `notify_deploy` was rejected because concurrent events could receive mixed correlation. Adding direct dependencies on Capistrano, Kamal, or GitHub SDKs was rejected for the legacy compatibility line.
|
|
22
|
+
|
|
23
|
+
## Consequences
|
|
24
|
+
|
|
25
|
+
Every event has a stable bounded correlation shape and deploy commands receive an explicit success result. Applications must pass release metadata into each newly started process. Integration setup remains explicit, and delivery failure policy stays under the deployment pipeline's control.
|
data/docs/architecture.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
Chronos Ruby 0.
|
|
3
|
+
Chronos Ruby 0.9 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
|
|
4
4
|
|
|
5
5
|
```mermaid
|
|
6
6
|
flowchart TB
|
|
@@ -24,6 +24,8 @@ flowchart TB
|
|
|
24
24
|
NetHTTP[Explicit Net::HTTP instance wrapper] --> Facade
|
|
25
25
|
Rails --> Cache[Core / CacheNormalizer]
|
|
26
26
|
Application --> Dependencies[Application / DependencyReporter]
|
|
27
|
+
Deploy[Manual, Capistrano, Kamal command, GitHub workflow] --> Facade
|
|
28
|
+
Core --> Correlation[Core / DeployNormalizer and CorrelationContext]
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
## Boundaries
|
|
@@ -55,6 +57,10 @@ Version 0.7 routes request, query, and job observations through `ApmAggregator`.
|
|
|
55
57
|
|
|
56
58
|
Version 0.8 prepends a wrapper only to each explicitly selected `Net::HTTP` object. The wrapper records bounded outcome metadata and feeds the existing APM aggregator; it does not modify `Net::HTTP` globally. `CacheNormalizer` turns public Rails cache notifications into bounded metadata before delivery. `DependencyReporter` reads already loaded runtime metadata once per agent under a mutex and queues it as a separate event.
|
|
57
59
|
|
|
60
|
+
Version 0.9 routes explicit deploy attributes through `DeployNormalizer`, synchronous `CaptureTelemetry`, the common sanitizer, and `DeliveryPipeline`. `CorrelationContext` supplies the same fixed seven-field shape to exception and telemetry serializers. Deploy event overrides apply only while serializing that event; the agent configuration remains immutable for concurrent application capture. A successful deployment resets the dependency reporter once and flushes the refreshed inventory.
|
|
61
|
+
|
|
62
|
+
Capistrano remains an optional integration task with no runtime dependency. Kamal and GitHub Actions call the same manual Ruby command rather than adding SDKs or transport paths.
|
|
63
|
+
|
|
58
64
|
## Failure policy
|
|
59
65
|
|
|
60
66
|
Explicit invalid configuration raises `Chronos::ConfigurationError`. Capture, serialization, logger, worker, retry, circuit, TLS, network, HTTP, and remote-policy failures do not escape into the host application. They produce `false`, a classified transport result, a bounded state transition, or a bounded logger diagnostic.
|
data/docs/compatibility.md
CHANGED
|
@@ -23,6 +23,8 @@ Version `0.7.0.pre.1` keeps the same Ruby/Rails matrix and implements APM aggreg
|
|
|
23
23
|
|
|
24
24
|
Version `0.8.0.pre.1` uses per-object `Module#prepend`, legacy `Net::HTTP`, standard-library SHA-256, and loaded-spec feature detection. It adds no runtime dependency and keeps the same matrix. Outbound HTTP, cache, and dependency gates must pass every listed runtime before support is promoted.
|
|
25
25
|
|
|
26
|
+
Version `0.9.0.pre.1` adds only standard-library URI/SecureRandom processing, bounded hashes, and the existing synchronous delivery path. Capistrano is optional and feature-detected; Kamal and GitHub Actions integrations are commands/examples. The Ruby/Rails matrix remains unchanged and `Best effort` until deploy/correlation payload gates pass every listed runtime.
|
|
27
|
+
|
|
26
28
|
Status meanings:
|
|
27
29
|
|
|
28
30
|
- Supported: the complete required compatibility gate passes.
|
data/docs/configuration.md
CHANGED
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
| `project_key` | Yes when enabled | `nil` | Secret authentication key sent only as an HTTP header |
|
|
9
9
|
| `host` | Yes when enabled | `nil` | Absolute Chronos HTTP endpoint; HTTPS is required by default |
|
|
10
10
|
| `environment` | Recommended | `production` | Application environment |
|
|
11
|
-
| `app_version` | Optional | `nil` |
|
|
11
|
+
| `app_version` | Optional | `nil` | Application release/version identifier |
|
|
12
12
|
| `service_name` | Recommended | `nil` | Logical service name |
|
|
13
|
+
| `revision` | Optional | `nil` | Source revision correlated with every event; maximum 128 bytes |
|
|
14
|
+
| `deploy_id` | Optional | `nil` | Deployment identifier correlated with every event; maximum 128 bytes |
|
|
15
|
+
| `region` | Optional | `nil` | Deployment region correlated with every event; maximum 128 bytes |
|
|
16
|
+
| `instance_id` | Optional | `nil` | Explicit instance correlation; otherwise runtime hostname is used |
|
|
13
17
|
| `root_directory` | Optional | `Dir.pwd` | Used to identify application backtrace frames |
|
|
14
18
|
| `logger` | Optional | `nil` | Logger receiving bounded internal diagnostics |
|
|
15
19
|
| `timeout` | Optional | `5.0` | HTTP read timeout in seconds |
|
|
@@ -39,7 +43,7 @@
|
|
|
39
43
|
| `remote_configuration` | Optional | `true` | Accepts only the documented bounded remote policy fields |
|
|
40
44
|
| `remote_config_max_bytes` | Optional | `4096` | Maximum remote policy response-header bytes |
|
|
41
45
|
| `sampling_rate` | Optional | `1.0` | Local upper bound for event sampling |
|
|
42
|
-
| `enabled_event_types` | Optional | exception, request, query, job, cache, external_http, dependencies, metric_batch | Local allowlist for supported event envelopes |
|
|
46
|
+
| `enabled_event_types` | Optional | exception, request, query, job, cache, external_http, dependencies, deploy, metric_batch | Local allowlist for supported event envelopes |
|
|
43
47
|
| `max_remote_send_interval` | Optional | `60.0` | Local upper bound for remotely requested send spacing |
|
|
44
48
|
| `context_store` | Optional | `:thread_local` | `:thread_local` or an object implementing `get`, `set`, `clear`, and `with_context` |
|
|
45
49
|
| `breadcrumb_capacity` | Optional | `20` | Positive count of newest breadcrumbs retained per execution |
|
|
@@ -70,6 +74,11 @@ Chronos.configure do |config|
|
|
|
70
74
|
config.host = ENV["CHRONOS_HOST"]
|
|
71
75
|
config.environment = ENV["APP_ENV"] || "production"
|
|
72
76
|
config.service_name = "billing"
|
|
77
|
+
config.app_version = ENV["APP_VERSION"]
|
|
78
|
+
config.revision = ENV["GIT_SHA"]
|
|
79
|
+
config.deploy_id = ENV["DEPLOY_ID"]
|
|
80
|
+
config.region = ENV["REGION"]
|
|
81
|
+
config.instance_id = ENV["INSTANCE_ID"]
|
|
73
82
|
config.queue_size = 100
|
|
74
83
|
config.workers = 1
|
|
75
84
|
config.blocklist_keys += [:medical_record, /bank_account/i]
|
|
@@ -102,3 +111,5 @@ Privacy matcher collections and filters are copied into the immutable runtime sn
|
|
|
102
111
|
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.
|
|
103
112
|
|
|
104
113
|
Remote configuration can only reduce collection within local bounds. It cannot increase `max_payload_size` or `sampling_rate`, enable an unsupported or locally disabled event type, change the endpoint, replace credentials, disable TLS verification, or install executable matching rules.
|
|
114
|
+
|
|
115
|
+
Release correlation values are copied into the immutable snapshot. Calling `Chronos.notify_deploy` reports a deployment but does not mutate those values for concurrent application events; configure each deployed process with its own release identity.
|
data/docs/data-collected.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Data collected
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
3
|
+
Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and aggregated request/query/job/external-HTTP metric batches. 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
|
|---|---|---|
|
|
@@ -45,8 +45,10 @@ Version 0.8 emits exceptions, cache telemetry, one dependency inventory, and agg
|
|
|
45
45
|
| Cache key hash | Disabled by default | Project-scoped SHA-256 when `cache_key_mode = :sha256` |
|
|
46
46
|
| Loaded gem names/versions and Ruby runtime | Once per agent by default; bounded | `Gem.loaded_specs` and Ruby constants |
|
|
47
47
|
| Rails, web server, database adapter, Sidekiq, release | Included when safely detectable/configured | Loaded constants/specs and `app_version` |
|
|
48
|
+
| Event release, revision, deploy ID, environment, service, region, instance | Present as bounded correlation; values optional | Explicit immutable configuration or normalized deploy payload |
|
|
49
|
+
| Deploy environment, revision, version, repository, actor, deploy ID, service, region, instance | Explicit synchronous deploy API | Application/deployment integration arguments |
|
|
48
50
|
|
|
49
|
-
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, raw cache keys/values, mail bodies/recipients, gem paths, or lockfile contents. Sidekiq JIDs/arguments
|
|
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.
|
|
50
52
|
|
|
51
53
|
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.
|
|
52
54
|
|
data/docs/examples/plain-ruby.md
CHANGED
|
@@ -29,4 +29,10 @@ For network-free per-instance outbound HTTP instrumentation, run:
|
|
|
29
29
|
bundle _1.17.3_ exec ruby examples/plain-ruby/external_http.rb
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
For a network-free deploy event with complete release correlation, run:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bundle _1.17.3_ exec ruby examples/plain-ruby/deploy_tracking.rb
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The examples demonstrate final event construction, privacy filtering, bounded external HTTP fields, and deploy correlation without opening a socket.
|
|
@@ -11,3 +11,5 @@ Tests in `spec/unit/configuration_spec.rb` verify required fields, HTTPS default
|
|
|
11
11
|
Version 0.7 adds bounded APM capacities, thresholds, and histogram boundaries. Invalid zero/negative capacities, batches above 50, N+1 thresholds below two, and non-increasing histogram boundaries are rejected before agent construction. See [Essential APM aggregation](apm-aggregation.md).
|
|
12
12
|
|
|
13
13
|
Version 0.8 adds Boolean HTTP/dependency switches, `:none`/`:sha256` cache-key policy, and a dependency limit from 1 to 200. Invalid values are rejected before instrumentation or collection starts. See [External HTTP](external-http.md), [Cache observability](cache-observability.md), and [Dependency inventory](dependencies.md).
|
|
14
|
+
|
|
15
|
+
Version 0.9 adds optional `revision`, `deploy_id`, `region`, and `instance_id` strings, each limited to 128 bytes. Together with the existing release/environment/service options they form immutable event correlation. See [Deploy tracking and release correlation](deploy-tracking.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Dependency inventory
|
|
2
2
|
|
|
3
|
-
Version `0.
|
|
3
|
+
Version `0.9.0.pre.1` emits a separate `dependencies` event at most once per configured agent and once again after a successful deploy notification. It is queued before the first capture or during explicit flush/close and is never attached to each exception.
|
|
4
4
|
|
|
5
5
|
The inventory contains Ruby version/engine/platform, bounded names and versions from already loaded gem specs, the configured `app_version` release identifier, and Rails, Sidekiq, web server, or database adapter labels when safely detectable. The agent does not parse `Gemfile.lock`, activate missing gems, inspect gem paths/source, scan environment variables, open a database connection, or collect dependency configuration.
|
|
6
6
|
|
|
@@ -15,4 +15,4 @@ end
|
|
|
15
15
|
Chronos.report_dependencies
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
`dependency_max_items` accepts 1–200 and defaults to 100. Entries are sorted by name
|
|
18
|
+
`dependency_max_items` accepts 1–200 and defaults to 100. Entries are sorted by name after bounded selection. Set `dependency_reporting = false` when inventory collection is unnecessary or prohibited. A successful `notify_deploy` resets the once-only guard and uses the deploy version for the refreshed inventory; otherwise gems loaded after the first event appear only after a new agent configuration or process boot.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Deploy tracking and release correlation
|
|
2
|
+
|
|
3
|
+
Version `0.9.0.pre.1` adds synchronous deploy notifications and a bounded correlation block to every exception and telemetry envelope. The SaaS can compare errors and performance before and after a release using release, revision, deploy ID, environment, service, region, and instance.
|
|
4
|
+
|
|
5
|
+
## Public API
|
|
6
|
+
|
|
7
|
+
Configure the agent, then provide deploy metadata explicitly:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
Chronos.notify_deploy(
|
|
11
|
+
:environment => "production",
|
|
12
|
+
:revision => ENV["GIT_SHA"],
|
|
13
|
+
:version => ENV["APP_VERSION"],
|
|
14
|
+
:repository => "owner/repository",
|
|
15
|
+
:actor => ENV["DEPLOY_USER"],
|
|
16
|
+
:deploy_id => ENV["DEPLOY_ID"],
|
|
17
|
+
:service => "billing",
|
|
18
|
+
:region => "sa-east-1",
|
|
19
|
+
:instance => "web-1"
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Environment is required, and either revision or version must be present. A UUID deploy ID is generated when none is supplied. The call uses synchronous delivery, bypasses ordinary event sampling, then refreshes the bounded dependency inventory for the new release and flushes it before returning. Local/remote event disabling and the kill switch still apply. It returns `false` instead of leaking configuration, normalization, serialization, or transport failures.
|
|
24
|
+
|
|
25
|
+
Fields are limited to 128 bytes, except repository at 512 bytes. HTTP and SCP-style repository credentials are stripped before serialization. The shared sanitizer still filters explicit actor/repository content. Do not send access tokens, e-mail addresses, or unnecessary personal data.
|
|
26
|
+
|
|
27
|
+
## Correlation on application events
|
|
28
|
+
|
|
29
|
+
Set the values that identify the currently running release during normal application configuration:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
Chronos.configure do |config|
|
|
33
|
+
# project and endpoint settings omitted
|
|
34
|
+
config.app_version = ENV["APP_VERSION"]
|
|
35
|
+
config.revision = ENV["GIT_SHA"]
|
|
36
|
+
config.deploy_id = ENV["DEPLOY_ID"]
|
|
37
|
+
config.environment = ENV["APP_ENV"] || "production"
|
|
38
|
+
config.service_name = "billing"
|
|
39
|
+
config.region = ENV["REGION"]
|
|
40
|
+
config.instance_id = ENV["INSTANCE_ID"]
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The gem never reads these variables itself. The host application chooses each source. Configuration snapshots are immutable, so `notify_deploy` does not mutate correlation for an already running process; newly deployed processes must start with their own release values.
|
|
45
|
+
|
|
46
|
+
## Capistrano
|
|
47
|
+
|
|
48
|
+
Require the optional entry point after Capistrano loads:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
require "chronos/capistrano"
|
|
52
|
+
|
|
53
|
+
set :chronos_version, ENV["APP_VERSION"]
|
|
54
|
+
set :chronos_actor, ENV["DEPLOY_USER"]
|
|
55
|
+
set :chronos_deploy_id, ENV["DEPLOY_ID"]
|
|
56
|
+
set :chronos_service, "billing"
|
|
57
|
+
set :chronos_region, ENV["DEPLOY_REGION"]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
It registers `chronos:notify_deploy` after `deploy:published`, once per DSL object. Revision, stage, and repository use the public Capistrano variables `current_revision`, `stage`, and `repo_url`; all Chronos-specific values are optional and explicit. The integration adds no Capistrano runtime dependency.
|
|
61
|
+
|
|
62
|
+
## Manual, Kamal, and GitHub Actions
|
|
63
|
+
|
|
64
|
+
[`examples/deploy/notify.rb`](../../examples/deploy/notify.rb) is the shared command. It requires project credentials/host and deploy environment, reads only named variables, notifies synchronously, closes the agent, and exits nonzero on failure.
|
|
65
|
+
|
|
66
|
+
For Kamal, pass the variables to the deployed container or command environment and execute after publication:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
kamal app exec --reuse "bundle exec ruby examples/deploy/notify.rb"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Adapt the command to the application's Kamal hook lifecycle and secret-management policy. The repository also provides a non-active [GitHub Actions workflow example](../../examples/deploy/github-actions.yml) using read-only repository permission and secret-scoped Chronos credentials.
|
|
73
|
+
|
|
74
|
+
For a network-free payload demonstration:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bundle _1.17.3_ exec ruby examples/plain-ruby/deploy_tracking.rb
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Limitations
|
|
81
|
+
|
|
82
|
+
- delivery is process-local and uses the existing finite retry/circuit/backlog policy;
|
|
83
|
+
- an unsuccessful synchronous delivery returns `false`; deployment policy decides whether that blocks publication;
|
|
84
|
+
- Capistrano auto-registration requires its task DSL to be loaded first;
|
|
85
|
+
- Kamal integration is command/documentation based, not a Kamal plugin;
|
|
86
|
+
- repository and actor are explicit metadata and remain subject to application privacy review.
|
|
@@ -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.
|
|
7
|
+
gem "chronos-ruby", "0.9.0.pre.1", :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.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Framework telemetry events
|
|
2
2
|
|
|
3
|
-
`Chronos::Core::TelemetryEvent` and `Chronos::Core::TelemetrySerializer` extend the v1 envelope to `request`, `query`, `job`, `cache`, `external_http`, `dependencies`, and `metric_batch` events. These values exist so integrations do not model operational telemetry as fake exceptions.
|
|
3
|
+
`Chronos::Core::TelemetryEvent` and `Chronos::Core::TelemetrySerializer` extend the v1 envelope to `request`, `query`, `job`, `cache`, `external_http`, `dependencies`, `deploy`, and `metric_batch` events. These values exist so integrations do not model operational telemetry as fake exceptions.
|
|
4
4
|
|
|
5
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
6
|
|
|
7
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
8
|
|
|
9
9
|
Version 0.8 aggregates request, query, job, and enabled external HTTP observations into `metric_batch` events by default. Cache remains an individual event while contributing to an existing traced request breakdown. Dependencies use one separate event per agent. Set `apm_enabled = false` only when individual legacy telemetry is required for diagnostics. Percentiles remain server-side; see [Essential APM aggregation](apm-aggregation.md).
|
|
10
|
+
|
|
11
|
+
Version 0.9 delivers deploy telemetry synchronously because deployment commands are short-lived. Every telemetry and exception envelope contains the fixed correlation fields documented in [Deploy tracking and release correlation](deploy-tracking.md).
|
data/docs/performance.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Performance
|
|
2
2
|
|
|
3
|
-
Performance is a functional requirement, but version 0.
|
|
3
|
+
Performance is a functional requirement, but version 0.9 makes no unverified speed claim.
|
|
4
4
|
|
|
5
5
|
Current controls:
|
|
6
6
|
|
|
@@ -24,6 +24,8 @@ Current controls:
|
|
|
24
24
|
- outbound HTTP instrumentation uses two clock reads and bounded metadata without body/header traversal;
|
|
25
25
|
- cache normalization is bounded and SHA-256 runs only when explicitly enabled;
|
|
26
26
|
- dependency inventory runs at most once per agent and is capped at 200 loaded specs.
|
|
27
|
+
- event correlation copies exactly seven strings with a 128-byte bound each;
|
|
28
|
+
- deploy notification uses the existing synchronous delivery path and creates no timer or worker type.
|
|
27
29
|
|
|
28
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.
|
|
29
31
|
|
|
@@ -115,3 +117,15 @@ ITERATIONS=100000 bundle _1.17.3_ exec ruby benchmarks/external_http.rb
|
|
|
115
117
|
The fixture compares a no-op Net::HTTP-compatible object with the per-instance wrapper. It includes trace-header injection, bounded outcome capture, and clock reads, but excludes DNS, sockets, TLS, serialization, queueing, and network delivery. Record a controlled result before making a performance claim.
|
|
116
118
|
|
|
117
119
|
A local diagnostic run on 2026-07-20 used Ruby 2.2.10 on macOS arm64 and 10,000 calls. It measured approximately 15.155 microseconds of wrapper overhead per call. This single run has no controlled warmup, median, or dispersion and is not a production performance claim.
|
|
120
|
+
|
|
121
|
+
## Version 0.9 correlation benchmark
|
|
122
|
+
|
|
123
|
+
Run:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ITERATIONS=100000 bundle _1.17.3_ exec ruby benchmarks/correlation.rb
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The fixture builds the seven-field frozen correlation hash from an immutable configuration. It excludes notice/event construction, sanitization, JSON, queueing, retry, and network delivery. Record a controlled result before making a performance claim.
|
|
130
|
+
|
|
131
|
+
A local diagnostic run on 2026-07-21 used Ruby 2.2.10 on macOS arm64 and 10,000 correlations. It measured approximately 20.490 microseconds per correlation. This single run has no controlled warmup, median, or dispersion and is not a production performance claim.
|
data/docs/privacy-lgpd.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Privacy and LGPD
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
3
|
+
Version 0.9 sanitizes exception, framework telemetry, dependency/deploy inventory, correlation, and APM metric batches 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
|
|
|
@@ -19,8 +19,17 @@ Version 0.8 sanitizes exception, framework telemetry, dependency inventory, and
|
|
|
19
19
|
| External HTTP | Host/method/status/timing only; URL path/query, Authorization, bodies, headers, and error messages omitted |
|
|
20
20
|
| Cache key | Omitted by default; optional project-scoped SHA-256 hash; cache value never read |
|
|
21
21
|
| Dependencies | Bounded loaded gem names/versions and detected runtime labels; paths and lockfiles omitted |
|
|
22
|
+
| Deploy correlation | Explicit bounded operational identifiers; no automatic Git or environment scan |
|
|
23
|
+
| Repository | Credentials removed from common HTTP/SCP forms; query/fragment omitted for parsed URLs |
|
|
24
|
+
| Actor | Explicit and sanitized; omit it when personal identity is unnecessary |
|
|
22
25
|
|
|
23
|
-
The retry backlog exists only in process memory, accepts only `SerializedEvent`, has a fixed capacity, and disappears on process exit. Version 0.
|
|
26
|
+
The retry backlog exists only in process memory, accepts only `SerializedEvent`, has a fixed capacity, and disappears on process exit. Version 0.9 does not persist telemetry to disk.
|
|
27
|
+
|
|
28
|
+
## Deploy metadata
|
|
29
|
+
|
|
30
|
+
Release, revision, deploy ID, environment, service, region, and instance are operational identifiers but can become personal or sensitive when applications encode customer, tenant, employee, or infrastructure-secret values in them. Use opaque bounded identifiers. Repository should be an owner/name identifier rather than a credential-bearing clone URL. Actor is optional; prefer a service account label when individual identity is unnecessary.
|
|
31
|
+
|
|
32
|
+
The deploy normalizer removes user information from standard HTTP(S) repository URLs and usernames from SCP-style Git references. This is defensive, not a substitute for secret management. Never pass access tokens in any deploy field. All values still traverse the common sanitizer before transport.
|
|
24
33
|
|
|
25
34
|
## External services, cache, and dependencies
|
|
26
35
|
|
data/docs/troubleshooting.md
CHANGED
|
@@ -36,6 +36,18 @@ Set `external_http_trace_headers = false` before configuring the agent, or do no
|
|
|
36
36
|
|
|
37
37
|
The event is emitted once per agent and contains only gems loaded at collection time, capped by `dependency_max_items`. It does not parse the lockfile or activate optional frameworks. Call `Chronos.report_dependencies` after application boot if the first event can occur before all integrations load, or set `dependency_reporting = false` to disable collection.
|
|
38
38
|
|
|
39
|
+
## `Chronos.notify_deploy` returns false
|
|
40
|
+
|
|
41
|
+
Confirm Chronos is configured/enabled, environment is present, and at least revision or version is non-empty. Check credentials, TLS, proxy, timeout, retry/circuit state, and endpoint response. The call is synchronous and deliberately returns the delivery result. Repository credentials are removed and do not help authenticate with Chronos.
|
|
42
|
+
|
|
43
|
+
## Events have empty or stale release correlation
|
|
44
|
+
|
|
45
|
+
Set `app_version`, `revision`, `deploy_id`, `environment`, `service_name`, `region`, and `instance_id` before `Chronos.configure` creates its snapshot. `notify_deploy` does not mutate a running agent because that would mix release identities across concurrent events. Restart/configure each deployed process with the new values.
|
|
46
|
+
|
|
47
|
+
## Capistrano task is missing
|
|
48
|
+
|
|
49
|
+
Require `chronos/capistrano` after the Capistrano/Rake task DSL is loaded. The installer registers once per DSL and hooks `chronos:notify_deploy` after `deploy:published`. Confirm the base Chronos configuration runs in the task process and the expected `stage`, `current_revision`, `repo_url`, and `chronos_*` variables exist.
|
|
50
|
+
|
|
39
51
|
## Sidekiq telemetry is missing
|
|
40
52
|
|
|
41
53
|
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.
|
data/lib/chronos/agent.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Chronos
|
|
|
37
37
|
pipeline_options
|
|
38
38
|
)
|
|
39
39
|
initialize_capture(options)
|
|
40
|
-
|
|
40
|
+
initialize_observability(options)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def notify(exception, context = {})
|
|
@@ -67,6 +67,8 @@ module Chronos
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def record_event(event_type, payload = {}, context = {})
|
|
70
|
+
return false if event_type.to_s == "deploy"
|
|
71
|
+
|
|
70
72
|
report_dependencies unless event_type.to_s == "dependencies"
|
|
71
73
|
@telemetry.call(event_type, payload, telemetry_context(context))
|
|
72
74
|
end
|
|
@@ -81,6 +83,18 @@ module Chronos
|
|
|
81
83
|
false
|
|
82
84
|
end
|
|
83
85
|
|
|
86
|
+
def notify_deploy(attributes = {}, timeout = DEFAULT_FLUSH_TIMEOUT)
|
|
87
|
+
payload = @deploy_normalizer.call(attributes)
|
|
88
|
+
return false unless @telemetry.call_sync("deploy", payload, {})
|
|
89
|
+
|
|
90
|
+
@dependency_reporter.reset(payload["version"]) if @dependency_reporter.respond_to?(:reset)
|
|
91
|
+
report_dependencies
|
|
92
|
+
flush(timeout)
|
|
93
|
+
rescue StandardError => error
|
|
94
|
+
@logger.warn("Chronos deploy notification failed: #{error.class}")
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
|
|
84
98
|
def record_event_once(key, event_type, payload = {}, context = {})
|
|
85
99
|
execution = @context_store.get
|
|
86
100
|
captured = execution[:__chronos_captured_events] || {}
|
|
@@ -176,6 +190,11 @@ module Chronos
|
|
|
176
190
|
@telemetry = options[:telemetry] || Application::CaptureTelemetry.new(@config, @delivery_pipeline, @logger)
|
|
177
191
|
end
|
|
178
192
|
|
|
193
|
+
def initialize_observability(options)
|
|
194
|
+
@dependency_reporter = options[:dependency_reporter] || Application::DependencyReporter.new(@config)
|
|
195
|
+
@deploy_normalizer = options[:deploy_normalizer] || Core::DeployNormalizer.new(@config)
|
|
196
|
+
end
|
|
197
|
+
|
|
179
198
|
def build_context_store(strategy)
|
|
180
199
|
return Adapters::ThreadLocalContextStore.new if strategy == :thread_local
|
|
181
200
|
|
|
@@ -40,6 +40,17 @@ module Chronos
|
|
|
40
40
|
false
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
def call_sync(event_type, payload = {}, context = {})
|
|
44
|
+
return false unless @config.enabled_for_environment?
|
|
45
|
+
return false unless synchronous_capture_allowed?(event_type)
|
|
46
|
+
|
|
47
|
+
event = Core::TelemetryEvent.new(event_type, payload, context)
|
|
48
|
+
@delivery_pipeline.deliver_sync(@serializer.call(event))
|
|
49
|
+
rescue StandardError => error
|
|
50
|
+
@logger.warn("Chronos synchronous telemetry capture failed: #{error.class}")
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
43
54
|
def flush
|
|
44
55
|
enqueue_batches(@aggregator.flush)
|
|
45
56
|
rescue StandardError => error
|
|
@@ -55,6 +66,12 @@ module Chronos
|
|
|
55
66
|
|
|
56
67
|
private
|
|
57
68
|
|
|
69
|
+
def synchronous_capture_allowed?(event_type)
|
|
70
|
+
return @delivery_pipeline.event_enabled?(event_type) if event_type.to_s == "deploy"
|
|
71
|
+
|
|
72
|
+
@delivery_pipeline.capture_allowed?(event_type)
|
|
73
|
+
end
|
|
74
|
+
|
|
58
75
|
def aggregate_type?(event_type)
|
|
59
76
|
ApmAggregator::METRIC_TYPES.include?(event_type.to_s)
|
|
60
77
|
end
|
|
@@ -19,6 +19,17 @@ module Chronos
|
|
|
19
19
|
@constants = options[:constants] || {}
|
|
20
20
|
@mutex = Mutex.new
|
|
21
21
|
@reported = false
|
|
22
|
+
@release_override = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def reset(release = nil)
|
|
26
|
+
@mutex.synchronize do
|
|
27
|
+
@release_override = bounded(release, 128)
|
|
28
|
+
@reported = false
|
|
29
|
+
end
|
|
30
|
+
true
|
|
31
|
+
rescue StandardError
|
|
32
|
+
false
|
|
22
33
|
end
|
|
23
34
|
|
|
24
35
|
def call
|
|
@@ -45,7 +56,7 @@ module Chronos
|
|
|
45
56
|
"web_server" => detected("web_server") { web_server },
|
|
46
57
|
"database_adapter" => detected("database_adapter") { database_adapter },
|
|
47
58
|
"sidekiq" => detected("sidekiq") { sidekiq_version },
|
|
48
|
-
"release" => bounded(@config.app_version.to_s, 128)
|
|
59
|
+
"release" => @release_override || bounded(@config.app_version.to_s, 128)
|
|
49
60
|
}
|
|
50
61
|
payload.delete_if do |key, value|
|
|
51
62
|
!["dependencies", "ruby"].include?(key) && value.to_s.empty?
|
|
@@ -14,7 +14,7 @@ module Chronos
|
|
|
14
14
|
# @performance Validation is bounded by configured document and list limits.
|
|
15
15
|
class RemoteConfiguration
|
|
16
16
|
SUPPORTED_EVENT_TYPES = %w(
|
|
17
|
-
exception request query job cache external_http dependencies metric_batch
|
|
17
|
+
exception request query job cache external_http dependencies deploy metric_batch
|
|
18
18
|
).freeze
|
|
19
19
|
MAX_IGNORED_FINGERPRINTS = 100
|
|
20
20
|
MAX_FINGERPRINT_BYTES = 256
|
|
@@ -65,9 +65,20 @@ module Chronos
|
|
|
65
65
|
unless dependency_max_items.is_a?(Integer) && dependency_max_items >= 1 && dependency_max_items <= 200
|
|
66
66
|
errors << "dependency_max_items must be between 1 and 200"
|
|
67
67
|
end
|
|
68
|
+
correlation_attributes.each do |name, value|
|
|
69
|
+
errors << "#{name} must be a String with at most 128 bytes" unless bounded_optional_string?(value, 128)
|
|
70
|
+
end
|
|
68
71
|
errors
|
|
69
72
|
end
|
|
70
73
|
|
|
74
|
+
def correlation_attributes
|
|
75
|
+
{:revision => revision, :deploy_id => deploy_id, :region => region, :instance_id => instance_id}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def bounded_optional_string?(value, limit)
|
|
79
|
+
value.nil? || (value.is_a?(String) && value.bytesize <= limit)
|
|
80
|
+
end
|
|
81
|
+
|
|
71
82
|
def boolean?(value)
|
|
72
83
|
[true, false].include?(value)
|
|
73
84
|
end
|
|
@@ -32,7 +32,8 @@ module Chronos
|
|
|
32
32
|
|
|
33
33
|
ATTRIBUTES = [
|
|
34
34
|
:project_id, :project_key, :host, :environment, :app_version,
|
|
35
|
-
:service_name, :
|
|
35
|
+
:service_name, :revision, :deploy_id, :region, :instance_id,
|
|
36
|
+
:root_directory, :logger, :timeout, :open_timeout,
|
|
36
37
|
:queue_size, :workers, :enabled, :error_notifications,
|
|
37
38
|
:ignored_environments, :proxy, :ssl_verify, :user_agent,
|
|
38
39
|
:max_payload_size, :gzip, :blocklist_keys, :allowlist_keys,
|
|
@@ -59,6 +60,7 @@ module Chronos
|
|
|
59
60
|
initialize_rails_defaults
|
|
60
61
|
initialize_apm_defaults
|
|
61
62
|
initialize_observability_defaults
|
|
63
|
+
initialize_correlation_defaults
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
def snapshot
|
|
@@ -120,6 +122,13 @@ module Chronos
|
|
|
120
122
|
@breadcrumb_max_bytes = 2048
|
|
121
123
|
end
|
|
122
124
|
|
|
125
|
+
def initialize_correlation_defaults
|
|
126
|
+
@revision = nil
|
|
127
|
+
@deploy_id = nil
|
|
128
|
+
@region = nil
|
|
129
|
+
@instance_id = nil
|
|
130
|
+
end
|
|
131
|
+
|
|
123
132
|
def initialize_rails_defaults
|
|
124
133
|
@rails_enabled = true
|
|
125
134
|
@rails_capture_in_console = false
|
|
@@ -167,7 +176,7 @@ module Chronos
|
|
|
167
176
|
@remote_config_max_bytes = 4096
|
|
168
177
|
@sampling_rate = 1.0
|
|
169
178
|
@enabled_event_types = [
|
|
170
|
-
"exception", "request", "query", "job", "cache", "external_http", "dependencies", "metric_batch"
|
|
179
|
+
"exception", "request", "query", "job", "cache", "external_http", "dependencies", "deploy", "metric_batch"
|
|
171
180
|
]
|
|
172
181
|
@max_remote_send_interval = 60.0
|
|
173
182
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Chronos
|
|
2
|
+
module Core
|
|
3
|
+
# Builds the bounded release/deploy correlation shared by every event envelope.
|
|
4
|
+
#
|
|
5
|
+
# @responsibility Normalize release, revision, deploy, environment, service, region, and instance.
|
|
6
|
+
# @motivation Give the SaaS stable before/after-deploy dimensions without integration-specific fields.
|
|
7
|
+
# @limits Values come only from explicit configuration or caller overrides; no environment is scanned.
|
|
8
|
+
# @collaborators PayloadSerializer, TelemetrySerializer, and Configuration::Snapshot.
|
|
9
|
+
# @thread_safety Instances hold immutable configuration and calls allocate independent frozen hashes.
|
|
10
|
+
# @compatibility Ruby 2.2.10 through Ruby 2.6.
|
|
11
|
+
# @example
|
|
12
|
+
# CorrelationContext.new(config).call("revision" => "abc123")
|
|
13
|
+
# @errors Unreadable optional values become nil and do not escape.
|
|
14
|
+
# @performance Seven strings are copied with a fixed 128-byte maximum each.
|
|
15
|
+
class CorrelationContext
|
|
16
|
+
FIELDS = %w(release revision deploy_id environment service region instance).freeze
|
|
17
|
+
|
|
18
|
+
def initialize(config)
|
|
19
|
+
@config = config
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(overrides = {})
|
|
23
|
+
values = defaults.merge(string_hash(overrides))
|
|
24
|
+
result = FIELDS.each_with_object({}) do |name, correlation|
|
|
25
|
+
correlation[name.freeze] = bounded(values[name], 128)
|
|
26
|
+
end
|
|
27
|
+
result.each_value { |value| value.freeze if value }
|
|
28
|
+
result.freeze
|
|
29
|
+
rescue StandardError
|
|
30
|
+
FIELDS.each_with_object({}) { |name, fallback| fallback[name] = nil }.freeze
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def defaults
|
|
36
|
+
{
|
|
37
|
+
"release" => @config.app_version, "revision" => @config.revision,
|
|
38
|
+
"deploy_id" => @config.deploy_id, "environment" => @config.environment,
|
|
39
|
+
"service" => @config.service_name, "region" => @config.region,
|
|
40
|
+
"instance" => @config.instance_id
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def string_hash(value)
|
|
45
|
+
return {} unless value.is_a?(Hash)
|
|
46
|
+
|
|
47
|
+
value.each_with_object({}) do |(key, child), result|
|
|
48
|
+
result[key.to_s] = child if key.is_a?(String) || key.is_a?(Symbol)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def bounded(value, limit)
|
|
53
|
+
return nil if value.nil?
|
|
54
|
+
return nil unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric)
|
|
55
|
+
|
|
56
|
+
text = value.to_s
|
|
57
|
+
text = text.scrub("?") if text.respond_to?(:scrub)
|
|
58
|
+
text = text.byteslice(0, limit) if text.bytesize > limit
|
|
59
|
+
text
|
|
60
|
+
rescue StandardError
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
require "uri"
|
|
3
|
+
|
|
4
|
+
module Chronos
|
|
5
|
+
module Core
|
|
6
|
+
# Normalizes explicit deployment metadata into the versioned deploy payload.
|
|
7
|
+
#
|
|
8
|
+
# @responsibility Validate required deploy identity and bound every public API field.
|
|
9
|
+
# @motivation Deployment commands need predictable safe input independent of frameworks.
|
|
10
|
+
# @limits It does not inspect Git, environment variables, credentials, or deployment systems.
|
|
11
|
+
# @collaborators Chronos.notify_deploy, CorrelationContext, and Configuration::Snapshot.
|
|
12
|
+
# @thread_safety Stateless apart from immutable configuration and an injected ID generator.
|
|
13
|
+
# @compatibility Ruby 2.2.10 through Ruby 2.6.
|
|
14
|
+
# @example
|
|
15
|
+
# DeployNormalizer.new(config).call(:revision => "abc", :version => "1.2.3")
|
|
16
|
+
# @errors Missing environment or both revision/version raise ArgumentError for the agent to contain.
|
|
17
|
+
# @performance Constant work over nine bounded scalar fields.
|
|
18
|
+
class DeployNormalizer
|
|
19
|
+
def initialize(config, options = {})
|
|
20
|
+
@config = config
|
|
21
|
+
@id_generator = options[:id_generator] || proc { SecureRandom.uuid }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(attributes = {})
|
|
25
|
+
values = string_hash(attributes)
|
|
26
|
+
payload = {
|
|
27
|
+
"deploy_id" => deploy_id(values["deploy_id"] || @config.deploy_id),
|
|
28
|
+
"environment" => bounded(values["environment"] || @config.environment, 128),
|
|
29
|
+
"revision" => bounded(values["revision"] || @config.revision, 128),
|
|
30
|
+
"version" => bounded(values["version"] || @config.app_version, 128),
|
|
31
|
+
"repository" => repository(values["repository"]),
|
|
32
|
+
"actor" => bounded(values["actor"], 128),
|
|
33
|
+
"service" => bounded(values["service"] || @config.service_name, 128),
|
|
34
|
+
"region" => bounded(values["region"] || @config.region, 128),
|
|
35
|
+
"instance" => bounded(values["instance"] || @config.instance_id, 128)
|
|
36
|
+
}
|
|
37
|
+
validate!(payload)
|
|
38
|
+
payload
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def validate!(payload)
|
|
44
|
+
raise ArgumentError, "deploy environment is required" if payload["environment"].to_s.empty?
|
|
45
|
+
return unless payload["revision"].to_s.empty? && payload["version"].to_s.empty?
|
|
46
|
+
|
|
47
|
+
raise ArgumentError, "deploy revision or version is required"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def generated_id
|
|
51
|
+
value = bounded(@id_generator.call, 128)
|
|
52
|
+
raise ArgumentError, "deploy ID is required" if value.to_s.empty?
|
|
53
|
+
|
|
54
|
+
value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def deploy_id(value)
|
|
58
|
+
normalized = bounded(value, 128)
|
|
59
|
+
normalized.to_s.empty? ? generated_id : normalized
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def repository(value)
|
|
63
|
+
text = bounded(value, 512)
|
|
64
|
+
return nil if text.nil?
|
|
65
|
+
|
|
66
|
+
scp = text.match(/\A[^@]+@([^:]+):(.+)\z/)
|
|
67
|
+
return bounded("#{scp[1]}/#{scp[2]}", 512) if scp
|
|
68
|
+
|
|
69
|
+
uri = URI.parse(text)
|
|
70
|
+
return bounded("#{uri.host}#{uri.path}", 512) if uri.host
|
|
71
|
+
return bounded(uri.path, 512) unless uri.path.to_s.empty?
|
|
72
|
+
|
|
73
|
+
nil
|
|
74
|
+
rescue StandardError
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def string_hash(value)
|
|
79
|
+
raise ArgumentError, "deploy attributes must be a Hash" unless value.is_a?(Hash)
|
|
80
|
+
|
|
81
|
+
value.each_with_object({}) do |(key, child), result|
|
|
82
|
+
result[key.to_s] = child if key.is_a?(String) || key.is_a?(Symbol)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def bounded(value, limit)
|
|
87
|
+
return nil if value.nil?
|
|
88
|
+
return nil unless scalar?(value)
|
|
89
|
+
|
|
90
|
+
text = value.to_s
|
|
91
|
+
text = text.scrub("?") if text.respond_to?(:scrub)
|
|
92
|
+
text.bytesize > limit ? text.byteslice(0, limit) : text
|
|
93
|
+
rescue StandardError
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def scalar?(value)
|
|
98
|
+
value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -44,6 +44,7 @@ module Chronos
|
|
|
44
44
|
@sanitizer = options[:sanitizer] || Sanitizer.new(config)
|
|
45
45
|
@safe_serializer = options[:safe_serializer] || SafeSerializer.new
|
|
46
46
|
@max_payload_size = options[:max_payload_size] || proc { @config.max_payload_size }
|
|
47
|
+
@correlation = options[:correlation] || CorrelationContext.new(config)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def call(notice)
|
|
@@ -70,8 +71,10 @@ module Chronos
|
|
|
70
71
|
"service" => {
|
|
71
72
|
"name" => @config.service_name,
|
|
72
73
|
"version" => @config.app_version,
|
|
73
|
-
"instance_id" => notice.host
|
|
74
|
+
"instance_id" => @config.instance_id || notice.host
|
|
74
75
|
},
|
|
76
|
+
"correlation" => @correlation.call("environment" => notice.environment,
|
|
77
|
+
"instance" => @config.instance_id || notice.host),
|
|
75
78
|
"runtime" => notice.runtime,
|
|
76
79
|
"context" => notice.context,
|
|
77
80
|
"payload" => payload(notice)
|
|
@@ -16,7 +16,7 @@ module Chronos
|
|
|
16
16
|
# @errors Unsupported types raise ArgumentError during construction.
|
|
17
17
|
# @performance Construction is linear in supplied context and payload size.
|
|
18
18
|
class TelemetryEvent
|
|
19
|
-
TYPES = %w(request query job cache external_http dependencies metric_batch).freeze
|
|
19
|
+
TYPES = %w(request query job cache external_http dependencies deploy metric_batch).freeze
|
|
20
20
|
|
|
21
21
|
attr_reader :event_id, :event_type, :timestamp, :context, :payload
|
|
22
22
|
|
|
@@ -68,6 +68,7 @@ module Chronos
|
|
|
68
68
|
@safe_serializer = options[:safe_serializer] || SafeSerializer.new
|
|
69
69
|
@max_payload_size = options[:max_payload_size] || proc { @config.max_payload_size }
|
|
70
70
|
@runtime_info = RuntimeInfo.new
|
|
71
|
+
@correlation = options[:correlation] || CorrelationContext.new(config)
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
def call(event)
|
|
@@ -89,13 +90,39 @@ module Chronos
|
|
|
89
90
|
"schema_version" => "1.0", "event_id" => event.event_id,
|
|
90
91
|
"event_type" => event.event_type, "occurred_at" => event.timestamp,
|
|
91
92
|
"sent_at" => @clock.call.utc.iso8601(6), "project_key" => @config.project_id,
|
|
92
|
-
"environment" =>
|
|
93
|
-
"service" =>
|
|
94
|
-
|
|
93
|
+
"environment" => event_environment(event),
|
|
94
|
+
"service" => event_service(event, runtime),
|
|
95
|
+
"correlation" => event_correlation(event, runtime),
|
|
95
96
|
"runtime" => runtime[:runtime], "context" => event.context, "payload" => event.payload
|
|
96
97
|
}
|
|
97
98
|
end
|
|
98
99
|
|
|
100
|
+
def event_environment(event)
|
|
101
|
+
event.event_type == "deploy" ? event.payload["environment"] : @config.environment
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def event_service(event, runtime)
|
|
105
|
+
deploy = event.event_type == "deploy" ? event.payload : {}
|
|
106
|
+
{
|
|
107
|
+
"name" => deploy["service"] || @config.service_name,
|
|
108
|
+
"version" => deploy["version"] || @config.app_version,
|
|
109
|
+
"instance_id" => deploy["instance"] || @config.instance_id || runtime[:host]
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def event_correlation(event, runtime)
|
|
114
|
+
overrides = {"instance" => @config.instance_id || runtime[:host]}
|
|
115
|
+
if event.event_type == "deploy"
|
|
116
|
+
overrides.merge!(
|
|
117
|
+
"release" => event.payload["version"], "revision" => event.payload["revision"],
|
|
118
|
+
"deploy_id" => event.payload["deploy_id"], "environment" => event.payload["environment"],
|
|
119
|
+
"service" => event.payload["service"], "region" => event.payload["region"],
|
|
120
|
+
"instance" => event.payload["instance"]
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
@correlation.call(overrides)
|
|
124
|
+
end
|
|
125
|
+
|
|
99
126
|
def compact_envelope(envelope)
|
|
100
127
|
envelope["context"] = {"_truncated" => true}
|
|
101
128
|
envelope["payload"] = {"_truncated" => true}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Chronos
|
|
2
|
+
module Integrations
|
|
3
|
+
# Optional Capistrano task installer using only the public task DSL.
|
|
4
|
+
#
|
|
5
|
+
# @responsibility Register one post-publish task that delegates explicit values to notify_deploy.
|
|
6
|
+
# @motivation Legacy deployments need release reporting without a Capistrano runtime dependency.
|
|
7
|
+
# @limits Values are read only from configured Capistrano variables; Git and ENV are not inspected.
|
|
8
|
+
# @collaborators Capistrano/Rake DSL and the Chronos public facade.
|
|
9
|
+
# @thread_safety Installation is idempotent per DSL object before task execution.
|
|
10
|
+
# @compatibility Capistrano task DSLs available to supported Ruby 2.2.10 through Ruby 2.6 apps.
|
|
11
|
+
# @example
|
|
12
|
+
# require "chronos/capistrano"
|
|
13
|
+
# @errors Missing DSL methods return false; notification failures are contained by Chronos.
|
|
14
|
+
# @performance Adds one task and one after hook; no background worker is created.
|
|
15
|
+
module Capistrano
|
|
16
|
+
INSTALLED_KEY = :@__chronos_capistrano_installed
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def install(dsl)
|
|
20
|
+
return false if dsl.instance_variable_get(INSTALLED_KEY)
|
|
21
|
+
return false unless compatible?(dsl)
|
|
22
|
+
|
|
23
|
+
dsl.send(:namespace, :chronos) do
|
|
24
|
+
dsl.send(:desc, "Notify Chronos about the published deployment") if dsl.respond_to?(:desc, true)
|
|
25
|
+
dsl.send(:task, :notify_deploy) { notify(dsl) }
|
|
26
|
+
end
|
|
27
|
+
dsl.send(:after, "deploy:published", "chronos:notify_deploy")
|
|
28
|
+
dsl.instance_variable_set(INSTALLED_KEY, true)
|
|
29
|
+
true
|
|
30
|
+
rescue StandardError
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def notify(dsl)
|
|
35
|
+
Chronos.notify_deploy(
|
|
36
|
+
:environment => value(dsl, :stage).to_s,
|
|
37
|
+
:revision => value(dsl, :current_revision) || value(dsl, :branch),
|
|
38
|
+
:version => value(dsl, :chronos_version) || value(dsl, :release_name),
|
|
39
|
+
:repository => value(dsl, :repo_url), :actor => value(dsl, :chronos_actor),
|
|
40
|
+
:deploy_id => value(dsl, :chronos_deploy_id), :service => value(dsl, :chronos_service),
|
|
41
|
+
:region => value(dsl, :chronos_region), :instance => value(dsl, :chronos_instance)
|
|
42
|
+
)
|
|
43
|
+
rescue StandardError
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def compatible?(dsl)
|
|
50
|
+
[:namespace, :task, :after, :fetch].all? { |name| dsl.respond_to?(name, true) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def value(dsl, name)
|
|
54
|
+
dsl.send(:fetch, name, nil)
|
|
55
|
+
rescue StandardError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Chronos
|
|
2
|
-
# Public entry points for optional
|
|
2
|
+
# Public entry points for optional observability and deployment integrations.
|
|
3
3
|
#
|
|
4
|
-
# @responsibility Delegate dependency, cache,
|
|
4
|
+
# @responsibility Delegate dependency, cache, outbound HTTP, and deploy calls to the agent.
|
|
5
5
|
# @motivation Keep the main public facade small while preserving one stable Chronos namespace.
|
|
6
6
|
# @limits It installs only explicitly supplied Net::HTTP connections and never enables features.
|
|
7
7
|
# @collaborators Chronos::Agent and Chronos::Integrations::NetHttp.
|
|
@@ -19,6 +19,13 @@ module Chronos
|
|
|
19
19
|
false
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def notify_deploy(attributes = {}, timeout = Agent::DEFAULT_FLUSH_TIMEOUT)
|
|
23
|
+
agent = current_agent
|
|
24
|
+
agent ? agent.notify_deploy(attributes, timeout) : false
|
|
25
|
+
rescue StandardError
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
|
|
22
29
|
def external_http_integration_options
|
|
23
30
|
agent = current_agent
|
|
24
31
|
agent ? agent.external_http_integration_options : {:enabled => false}
|
data/lib/chronos/version.rb
CHANGED
data/lib/chronos.rb
CHANGED
|
@@ -14,6 +14,8 @@ require "chronos/core/notice_builder"
|
|
|
14
14
|
require "chronos/core/sensitive_value_filter"
|
|
15
15
|
require "chronos/core/sanitizer"
|
|
16
16
|
require "chronos/core/safe_serializer"
|
|
17
|
+
require "chronos/core/correlation_context"
|
|
18
|
+
require "chronos/core/deploy_normalizer"
|
|
17
19
|
require "chronos/core/payload_serializer"
|
|
18
20
|
require "chronos/core/telemetry_event"
|
|
19
21
|
require "chronos/core/sql_normalizer"
|
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.
|
|
4
|
+
version: 0.9.0.pre.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Antonio Jefferson
|
|
@@ -88,6 +88,7 @@ files:
|
|
|
88
88
|
- SECURITY.md
|
|
89
89
|
- contracts/apm-batch-v1.schema.json
|
|
90
90
|
- contracts/dependencies-v1.schema.json
|
|
91
|
+
- contracts/deploy-v1.schema.json
|
|
91
92
|
- contracts/event-v1.schema.json
|
|
92
93
|
- contracts/rack-context-v1.schema.json
|
|
93
94
|
- contracts/sidekiq-job-v1.schema.json
|
|
@@ -103,6 +104,7 @@ files:
|
|
|
103
104
|
- docs/adr/ADR-014-sidekiq-envelope-context.md
|
|
104
105
|
- docs/adr/ADR-015-bounded-apm-aggregation.md
|
|
105
106
|
- docs/adr/ADR-016-explicit-observability-integrations.md
|
|
107
|
+
- docs/adr/ADR-017-deploy-events-and-correlation.md
|
|
106
108
|
- docs/architecture.md
|
|
107
109
|
- docs/compatibility.md
|
|
108
110
|
- docs/configuration.md
|
|
@@ -113,6 +115,7 @@ files:
|
|
|
113
115
|
- docs/modules/cache-observability.md
|
|
114
116
|
- docs/modules/configuration.md
|
|
115
117
|
- docs/modules/dependencies.md
|
|
118
|
+
- docs/modules/deploy-tracking.md
|
|
116
119
|
- docs/modules/external-http.md
|
|
117
120
|
- docs/modules/notice-pipeline.md
|
|
118
121
|
- docs/modules/rack-context.md
|
|
@@ -142,6 +145,7 @@ files:
|
|
|
142
145
|
- lib/chronos/application/dependency_reporter.rb
|
|
143
146
|
- lib/chronos/application/remote_configuration.rb
|
|
144
147
|
- lib/chronos/application/retry_policy.rb
|
|
148
|
+
- lib/chronos/capistrano.rb
|
|
145
149
|
- lib/chronos/configuration.rb
|
|
146
150
|
- lib/chronos/configuration/apm_validation.rb
|
|
147
151
|
- lib/chronos/configuration/snapshot.rb
|
|
@@ -150,6 +154,8 @@ files:
|
|
|
150
154
|
- lib/chronos/core/backtrace_parser.rb
|
|
151
155
|
- lib/chronos/core/breadcrumb.rb
|
|
152
156
|
- lib/chronos/core/cache_normalizer.rb
|
|
157
|
+
- lib/chronos/core/correlation_context.rb
|
|
158
|
+
- lib/chronos/core/deploy_normalizer.rb
|
|
153
159
|
- lib/chronos/core/exception_cause_collector.rb
|
|
154
160
|
- lib/chronos/core/metric_aggregate.rb
|
|
155
161
|
- lib/chronos/core/notice.rb
|
|
@@ -163,6 +169,7 @@ files:
|
|
|
163
169
|
- lib/chronos/core/telemetry_event.rb
|
|
164
170
|
- lib/chronos/errors.rb
|
|
165
171
|
- lib/chronos/integrations.rb
|
|
172
|
+
- lib/chronos/integrations/capistrano.rb
|
|
166
173
|
- lib/chronos/integrations/job_payload.rb
|
|
167
174
|
- lib/chronos/integrations/net_http.rb
|
|
168
175
|
- lib/chronos/integrations/rack.rb
|