chronos-ruby 1.1.3 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 348e3ca2ba28d8d97f80281bf0ffb545b013dfac248d7f14527f5d955513d7a2
4
- data.tar.gz: 44e7047ef9d47aac49f5a22e416447aa82efd3944a93eabd61eb36fa74d3fb2b
3
+ metadata.gz: c13c2858edcb41d4b2a0bfc83049349ee13a5a9ecee873798750b4bf9f1da48c
4
+ data.tar.gz: a21babcc28ce763e7d867f35064735d661214724c1ed9d256e8d174a1a04c5dc
5
5
  SHA512:
6
- metadata.gz: 2b4b15808ff7363b470d0c4348fc0a338f4c2ea3eb4112db5b5d6e9c1437315ea57f7ac70f84e7618ea2f120e9210425c31041b328cdc9940c2ebc8593f4a2f3
7
- data.tar.gz: 92a4bdf9041d68747c5c139061f9000f9526ac26ba9b1b913d73fb1413dd4492764bbb700067e7ffdec7610ebf4e16d671b49792c05e2e694664ca97b72b6e99
6
+ metadata.gz: 81ab0672785236e1ffaeecf0fbb5ae3187f319be349a06f724eee50dd0d20963afb74a84485267e8ea5e3a7dd37dacef15ab456783fee622bef901110b98344a
7
+ data.tar.gz: 835ee551ddcec646a44376dcdfc09748e12c0fa360fee62365455e6776503a0597baed504a33276059853e652f5a90458ad7ae43bb582f7647303af9940a5d1f
data/CHANGELOG.md CHANGED
@@ -4,40 +4,23 @@ All notable changes are documented here. The project follows Semantic Versioning
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- ## [1.1.3] - 2026-08-13
7
+ ## [1.2.0.pre.1] - 2026-08-11
8
8
 
9
- ### Changed
10
-
11
- - expanded the gem summary and description to clarify its exception, metrics, and telemetry monitoring purpose;
12
- - highlighted https://chronosmonitor.com.br as the official Chronos Monitor platform for receiving and visualizing observability data;
13
- - synchronized stable legacy release references and installation examples with version `1.1.3`.
9
+ ### Added
14
10
 
15
- ## [1.1.2] - 2026-08-13
11
+ - fiber-aware execution context with a thread-local fallback;
12
+ - Sidekiq 7 and Rails 7 error reporter integration using public extension APIs;
13
+ - Action Cable notification spans and a dependency-free Faraday middleware;
14
+ - optional W3C `traceparent` propagation and an OpenTelemetry bridge that consumes the active span without installing an SDK or exporter.
16
15
 
17
16
  ### Changed
18
17
 
19
- - the generated Rails initializer now exposes only common application-facing options with safe environment overrides;
20
- - the official Chronos endpoint remains an internal default and is no longer assigned in generated initializers;
21
- - Rails derives the service name from the application namespace when `CHRONOS_SERVICE_NAME` is absent;
22
- - Rails applications automatically install the optional Sidekiq 4/5 client and server middleware when Sidekiq is available.
23
-
24
- ### Fixed
25
-
26
- - configuration snapshots no longer freeze the Rails-owned logger, preventing later logger extensions from raising `FrozenError`;
27
- - logger lookup and optional Sidekiq installation failures are contained so they cannot abort Rails boot.
18
+ - the transitional runtime range is Ruby 2.7 through 3.4 and the default context strategy is `:fiber_local`;
19
+ - Active Job and Sidekiq envelopes may propagate allowlisted span ID and trace flags in addition to trace/request IDs.
28
20
 
29
21
  ### Security
30
22
 
31
- - `CHRONOS_SSL_VERIFY` remains enabled by default and unknown Boolean values retain the secure default;
32
- - generated Boolean overrides accept only explicit `1/0`, `true/false`, `yes/no`, and `on/off` forms.
33
-
34
- ## [1.1.1] - 2026-08-11
35
-
36
- ### Changed
37
-
38
- - the Rails install generator now writes every public configuration option with explanatory English comments;
39
- - the official `https://chronosmonitor.com.br` endpoint is now the default host when applications omit `config.host`;
40
- - generated initializers no longer read `CHRONOS_HOST`, while explicit `config.host` assignments remain supported.
23
+ - W3C identifiers are syntax-validated, all-zero IDs are rejected, and Rails error reporter context is reduced to bounded allowlisted metadata.
41
24
 
42
25
  ## [1.1.0] - 2026-08-05
43
26
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Chronos Ruby
2
2
 
3
- Chronos Ruby 1.1.3 é o agente independente de framework para enviar exceções e telemetria limitada de aplicações Ruby ao Chronos. Esta é a linha estável legado, compatível com o protocolo v1 e voltada a Ruby 2.2.10–2.6.
3
+ Chronos Ruby 1.1.0 é o agente independente de framework para enviar exceções e telemetria limitada de aplicações Ruby ao Chronos. Esta é a linha estável legado, compatível com o protocolo v1 e voltada a Ruby 2.2.10–2.6.
4
4
 
