chronos-ruby 1.1.0 → 1.2.0.pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0127def72275a2d4c08314040c04e34a9606fed13982d84b06c465dee7be4cf6
4
- data.tar.gz: 99f272314517e6a525223ce86964b70fcdfce717eb0e434b9c77e275031913b7
3
+ metadata.gz: 28ccfc363bc7f73eb18a10d36e0457d8697ffe52dae4f939fa81a4cbe6938609
4
+ data.tar.gz: ac5d64237250a9d9d1e26da86649698dd09eda1afb7a115fb87eb2edf6d3183b
5
5
  SHA512:
6
- metadata.gz: 93b37af1a5b51284a6c6de1c19e12de18ac23b04bfa4a04f33b7fce5cb611ec6f940d4ec2508c8a2b8d72f91e9a2a9a812a3b48d72f60dfefd642fb7cf3fe138
7
- data.tar.gz: 4c653acb6d7aa90d534a7d496568fca64fb9ba9bce9039c6ca68fad71c1b5aef3f4e5de541a3f3d0f6064f08653359bcc2afc30e3a2d82c910942f0c1e103d4a
6
+ metadata.gz: 58987b0079af3abcd99ee06f6c2739833719d95ae29ef7764997f37c01745f269521453d610a7bfd7b9081c26593590c469d58d52a502ab752668d1d1d2d4e19
7
+ data.tar.gz: ba3277ee0bbc7fd67186e605505659ae2db7a1add6cad35c08bcf30669b1e7140f5c004ad9ace8154d2a25a72af9d6938aacf2ac04ea0da4dd9924700b0c2df8
data/CHANGELOG.md CHANGED
@@ -4,6 +4,32 @@ All notable changes are documented here. The project follows Semantic Versioning
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.0.pre.2] - 2026-08-11
8
+
9
+ ### Changed
10
+
11
+ - the Rails install generator now writes every public configuration option with explanatory English comments;
12
+ - the official `https://chronosmonitor.com.br` endpoint is now the default host, including when applications omit `config.host`;
13
+ - generated initializers no longer read `CHRONOS_HOST`, while explicit `config.host` assignments remain supported.
14
+
15
+ ## [1.2.0.pre.1] - 2026-08-11
16
+
17
+ ### Added
18
+
19
+ - fiber-aware execution context with a thread-local fallback;
20
+ - Sidekiq 7 and Rails 7 error reporter integration using public extension APIs;
21
+ - Action Cable notification spans and a dependency-free Faraday middleware;
22
+ - optional W3C `traceparent` propagation and an OpenTelemetry bridge that consumes the active span without installing an SDK or exporter.
23
+
24
+ ### Changed
25
+
26
+ - the transitional runtime range is Ruby 2.7 through 3.4 and the default context strategy is `:fiber_local`;
27
+ - Active Job and Sidekiq envelopes may propagate allowlisted span ID and trace flags in addition to trace/request IDs.
28
+
29
+ ### Security
30
+
31
+ - W3C identifiers are syntax-validated, all-zero IDs are rejected, and Rails error reporter context is reduced to bounded allowlisted metadata.
32
+
7
33
  ## [1.1.0] - 2026-08-05
8
34
 
9
35
  ### Added
data/README.md CHANGED
@@ -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.0 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.2 continua 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
 
@@ -67,7 +67,8 @@ 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`, `project_key` e `host` HTTPS. Recomendado: definir ambiente e serviço explicitamente.
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.
71
72
 
72
73
  ```ruby
73
74
  require "chronos"
@@ -75,7 +76,7 @@ require "chronos"
75
76
  Chronos.configure do |config|
76
77
  config.project_id = ENV["CHRONOS_PROJECT_ID"]
77
78
  config.project_key = ENV["CHRONOS_PROJECT_KEY"]
78
- config.host = "https://chronos.example.com"
79
+ config.host = "https://chronosmonitor.com.br"
79
80
  config.environment = ENV["APP_ENV"] || "production"
80
81
  config.service_name = "billing"
81
82
  config.app_version = ENV["APP_VERSION"]
@@ -204,14 +205,16 @@ Por padrão, SQL bruto e binds não são lidos pelo pipeline de análise. A insp
204
205
 
205
206
  ## Sidekiq e Active Job
206
207
 
207
- A versão `0.6.0.pre.1` introduziu middleware Sidekiq 4/5; a API estável mantém o require explícito:
208
+ A integração mantém o require explícito e suporta a API pública de middleware do Sidekiq 7:
209
+
210
+ 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
211
 
209
212
  ```ruby
210
213
  gem "sidekiq", "~> 5.0"
211
214
  gem "chronos-ruby", "~> 1.1.0", :require => "chronos/sidekiq"
212
215
  ```
