chronos-ruby 1.0.0 → 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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/README.md +27 -13
  4. data/contracts/apm-batch-v1.schema.json +51 -1
  5. data/docs/adr/ADR-010-opentelemetry-interoperability.md +3 -3
  6. data/docs/adr/ADR-015-bounded-apm-aggregation.md +3 -3
  7. data/docs/adr/ADR-019-bounded-query-diagnostics.md +44 -0
  8. data/docs/architecture.md +5 -2
  9. data/docs/compatibility.md +12 -18
  10. data/docs/configuration.md +27 -2
  11. data/docs/data-collected.md +9 -3
  12. data/docs/examples/plain-ruby.md +6 -0
  13. data/docs/modules/apm-aggregation.md +18 -5
  14. data/docs/modules/context.md +1 -1
  15. data/docs/modules/external-http.md +3 -2
  16. data/docs/modules/sidekiq-legacy.md +1 -1
  17. data/docs/modules/sql-monitoring.md +60 -6
  18. data/docs/modules/telemetry-events.md +1 -1
  19. data/docs/performance.md +19 -3
  20. data/docs/privacy-lgpd.md +6 -3
  21. data/docs/protocol-v1.md +1 -1
  22. data/docs/release-1.1-readiness.md +51 -0
  23. data/docs/security-review.md +7 -3
  24. data/docs/troubleshooting.md +6 -0
  25. data/lib/chronos/adapters/fiber_local_context_store.rb +57 -0
  26. data/lib/chronos/agent.rb +19 -3
  27. data/lib/chronos/application/apm_aggregator.rb +179 -29
  28. data/lib/chronos/configuration/apm_validation.rb +53 -1
  29. data/lib/chronos/configuration/validation.rb +2 -2
  30. data/lib/chronos/configuration.rb +21 -2
  31. data/lib/chronos/core/metric_aggregate.rb +69 -7
  32. data/lib/chronos/core/sql_query_analyzer.rb +309 -0
  33. data/lib/chronos/core/trace_context.rb +38 -0
  34. data/lib/chronos/integrations/active_job.rb +2 -2
  35. data/lib/chronos/integrations/faraday.rb +76 -0
  36. data/lib/chronos/integrations/net_http.rb +5 -1
  37. data/lib/chronos/integrations/opentelemetry.rb +44 -0
  38. data/lib/chronos/integrations/rack/middleware.rb +8 -2
  39. data/lib/chronos/integrations/sidekiq.rb +1 -1
  40. data/lib/chronos/ports/query_inspector.rb +23 -0
  41. data/lib/chronos/rails/active_record_query_inspector.rb +235 -0
  42. data/lib/chronos/rails/error_reporter_subscriber.rb +39 -0
  43. data/lib/chronos/rails/installer.rb +13 -0
  44. data/lib/chronos/rails/notifications_subscriber.rb +175 -2
  45. data/lib/chronos/rails.rb +2 -0
  46. data/lib/chronos/version.rb +2 -2
  47. data/lib/chronos.rb +6 -0
  48. metadata +46 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3fc9daff111666fdf7309797c5f8aa81a639c8f59ea1733be48740b9aea6b247
4
- data.tar.gz: a8d7cd786b233078e5516e3d1ce0c92b986d045ebd4d8c29a7d9cd80ae75fb93
3
+ metadata.gz: c13c2858edcb41d4b2a0bfc83049349ee13a5a9ecee873798750b4bf9f1da48c
4
+ data.tar.gz: a21babcc28ce763e7d867f35064735d661214724c1ed9d256e8d174a1a04c5dc
5
5
  SHA512:
