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.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/lib/closeyourit/background_worker.rb +28 -9
- data/lib/closeyourit/breadcrumb.rb +2 -2
- data/lib/closeyourit/breadcrumb_buffer.rb +2 -2
- data/lib/closeyourit/client.rb +42 -29
- data/lib/closeyourit/configuration.rb +105 -101
- data/lib/closeyourit/event.rb +6 -6
- data/lib/closeyourit/events/error_event.rb +16 -16
- data/lib/closeyourit/events/job_metric_event.rb +8 -8
- data/lib/closeyourit/events/log_event.rb +17 -17
- data/lib/closeyourit/events/message_event.rb +4 -4
- data/lib/closeyourit/events/performance_issue_event.rb +4 -4
- data/lib/closeyourit/events/slow_method_event.rb +6 -6
- data/lib/closeyourit/events/slow_query_event.rb +4 -4
- data/lib/closeyourit/instrumenter.rb +12 -4
- data/lib/closeyourit/line_cache.rb +4 -4
- data/lib/closeyourit/log_buffer.rb +10 -10
- data/lib/closeyourit/log_device.rb +18 -18
- data/lib/closeyourit/monitor.rb +2 -2
- data/lib/closeyourit/performance/request_profile.rb +5 -5
- data/lib/closeyourit/performance/rollup.rb +3 -3
- data/lib/closeyourit/rails/active_job_extension.rb +33 -31
- data/lib/closeyourit/rails/capture_exceptions.rb +3 -3
- data/lib/closeyourit/rails/error_subscriber.rb +4 -4
- data/lib/closeyourit/rails/log_broadcast.rb +12 -12
- data/lib/closeyourit/rails/net_http_patch.rb +22 -22
- data/lib/closeyourit/rails/query_source.rb +3 -3
- data/lib/closeyourit/rails/railtie.rb +32 -52
- data/lib/closeyourit/rails/request_body.rb +7 -7
- data/lib/closeyourit/rails/request_context.rb +27 -27
- data/lib/closeyourit/scope.rb +29 -29
- data/lib/closeyourit/scrubber.rb +47 -48
- data/lib/closeyourit/sidekiq/error_handler.rb +2 -2
- data/lib/closeyourit/sidekiq/job_metrics_middleware.rb +10 -11
- data/lib/closeyourit/stats.rb +8 -8
- data/lib/closeyourit/subscribers/job_performance.rb +30 -19
- data/lib/closeyourit/subscribers/request_performance.rb +4 -4
- data/lib/closeyourit/subscribers/slow_query.rb +42 -20
- data/lib/closeyourit/trace_context.rb +22 -22
- data/lib/closeyourit/transport.rb +25 -20
- data/lib/closeyourit/usage_registry.rb +17 -16
- data/lib/closeyourit/version.rb +1 -1
- data/lib/closeyourit-ruby.rb +125 -125
- metadata +1 -1
|
@@ -6,17 +6,22 @@ require "uri"
|
|
|
6
6
|
require "timeout"
|
|
7
7
|
|
|
8
8
|
module CloseYourIt
|
|
9
|
-
#
|
|
10
|
-
#
|
|
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
|
|
15
|
-
#
|
|
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
|
-
#
|
|
19
|
-
#
|
|
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)}
|
|
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
|
-
#
|
|
79
|
-
# (
|
|
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
|
|
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
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
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
|
-
#
|
|
106
|
-
#
|
|
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
|
-
#
|
|
113
|
-
#
|
|
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
|
-
#
|
|
119
|
-
#
|
|
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 —
|
|
7
|
-
#
|
|
8
|
-
#
|
|
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
|
-
#
|
|
11
|
-
# -
|
|
12
|
-
#
|
|
13
|
-
# -
|
|
14
|
-
#
|
|
15
|
-
# -
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
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
|
-
#
|
|
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 #
|
|
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
|
-
#
|
|
65
|
-
# TimerTask.
|
|
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
|
|
data/lib/closeyourit/version.rb
CHANGED