5
5
  ## O que a gem coleta
6
6
 
@@ -24,7 +24,7 @@ A gem não varre variáveis de ambiente, sistema de arquivos ou lockfiles e não
24
24
 
25
25
  ## Versões Ruby e Rails suportadas
26
26
 
27
- A versão 1.1.3 suporta Ruby puro e Rack em Ruby 2.2.10, 2.3.8, 2.4.10, 2.5.9 e 2.6.10. As combinações Rails validadas são Rails 4.2 com Ruby 2.2.10/2.3.8 e Rails 5.2 com Ruby 2.5.9/2.6.10. Sidekiq 4.2.10 com Ruby 2.2.10 e Sidekiq 5.2.10 com Ruby 2.5.9 também possuem gates dedicados. Ruby 2.7/Rails 6 não é declarado nesta release porque ainda não possui aplicação e matriz completas.
27
+ A versão candidata 1.2.0.pre.1 inicia a linha transitional para Ruby 2.7–3.4, Rails 7.x e Sidekiq 7. Ela adiciona contexto por Fiber, Rails Error Reporter, Action Cable, Faraday, Trace Context W3C opcional e coexistência com um SDK OpenTelemetry configurado. Use-a explicitamente em staging enquanto a matriz transitional é validada; as linhas legadas permanecem disponíveis em releases anteriores.
28
28
 
29
29
  Rubies e frameworks antigos estão fora do suporte de segurança de seus mantenedores. A Chronos oferece compatibilidade técnica, não manutenção de segurança do runtime. Veja [Compatibilidade](docs/compatibility.md).
30
30
 
@@ -33,7 +33,7 @@ Rubies e frameworks antigos estão fora do suporte de segurança de seus mantene
33
33
  Obrigatório: adicione a versão estável ao `Gemfile`.
34
34
 
35
35
  ```ruby
36
- gem "chronos-ruby", "~> 1.1.3"
36
+ gem "chronos-ruby", "~> 1.1.0"
37
37
  ```
38
38
 
39
39
  Em runtimes antigos, use Bundler compatível:
@@ -46,7 +46,7 @@ bundle _1.17.3_ install
46
46
  Sem Bundler:
47
47
 
48
48
  ```bash
49
- gem install chronos-ruby -v 1.1.3
49
+ gem install chronos-ruby -v 1.1.0
50
50
  ```
51
51
 
52
52
  ## Instalação em Rails
@@ -54,7 +54,7 @@ gem install chronos-ruby -v 1.1.3
54
54
  Obrigatório: carregue a integração Rails explicitamente para manter Rails/ActiveSupport fora de aplicações Ruby puras.
55
55
 
56
56
  ```ruby
57
- gem "chronos-ruby", "~> 1.1.3", :require => "chronos/rails"
57
+ gem "chronos-ruby", "~> 1.1.0", :require => "chronos/rails"
58
58
  ```
59
59
 
60
60
  Gere o initializer:
@@ -67,8 +67,7 @@ A Versão 0.5 introduziu Railtie, middleware e subscribers idempotentes por APIs
67
67
 
68
68
  ## Configuração mínima
69
69
 
70
- Obrigatório quando o agente está habilitado: `project_id` e `project_key`. O `host` usa
71
- `https://chronosmonitor.com.br` por padrão. Recomendado: definir ambiente e serviço explicitamente.
70
+ Obrigatório quando o agente está habilitado: `project_id`, `project_key` e `host` HTTPS. Recomendado: definir ambiente e serviço explicitamente.
72
71
 
73
72
  ```ruby
74
73
  require "chronos"
@@ -76,6 +75,7 @@ require "chronos"
76
75
  Chronos.configure do |config|
77
76
  config.project_id = ENV["CHRONOS_PROJECT_ID"]
78
77
  config.project_key = ENV["CHRONOS_PROJECT_KEY"]
78
+ config.host = "https://chronos.example.com"
79
79
  config.environment = ENV["APP_ENV"] || "production"
80
80
  config.service_name = "billing"
81
81
  config.app_version = ENV["APP_VERSION"]
@@ -204,16 +204,16 @@ Por padrão, SQL bruto e binds não são lidos pelo pipeline de análise. A insp
204
204
 
205
205
  ## Sidekiq e Active Job
206
206
 
207
- A versão `0.6.0.pre.1` introduziu middleware Sidekiq 4/5. Em Rails, a versão 1.1.3 detecta Sidekiq e instala automaticamente os middlewares de cliente e servidor:
207
+ A integração mantém o require explícito e suporta a API pública de middleware do Sidekiq 7:
208
+
209
+ O suporte histórico começou em `0.6.0.pre.1`; aplicações em Sidekiq 4/5 devem permanecer numa release legada compatível.
208
210
 
209
211
  ```ruby
210
212
  gem "sidekiq", "~> 5.0"