6
- metadata.gz: 1f60a09480aa5766676d673579ffe1b5b007d044881fc9b1f70f672c414ddf7e1a014cc63eaeee8ee6f63fa7261aa5dc92f8f9b1cc6089898ecf015d1940a83c
7
- data.tar.gz: '08342c5a665004cbfa9fec52164f86e82d003b3d587bf931ae220c44ec4188c91141852e424f726f55a7b7d5dec4592190dc7cef9dc6c4e05f0eec8255bd32b4'
6
+ metadata.gz: 81ab0672785236e1ffaeecf0fbb5ae3187f319be349a06f724eee50dd0d20963afb74a84485267e8ea5e3a7dd37dacef15ab456783fee622bef901110b98344a
7
+ data.tar.gz: 835ee551ddcec646a44376dcdfc09748e12c0fa360fee62365455e6776503a0597baed504a33276059853e652f5a90458ad7ae43bb582f7647303af9940a5d1f
data/CHANGELOG.md CHANGED
@@ -4,6 +4,42 @@ All notable changes are documented here. The project follows Semantic Versioning
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.0.pre.1] - 2026-08-11
8
+
9
+ ### Added
10
+
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.
15
+
16
+ ### Changed
17
+
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.
20
+
21
+ ### Security
22
+
23
+ - W3C identifiers are syntax-validated, all-zero IDs are rejected, and Rails error reporter context is reduced to bounded allowlisted metadata.
24
+
25
+ ## [1.1.0] - 2026-08-05
26
+
27
+ ### Added
28
+
29
+ - bounded normalized SELECT analysis with access columns, index candidates, existing-index comparison, optional table statistics, and allowlisted non-executing query plans;
30
+ - structured query diagnostics and severity counts for errors, warnings, information, and suggestions, including actionable N+1 and index guidance;
31
+ - approximate p50/p95/p99 metrics, complete outer-transaction timing, expanded adapter error families, and trace/fingerprint loss counters;
32
+ - explicit low-risk defaults and bounded opt-in configuration for database index/statistics/plan inspection.
33
+
34
+ ### Changed
35
+
36
+ - aggregate drains preserve active trace trackers until request completion or idle TTL instead of discarding incomplete correlation;
37
+ - possible N+1 now requires a non-cached SELECT, and query analysis sent to consumers prefers inspected evidence over an earlier static-only observation.
38
+
39
+ ### Security
40
+
41
+ - query inspection never uses `EXPLAIN ANALYZE`, never executes DDL, omits raw SQL/binds/predicates/messages, and retains only bounded schema and planner fields.
42
+
7
43
  ## [1.0.0] - 2026-07-29
8
44
 
9
45
  ### Added
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Chronos Ruby
2
2
 
3
- Chronos Ruby 1.0.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.
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
 
7
- A versão 1.0 pode coletar:
7
+ A versão 1.1 pode coletar:
8
8
 
9
9
  - classe, mensagem, backtrace estruturado e causas encadeadas da exceção;
10
10
  - timestamp, severidade, tags e fingerprint opcional;
@@ -20,11 +20,11 @@ Veja a tabela completa em [Dados coletados](docs/data-collected.md).
20
20
 
21
21
  ## O que não é coletado por padrão
22
22
 
23
- A gem não varre variáveis de ambiente, sistema de arquivos ou lockfiles e não bodies HTTP, cookies, headers de autorização, conteúdo de e-mail, SQL bruto, binds, valores de cache ou código-fonte. O inventário de dependências contém somente nomes e versões já carregados, uma vez por agente. A aplicação continua responsável por minimização e base legal dos dados enviados.
23
+ A gem não varre variáveis de ambiente, sistema de arquivos ou lockfiles e não coleta bodies HTTP, cookies, headers de autorização, conteúdo de e-mail, SQL bruto, binds, valores de cache ou código-fonte. A inspeção de plano, desativada por padrão, usa o SQL original somente na conexão local para `EXPLAIN` sem `ANALYZE` e o descarta. O inventário de dependências contém somente nomes e versões já carregados, uma vez por agente. A aplicação continua responsável por minimização e base legal dos dados enviados.
24
24
 
25
25
  ## Versões Ruby e Rails suportadas
26
26
 
27
- A versão 1.0.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.
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 já 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.0.0"
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.0.0
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.0.0
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.0.0", :require => "chronos/rails"
57
+ gem "chronos-ruby", "~> 1.1.0", :require => "chronos/rails"
58
58
  ```
59
59
 
60
60
  Gere o initializer:
@@ -174,7 +174,7 @@ A regra recebe um notice normalizado e imutável, e somente `true` descarta. Fal
174
174
 
175
175
  ## Monitoramento de performance
176
176
 
177
- A Versão 0.7 introduziu agregação local de requests, queries e jobs em `metric_batch`; a Versão 0.8 adicionou HTTP externo. Grupos possuem contagem, erro, duração, histograma, status e breakdown limitados. Percentis são calculados no SaaS.
177
+ A Versão 0.7 introduziu agregação local de requests, queries e jobs em `metric_batch`; a Versão 0.8 adicionou HTTP externo. Grupos possuem contagem, erro, duração, histograma, percentis aproximados, severidades, diagnósticos, status e breakdown limitados.
178
178
 
179
179
  ```ruby
180
180
  Chronos.configure do |config|
@@ -185,21 +185,35 @@ Chronos.configure do |config|
185
185
  config.apm_max_queries_per_request = 100
186
186
  config.apm_slow_query_threshold_ms = 500.0
187
187
  config.apm_n_plus_one_threshold = 5