213
216
 
214
- 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).
217
+ 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).
215
218
 
216
219
  ## Deploy tracking
217
220
 
@@ -319,7 +322,7 @@ Execute suíte e lint no runtime atual:
319
322
  bundle _1.17.3_ exec rake
320
323
  ```
321
324
 
322
- 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.
325
+ A matriz CI cobre a linha transitional e os checks de pacote/documentação; workflows legados preservam evidência das releases anteriores.
323
326
 
324
327
  ## Contribuição
325
328
 
@@ -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.2 is the current 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. Historical 1.0 evidence remains in [Version 1.0 readiness](release-1.0-readiness.md); the 1.1 candidate is recorded in [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` | Yes when enabled | `nil` | Absolute Chronos HTTP endpoint; HTTPS is required by default |
9
+ | `host` | Optional | `https://chronosmonitor.com.br` | 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.
@@ -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
@@ -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,6 +23,7 @@ module Chronos
23
23
  class Configuration
24
24
  include Internal::ConfigurationValidation
25
25
  include Internal::ApmConfigurationValidation
26
+ DEFAULT_HOST = "https://chronosmonitor.com.br".freeze
26
27
  DEFAULT_BLOCKLIST_KEYS = %w(
27
28
  password password_confirmation passwd secret api_key apikey authorization
28
29
  token access_token refresh_token private_key client_secret cookie set-cookie
@@ -54,6 +55,7 @@ module Chronos
54
55
  :apm_query_inspection_min_duration_ms, :apm_query_inspection_max_queries,
55
56
  :apm_transaction_tracking_enabled, :apm_transaction_max_connections,
56
57
  :external_http_enabled, :external_http_trace_headers,
58
+ :w3c_trace_context, :opentelemetry_bridge,
57
59
  :cache_key_mode, :dependency_reporting, :dependency_max_items
58
60
  ].freeze
59
61
 
@@ -105,7 +107,7 @@ module Chronos
105
107
  def initialize_core_defaults
106
108
  @project_id = nil
107
109
  @project_key = nil
108
- @host = nil
110
+ @host = DEFAULT_HOST
109
111
  @environment = "production"
110
112
  @app_version = nil
111
113
  @service_name = nil
@@ -123,7 +125,7 @@ module Chronos
123
125
  @user_agent = "chronos-ruby/#{Chronos::VERSION}"
124
126
  @max_payload_size = 1_048_576
125
127
  @gzip = false
126
- @context_store = :thread_local
128
+ @context_store = :fiber_local
127
129
  @breadcrumb_capacity = 20
128
130
  @breadcrumb_max_bytes = 2048
129
131
  end
@@ -167,6 +169,8 @@ module Chronos
167
169
  def initialize_observability_defaults
168
170
  @external_http_enabled = false
169
171
  @external_http_trace_headers = true
172
+ @w3c_trace_context = false
173
+ @opentelemetry_bridge = true
170
174
  @cache_key_mode = :none
171
175
  @dependency_reporting = true
172
176
  @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
