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 +4 -4
- data/README.md +6 -0
- data/lib/closeyourit/background_worker.rb +21 -2
- data/lib/closeyourit/client.rb +25 -12
- data/lib/closeyourit/configuration.rb +5 -1
- data/lib/closeyourit/instrumenter.rb +10 -2
- data/lib/closeyourit/transport.rb +5 -0
- data/lib/closeyourit/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0bc659e436ad9b08b9861d4898e6876a3b753ec88ebb7f8e81075729d958fcb4
|
|
4
|
+
data.tar.gz: 2bb509651ce4aa880a40f2d49f29c9584bb1bfde984b6fba3065c1a954800f82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
data/lib/closeyourit/client.rb
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
17
|
-
|
|
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
|
data/lib/closeyourit/version.rb
CHANGED