188
+ config.apm_trace_ttl_seconds = 60.0
189
+ config.apm_query_analysis_enabled = true
190
+ config.apm_query_analysis_max_queries = 100
191
+
192
+ # Opt-in: cada fingerprint elegível pode consultar catálogo/estatística/plano.
193
+ config.apm_query_inspection_enabled = false
194
+ config.apm_query_statistics_enabled = false
195
+ config.apm_query_plan_enabled = false
196
+ config.apm_query_inspection_min_duration_ms = 500.0
197
+ config.apm_query_inspection_max_queries = 20
198
+ config.apm_transaction_tracking_enabled = true
199
+ config.apm_transaction_max_connections = 100
188
200
  end
189
201
  ```
190
202
 
191
- SQL bruto e binds não são lidos. Sinais de query lenta, repetição, possível N+1, transação longa, conexão e deadlock são heurísticos. Veja [APM](docs/modules/apm-aggregation.md), [Requests](docs/modules/request-monitoring.md) e [SQL](docs/modules/sql-monitoring.md).
203
+ Por padrão, SQL bruto e binds não são lidos pelo pipeline de análise. A inspeção opt-in usa o SQL original apenas localmente para solicitar `EXPLAIN` sem `ANALYZE`; nunca o inclui no evento. A análise estática produz candidatos, não ordens de criação de índice. Erros usam severidade `error`; lentidão e risco usam `warning`; padrões observados usam `info`; correções propostas usam `suggestion`. Veja [APM](docs/modules/apm-aggregation.md), [Requests](docs/modules/request-monitoring.md) e [SQL](docs/modules/sql-monitoring.md).
192
204
 
193
205
  ## Sidekiq e Active Job
194
206
 
195
- A versão `0.6.0.pre.1` introduziu middleware Sidekiq 4/5; a API estável mantém o require explícito:
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.
196
210
 
197
211
  ```ruby
198
212
  gem "sidekiq", "~> 5.0"
199
- gem "chronos-ruby", "~> 1.0.0", :require => "chronos/sidekiq"
213
+ gem "chronos-ruby", "~> 1.1.0", :require => "chronos/sidekiq"
200
214
  ```
201
215
 
202
- 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).
203
217
 
204
218
  ## Deploy tracking
205
219
 
@@ -307,7 +321,7 @@ Execute suíte e lint no runtime atual:
307
321
  bundle _1.17.3_ exec rake
308
322
  ```
309
323
 
310
- 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.
311
325
 
312
326
  ## Contribuição
313
327
 
@@ -24,10 +24,60 @@
24
24
  "type": "object",
25
25
  "required": ["total", "min", "max", "average"]
26
26
  },
27
+ "percentiles_ms": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "required": ["p50", "p95", "p99"],
31
+ "properties": {
32
+ "p50": {"type": "number", "minimum": 0},
33
+ "p95": {"type": "number", "minimum": 0},
34
+ "p99": {"type": "number", "minimum": 0}
35
+ }
36
+ },
27
37
  "histogram": {"type": "array", "maxItems": 20},
28
38
  "breakdown_ms": {"type": "object"},
29
39
  "signals": {"type": "object"},
30
- "status_codes": {"type": "object"}
40
+ "status_codes": {"type": "object"},
41
+ "severity_counts": {
42
+ "type": "object",
43
+ "additionalProperties": false,
44
+ "properties": {
45
+ "error": {"type": "integer", "minimum": 0},
46
+ "warning": {"type": "integer", "minimum": 0},
47
+ "info": {"type": "integer", "minimum": 0},
48
+ "suggestion": {"type": "integer", "minimum": 0}
49
+ }
50
+ },
51
+ "diagnostics": {
52
+ "type": "array",
53
+ "maxItems": 20,
54
+ "items": {
55
+ "type": "object",
56
+ "additionalProperties": false,
57
+ "required": ["code", "severity", "category", "message", "evidence", "count"],
58
+ "properties": {
59
+ "code": {"type": "string", "maxLength": 128},
60
+ "severity": {"enum": ["error", "warning", "info", "suggestion"]},
61
+ "category": {"type": "string", "maxLength": 64},
62
+ "message": {"type": "string", "maxLength": 512},
63
+ "recommendation": {"type": "string", "maxLength": 512},
64
+ "evidence": {"type": "object"},
65
+ "count": {"type": "integer", "minimum": 1}
66
+ }
67
+ }
68
+ },
69
+ "query_analysis": {"type": "object"},
70
+ "tracking": {
71
+ "type": "object",
72
+ "additionalProperties": false,
73
+ "required": ["active_traces", "dropped_trace_trackers", "expired_trace_trackers", "dropped_query_fingerprints"],
74
+ "properties": {
75
+ "active_traces": {"type": "integer", "minimum": 0},
76
+ "dropped_trace_trackers": {"type": "integer", "minimum": 0},
77
+ "expired_trace_trackers": {"type": "integer", "minimum": 0},
78
+ "dropped_query_fingerprints": {"type": "integer", "minimum": 0}
79
+ }
80
+ }
31
81
  }