@@ -0,0 +1,76 @@
1
+ module Chronos
2
+ module Integrations
3
+ # Optional Faraday middleware for outbound timing and W3C propagation.
4
+ # @responsibility Instrument one explicit Faraday connection without global patches.
5
+ # @motivation Modern Rails applications commonly use Faraday for outbound HTTP.
6
+ # @limits It never reads paths, queries, headers other than trace fields, or bodies.
7
+ # @collaborators Faraday middleware stack, TraceContext, and Chronos facade.
8
+ # @thread_safety Calls retain request state in local variables.
9
+ # @compatibility Faraday 1.x and 2.x middleware contracts.
10
+ # @example builder.use Chronos::Integrations::FaradayMiddleware
11
+ # @errors Original client exceptions are recorded by class and re-raised.
12
+ # @performance Two clock reads and one bounded asynchronous event per request.
13
+ class FaradayMiddleware
14
+ def initialize(app, options = {})
15
+ @app = app
16
+ @notifier = options[:notifier] || Chronos
17
+ @clock = options[:clock] || proc { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
18
+ end
19
+
20
+ def call(environment)
21
+ started_at = @clock.call
22
+ inject_headers(environment)
23
+ response = @app.call(environment)
24
+ if response.respond_to?(:on_complete)
25
+ response.on_complete { |completed| record(completed, started_at, nil) }
26
+ else
27
+ record(environment, started_at, nil)
28
+ end
29
+ response
30
+ rescue StandardError => error
31
+ record(environment, started_at, error) if started_at
32
+ raise
33
+ end
34
+
35
+ private
36
+
37
+ def inject_headers(environment)
38
+ options = integration_options
39
+ return unless options[:trace_headers] && environment.respond_to?(:request_headers)
40
+
41
+ context = @notifier.respond_to?(:propagation_context) ? @notifier.propagation_context : {}
42
+ headers = environment.request_headers
43
+ traceparent = Core::TraceContext.format(context) if options[:w3c_trace_context]
44
+ headers["traceparent"] ||= traceparent if traceparent
45
+ headers["X-Chronos-Trace-ID"] ||= context["trace_id"] if context["trace_id"]
46
+ headers["X-Chronos-Request-ID"] ||= context["request_id"] if context["request_id"]
47
+ rescue StandardError
48
+ nil
49
+ end
50
+
51
+ def record(environment, started_at, error)
52
+ url = environment.url if environment.respond_to?(:url)
53
+ payload = {
54
+ "host" => (url.host.to_s if url && url.respond_to?(:host)),
55
+ "method" => (environment.method.to_s.upcase if environment.respond_to?(:method)),
56
+ "status" => (environment.status.to_i if environment.respond_to?(:status) && environment.status),
57
+ "duration_ms" => ((@clock.call - started_at) * 1000.0).round(3),
58
+ "error_class" => (error.class.name.to_s if error)
59
+ }
60
+ payload.delete_if { |_key, value| value.nil? || value == "" }
61
+ @notifier.record_event("external_http", payload)
62
+ rescue StandardError
63
+ false
64
+ end
65
+
66
+ def integration_options
67
+ return {:enabled => true, :trace_headers => true, :w3c_trace_context => false} unless
68
+ @notifier.respond_to?(:external_http_integration_options)
69
+
70
+ @notifier.external_http_integration_options
71
+ rescue StandardError
72
+ {:enabled => false, :trace_headers => false, :w3c_trace_context => false}
73
+ end
74
+ end
75
+ end
76
+ end
@@ -53,7 +53,8 @@ module Chronos
53
53
  {
54
54
  :notifier => notifier,
55
55
  :clock => options[:clock] || proc { monotonic_time },
56
- :trace_headers => configured.fetch(:trace_headers, true)
56
+ :trace_headers => configured.fetch(:trace_headers, true),
57
+ :w3c_trace_context => configured.fetch(:w3c_trace_context, false)
57
58
  }
58
59
  end
59
60
 
@@ -106,6 +107,9 @@ module Chronos
106
107
  end
107
108
  set_header(request, "X-Chronos-Trace-ID", context["trace_id"] || context[:trace_id])
108
109
  set_header(request, "X-Chronos-Request-ID", context["request_id"] || context[:request_id])
110
+ if options[:w3c_trace_context]
111
+ set_header(request, "traceparent", Core::TraceContext.format(context))
112
+ end
109
113
  rescue StandardError
110
114
  nil
111
115
  end
@@ -0,0 +1,44 @@
1
+ module Chronos
2
+ module Integrations
3
+ # Optional, dependency-free bridge to an already configured OpenTelemetry SDK.
4
+ module OpenTelemetry
5
+ module_function
6
+
7
+ def current_context
8
+ return {} unless defined?(::OpenTelemetry::Trace) && ::OpenTelemetry::Trace.respond_to?(:current_span)
9
+
10
+ span = ::OpenTelemetry::Trace.current_span
11
+ context = span.context if span && span.respond_to?(:context)
12
+ return {} unless context && (!context.respond_to?(:valid?) || context.valid?)
13
+
14
+ trace_id = hex_identifier(context, :hex_trace_id, :trace_id, 32)
15
+ span_id = hex_identifier(context, :hex_span_id, :span_id, 16)
16
+ return {} if trace_id.empty? || span_id.empty?
17
+
18
+ {"trace_id" => trace_id, "span_id" => span_id,
19
+ "trace_flags" => sampled?(context) ? "01" : "00", "source" => "opentelemetry"}
20
+ rescue StandardError
21
+ {}
22
+ end
23
+
24
+ def active?
25
+ !current_context.empty?
26
+ end
27
+
28
+ def hex_identifier(context, hex_method, numeric_method, width)
29
+ value = context.public_send(hex_method) if context.respond_to?(hex_method)
30
+ value ||= context.public_send(numeric_method).to_i.to_s(16) if context.respond_to?(numeric_method)
31
+ value.to_s.downcase.rjust(width, "0")[-width, width]
32
+ rescue StandardError
33
+ ""
34
+ end
35
+
36
+ def sampled?(context)
37
+ flags = context.trace_flags if context.respond_to?(:trace_flags)
38
+ flags.respond_to?(:sampled?) ? flags.sampled? : flags.to_i.odd?
39
+ rescue StandardError
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
@@ -62,7 +62,7 @@ module Chronos
62
62
  def request_capture_context(env)
63
63
  request = request_values(env)
64
64
  {
65
- :context => {"request" => request, "trace_id" => trace_id(env)},
65
+ :context => trace_context(env).merge("request" => request),
66
66
  :parameters => parameters(env),
67
67
  :user => hash_value(env["chronos.user"])
68
68
  }
@@ -124,7 +124,13 @@ module Chronos
124
124
  end
125
125
 
126
126
  def trace_id(env)
127
- env["chronos.trace_id"] || SecureRandom.uuid
127
+ trace_context(env)["trace_id"]
128
+ end
129
+
130
+ def trace_context(env)
131
+ parsed = Core::TraceContext.parse(env["HTTP_TRACEPARENT"])
132
+ parsed["trace_id"] = env["chronos.trace_id"] || SecureRandom.hex(16) if parsed.empty?
133
+ parsed
128
134
  end
129
135
 
130
136
  def response_size(headers)
@@ -110,7 +110,7 @@ module Chronos
110
110
  source = @notifier.propagation_context
111
111
  return {} unless source.is_a?(Hash)
112
112
 
113
- %w(trace_id request_id).each_with_object({}) do |key, result|
113
+ %w(trace_id span_id trace_flags request_id).each_with_object({}) do |key, result|
114
114
  value = source[key] || source[key.to_sym]
115
115
  result[key] = value.to_s unless value.to_s.empty?
116
116
  end
@@ -0,0 +1,39 @@
1
+ module Chronos
2
+ module Rails
3
+ # Sends exceptions observed by the Rails 7 error reporter to Chronos once.
4
+ # @responsibility Translate the public Rails reporter callback into one notice.
5
+ # @motivation Rails can report handled errors that never reach Rack middleware.
6
+ # @limits Only handled, severity, source, and one bounded component are retained.
7
+ # @collaborators ActiveSupport::ErrorReporter and Chronos facade.
8
+ # @thread_safety Instances contain only an immutable notifier reference.
9
+ # @compatibility Rails 7 public error reporter subscriber API.
10
+ # @example Rails.error.subscribe(ErrorReporterSubscriber.new)
11
+ # @errors Agent and context failures are contained and return false.
12
+ # @performance Allocates one small allowlisted metadata hash per report.
13
+ class ErrorReporterSubscriber
14
+ def initialize(notifier = Chronos)
15
+ @notifier = notifier
16
+ end
17
+
18
+ def report(error, handled:, severity:, context:, source: nil)
19
+ details = {
20
+ :context => {"rails_error_reporter" => {
21
+ "handled" => handled == true, "severity" => severity.to_s, "source" => source.to_s
22
+ }}
23
+ }
24
+ details[:context]["rails_error_reporter"]["component"] = component(context)
25
+ @notifier.notify_once(error, details)
26
+ rescue StandardError
27
+ false
28
+ end
29
+
30
+ private
31
+
32
+ def component(context)
33
+ return "" unless context.is_a?(Hash)
34
+
35
+ (context[:controller] || context["controller"] || context[:job] || context["job"]).to_s[0, 128]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -34,6 +34,7 @@ module Chronos
34
34
 
35
35
  install_middleware(application, options)
36
36
  install_active_job
37
+ install_error_reporter
37
38
  @subscriber.install
38
39
  self.class.applications[application.object_id] = true
39
40
  end
@@ -65,6 +66,18 @@ module Chronos
65
66
  Chronos::Integrations::ActiveJob.install(::ActiveJob::Base, @notifier)
66
67
  end
67
68
 
69
+ def install_error_reporter
70
+ return false unless defined?(::Rails) && ::Rails.respond_to?(:error)
71
+
72
+ reporter = ::Rails.error
73
+ return false unless reporter && reporter.respond_to?(:subscribe)
74
+
75
+ reporter.subscribe(ErrorReporterSubscriber.new(@notifier))
76
+ true
77
+ rescue StandardError
78
+ false
79
+ end
80
+
68
81
  def environment
69
82
  defined?(::Rails) && ::Rails.respond_to?(:env) ? ::Rails.env.to_s : nil
70
83
  end
@@ -17,6 +17,7 @@ module Chronos
17
17
  process_action.action_controller render_template.action_view sql.active_record
18
18
  deliver.action_mailer perform.active_job cache_read.active_support
19
19
  cache_write.active_support cache_fetch_hit.active_support
20
+ perform_action.action_cable transmit.action_cable broadcast.action_cable
20
21
  ).freeze
