closeyourit-ruby 0.9.4 → 0.10.0

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: a1d5e126aa96c17501b6830b9c7763979c239cd4bf4b0e0d43685b0652025378
4
- data.tar.gz: c2d52c988da1288a5cea2986261481ec87fd67db8f3fb0921e2161c90653022e
3
+ metadata.gz: 0bc659e436ad9b08b9861d4898e6876a3b753ec88ebb7f8e81075729d958fcb4
4
+ data.tar.gz: 2bb509651ce4aa880a40f2d49f29c9584bb1bfde984b6fba3065c1a954800f82
5
5
  SHA512:
6
- metadata.gz: e9b3c6b9d352ad92055e710951157956c6fa3a75c8cbcee6bf6f9dbdbd3eb829ac6c990f54a38a72ef141c2e8d5a71eb714905355cbeee7be94a3957d6f6b22e
7
- data.tar.gz: 6bc37b84e2d6a19e6690df0c40732df11fcf32069f1c9942f2d2ddbf6bb76cedb29a94e86c6fd4269002c9f0f9a4c352c53e1c0b41da9a32f5588503ad645b45
6
+ metadata.gz: d3cfd52ac4b7ddda5a5a92485ebe1fb4be25b808e706b11af7c5e0c6767fff19af3aa3958a8a3d9e6d94ec6ec36162a942fa439700bd16d07d52987d34a4328e
7
+ data.tar.gz: fe22c4cae5a0e94f4eeaf8284d00c0a42d51a0d8c3830dcd703abcf9774eaf31c4e5e81c0c4b8ba6331a1e3d331078136b2cf05e09316325c5f545acaa8b9c17
data/README.md CHANGED
@@ -79,6 +79,7 @@ end
79
79
  | `before_send` | `nil` | `->(payload) { ... }` — scrub finale; ritorna payload o `nil` per scartare |
80
80
  | `sample_rate` | `1.0` | Frazione di errori/messaggi inviata (`1.0` tutto, `0.0` niente) |
81
81
  | `async_threads` | `cpu/2` | Thread di invio; `0` = sincrono (test) |