211
- gem "chronos-ruby", "~> 1.1.3", :require => "chronos/rails"
213
+ gem "chronos-ruby", "~> 1.1.0", :require => "chronos/sidekiq"
212
214
  ```
213
215
 
214
- Aplicações Ruby sem Rails continuam usando `require "chronos/sidekiq"` explicitamente.
215
-
216
- O envelope de contexto não altera argumentos públicos e contém somente IDs limitados de trace/request. Active Job usa um campo serializado com namespace (`chronos_context`) e hooks públicos. Erros aninhados são deduplicados e reerguidos. Veja [Sidekiq legado](docs/modules/sidekiq-legacy.md), [Active Job](docs/modules/active-job.md) e [Jobs](docs/modules/job-monitoring.md).
216
+ O envelope de contexto não altera argumentos públicos e contém somente IDs limitados de trace/span/request e flags. Active Job usa um campo serializado com namespace (`chronos_context`) e hooks públicos. Erros aninhados são deduplicados e reerguidos. Veja [Sidekiq legado](docs/modules/sidekiq-legacy.md), [Active Job](docs/modules/active-job.md) e [Jobs](docs/modules/job-monitoring.md).
217
217
 
218
218
  ## Deploy tracking
219
219
 
@@ -263,6 +263,7 @@ A gem não varre o ambiente. Leia somente variáveis escolhidas pela aplicação
263
263
  Chronos.configure do |config|
264
264
  config.project_id = ENV["CHRONOS_PROJECT_ID"]
265
265
  config.project_key = ENV["CHRONOS_PROJECT_KEY"]
266
+ config.host = ENV["CHRONOS_HOST"]
266
267
  config.environment = ENV["APP_ENV"] || "production"
267
268
  config.enabled = ENV["CHRONOS_ENABLED"] != "false"
268
269
  config.queue_size = 100
@@ -320,7 +321,7 @@ Execute suíte e lint no runtime atual:
320
321
  bundle _1.17.3_ exec rake
321
322
  ```
322
323
 
323
- A matriz CI cobre Ruby 2.2.10–2.6.10, aplicações Rails 4.2/5.2 e Sidekiq 4/5. O workflow de release repete toda a matriz, documentação, benchmark comparativo e carga antes de publicar.
324
+ A matriz CI cobre a linha transitional e os checks de pacote/documentação; workflows legados preservam evidência das releases anteriores.
324
325
 
325
326
  ## Contribuição
326
327
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Status
4
4
 
5
- Adiado; limite aceito para 1.0 legado.
5
+ Aceito para 1.2.
6
6
 
7
7
  ## Contexto
8
8
 
@@ -10,7 +10,7 @@ O Chronos precisa correlacionar trace/request sem duplicar SDKs ou impor depend
10
10
 
11
11
  ## Decisão
12
12
 
13
- Manter IDs de correlação e portas independentes do fornecedor na 1.0. Não depender de OpenTelemetry nem instalar instrumentação global na linha legado. Uma ponte opcional será projetada na linha transitional/modern e deverá traduzir somente campos permitidos.
13
+ Manter IDs de correlação e portas independentes do fornecedor. Na 1.2, a ponte opcional consulta somente o span atual de um SDK carregado, traduz `trace_id`, `span_id` e flags, e nunca instala SDK, instrumentação ou exporter. IDs Chronos explícitos têm precedência; a presença de OTel deve ser usada pelas integrações para evitar spans paralelos equivalentes.
14
14
 
15
15
  ## Alternativas
16
16
 
@@ -22,4 +22,4 @@ O protocolo v1 permanece estável e aplicações legadas não recebem novas depe
22
22
 
23
23
  ## Consequências negativas
24
24
 
25
- Não propagação automática com ecossistemas OpenTelemetry na 1.0; integrações futuras exigirão contrato e matriz próprios.
25
+ A ponte não exporta spans OTel completos nem controla exporters; consumidores que precisam desses dados continuam responsáveis pela configuração do SDK.
@@ -1,39 +1,33 @@
1
1
  # Compatibility
2
2
 
3
- Chronos Ruby 1.1 is the current stable legacy line. Technical compatibility does not make an end-of-life Ruby, Rails, Rack, or Sidekiq release secure. The planned Ruby 2.7/Rails 6 transitional matrix remains deferred until it has dedicated CI and a real application gate.
3
+ Chronos Ruby 1.2.0.pre.1 is the first candidate of the transitional line for Rails 7 and Sidekiq 7. Combinations remain `Best effort` until their dedicated CI and real-application gates pass. Technical compatibility does not make an end-of-life Ruby, Rails, Rack, or Sidekiq release secure. Versions 1.0 and 1.1 remain the frozen legacy line.
4
4
 
5
5
  ## Core and Rack
6
6
 
7
7
  | Ruby | Status | Evidence |
8
8
  |---|---|---|