21
22
 
22
23
  @mutex = Mutex.new
@@ -105,6 +106,8 @@ module Chronos
105
106
  when "sql.active_record" then sql(payload, duration)
106
107
  when "deliver.action_mailer" then mailer(payload, duration)
107
108
  when "perform.active_job" then active_job(payload, duration)
109
+ when "perform_action.action_cable", "transmit.action_cable", "broadcast.action_cable"
110
+ action_cable(name, payload, duration)
108
111
  else cache(name, payload, duration)
109
112
  end
110
113
  end
@@ -345,6 +348,15 @@ module Chronos
345
348
  @notifier.record_event("cache", data)
346
349
  end
347
350
 
351
+ def action_cable(name, payload, duration)
352
+ data = {
353
+ "kind" => "action_cable", "operation" => name.split(".").first,
354
+ "channel" => safe_class_name(value(payload, :channel)),
355
+ "action" => value(payload, :action).to_s[0, 128], "duration_ms" => duration
356
+ }
357
+ @notifier.record_event("request", data)
358
+ end
359
+
348
360
  def capture_controller_exception(payload)
349
361
  exception = value(payload, :exception_object)
350
362
  details = value(payload, :exception)
data/lib/chronos/rails.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "chronos"
2
2
  require "chronos/rails/active_record_query_inspector"