82
+ | `shutdown_timeout` | `15` | Secondi di attesa, alla chiusura, perché gli invii in volo completino; scaduto il tempo gli eventi residui sono contati come `dropped` (vedi [Flush di fine-vita](#flush-di-fine-vita)) |
82
83
  | `trap_signals` | `false` | Intercetta `SIGTERM` per garantire il flush di fine-vita (deploy/Kamal); opt-in perché sovrascrive un eventuale handler `TERM` dell'app ospite (vedi [Flush di fine-vita](#flush-di-fine-vita)) |
83
84
  | `slow_query_threshold_ms` | `100` | Soglia query lente |
84
85
  | `excluded_query_patterns` | `solid_queue_`, `solid_cache_`, `solid_cable_` | Query da NON misurare come rallentamento — **Regexp** (pattern) o **String** (testo letterale). Vale solo per la misura: breadcrumb e profiling N+1 vedono tutto |
@@ -227,6 +228,11 @@ buffer), così i log sotto-batch di rake/CLI non vanno persi all'uscita. `SIGTER
227
228
  termina il processo **senza** eseguire gli `at_exit`: imposta `trap_signals = true` per intercettarlo e
228
229
  convertirlo in un exit pulito, oppure chiama `CloseYourIt.shutdown` esplicitamente prima di terminare.
229
230
 
231
+ Il drain attende fino a `shutdown_timeout` secondi (default `15`, il tetto di durata di una singola
232
+ POST verso l'ingest). Gli eventi ancora in coda quando il tempo scade sono contati come `dropped`
233
+ nello snapshot diagnostico di fine-vita: nessuna perdita silenziosa. Abbassa il valore se l'uscita
234
+ del processo deve essere più rapida della consegna.
235
+
230
236
  #### Server che forkano (Puma cluster, Sidekiq, Unicorn)
231
237
 
232
238
  I server che precaricano l'app (`preload_app!`) inizializzano la gemma nel processo **master**, poi
@@ -32,15 +32,34 @@ module CloseYourIt
32
32
  accepted
33
33
  end
34
34
 
35
- def shutdown(timeout = 1)
35
+ # Drena la coda attendendo fino a `timeout` secondi. Il default è il tetto di durata di UNA POST
36
+ # (Transport::MAX_REQUEST_SECONDS): aspettare meno abbandona invii ancora sani, ed è quello che
37
+ # succedeva con il vecchio secondo fisso (CYRB-24). Se il drain scade, i task rimasti in coda sono
38
+ # persi: contali come `dropped`, altrimenti lo snapshot di fine vita dichiara zero perdite.
39
+ def shutdown(timeout = Transport::MAX_REQUEST_SECONDS)
36
40
  return unless @executor.respond_to?(:shutdown)
37
41
 
38
42
  @executor.shutdown
39
- @executor.wait_for_termination(timeout)
43
+ terminated = @executor.wait_for_termination(timeout)
44
+ report_abandoned(timeout) unless terminated
45
+ terminated
40
46
  end
41
47
 
42
48
  private
43
49
 
50
+ # Task accodati e mai eseguiti al momento della scadenza del drain. `queue_length` non esiste
51
+ # sull'ImmediateExecutor (sincrono, niente coda) → zero residui.
52
+ def report_abandoned(timeout)
53
+ abandoned = @executor.respond_to?(:queue_length) ? @executor.queue_length.to_i : 0
54
+ CloseYourIt.internal_logger.warn(
55
+ "CloseYourIt background worker: drain scaduto dopo #{timeout}s, #{abandoned} eventi abbandonati"
56
+ )
57
+ abandoned.times do
58
+ CloseYourIt.stats.increment(:dropped)
59
+ CloseYourIt.notify_diagnostic(:drop, reason: :shutdown_timeout)
60
+ end
61
+ end
62
+
44
63
  def build_executor(threads, max_queue)
45
64
  return Concurrent::ImmediateExecutor.new if threads <= 0
46
65
 
@@ -54,17 +54,7 @@ module CloseYourIt
54
54
  def flush_logs(events)
55
55
  return nil if events.nil? || events.empty?
56
56
 
57
- payloads = events.map(&:to_h)
58
- if @configuration.before_send
59
- kept = payloads.filter_map { |payload| @configuration.before_send.call(payload) }
60
- # I log che before_send porta a nil sono scarti voluti: contabilizzali come gli errori/metriche
61
- # (parità con #capture_event), altrimenti sparirebbero silenziosamente dai contatori (CYRB-12).
62
- (payloads.size - kept.size).times do
63
- CloseYourIt.stats.increment(:dropped)
64
- CloseYourIt.notify_diagnostic(:drop, reason: :before_send)
65
- end
66
- payloads = kept
67
- end
57
+ payloads = events.filter_map { |event| build_log_payload(event) }
68
58
  return nil if payloads.empty?
69
59
 
70
60
  path = events.first.ingest_path(@configuration.project_id)
@@ -95,7 +85,30 @@ module CloseYourIt
95
85
  end
96
86
 
97
87
  def shutdown
98
- @worker.shutdown
88
+ @worker.shutdown(@configuration.shutdown_timeout)
89
+ end
90
+
91
+ private
92
+
93
+ # Costruisce il payload di UNA voce di log. `to_h` (scrubber su un byte non UTF-8) e `before_send`
94
+ # possono sollevare: qui il buffer è già stato drenato, quindi una sola voce rotta valutata
95
+ # insieme alle altre porterebbe via l'intero batch di voci sane. Ogni voce è isolata e scartata
96
+ # da sola, come fa il client JS (CYRB-24). `nil` = voce da non spedire, già contabilizzata.
97
+ def build_log_payload(event)
98
+ payload = event.to_h
99
+ payload = @configuration.before_send.call(payload) if @configuration.before_send
100
+ return payload unless payload.nil?
101
+
102
+ # Scarto voluto di before_send: contabilizzato come in #capture_event, altrimenti sparirebbe
103
+ # silenziosamente dai contatori (CYRB-12).
104
+ CloseYourIt.stats.increment(:dropped)
105
+ CloseYourIt.notify_diagnostic(:drop, reason: :before_send)
106
+ nil
107
+ rescue StandardError => e
108
+ CloseYourIt.internal_logger.error("CloseYourIt client: log scartato — #{e.class}: #{e.message}")
109
+ CloseYourIt.stats.increment(:dropped)
110
+ CloseYourIt.notify_diagnostic(:drop, reason: :error, error: e.class.name)
111
+ nil
99
112
  end
100
113
  end
101
114
  end
@@ -27,7 +27,7 @@ module CloseYourIt
27
27
  ].freeze
28
28
 
29
29
  attr_accessor :endpoint_url, :token, :project_id, :environment, :before_send, :on_diagnostic,
30
- :async_threads, :background_worker_max_queue,
30
+ :async_threads, :background_worker_max_queue, :shutdown_timeout,
31
31
  :slow_query_threshold_ms, :slow_method_threshold_ms,
32
32
  :send_pii, :obfuscate_sql, :send_server_name,
33
33
  :capture_query_bindings, :capture_method_arguments,
@@ -65,6 +65,10 @@ module CloseYourIt
65
65
 
66
66
  @async_threads = default_threads
67
67
  @background_worker_max_queue = 30
68
+ # Quanto attendere, alla chiusura del processo, che gli invii in volo completino. Default: il
69
+ # tetto di durata di UNA POST, così l'ultimo batch non viene abbandonato a metà (CYRB-24).
70
+ # Abbassalo se l'uscita del processo deve essere più rapida della consegna.
71
+ @shutdown_timeout = Transport::MAX_REQUEST_SECONDS
68
72
 
69
73
  # Intercetta SIGTERM per garantire il flush di fine-vita (deploy/Kamal): SIGTERM di default
70
74
  # termina il processo SENZA eseguire gli at_exit. OPT-IN perché sovrascrive un eventuale handler
@@ -13,8 +13,16 @@ module CloseYourIt
13
13
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
14
14
  yield
15
15
  ensure
16
- duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0
17
- report(label, duration_ms, location, args: args, kwargs: kwargs)
16
+ # `measure` gira DENTRO il metodo dell'app (Monitor fa prepend): un guasto qui — soglia non
17
+ # configurata, ingest irraggiungibile solleverebbe in un chiamante che aveva già il suo
18
+ # risultato. È l'unico ingresso della gemma che era scoperto: ora è come tutti gli altri
19
+ # (CYRB-24). Un'eccezione del blocco misurato continua invece a propagare intatta.
20
+ begin
21
+ duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0
22
+ report(label, duration_ms, location, args: args, kwargs: kwargs)
23
+ rescue StandardError => e
24
+ CloseYourIt.internal_logger.error("CloseYourIt instrumenter: #{e.class}: #{e.message}")
25
+ end
18
26
  end
19
27
 
20
28
  def report(label, duration_ms, location = nil, args: nil, kwargs: nil)
@@ -15,6 +15,11 @@ module CloseYourIt
15
15
  # Ri-POSTiamo a Location preservando metodo + body, così l'evento non si perde in silenzio.
16
16
  MAX_REDIRECTS = 2
17
17
 
18
+ # Durata massima di UNA send_event: apertura + lettura, ripetute a ogni redirect seguito. È il
19
+ # tetto che il drain di fine vita deve poter aspettare, altrimenti abbandona un invio ancora sano
20
+ # (CYRB-24).
21
+ MAX_REQUEST_SECONDS = (OPEN_TIMEOUT + READ_TIMEOUT) * (MAX_REDIRECTS + 1)
22
+
18
23
  # Un timeout di rete (apertura o lettura) è un fallimento d'invio speciale: lo isoliamo dai non-2xx
19
24
  # e dagli altri errori di rete perché segnala tipicamente problemi di connettività (CYRB-12).
20
25
  TIMEOUT_ERRORS = [ Net::OpenTimeout, Net::ReadTimeout, Timeout::Error ].freeze
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloseYourIt
4
- VERSION = "0.9.4"
4
+ VERSION = "0.10.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: closeyourit-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessio Bussolari