9
- | 2.2.10 | Supported | Full unit, integration, contract, Rack, concurrency, fork, transport, privacy, and lint gate |
10
- | 2.3.8 | Supported | Same dedicated Docker gate |
11
- | 2.4.10 | Supported | Same dedicated Docker gate |
12
- | 2.5.9 | Supported | Same dedicated Docker gate |
13
- | 2.6.10 | Supported | Same dedicated Docker gate |
14
- | 2.7 and newer | Unsupported in 1.x legacy | Belongs to the transitional or modern lines |
9
+ | 2.7–3.2 | Best effort | Transitional matrix introduced in this candidate; promotion requires green CI evidence |
10
+ | 3.3–3.4 | Best effort | Core checks run in modern CI; Rails 8-specific behavior belongs to 2.x |
11
+ | Earlier than 2.7 | Unsupported in 1.2 | Use an appropriate frozen legacy release |
15
12
 
16
13
  ## Rails
17
14
 
18
15
  | Ruby | Rails | Status | Evidence |
19
16
  |---|---|---|---|
20
- | 2.2.10 | 4.2 | Supported | Real application boot, successful/error request, SQL, view, cache, Active Job, mailer, fake endpoint, flush, and shutdown |
21
- | 2.3.8 | 4.2 | Supported | Same dedicated application gate |
22
- | 2.5.9 | 5.2 | Supported | Same dedicated application gate |
23
- | 2.6.10 | 5.2 | Supported | Same dedicated application gate |
24
- | Other Ruby/Rails pairs | — | Unsupported | No complete release gate; feature detection alone is not a support claim |
17
+ | 2.7–3.2 | 7.0–7.1 | Best effort | Real Rails applications and the complete integration gate are pending |
18
+ | 3.1–3.4 | 7.2 | Best effort | Feature and package checks; use a validated application combination before production rollout |
19
+ | Rails 4–6 | — | Unsupported in 1.2 | Use a matching earlier Chronos release |
25
20
 
26
21
  ## Sidekiq
27
22
 
28
23
  | Ruby | Sidekiq | Status | Evidence |
29
24
  |---|---|---|---|
30
- | 2.2.10 | 4.2.10 | Supported | Real-gem client/server middleware smoke with context, success/failure, and deduplication |
31
- | 2.5.9 | 5.2.10 | Supported | Same dedicated application gate |
32
- | Other Ruby/Sidekiq pairs | — | Unsupported | No complete release gate |
25
+ | 2.7–3.4 | 7.x | Best effort | Real Sidekiq 7 smoke applications are pending |
26
+ | Sidekiq 4–6 | — | Unsupported in 1.2 | Use a matching earlier Chronos release |
33
27
 
34
28
  Active Job uses the public `serialize`, `deserialize`, and `perform_now` extension points with a bounded namespaced field. Support follows the validated Rails pairs above. Adapters that bypass these hooks require their own evidence.
35
29
 
36
- The release workflow repeats every supported pair before publishing. Version 1.1.3 evidence is recorded in [Version 1.1.3 readiness](release-1.1.3-readiness.md); historical evidence remains in [Version 1.1.2 readiness](release-1.1.2-readiness.md), [Version 1.0 readiness](release-1.0-readiness.md), and [Version 1.1 readiness](release-1.1-readiness.md).
30
+ Historical evidence remains in [Version 1.0 readiness](release-1.0-readiness.md) and [Version 1.1 readiness](release-1.1-readiness.md).
37
31
 
38
32
  Status meanings:
39
33
 
@@ -6,7 +6,7 @@
6
6
  |---|---:|---|---|
7
7
  | `project_id` | Yes when enabled | `nil` | Public project identifier included in the envelope |
8
8
  | `project_key` | Yes when enabled | `nil` | Secret authentication key sent only as an HTTP header |
9
- | `host` | Optional | `https://chronosmonitor.com.br` | Absolute Chronos HTTP endpoint; HTTPS is required by default |
9
+ | `host` | Yes when enabled | `nil` | Absolute Chronos HTTP endpoint; HTTPS is required by default |
10
10
  | `environment` | Recommended | `production` | Application environment |
11
11
  | `app_version` | Optional | `nil` | Application release/version identifier |
12
12
  | `service_name` | Recommended | `nil` | Logical service name |
@@ -47,7 +47,7 @@
47
47
  | `sampling_rate` | Optional | `1.0` | Local upper bound for event sampling |
48
48
  | `enabled_event_types` | Optional | exception, request, query, job, cache, external_http, dependencies, deploy, metric_batch | Local allowlist for supported event envelopes |
49
49
  | `max_remote_send_interval` | Optional | `60.0` | Local upper bound for remotely requested send spacing |
50
- | `context_store` | Optional | `:thread_local` | `:thread_local` or an object implementing `get`, `set`, `clear`, and `with_context` |
50
+ | `context_store` | Optional | `:fiber_local` | `:fiber_local`, `:thread_local`, or an object implementing `get`, `set`, `clear`, and `with_context` |
51
51
  | `breadcrumb_capacity` | Optional | `20` | Positive count of newest breadcrumbs retained per execution |
52
52
  | `breadcrumb_max_bytes` | Optional | `2048` | Maximum bytes per normalized breadcrumb; minimum `128` |
53
53
  | `rails_enabled` | Optional | `true` | Enables automatic Rails middleware and subscribers |