32
82
  }
33
83
  }
@@ -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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Status
4
4
 
5
- Accepted for version 0.7.
5
+ Accepted for version 0.7; trace-drain behavior amended by ADR-019.
6
6
 
7
7
  ## Context
8
8
 
@@ -12,7 +12,7 @@ Sending every request, SQL query, and job as an independent event increases netw
12
12
 
13
13
  Aggregate request, query, and job observations by bounded low-cardinality dimensions. Store only counters, error counters, duration sum/min/max, fixed histogram buckets, component totals, status counters, and heuristic signal counters. Calculate averages locally and percentiles in the SaaS.
14
14
 
15
- Use a fixed group limit, fixed query-fingerprint limit per trace, fixed batch size, and no new timer thread. Drain on observation threshold, explicit flush, and close. Clear incomplete trace trackers during drain. Normalize SQL comments and literal values before fingerprinting and never read binds. Use a `metric_batch` event through the existing sanitization, queue, retry, circuit breaker, and backlog pipeline.
15
+ Use a fixed group limit, fixed query-fingerprint limit per trace, fixed batch size, and no new timer thread. Drain on observation threshold, explicit flush, and close. ADR-019 later preserves incomplete trace trackers across drains under capacity and idle-TTL limits. Normalize SQL comments and literal values before fingerprinting and never read binds. Use a `metric_batch` event through the existing sanitization, queue, retry, circuit breaker, and backlog pipeline.
16
16
 
17
17
  ## Alternatives
18
18
 
@@ -24,4 +24,4 @@ Delivery volume is reduced, memory remains bounded, request breakdown is availab
24
24
 
25
25
  ## Negative consequences
26
26
 
27
- Process crashes can lose undrained aggregates, incomplete trackers are discarded on drain, local signals are heuristic, and a defensive SQL normalizer cannot understand every database dialect.
27
+ Process crashes can lose undrained aggregates, idle/over-capacity trackers can be discarded and are counted, local signals are heuristic, and a defensive SQL normalizer cannot understand every database dialect.
@@ -0,0 +1,44 @@
1
+ # ADR-019 — Bounded query diagnostics and opt-in database inspection
2
+
3
+ ## Status
4
+
5
+ Accepted.
6
+
7
+ ## Context
8
+
9
+ Fingerprint repetition and duration identify useful SQL symptoms but do not tell the consumer which access columns were observed, whether an existing index covers them, or what the database planner estimated. Running deep analysis inside a legacy application can add latency, expose data, increase cardinality, and violate the fixed-memory requirement.
10
+
11
+ ## Decision
12
+
13
+ Analyze normalized SELECT text by default with a dependency-free bounded heuristic. Emit structured diagnostics with stable code, severity (`error`, `warning`, `info`, `suggestion`), category, bounded evidence, count, and optional recommendation.
14
+
15
+ Define `Ports::QueryInspector` and keep its ActiveRecord implementation in `Chronos::Rails`. Index, statistics, and plan inspection are independent explicit opt-ins. Inspect each eligible fingerprint at most once per subscriber, cap inspected fingerprints, and require a duration threshold. Plans use `EXPLAIN` only, never `EXPLAIN ANALYZE`; DDL and automatic index creation are prohibited. Retain only allowlisted schema/planner fields and exception class names. Never retain raw SQL, binds, plan predicates, arbitrary rows, or exception messages.
16
+
17
+ Preserve trace trackers across metric drains with capacity and idle TTL. Report loss counters. Measure outer transaction time with bounded per-connection notification state. Derive approximate p50/p95/p99 from fixed histograms without retaining duration samples.
18
+
19
+ Keep new metric fields optional in the v1 JSON Schema for backward compatibility, although the new agent emits them. Consumer models use diagnostic code/severity as stable fields and treat message text as a fallback presentation value.
20
+
21
+ ## Alternatives
22
+
23
+ - Server-only static analysis was rejected because existing-index comparison requires local schema evidence and immediate signals remain useful.
24
+ - Automatic `EXPLAIN ANALYZE` was rejected because it executes the query.
25
+ - Automatic `CREATE INDEX` was rejected because it can block writes, consume storage, and regress mutation workloads.
26
+ - Shipping raw SQL/plans was rejected for privacy and cardinality.
27
+ - A full SQL-parser dependency was rejected for legacy compatibility, installation cost, and dialect complexity.
28
+ - Unbounded trace/inspection caches were rejected for memory safety.
29
+
30
+ ## Positive consequences
31
+
32
+ - Consumers receive actionable, typed evidence with clear confidence limits.
33
+ - Default analysis adds no database round trip.
34
+ - Opt-in inspection is read-only, sampled by fingerprint/duration, and contains adapter failures.
35
+ - Errors, warnings, information, and suggestions can map directly to observer models.
36
+ - Trace correlation no longer disappears at every aggregate flush.
37
+
38
+ ## Negative consequences
39
+
40
+ - Static parsing can miss complex SQL, aliases, expressions, partial indexes, and dialect features.
41
+ - Even non-executing `EXPLAIN` and catalog reads consume a connection/planner budget and have no portable legacy timeout.
42
+ - Index recommendations do not know true selectivity, write cost, storage, or production distribution.
43
+ - Approximate percentiles use histogram upper bounds and may be coarse.
44
+ - Transaction elapsed time is measured between notification callbacks, not database wire boundaries.
data/docs/architecture.md CHANGED
@@ -20,7 +20,10 @@ flowchart TB
20
20
  Sidekiq --> Envelope[Versioned job-envelope context]