3
3
  require "chronos/rails/notifications_subscriber"
4
+ require "chronos/rails/error_reporter_subscriber"
4
5
  require "chronos/integrations/active_job"
5
6
  require "chronos/rails/installer"
6
7
 
@@ -1,4 +1,4 @@
1
1
  module Chronos
2
- # Current version of the legacy Chronos Ruby agent.
3
- VERSION = "1.1.0".freeze
2
+ # Current version of the transitional Chronos Ruby agent.
3
+ VERSION = "1.2.0.pre.2".freeze
4
4
  end
data/lib/chronos.rb CHANGED
@@ -16,6 +16,7 @@ require "chronos/core/sensitive_value_filter"
16
16
  require "chronos/core/sanitizer"
17
17
  require "chronos/core/safe_serializer"
18
18
  require "chronos/core/correlation_context"
19
+ require "chronos/core/trace_context"
19
20
  require "chronos/core/deploy_normalizer"
20
21
  require "chronos/core/payload_serializer"
21
22
  require "chronos/core/telemetry_event"
@@ -32,6 +33,7 @@ require "chronos/internal/memory_backlog"
32
33
  require "chronos/internal/worker_pool"
33
34
  require "chronos/adapters/net_http_transport"
34
35
  require "chronos/adapters/thread_local_context_store"
36
+ require "chronos/adapters/fiber_local_context_store"
35
37
  require "chronos/core/breadcrumb"
36
38
  require "chronos/application/retry_policy"
37
39
  require "chronos/application/circuit_breaker"
@@ -49,6 +51,8 @@ require "chronos/observability_facade"
49
51
  require "chronos/integrations"
50
52
  require "chronos/integrations/rack"
51
53
  require "chronos/integrations/rack/middleware"
54
+ require "chronos/integrations/opentelemetry"
55
+ require "chronos/integrations/faraday"
52
56
 
53
57
  # Framework-independent public facade for the Chronos Ruby agent.
54
58
  #
@@ -1,20 +1,186 @@
1
1
  require "chronos/rails"
2
2
 
3
+ # This initializer lists every public Chronos option so the application's
4
+ # monitoring behavior is explicit and can be reviewed in one place.
3
5
  Chronos.configure do |config|
4
- # Chronos reads only the environment variables explicitly selected here.
6
+ # Identification and authentication
7
+
8
+ # Identifies the project that will receive monitoring events.
5
9
  config.project_id = ENV["CHRONOS_PROJECT_ID"]
10
+ # Authenticates event delivery. Keep this value in a secret manager.
6
11
  config.project_key = ENV["CHRONOS_PROJECT_KEY"]
7
- config.host = ENV["CHRONOS_HOST"]
12
+ # Defines the Chronos Monitor endpoint used to deliver events.
13
+ config.host = "https://chronosmonitor.com.br"
14
+ # Labels events with the current Rails environment.
8
15
  config.environment = Rails.env.to_s
9
- config.service_name = ENV["CHRONOS_SERVICE_NAME"]
16
+ # Identifies the deployed application version in events and releases.
10
17
  config.app_version = ENV["CHRONOS_APP_VERSION"]