@@ -75,6 +75,8 @@
75
75
  | `apm_transaction_max_connections` | Optional | `100` | Maximum concurrently tracked transaction connections; range 1–500 |
76
76
  | `external_http_enabled` | Optional | `false` | Allows explicit per-instance outbound `Net::HTTP` instrumentation |
77
77
  | `external_http_trace_headers` | Optional | `true` | Propagates Chronos trace/request headers on instrumented requests |
78
+ | `w3c_trace_context` | Optional | `false` | Injects a validated W3C `traceparent` header when trace and span IDs exist |
79
+ | `opentelemetry_bridge` | Optional | `true` | Consumes the current OpenTelemetry span when an SDK is already active; installs nothing |
78
80
  | `cache_key_mode` | Optional | `:none` | `:none` omits keys; `:sha256` emits a project-scoped key hash |
79
81
  | `dependency_reporting` | Optional | `true` | Emits one bounded dependency event per configured agent |
80
82
  | `dependency_max_items` | Optional | `100` | Loaded gem entries retained in the inventory; range 1–200 |
@@ -100,7 +102,7 @@ Chronos.configure do |config|
100
102
  config.max_retries = 3
101
103
  config.backlog_size = 100
102
104
  config.circuit_failure_threshold = 5
103
- config.context_store = :thread_local
105
+ config.context_store = :fiber_local
104
106
  config.breadcrumb_capacity = 20
105
107
  config.rails_capture_in_test = false
106
108
  config.rails_capture_in_console = false
@@ -125,6 +127,8 @@ Chronos.configure do |config|
125
127
  config.apm_transaction_max_connections = 100
126
128
  config.external_http_enabled = false
127
129
  config.external_http_trace_headers = true
130
+ config.w3c_trace_context = false
131
+ config.opentelemetry_bridge = true
128
132
  config.cache_key_mode = :none
129
133
  config.dependency_reporting = true
130
134
  config.dependency_max_items = 100
@@ -6,7 +6,7 @@ O contexto relaciona uma exceção ou métrica ao request/job atual sem criar de
6
6
 
7
7
  ## Fluxo e classes
8
8
 
9
- `Chronos::Ports::ContextStore` define `get`, `set`, `clear` e `with_context`. Na linha legado, `Chronos::Adapters::ThreadLocalContextStore` isola o valor por thread. A fachada combina o contexto explícito com o escopo atual; o serializer sanitiza e limita tudo antes da fila. Rack limpa o escopo em `ensure`, enquanto Sidekiq e Active Job propagam apenas `trace_id` e `request_id`.
9
+ `Chronos::Ports::ContextStore` define `get`, `set`, `clear` e `with_context`. Na linha 1.2, `Chronos::Adapters::FiberLocalContextStore` usa storage do Fiber quando disponível e volta com segurança ao adapter thread-local. A fachada combina o contexto explícito com o escopo atual; o serializer sanitiza e limita tudo antes da fila. Rack limpa o escopo em `ensure`, enquanto Sidekiq e Active Job propagam somente `trace_id`, `span_id`, `trace_flags` e `request_id`.
10
10
 
11
11
  ## Extensão, riscos e exemplo
12
12
 
@@ -10,6 +10,7 @@ Chronos.configure do |config|
10
10
  # connection settings omitted
11
11
  config.external_http_enabled = true
12
12
  config.external_http_trace_headers = true
13
+ config.w3c_trace_context = true
13
14
  end
14
15
 
15
16
  http = Net::HTTP.new("payments.example.com", 443)
@@ -18,10 +19,10 @@ Chronos.instrument_net_http(http)
18
19
  response = http.request(Net::HTTP::Get.new("/health"))
19
20
  ```
20
21
 
21
- The event contains a bounded lowercase host, uppercase method, response status, monotonic duration, timeout flag, connection-error flag, and error class. A request made inside a Chronos context receives `X-Chronos-Trace-ID` and `X-Chronos-Request-ID` unless the application already set those headers. Disable propagation with `external_http_trace_headers = false`.
22
+ The event contains a bounded lowercase host, uppercase method, response status, monotonic duration, timeout flag, connection-error flag, and error class. A request made inside a Chronos context receives `X-Chronos-Trace-ID` and `X-Chronos-Request-ID` unless the application already set those headers. With `w3c_trace_context = true`, Net::HTTP and `Chronos::Integrations::FaradayMiddleware` also preserve an existing header or inject a validated `traceparent`. Disable all propagation with `external_http_trace_headers = false`.
22
23
 
23
24
  The wrapper never reads or records the path, query string, Authorization, other request headers, request body, response headers/body, or exception message. The native streaming block is forwarded and the identical HTTP exception is re-raised. Telemetry failures are contained.
24
25
 
25
- Successful and failed calls become bounded `external_http` APM groups keyed only by host and method. A call carrying a trace ID contributes its duration to the enclosing request's `external_http` breakdown. Faraday, HTTP.rb, Excon, and RestClient are outside this release.
26
+ Successful and failed calls become bounded `external_http` APM groups keyed only by host and method. A call carrying a trace ID contributes its duration to the enclosing request's `external_http` breakdown. Faraday is supported through explicit middleware installation; HTTP.rb, Excon, and RestClient remain outside this release.
26
27
 
27
28
  Installation is idempotent per object. A `false` result means collection is disabled, the object is incompatible or already instrumented, or installation was contained after an internal error.
@@ -16,7 +16,7 @@ Run:
16
16
  rails generate chronos:install
17
17
  ```