21
21
  Application --> Telemetry[Core / TelemetryEvent]
22
22
  Telemetry --> APM[Application / ApmAggregator]
23
- APM --> Metrics[Core / MetricAggregate and SqlNormalizer]
23
+ APM --> Metrics[Core / MetricAggregate, SqlNormalizer, and SqlQueryAnalyzer]
24
+ Rails --> Inspector[ActiveRecordQueryInspector opt-in]
25
+ Inspector --> QueryPort[Ports / QueryInspector]
26
+ Inspector --> Metrics
24
27
  NetHTTP[Explicit Net::HTTP instance wrapper] --> Facade
25
28
  Rails --> Cache[Core / CacheNormalizer]
26
29
  Application --> Dependencies[Application / DependencyReporter]
@@ -53,7 +56,7 @@ Rails timings become immutable `TelemetryEvent` values. `CaptureTelemetry` appli
53
56
 
54
57
  Sidekiq client middleware writes a versioned, allowlisted trace/request context beside the job's public arguments. Server middleware restores a job scope, limits arguments and tags, emits a job observation, and routes a failure through the existing notice pipeline before re-raising it. Sidekiq retains queue, retry, thread, and connection lifecycle ownership.
55
58
 
56
- Version 0.7 routes request, query, and job observations through `ApmAggregator`. `SqlNormalizer` removes values before grouping, while `MetricAggregate` owns fixed numerical statistics. Aggregates and per-trace query trackers are bounded and mutex-protected. Threshold or lifecycle drains create `metric_batch` telemetry that passes through the existing sanitizer and delivery pipeline. No APM-specific thread is created.
59
+ Version 0.7 routes request, query, and job observations through `ApmAggregator`. `SqlNormalizer` removes values before grouping, `SqlQueryAnalyzer` derives bounded access evidence, and `MetricAggregate` owns fixed numerical statistics, approximate percentiles, severities, diagnostics, and one representative analysis. Active trace trackers survive group drains, expire by TTL, and expose loss counters. `ActiveRecordQueryInspector` is an optional Rails adapter behind `Ports::QueryInspector`; it reads public index metadata and allowlisted PostgreSQL/MySQL statistics/plans without `ANALYZE` or DDL. Threshold or lifecycle drains create `metric_batch` telemetry that passes through the existing sanitizer and delivery pipeline. No APM-specific thread is created.
57
60
 
58
61
  Version 0.8 prepends a wrapper only to each explicitly selected `Net::HTTP` object. The wrapper records bounded outcome metadata and feeds the existing APM aggregator; it does not modify `Net::HTTP` globally. `CacheNormalizer` turns public Rails cache notifications into bounded metadata before delivery. `DependencyReporter` reads already loaded runtime metadata once per agent under a mutex and queues it as a separate event.
59
62
 
@@ -1,43 +1,37 @@
1
1
  # Compatibility
2
2
 