18
+ # Distinguishes this application from other monitored services.
19
+ config.service_name = ENV["CHRONOS_SERVICE_NAME"]
20
+ # Records the source revision, such as a Git commit SHA.
21
+ config.revision = ENV["CHRONOS_REVISION"]
22
+ # Associates events with a specific deployment.
23
+ config.deploy_id = ENV["CHRONOS_DEPLOY_ID"]
24
+ # Identifies the infrastructure region where the application is running.
25
+ config.region = ENV["CHRONOS_REGION"]
26
+ # Identifies the process, container, or host that produced an event.
27
+ config.instance_id = ENV["CHRONOS_INSTANCE_ID"]
28
+ # Removes the application root from file paths reported to Chronos.
29
+ config.root_directory = Rails.root.to_s
30
+ # Sends internal Chronos diagnostics through the Rails logger.
11
31
  config.logger = Rails.logger if Rails.respond_to?(:logger)
12
32
 
13
- # Safe legacy defaults: test and console integrations remain disabled.
14
- config.rails_capture_in_test = false
33
+ # Transport and delivery
34
+
35
+ # Limits how long an established request may wait for the Chronos server.
36
+ config.timeout = 5
37
+ # Limits how long Chronos may wait while opening a network connection.
38
+ config.open_timeout = 2
39
+ # Sets the maximum number of events waiting in the in-memory queue.
40
+ config.queue_size = 100
41
+ # Sets the number of background workers that deliver queued events.
42
+ config.workers = 1
43
+ # Enables or disables all Chronos event collection and delivery.
44
+ config.enabled = true
45
+ # Controls whether captured exceptions are sent to Chronos.
46
+ config.error_notifications = true
47
+ # Prevents delivery from the listed application environments.
48
+ config.ignored_environments = []
49
+ # Routes outbound requests through a proxy URL when one is required.
50
+ config.proxy = nil
51
+ # Verifies the TLS certificate presented by the Chronos endpoint.
52
+ config.ssl_verify = true
53
+ # Identifies this SDK in outbound HTTP requests.
54
+ config.user_agent = "chronos-ruby/#{Chronos::VERSION}"
55
+ # Rejects serialized events larger than this number of bytes.
56
+ config.max_payload_size = 1_048_576
57
+ # Compresses request payloads with gzip when enabled.
58
+ config.gzip = false
59
+
60
+ # Privacy and filtering
61
+
62
+ # Removes values whose keys commonly contain credentials or private data.
63
+ config.blocklist_keys = Chronos::Configuration::DEFAULT_BLOCKLIST_KEYS.dup
64
+ # Keeps only the listed keys when this list is not empty.
65
+ config.allowlist_keys = []
66
+ # Applies custom callables that can transform or discard events.
67
+ config.filters = []
68
+ # Replaces values for these keys with stable hashes instead of raw data.
69
+ config.hash_keys = []
70
+ # Removes the final segment of captured IP addresses.
71
+ config.anonymize_ip = true
72
+ # Discards events that match any configured ignore rule.
73
+ config.ignore_rules = []
74
+ # Limits the number of ignore rules evaluated for each event.
75
+ config.max_ignore_rules = 20
76
+
77
+ # Reliability and remote configuration
78
+
79
+ # Sets the number of delivery retries after a recoverable failure.
80
+ config.max_retries = 3
81
+ # Sets the initial delay, in seconds, for exponential retry backoff.
82
+ config.retry_base_interval = 0.5
83
+ # Caps the retry delay, in seconds.
84
+ config.retry_max_interval = 30
85
+ # Randomizes retry delays to prevent synchronized retry traffic.
86
+ config.retry_jitter = 0.25
87
+ # Keeps this many failed events in memory for later delivery attempts.
88
+ config.backlog_size = 100
89
+ # Opens the circuit breaker after this many consecutive failures.
90
+ config.circuit_failure_threshold = 5
91
+ # Waits this many seconds before testing an open circuit again.
92
+ config.circuit_reset_timeout = 30
93
+ # Allows the server to supply bounded runtime configuration updates.
94
+ config.remote_configuration = true
95
+ # Limits the size of an accepted remote configuration response, in bytes.
96
+ config.remote_config_max_bytes = 4_096
97
+ # Captures this fraction of eligible events, from 0.0 to 1.0.
98
+ config.sampling_rate = 1.0
99
+ # Restricts remote collection to these event categories.
100
+ config.enabled_event_types = %w(
101
+ exception request query job cache external_http dependencies deploy metric_batch
102
+ )
103
+ # Caps the server-requested interval between deliveries, in seconds.
104
+ config.max_remote_send_interval = 60
105
+
106
+ # Execution context and breadcrumbs
107
+
108
+ # Isolates trace and request context between concurrent fibers.
109
+ config.context_store = :fiber_local
110
+ # Keeps this many recent breadcrumbs per execution context.
111
+ config.breadcrumb_capacity = 20
112
+ # Limits the serialized size of each breadcrumb, in bytes.
113
+ config.breadcrumb_max_bytes = 2_048
114
+
115
+ # Rails integration
116
+
117
+ # Installs Rails request, job, cache, SQL, and error integrations.
118
+ config.rails_enabled = true
119
+ # Captures events from Rails console sessions when enabled.
15
120
  config.rails_capture_in_console = false