18
18
 
19
- The generator creates `config/initializers/chronos.rb`. The concise template reads only explicitly named `CHRONOS_*` variables, uses documented safe defaults, derives the service name from the Rails application namespace, adopts `Rails.logger` without freezing it, and disables automatic integration in test and console by default. It invokes the idempotent installer as a fallback for applications that disabled Bundler auto-require, so the Railtie and initializer paths cannot create duplicate hooks. When Sidekiq is available, the installer also activates its existing optional client and server middleware. It never scans all environment variables or modifies routes and application classes.
19
+ The generator creates `config/initializers/chronos.rb`. The template reads only explicitly named `CHRONOS_*` variables, uses `Rails.env`, adopts `Rails.logger` when available, and disables automatic integration in test and console by default. It invokes the idempotent installer as a fallback for applications that disabled Bundler auto-require, so the Railtie and initializer paths cannot create duplicate hooks. It never scans all environment variables or modifies routes and application classes.
20
20
 
21
21
  ## Captured integrations
22
22
 
@@ -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 remains an optional dependency but is installed automatically when present in Rails. 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.
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.
@@ -1,13 +1,13 @@
1
1
  # Sidekiq 4/5 legacy integration
2
2
 
3
- Version `0.6.0.pre.1` starts the legacy jobs line with optional Sidekiq 4 and 5 client and server middleware. Version `1.1.3` installs it automatically when Sidekiq is available in a Rails application:
3
+ Version `0.6.0.pre.1` starts the legacy jobs line with optional Sidekiq 4 and 5 client and server middleware. Load it after declaring Sidekiq:
4
4
 
5
5
  ```ruby
6
6
  gem "sidekiq", "~> 5.0"
7
- gem "chronos-ruby", "~> 1.1.3", :require => "chronos/rails"
7
+ gem "chronos-ruby", "~> 1.1.0", :require => "chronos/sidekiq"
8
8
  ```
9
9
 
10
- Plain Ruby applications continue to use `require "chronos/sidekiq"` explicitly. Both entry points install middleware through the public `configure_client` and `configure_server` APIs. They do nothing when Sidekiq is unavailable, and the framework-independent core never requires Sidekiq. Installation adds no Chronos thread or Redis/database connection per job; delivery continues through the agent's existing fixed worker pool.
10
+ `chronos/sidekiq` installs middleware through the public `configure_client` and `configure_server` APIs. It does nothing when Sidekiq is unavailable, and the core gem never requires Sidekiq. Installation adds no Chronos thread or Redis/database connection per job; delivery continues through the agent's existing fixed worker pool.
11
11
 
12
12
  The client middleware adds a top-level `chronos` metadata object to the Sidekiq envelope. It never changes the public `args` array. Only `trace_id` and `request_id` propagate, with schema version and enqueue time. The server scopes that context for the worker, measures duration and calculable queue latency, and emits one `job` event.
13
13
 
@@ -56,7 +56,7 @@ Require `chronos/capistrano` after the Capistrano/Rake task DSL is loaded. The i
56
56
 
57
57
  ## Sidekiq telemetry is missing
58
58
 
59
- In Rails, require `chronos/rails` and configure Chronos before jobs run; version 1.1.3 detects Sidekiq and installs its public client/server middleware automatically. In plain Ruby, require `chronos/sidekiq` explicitly after Sidekiq is available. Sidekiq remains optional in both cases. A job already enqueued before client middleware installation may lack propagated trace context, but server timing and failure capture can still run.
59
+ 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.
60
60
 
61
61
  ## A failed Sidekiq job appears twice
62
62
 
