closeyourit-ruby 0.9.4 → 0.10.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -0
  3. data/lib/closeyourit/background_worker.rb +28 -9
  4. data/lib/closeyourit/breadcrumb.rb +2 -2
  5. data/lib/closeyourit/breadcrumb_buffer.rb +2 -2
  6. data/lib/closeyourit/client.rb +42 -29
  7. data/lib/closeyourit/configuration.rb +105 -101
  8. data/lib/closeyourit/event.rb +6 -6
  9. data/lib/closeyourit/events/error_event.rb +16 -16
  10. data/lib/closeyourit/events/job_metric_event.rb +8 -8
  11. data/lib/closeyourit/events/log_event.rb +17 -17
  12. data/lib/closeyourit/events/message_event.rb +4 -4
  13. data/lib/closeyourit/events/performance_issue_event.rb +4 -4
  14. data/lib/closeyourit/events/slow_method_event.rb +6 -6
  15. data/lib/closeyourit/events/slow_query_event.rb +4 -4
  16. data/lib/closeyourit/instrumenter.rb +12 -4
  17. data/lib/closeyourit/line_cache.rb +4 -4
  18. data/lib/closeyourit/log_buffer.rb +10 -10
  19. data/lib/closeyourit/log_device.rb +18 -18
  20. data/lib/closeyourit/monitor.rb +2 -2
  21. data/lib/closeyourit/performance/request_profile.rb +5 -5
  22. data/lib/closeyourit/performance/rollup.rb +3 -3
  23. data/lib/closeyourit/rails/active_job_extension.rb +33 -31
  24. data/lib/closeyourit/rails/capture_exceptions.rb +3 -3
  25. data/lib/closeyourit/rails/error_subscriber.rb +4 -4
  26. data/lib/closeyourit/rails/log_broadcast.rb +12 -12
  27. data/lib/closeyourit/rails/net_http_patch.rb +22 -22
  28. data/lib/closeyourit/rails/query_source.rb +3 -3
  29. data/lib/closeyourit/rails/railtie.rb +32 -52
  30. data/lib/closeyourit/rails/request_body.rb +7 -7
  31. data/lib/closeyourit/rails/request_context.rb +27 -27
  32. data/lib/closeyourit/scope.rb +29 -29
  33. data/lib/closeyourit/scrubber.rb +47 -48
  34. data/lib/closeyourit/sidekiq/error_handler.rb +2 -2
  35. data/lib/closeyourit/sidekiq/job_metrics_middleware.rb +10 -11
  36. data/lib/closeyourit/stats.rb +8 -8
  37. data/lib/closeyourit/subscribers/job_performance.rb +30 -19
  38. data/lib/closeyourit/subscribers/request_performance.rb +4 -4
  39. data/lib/closeyourit/subscribers/slow_query.rb +42 -20
  40. data/lib/closeyourit/trace_context.rb +22 -22
  41. data/lib/closeyourit/transport.rb +25 -20
  42. data/lib/closeyourit/usage_registry.rb +17 -16
  43. data/lib/closeyourit/version.rb +1 -1
  44. data/lib/closeyourit-ruby.rb +125 -125
  45. metadata +1 -1
@@ -6,17 +6,22 @@ require "uri"
6
6
  require "timeout"
7
7
 
8
8
  module CloseYourIt
9
- # Spedisce un payload a un path di ingest (errori → /events, metriche → /metrics) via HTTP POST
10
- # con `Authorization: Bearer`. Mai solleva: ogni errore di rete è loggato e ingoiato.
9
+ # Sends a payload to an ingest path (errors → /events, metrics → /metrics) via HTTP POST
10
+ # with `Authorization: Bearer`. Never raises: every network error is logged and swallowed.
11
11
  class Transport
12
12
  OPEN_TIMEOUT = 2
13
13
  READ_TIMEOUT = 3
14
- # Net::HTTP non segue i redirect: l'host canonico può rispondere 301 (es. apex → www).
15
- # Ri-POSTiamo a Location preservando metodo + body, così l'evento non si perde in silenzio.
14
+ # Net::HTTP does not follow redirects: the canonical host may answer 301 (e.g. apex → www).
15
+ # We re-POST to Location keeping method + body, so the event is not silently lost.
16
16
  MAX_REDIRECTS = 2
17
17
 