3
- Chronos Ruby 1.0 is the stable legacy line. Technical compatibility does not make an end-of-life Ruby, Rails, Rack, or Sidekiq release secure.
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. The green candidate evidence that permitted the 1.0 promotion is recorded in [Version 1.0 readiness](release-1.0-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
 
40
34
  - Supported: every mandatory compatibility gate for the exact pair passes.
41
- - Best effort: intended to work but missing a complete gate; no 1.0 pair is advertised this way.
35
+ - Best effort: intended to work but missing a complete gate; no 1.1 pair is advertised this way.
42
36
  - Deprecated: still tested while removal is planned.
43
- - Unsupported: outside the tested 1.0 contract.
37
+ - Unsupported: outside the tested 1.1 contract.
@@ -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 |
@@ -63,8 +63,20 @@
63
63
  | `apm_long_transaction_threshold_ms` | Optional | `1000.0` | Transaction-labelled SQL duration that produces a signal |
64
64
  | `apm_n_plus_one_threshold` | Optional | `5` | Repeated fingerprint count producing one possible-N+1 signal; minimum 2 |
65
65
  | `apm_histogram_buckets` | Optional | Fixed millisecond boundaries | Increasing positive duration boundaries; at most 19 plus `+Inf` |
66
+ | `apm_trace_ttl_seconds` | Optional | `60.0` | Maximum idle time retained for an incomplete trace or transaction tracker |
67
+ | `apm_query_analysis_enabled` | Optional | `true` | Analyzes bounded normalized SELECT shapes and emits informational/index-candidate diagnostics |
68
+ | `apm_query_analysis_max_queries` | Optional | `100` | Maximum normalized fingerprints analyzed and cached per subscriber; range 1–500 |
69
+ | `apm_query_inspection_enabled` | Optional | `false` | Opts in to read-only comparison with ActiveRecord index metadata |
70
+ | `apm_query_statistics_enabled` | Optional | `false` | Adds adapter-specific estimated table-row statistics; requires query inspection |
71
+ | `apm_query_plan_enabled` | Optional | `false` | Adds allowlisted `EXPLAIN` evidence for eligible SELECTs; never uses `ANALYZE` |
72
+ | `apm_query_inspection_min_duration_ms` | Optional | `500.0` | Minimum observed query duration before an opt-in database inspection |
73
+ | `apm_query_inspection_max_queries` | Optional | `20` | Maximum unique fingerprints inspected per subscriber; range 1–100 |
74
+ | `apm_transaction_tracking_enabled` | Optional | `true` | Measures bounded outer transaction elapsed time by notification connection |
75
+ | `apm_transaction_max_connections` | Optional | `100` | Maximum concurrently tracked transaction connections; range 1–500 |
66
76
  | `external_http_enabled` | Optional | `false` | Allows explicit per-instance outbound `Net::HTTP` instrumentation |
67
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 |
68
80
  | `cache_key_mode` | Optional | `:none` | `:none` omits keys; `:sha256` emits a project-scoped key hash |
69
81
  | `dependency_reporting` | Optional | `true` | Emits one bounded dependency event per configured agent |
70
82
  | `dependency_max_items` | Optional | `100` | Loaded gem entries retained in the inventory; range 1–200 |
@@ -90,7 +102,7 @@ Chronos.configure do |config|
90
102
  config.max_retries = 3
91
103
  config.backlog_size = 100
92
104
  config.circuit_failure_threshold = 5
93
- config.context_store = :thread_local
105
+ config.context_store = :fiber_local
94
106
  config.breadcrumb_capacity = 20
95
107
  config.rails_capture_in_test = false
96
108
  config.rails_capture_in_console = false
@@ -102,8 +114,21 @@ Chronos.configure do |config|
102
114
  config.apm_slow_query_threshold_ms = 500.0
103
115
  config.apm_long_transaction_threshold_ms = 1000.0
104
116
  config.apm_n_plus_one_threshold = 5
117
+ config.apm_trace_ttl_seconds = 60.0
118
+ config.apm_query_analysis_enabled = true
119
+ config.apm_query_analysis_max_queries = 100
120
+ # Database access is explicit because each inspected fingerprint adds read-only work.
121
+ config.apm_query_inspection_enabled = false
122
+ config.apm_query_statistics_enabled = false
123
+ config.apm_query_plan_enabled = false
124
+ config.apm_query_inspection_min_duration_ms = 500.0
125
+ config.apm_query_inspection_max_queries = 20
126
+ config.apm_transaction_tracking_enabled = true
127
+ config.apm_transaction_max_connections = 100
105
128
  config.external_http_enabled = false
106
129
  config.external_http_trace_headers = true
130
+ config.w3c_trace_context = false
131
+ config.opentelemetry_bridge = true
107
132
  config.cache_key_mode = :none
108
133
  config.dependency_reporting = true
109
134
  config.dependency_max_items = 100
@@ -33,13 +33,19 @@ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and agg
33
33
  | Sidekiq arguments | Collected, sanitized, and bounded | First 20 job arguments; collections/depth/strings limited |
34
34
  | Sidekiq tags | Collected and bounded | Job payload or public worker options |
35
35
  | Sidekiq trace/request IDs | Propagated when present; trace generated otherwise | Chronos job-envelope metadata |
36
- | APM counts, error counts/rates, duration total/min/max/average | Aggregated by default | Request, query, and job observations |
37
- | Fixed duration histogram and status counts | Aggregated by default | Local bounded counters |
36
+ | APM counts, error counts/rates, duration total/min/max/average and approximate p50/p95/p99 | Aggregated by default | Request, query, and job observations plus fixed histogram |
37
+ | Fixed duration histogram, status counts, severity counts and bounded diagnostics | Aggregated by default | Local bounded counters and classifiers |
38
38
  | Component breakdown | database/view/external_http/cache/queue/application when observable | Trace-local bounded totals |
39
39
  | Normalized SQL and SHA-256 fingerprint | Collected without comments, literals, or binds | `sql.active_record` payload |
40
40
  | SQL adapter, operation, inferred table, AR name, cache flag, role/shard | Collected when exposed | Public notification payload and connection feature detection |
41
41
  | Slow SQL source frame | Collected only for threshold-selected slow queries | Bounded application call frame |
42
42
  | APM diagnostic signals | Heuristic counters | Local threshold and repetition detection |
43
+ | SELECT tables and equality/range/join/order columns; index candidates | Collected by default from normalized SQL; bounded | Local static query analyzer |
44
+ | Existing index names/columns/uniqueness | Disabled by default; read-only opt-in | Public ActiveRecord schema metadata |
45
+ | Estimated table rows | Disabled by default; read-only opt-in | PostgreSQL `pg_class` or MySQL `information_schema` |
46
+ | Allowlisted plan node type/table/index/estimated rows/cost | Disabled by default; `EXPLAIN` without `ANALYZE` | Local database planner |
47
+ | Complete outer transaction approximate duration | Collected by default with bounded state | BEGIN/COMMIT/ROLLBACK notifications keyed by local connection identity |
48
+ | Trace tracking loss/expiration counters | Aggregated by default | Local bounded tracker lifecycle |
43
49
  | External HTTP host, method, status, duration, timeout, connection-error flag, error class | Disabled by default; per-instance opt-in | Instrumented `Net::HTTP` object |
44
50
  | Chronos trace/request headers | Propagated when available | Current execution context |
45
51
  | Cache operation, backend, namespace, hit/miss, duration | Collected; key/value omitted | ActiveSupport cache notifications |
@@ -50,7 +56,7 @@ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and agg
50
56
  | Deploy environment, revision, version, repository, actor, deploy ID, service, region, instance | Explicit synchronous deploy API | Application/deployment integration arguments |
51
57
  | Integration verification ID, fixed test marker, synthetic exception class/message, and receipt correlation | Only when `Chronos.verify_integration` or its Rake task is invoked | Locally generated bounded values |
52
58
 
53
- The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, Git state, source code, raw SQL, SQL bind values, database rows, raw cache keys/values, mail bodies/recipients, Active Job arguments, gem paths, or lockfile contents. Active Job IDs, Sidekiq JIDs/arguments, loaded gem names/versions, and deploy fields are documented integration data.
59
+ The gem never transmits request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, Git state, source code, raw SQL, SQL bind values, database rows, plan predicates, raw cache keys/values, mail bodies/recipients, Active Job arguments, gem paths, or lockfile contents. Opt-in plan inspection temporarily supplies the already observed local SELECT to `EXPLAIN` without `ANALYZE` and discards it. Active Job IDs, Sidekiq JIDs/arguments, loaded gem names/versions, and deploy fields are documented integration data.
54
60
 
55
61
  APM dimensions never include user ID, job ID, raw URL, exception message, bind value, or cache key. Normalized routes replace common numeric/UUID segments. Normalized SQL can retain schema, table, and column identifiers; review those identifiers as part of the privacy audit.
56
62
 
@@ -29,6 +29,12 @@ For network-free per-instance outbound HTTP instrumentation, run:
29
29
  bundle _1.17.3_ exec ruby examples/plain-ruby/external_http.rb
30
30
  ```
31
31
 
32
+ For a network-free normalized-query/index diagnostic example, run:
33
+
34
+ ```bash
35
+ bundle _1.17.3_ exec ruby examples/plain-ruby/query_analysis.rb
36
+ ```
37
+
32
38
  For a network-free deploy event with complete release correlation, run:
33
39
 
34
40
  ```bash
@@ -4,13 +4,13 @@ Version `0.8.0.pre.1` aggregates request, SQL, job, and explicitly instrumented
4
4
 
5
5
  ## Request metrics
6
6
 
7
- Request groups use normalized route and HTTP method. Status codes are counted inside the group so error rate can be calculated without making status part of an unbounded key. Each metric contains count, error count/rate, total, minimum, maximum, average, fixed histogram buckets, and component breakdown. Percentiles remain server-side because retaining every duration locally would violate the memory boundary.
7
+ Request groups use normalized route and HTTP method. Status codes are counted inside the group so error rate can be calculated without making status part of an unbounded key. Each metric contains count, error count/rate, total, minimum, maximum, average, fixed histogram buckets, approximate p50/p95/p99 upper bounds, severity counts, bounded diagnostics, and component breakdown. The SaaS may calculate more precise percentiles from merged histograms; the agent never retains raw duration arrays.
8
8
 
9
9
  Rack records request metrics for non-Rails applications. Rails controller notifications and Rack share `record_event_once`, so the same request is counted only once. Controller notification data wins when available because its route is more precise.
10
10
 
11
11
  ## SQL metrics and signals
12
12
 
13
- `Chronos::Core::SqlNormalizer` removes block/line comments, quoted literal values, numeric values, booleans, nulls, and repeated `IN` values before producing a SHA-256 fingerprint. Binds are never read. Query dimensions can contain adapter, operation, inferred table, bounded normalized query, fingerprint, Active Record operation name, cache flag, connection role/shard, and a bounded source frame for slow sampled queries.
13
+ `Chronos::Core::SqlNormalizer` removes block/line comments, quoted literal values, numeric values, booleans, nulls, and repeated `IN` values before producing a SHA-256 fingerprint. Binds are never read. `SqlQueryAnalyzer` extracts bounded SELECT access columns. The optional `ActiveRecordQueryInspector` compares existing indexes and adds allowlisted statistics/plans without retaining raw SQL. Query dimensions can contain adapter, operation, inferred table, bounded normalized query, fingerprint, Active Record operation name, cache flag, connection role/shard, and a bounded source frame for slow sampled queries.
14
14
 
15
15
  Local signals are intentionally heuristic:
16
16
 
@@ -18,7 +18,8 @@ Local signals are intentionally heuristic:
18
18
  - `repeated_query` after the same fingerprint appears again in one trace;
19
19
  - `possible_n_plus_one` once when the configured repetition threshold is reached;
20
20
  - `long_transaction` for transaction-labelled SQL over its threshold;
21
- - `connection_error` and `deadlock` from bounded exception class names.
21
+ - `connection_error`, query/pool/lock timeout, constraint violation, and `deadlock` from bounded exception class names;
22
+ - index candidate/coverage and sequential-scan evidence from bounded query analysis.
22
23
 
23
24
  The SaaS must confirm and correlate these signals. They are not proof of an N+1, deadlock, or application defect.
24
25
 
@@ -29,9 +30,11 @@ The SaaS must confirm and correlate these signals. They are not proof of an N+1,
29
30
  - at most `apm_max_queries_per_request` fingerprints per trace;
30
31
  - at most 19 configured histogram boundaries plus `+Inf`;
31
32
  - at most `apm_batch_size` groups per payload, with a hard maximum of 50;
32
- - trackers are removed when their request completes and all remaining trackers are cleared on aggregate drain.
33
+ - trackers are removed when their request completes or their idle TTL expires; aggregate drain preserves active trackers;
34
+ - at most 20 diagnostics and one representative bounded query analysis per metric group;
35
+ - at most `apm_query_inspection_max_queries` database inspections per Rails subscriber.
33
36
 
34
- New groups beyond capacity are dropped and counted in `dropped_groups`. Existing groups continue accumulating. The state is process-local and is lost on restart.
37
+ New groups beyond capacity are dropped and counted in `dropped_groups`. Tracking loss is reported through `dropped_trace_trackers`, `expired_trace_trackers`, and `dropped_query_fingerprints`. Existing groups continue accumulating. The state is process-local and is lost on restart.
35
38
 
36
39
  ## Breakdown
37
40
 
@@ -51,6 +54,16 @@ Chronos.configure do |config|
51
54
  config.apm_long_transaction_threshold_ms = 1000.0
52
55
  config.apm_n_plus_one_threshold = 5
53
56
  config.apm_histogram_buckets = [5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0]
57
+ config.apm_trace_ttl_seconds = 60.0
58
+ config.apm_query_analysis_enabled = true
59
+ config.apm_query_analysis_max_queries = 100
60
+ config.apm_query_inspection_enabled = false
61
+ config.apm_query_statistics_enabled = false
62
+ config.apm_query_plan_enabled = false
63
+ config.apm_query_inspection_min_duration_ms = 500.0
64
+ config.apm_query_inspection_max_queries = 20
65
+ config.apm_transaction_tracking_enabled = true
66
+ config.apm_transaction_max_connections = 100
54
67
  end
55
68
  ```
56
69
 
@@ -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.