@@ -0,0 +1,57 @@
1
+ module Chronos
2
+ module Adapters
3
+ # Stores execution context on the current Fiber when Ruby exposes Fiber storage.
4
+ #
5
+ # @responsibility Isolate context between concurrent fibers and restore nested scopes.
6
+ # @motivation Rails 7 applications increasingly multiplex work on one thread.
7
+ # @limits Fiber storage is not propagated automatically to newly-created fibers.
8
+ # @collaborators ContextStore port and Agent composition root.
9
+ # @thread_safety Each fiber owns its value and may be used from concurrent threads.
10
+ # @compatibility Ruby 3.2+ Fiber storage; falls back to thread-local storage otherwise.
11
+ # @example
12
+ # store.with_context(:request_id => "r1") { store.get }
13
+ # @errors Previous context is restored even when the block raises.
14
+ # @performance Constant-time storage operations with a bounded hash merge.
15
+ class FiberLocalContextStore
16
+ def initialize(fallback = ThreadLocalContextStore.new)
17
+ @key = "chronos_context_#{object_id}".to_sym
18
+ @fallback = fallback
19
+ end
20
+
21
+ def get
22
+ supported? ? (Fiber[@key] || {}) : @fallback.get
23
+ end
24
+
25
+ def set(context)
26
+ raise ArgumentError, "context must be a Hash" unless context.is_a?(Hash)
27
+
28
+ supported? ? Fiber[@key] = context : @fallback.set(context)
29
+ end
30
+
31
+ def clear
32
+ supported? ? Fiber[@key] = nil : @fallback.clear
33
+ nil
34
+ end
35
+
36
+ def with_context(context)
37
+ previous = get
38
+ set(previous.merge(valid_context(context)))
39
+ yield
40
+ ensure
41
+ previous && !previous.empty? ? set(previous) : clear
42
+ end
43
+
44
+ private
45
+
46
+ def supported?
47
+ Fiber.respond_to?(:[]) && Fiber.respond_to?(:[]=)
48
+ end
49
+
50
+ def valid_context(context)
51
+ raise ArgumentError, "context must be a Hash" unless context.is_a?(Hash)
52
+
53
+ context
54
+ end
55
+ end
56
+ end
57
+ end
data/lib/chronos/agent.rb CHANGED
@@ -148,7 +148,8 @@ module Chronos
148
148
  end
149
149
 
150
150
  def external_http_integration_options
151
- {:enabled => @config.external_http_enabled, :trace_headers => @config.external_http_trace_headers}
151
+ {:enabled => @config.external_http_enabled, :trace_headers => @config.external_http_trace_headers,
152
+ :w3c_trace_context => @config.w3c_trace_context}
152
153
  end
153
154
 
154
155
  def cache_integration_options
@@ -165,6 +166,10 @@ module Chronos
165
166
  "request_id" => nested["request_id"] || nested[:request_id] ||
166
167
  request["request_id"] || request[:request_id]
167
168
  }
169
+ if @config.opentelemetry_bridge
170
+ otel = Integrations::OpenTelemetry.current_context
171
+ values = otel.merge(values.delete_if { |_key, value| value.to_s.empty? })
172
+ end
168
173
  values.delete_if { |_key, value| value.to_s.empty? }
169
174
  rescue StandardError
170
175
  {}
@@ -237,6 +242,7 @@ module Chronos
237
242
 
238
243
  def build_context_store(strategy)
239
244
  return Adapters::ThreadLocalContextStore.new if strategy == :thread_local
245
+ return Adapters::FiberLocalContextStore.new if strategy == :fiber_local
240
246
 
241
247
  strategy
242
248
  end
@@ -110,6 +110,8 @@ module Chronos
110
110
  errors = []
111
111
  errors << "external_http_enabled must be true or false" unless boolean?(external_http_enabled)
112
112
  errors << "external_http_trace_headers must be true or false" unless boolean?(external_http_trace_headers)
113
+ errors << "w3c_trace_context must be true or false" unless boolean?(w3c_trace_context)
114
+ errors << "opentelemetry_bridge must be true or false" unless boolean?(opentelemetry_bridge)
113
115
  errors << "cache_key_mode must be :none or :sha256" unless [:none, :sha256].include?(cache_key_mode)
114
116
  errors << "dependency_reporting must be true or false" unless boolean?(dependency_reporting)
115
117
  unless dependency_max_items.is_a?(Integer) && dependency_max_items >= 1 && dependency_max_items <= 200
@@ -3,7 +3,7 @@ module Chronos
3
3
  #
4
4
  # @responsibility Expose validated settings without mutable containers.
5
5
  # @motivation Keep capture behavior stable while multiple threads run.
6
- # @limits Snapshot values cannot be edited; the host application's logger remains externally owned.
6
+ # @limits It cannot be edited after creation.
7
7
  # @collaborators Configuration and runtime services.
8
8
  # @thread_safety Safe to share between threads after construction.
9
9
  # @compatibility Ruby 2.2.10 through Ruby 2.6.
@@ -17,7 +17,7 @@ module Chronos
17
17
  def initialize(values)
18
18
  Configuration::ATTRIBUTES.each do |attribute|
19
19
  value = values[attribute]
20
- deep_freeze(value) unless attribute == :logger
20
+ deep_freeze(value)
21
21
  instance_variable_set("@#{attribute}", value)
22
22
  end
23
23
  freeze
@@ -67,8 +67,8 @@ module Chronos
67
67
 
68
68
  def context_errors
69
69
  errors = []
70
- unless context_store == :thread_local || compatible_context_store?
71
- errors << "context_store must be :thread_local or implement get, set, clear, and with_context"
70
+ unless [:thread_local, :fiber_local].include?(context_store) || compatible_context_store?
71
+ errors << "context_store must be :thread_local, :fiber_local, or implement get, set, clear, and with_context"
72
72
  end
73
73
  errors << "breadcrumb_capacity must be a positive integer" unless positive_integer?(breadcrumb_capacity)