18
- # Un timeout di rete (apertura o lettura) è un fallimento d'invio speciale: lo isoliamo dai non-2xx
19
- # e dagli altri errori di rete perché segnala tipicamente problemi di connettività (CYRB-12).
18
+ # Maximum duration of ONE send_event: open + read, repeated for every followed redirect. It is the
19
+ # cap the end-of-life drain must be able to wait for, otherwise it abandons a still healthy send
20
+ # (CYRB-24).
21
+ MAX_REQUEST_SECONDS = (OPEN_TIMEOUT + READ_TIMEOUT) * (MAX_REDIRECTS + 1)
22
+
23
+ # A network timeout (open or read) is a special send failure: we keep it apart from non-2xx and
24
+ # other network errors because it typically signals connectivity problems (CYRB-12).
20
25
  TIMEOUT_ERRORS = [ Net::OpenTimeout, Net::ReadTimeout, Timeout::Error ].freeze
21
26
 
22
27
  def initialize(configuration)
@@ -30,7 +35,7 @@ module CloseYourIt
30
35
  CloseYourIt.notify_diagnostic(:send, status: response.code.to_i)
31
36
  else
32
37
  CloseYourIt.stats.increment(:failed)
33
- CloseYourIt.internal_logger.warn("CloseYourIt transport: HTTP #{response.code}#{error_detail(response)} su #{path}")
38
+ CloseYourIt.internal_logger.warn("CloseYourIt transport: HTTP #{response.code}#{error_detail(response)} on #{path}")
34
39
  CloseYourIt.notify_diagnostic(:drop, reason: :response, status: response.code.to_i)
35
40
  end
36
41
  response
@@ -75,8 +80,8 @@ module CloseYourIt
75
80
  http.read_timeout = READ_TIMEOUT
76
81
 
77
82
  request = Net::HTTP::Post.new(uri.request_uri)
78
- # Il Bearer è un segreto d'ingest: non ri-inviarlo dopo un redirect verso un host diverso
79
- # (o un downgrade https→http), per non consegnarlo a un endpoint non fidato.
83
+ # The Bearer is an ingest secret: do not resend it after a redirect to a different host
84
+ # (or an https→http downgrade), so it is never handed to an untrusted endpoint.
80
85
  request["Authorization"] = "Bearer #{@configuration.token}" if authorize
81
86
  request["Content-Type"] = "application/json"
82
87
  request["User-Agent"] = "closeyourit-ruby/#{VERSION}"
@@ -85,16 +90,16 @@ module CloseYourIt
85
90
  http.request(request)
86
91
  end
87
92
 
88
- # Location può essere assoluto (https://www.…) o relativo (/api/…): risolvilo sull'URI corrente.
93
+ # Location may be absolute (https://www.…) or relative (/api/…): resolve it against the current URI.
89
94
  def redirect_uri(current, location)
90
95
  target = URI.parse(location)
91
96
  target.relative? ? current + target : target
92
97
  end
93
98
 
94
- # Il Bearer va ri-inviato solo se il redirect resta sull'autorità dell'endpoint configurato:
95
- # stesso host (o la sua variante www), stessa porta effettiva e senza downgrade https→http.
96
- # La porta fa parte del confine di fiducia (CYRB-22): sullo stesso server una porta diversa è
97
- # un servizio distinto, che non deve ricevere il segreto d'ingest.
99
+ # The Bearer is resent only if the redirect stays on the configured endpoint's authority:
100
+ # same host (or its www variant), same effective port and no https→http downgrade.
101
+ # The port is part of the trust boundary (CYRB-22): on the same server a different port is a
102
+ # separate service, which must not receive the ingest secret.
98
103
  def same_authority?(origin, target)
99
104
  return false if origin.scheme == "https" && target.scheme != "https"
100
105
  return false unless canonical_host(origin) == canonical_host(target)
@@ -102,21 +107,21 @@ module CloseYourIt
102
107
  origin.port == target.port || canonical_upgrade?(origin, target)
103
108
  end
104
109
 
105
- # Un endpoint configurato in `http` che redirige a `https` resta fidato: la porta cambia solo
106
- # perché cambia lo schema (80 → 443), non perché punti a un servizio su porta non standard.
110
+ # An endpoint configured as `http` that redirects to `https` stays trusted: the port changes only
111
+ # because the scheme changes (80 → 443), not because it points to a service on a non-standard port.
107
112
  def canonical_upgrade?(origin, target)
108
113
  origin.scheme == "http" && target.scheme == "https" &&
109
114
  origin.port == origin.default_port && target.port == target.default_port
110
115
  end
111
116
 
112
- # Una porta esplicita pari alla default (`:443` su https) equivale a ometterla: `URI#port` la
113
- # normalizza già, qui resta solo da neutralizzare la variante www dell'host canonico.
117
+ # An explicit port equal to the default (`:443` on https) is the same as omitting it: `URI#port`
118
+ # already normalizes it, only the www variant of the canonical host is left to neutralize here.
114
119
  def canonical_host(uri)