121
+ # Captures events while the Rails test environment is running when enabled.
122
+ config.rails_capture_in_test = false
123
+ # Includes the request User-Agent in request metadata when enabled.
16
124
  config.rails_capture_user_agent = false
125
+
126
+ # Application performance monitoring
127
+
128
+ # Enables transaction, span, query, and aggregate performance monitoring.
129
+ config.apm_enabled = true
130
+ # Limits the number of metric groups retained between flushes.
131
+ config.apm_max_groups = 200
132
+ # Flushes APM aggregates after this many observations.
133
+ config.apm_flush_count = 100
134
+ # Sends at most this many APM items in one request.
135
+ config.apm_batch_size = 50
136
+ # Limits the SQL queries recorded for a single request or transaction.
137
+ config.apm_max_queries_per_request = 100
138
+ # Marks SQL queries slower than this duration, in milliseconds.
139
+ config.apm_slow_query_threshold_ms = 500
140
+ # Marks transactions longer than this duration, in milliseconds.
141
+ config.apm_long_transaction_threshold_ms = 1_000
142
+ # Reports repeated equivalent queries after this count is reached.
143
+ config.apm_n_plus_one_threshold = 5
144
+ # Defines the duration buckets, in milliseconds, used by APM histograms.
145
+ config.apm_histogram_buckets = [5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000]
146
+ # Retains active trace state for this many seconds.
147
+ config.apm_trace_ttl_seconds = 60
148
+ # Normalizes SQL and records bounded query analysis metadata.
149
+ config.apm_query_analysis_enabled = true
150
+ # Limits the number of analyzed queries retained per transaction.
151
+ config.apm_query_analysis_max_queries = 100
152
+ # Enables database-specific query inspection for slow queries.
153
+ config.apm_query_inspection_enabled = false
154
+ # Collects database query statistics when inspection supports them.
155
+ config.apm_query_statistics_enabled = false
156
+ # Collects query execution plans when inspection supports them.
157
+ config.apm_query_plan_enabled = false
158
+ # Inspects only queries at least this slow, in milliseconds.
159
+ config.apm_query_inspection_min_duration_ms = 500
160
+ # Limits the inspected queries retained per transaction.
161
+ config.apm_query_inspection_max_queries = 20
162
+ # Tracks database transaction lifecycles and durations.
163
+ config.apm_transaction_tracking_enabled = true
164
+ # Limits the database connections tracked for transaction state.
165
+ config.apm_transaction_max_connections = 100
166
+
167
+ # Distributed tracing and integrations
168
+
169
+ # Instruments supported outbound HTTP clients when enabled.
170
+ config.external_http_enabled = false
171
+ # Propagates Chronos trace headers on instrumented outbound requests.
172
+ config.external_http_trace_headers = true
173
+ # Extracts and injects the standard W3C traceparent header when enabled.
174
+ config.w3c_trace_context = false
175
+ # Reuses an active OpenTelemetry span without configuring its SDK.
176
+ config.opentelemetry_bridge = true
177
+ # Controls cache key reporting; :none avoids collecting cache key material.
178
+ config.cache_key_mode = :none
179
+ # Reports a bounded inventory of application dependencies.
180
+ config.dependency_reporting = true
181
+ # Limits the number of dependencies included in an inventory event.
182
+ config.dependency_max_items = 100
17
183
  end
18
184
 
19
- # Safe when the Railtie already ran or will run later; installation is idempotent.
185
+ # Installation is idempotent, regardless of when the Railtie was evaluated.
20
186
  Chronos::Rails::Installer.new.install(Rails.application)
metadata CHANGED
@@ -1,93 +1,91 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronos-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antonio Jefferson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-05 00:00:00.000000000 Z
11
+ date: 2026-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.17'
19
+ version: '2.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
20
23
  type: :development
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '2.1'
30
+ - - "<"
25
31
  - !ruby/object:Gem::Version
26
- version: '1.17'
32
+ version: '3'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rake
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - "~>"
37
+ - - ">="
32
38
  - !ruby/object:Gem::Version
33
39
  version: '12.3'
34
- - - ">="
40
+ - - "<"
35
41
  - !ruby/object:Gem::Version
36
- version: 12.3.3
42
+ version: '14'
37
43
  type: :development
38
44
  prerelease: false
39
45
  version_requirements: !ruby/object:Gem::Requirement
40
46
  requirements:
41
- - - "~>"
47
+ - - ">="
42
48
  - !ruby/object:Gem::Version