74
74
  unless breadcrumb_max_bytes.is_a?(Integer) && breadcrumb_max_bytes >= 128
@@ -23,7 +23,6 @@ module Chronos
23
23
  class Configuration
24
24
  include Internal::ConfigurationValidation
25
25
  include Internal::ApmConfigurationValidation
26
- DEFAULT_HOST = "https://chronosmonitor.com.br".freeze
27
26
  DEFAULT_BLOCKLIST_KEYS = %w(
28
27
  password password_confirmation passwd secret api_key apikey authorization
29
28
  token access_token refresh_token private_key client_secret cookie set-cookie
@@ -55,6 +54,7 @@ module Chronos
55
54
  :apm_query_inspection_min_duration_ms, :apm_query_inspection_max_queries,
56
55
  :apm_transaction_tracking_enabled, :apm_transaction_max_connections,
57
56
  :external_http_enabled, :external_http_trace_headers,
57
+ :w3c_trace_context, :opentelemetry_bridge,
58
58
  :cache_key_mode, :dependency_reporting, :dependency_max_items
59
59
  ].freeze
60
60
 
@@ -106,7 +106,7 @@ module Chronos
106
106
  def initialize_core_defaults
107
107
  @project_id = nil
108
108
  @project_key = nil
109
- @host = DEFAULT_HOST
109
+ @host = nil
110
110
  @environment = "production"
111
111
  @app_version = nil
112
112
  @service_name = nil
@@ -124,7 +124,7 @@ module Chronos
124
124
  @user_agent = "chronos-ruby/#{Chronos::VERSION}"
125
125
  @max_payload_size = 1_048_576
126
126
  @gzip = false
127
- @context_store = :thread_local
127
+ @context_store = :fiber_local
128
128
  @breadcrumb_capacity = 20
129
129
  @breadcrumb_max_bytes = 2048
130
130
  end
@@ -168,6 +168,8 @@ module Chronos
168
168
  def initialize_observability_defaults
169
169
  @external_http_enabled = false
170
170
  @external_http_trace_headers = true
171
+ @w3c_trace_context = false
172
+ @opentelemetry_bridge = true
171
173
  @cache_key_mode = :none
172
174
  @dependency_reporting = true
173
175
  @dependency_max_items = 100
@@ -0,0 +1,38 @@
1
+ module Chronos
2
+ module Core
3
+ # Parses and formats the W3C Trace Context headers without an OpenTelemetry dependency.
4
+ module TraceContext
5
+ TRACEPARENT = /\A([\da-f]{2})-([\da-f]{32})-([\da-f]{16})-([\da-f]{2})\z/.freeze
6
+
7
+ module_function
8
+
9
+ def parse(value)
10
+ match = TRACEPARENT.match(value.to_s.downcase)
11
+ return {} unless match && match[1] == "00"
12
+ return {} if match[2] == ("0" * 32) || match[3] == ("0" * 16)
13
+
14
+ {"trace_id" => match[2], "span_id" => match[3], "trace_flags" => match[4]}
15
+ rescue StandardError
16
+ {}
17
+ end
18
+
19
+ def format(context)
20
+ trace_id = value(context, "trace_id")
21
+ span_id = value(context, "span_id")
22
+ flags = value(context, "trace_flags")
23
+ return nil unless trace_id =~ /\A[\da-f]{32}\z/ && span_id =~ /\A[\da-f]{16}\z/
24
+ return nil if trace_id == ("0" * 32) || span_id == ("0" * 16)
25
+
26
+ "00-#{trace_id}-#{span_id}-#{flags =~ /\A[\da-f]{2}\z/ ? flags : '01'}"
27
+ rescue StandardError
28
+ nil
29
+ end
30
+
31
+ def value(context, key)
32
+ return "" unless context.is_a?(Hash)
33
+
34
+ (context[key] || context[key.to_sym]).to_s.downcase
35
+ end
36
+ end
37
+ end
38
+ end
@@ -40,7 +40,7 @@ module Chronos
40
40
  def envelope(notifier)
41
41
  source = notifier.respond_to?(:propagation_context) ? notifier.propagation_context : {}
42
42
  source = {} unless source.is_a?(Hash)
43
- context = %w(trace_id request_id).each_with_object({}) do |key, result|
43
+ context = %w(trace_id span_id trace_flags request_id).each_with_object({}) do |key, result|
44
44
  value = source[key] || source[key.to_sym]
45
45
  result[key] = bounded(value) unless value.to_s.empty?
46
46
  end
@@ -57,7 +57,7 @@ module Chronos
57
57
  source = value["context"] || value[:context]
58
58
  return {} unless source.is_a?(Hash)
59
59
 
60
- %w(trace_id request_id).each_with_object({}) do |key, result|
60
+ %w(trace_id span_id trace_flags request_id).each_with_object({}) do |key, result|
61
61
  candidate = source[key] || source[key.to_sym]
62
62
  result[key] = bounded(candidate) unless candidate.to_s.empty?
63
63
  end