115
120
  uri.host.to_s.sub(/\Awww\./, "")
116
121
  end
117
122
 
118
- # Estrae " R…: messaggio" dall'envelope d'errore del backend, così il dev vede il codice d'errore
119
- # nel log diagnostico (non solo lo status HTTP). Silenzioso se il body non è l'envelope atteso.
123
+ # Extracts " R…: message" from the backend error envelope, so the dev sees the error code in the
124
+ # diagnostic log (not only the HTTP status). Silent if the body is not the expected envelope.
120
125
  def error_detail(response)
121
126
  body = JSON.parse(response.body.to_s)
122
127
  error = body["error"]
@@ -3,25 +3,26 @@
3
3
  require "concurrent"
4
4
 
5
5
  module CloseYourIt
6
- # CYSK-29 — la telemetria d'uso: registra QUALI simboli girano davvero (route come
7
- # `Controller#action`, job, chiavi custom letterali) e li flusha in una POST ogni
8
- # `usage_flush_interval` secondi per processo. Sul percorso caldo: una lookup e un increment.
6
+ # CYSK-29 — usage telemetry: records WHICH symbols actually run (routes as `Controller#action`,
7
+ # jobs, literal custom keys) and flushes them in one POST every `usage_flush_interval` seconds per
8
+ # process. On the hot path: one lookup and one increment.
9
9
  #
10
- # Le regole che rendono il sistema immune alle perdite:
11
- # - i conteggi sono INDICATIVI, solo `last_seen_at` è portante: un flush perso costa una finestra
12
- # su un simbolo che si rivede subito dopo;
13
- # - il registro SI SVUOTA a ogni flush: il tetto (`usage_max_symbols`) limita i simboli distinti
14
- # visti in una finestra — una grandezza legata al traffico, non alla dimensione del codice;
15
- # - oltre il tetto il flush porta `truncated: true`, e lato scanner quel flag SQUALIFICA il kind;
16
- # - nessun sampling, mai: un campionamento su una rotta chiamata 3 volte al mese fabbrica
17
- # esattamente il falso «mai vista» che il sistema esiste per evitare;
18
- # - `route` è `Controller#action`, MAI l'URL: niente path, parametri, user, IP.
10
+ # The rules that make the system immune to losses:
11
+ # - counts are INDICATIVE, only `last_seen_at` is load-bearing: a lost flush costs one window on a
12
+ # symbol that shows up again right after;
13
+ # - the registry is EMPTIED on every flush: the cap (`usage_max_symbols`) limits the distinct
14
+ # symbols seen in one window — a quantity tied to traffic, not to code size;
15
+ # - beyond the cap the flush carries `truncated: true`, and on the scanner side that flag
16
+ # DISQUALIFIES the kind;
17
+ # - no sampling, ever: sampling a route called 3 times a month fabricates exactly the false
18
+ # "never seen" the system exists to avoid;
19
+ # - `route` is `Controller#action`, NEVER the URL: no path, parameters, user, IP.
19
20
  class UsageRegistry
20
- # I simboli vengono da payload di framework o da stringhe letterali: il pattern è il contratto.
21
+ # Symbols come from framework payloads or literal strings: the pattern is the contract.
21
22
  SYMBOL_FORMAT = %r{\A[A-Za-z0-9_:#./-]{1,200}\z}
22
23
  KINDS = %w[route job custom].freeze
23
24
 
24
- attr_reader :timer # esposto per i test (verifica dell'intervallo configurato)
25
+ attr_reader :timer # exposed for tests (checks the configured interval)
25
26
 
26
27
  def initialize(client:, configuration:)
27
28
  @client = client
@@ -61,8 +62,8 @@ module CloseYourIt
61
62
 
62
63
  @client.flush_usage(**payload)
63
64
  rescue StandardError => e
64
- # Il flush gira sul thread timer: un errore non deve propagare nell'app né uccidere il
65
- # TimerTask. La semantica portante è last_seen_at: una finestra persa non falsifica niente.
65
+ # The flush runs on the timer thread: an error must not propagate into the app nor kill the
66
+ # TimerTask. The load-bearing semantics is last_seen_at: a lost window falsifies nothing.
66
67
  CloseYourIt.internal_logger.error("CloseYourIt usage registry: #{e.class}: #{e.message}")
67
68
  end
68
69
 
@@ -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.2"
5
5
  end