43
49
  version: '12.3'
44
- - - ">="
50
+ - - "<"
45
51
  - !ruby/object:Gem::Version
46
- version: 12.3.3
52
+ version: '14'
47
53
  - !ruby/object:Gem::Dependency
48
54
  name: rspec
49
55
  requirement: !ruby/object:Gem::Requirement
50
56
  requirements:
51
- - - "~>"
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '3.10'
60
+ - - "<"
52
61
  - !ruby/object:Gem::Version
53
- version: '3.0'
62
+ version: '4'
54
63
  type: :development
55
64
  prerelease: false
56
65
  version_requirements: !ruby/object:Gem::Requirement
57
66
  requirements:
58
- - - "~>"
67
+ - - ">="
59
68
  - !ruby/object:Gem::Version
60
- version: '3.0'
69
+ version: '3.10'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '4'
61
73
  - !ruby/object:Gem::Dependency
62
74
  name: rubocop
63
75
  requirement: !ruby/object:Gem::Requirement
64
76
  requirements:
65
77
  - - "~>"
66
78
  - !ruby/object:Gem::Version
67
- version: 0.49.0
79
+ version: 1.57.0
68
80
  type: :development
69
81
  prerelease: false
70
82
  version_requirements: !ruby/object:Gem::Requirement
71
83
  requirements:
72
84
  - - "~>"
73
85
  - !ruby/object:Gem::Version
74
- version: 0.49.0
75
- - !ruby/object:Gem::Dependency
76
- name: parallel
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - '='
80
- - !ruby/object:Gem::Version
81
- version: 1.19.2
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - '='
87
- - !ruby/object:Gem::Version
88
- version: 1.19.2
89
- description: Base do cliente Chronos para excecoes, telemetria e observabilidade em
90
- aplicacoes Ruby legadas.
86
+ version: 1.57.0
87
+ description: Cliente Chronos para excecoes, telemetria e observabilidade em aplicacoes
88
+ Ruby e Rails.
91
89
  email:
92
90
  - antoniojeferson96@gmail.com
93
91
  executables: []
@@ -171,6 +169,7 @@ files:
171
169
  - docs/troubleshooting.md
172
170
  - lib/chronos.rb
173
171
  - lib/chronos/adapters.rb
172
+ - lib/chronos/adapters/fiber_local_context_store.rb
174
173
  - lib/chronos/adapters/net_http_transport.rb
175
174
  - lib/chronos/adapters/thread_local_context_store.rb
176
175
  - lib/chronos/agent.rb
@@ -210,12 +209,15 @@ files:
210
209
  - lib/chronos/core/sql_normalizer.rb
211
210
  - lib/chronos/core/sql_query_analyzer.rb
212
211
  - lib/chronos/core/telemetry_event.rb
212
+ - lib/chronos/core/trace_context.rb
213
213
  - lib/chronos/errors.rb
214
214
  - lib/chronos/integrations.rb
215
215
  - lib/chronos/integrations/active_job.rb
216
216
  - lib/chronos/integrations/capistrano.rb
217
+ - lib/chronos/integrations/faraday.rb
217
218
  - lib/chronos/integrations/job_payload.rb
218
219
  - lib/chronos/integrations/net_http.rb
220
+ - lib/chronos/integrations/opentelemetry.rb
219
221
  - lib/chronos/integrations/rack.rb
220
222
  - lib/chronos/integrations/rack/middleware.rb
221
223
  - lib/chronos/integrations/sidekiq.rb
@@ -232,6 +234,7 @@ files:
232
234
  - lib/chronos/ports/transport.rb
233
235
  - lib/chronos/rails.rb
234
236
  - lib/chronos/rails/active_record_query_inspector.rb
237
+ - lib/chronos/rails/error_reporter_subscriber.rb
235
238
  - lib/chronos/rails/installer.rb
236
239
  - lib/chronos/rails/notifications_subscriber.rb
237
240
  - lib/chronos/rails/railtie.rb
@@ -258,15 +261,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
258
261
  requirements:
259
262
  - - ">="
260
263
  - !ruby/object:Gem::Version
261
- version: 2.2.10
264
+ version: '2.7'
262
265
  - - "<"
263
266
  - !ruby/object:Gem::Version
264
- version: '2.7'
267
+ version: '3.5'
265
268
  required_rubygems_version: !ruby/object:Gem::Requirement
266
269
  requirements:
267
- - - ">="
270
+ - - ">"
268
271
  - !ruby/object:Gem::Version
269
- version: '0'
272
+ version: 1.3.1
270
273
  requirements: []
271
274
  rubygems_version: 3.4.22
272
275
  signing_key: