closeyourit-ruby 0.3.3 → 0.3.4

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: 82202d393845e74a379b6470a2f5f1c9e093d70840669266fb86624edd326c48
4
- data.tar.gz: 7a302acfe53c9e0eb866ba407e9133bb63681c3d365126abdbf55368527ab7e4
3
+ metadata.gz: fce4d4eb3a92009bf70daeb903091ec30b4274d926b52d5b8df44644a808c35e
4
+ data.tar.gz: be83377677c8567c700d8ba5d6a47aa1664474db87aa750ab8a61dbed85fadb2
5
5
  SHA512:
6
- metadata.gz: b85ebd77af24ee4aaa41ab447ba434be2ac3892c0ba4c7b09cf5a39069aca91ddb649a179ef621d72c263449452dcb5942c82df5449cd17e37a3494c374b8ed0
7
- data.tar.gz: 037b9982aad55978b4c124d0751da800983327d18d8786228d8bbd00233f913a0e6ef8dd7e741c06307f11ed425ba2e37b8eae9dc985fba7c2067a9cbafda7fa
6
+ metadata.gz: 4533f36e714d2c8ac1706a8bdce8652beed02384f0ce09f5965679c0d76a5e098b5ed006530bc88bbc4c92ed982ed373bd9dd5d5db04cf015770949deebe3e00
7
+ data.tar.gz: 3500e6f71ea42ba8bb900d78dabde73941913e5b03459ab0f2e570b27c26b6637df27a786d5ea1eb6e63b7ffcb8c8b84d07b6299d5553d5711ea3700543449dd
@@ -24,7 +24,10 @@ module CloseYourIt
24
24
  :breadcrumbs_enabled, :max_breadcrumbs, :sample_rate,
25
25
  :capture_handled_errors, :report_active_job_errors,
26
26
  :logs_enabled, :logs_sample_rate, :logs_batch_size, :logs_flush_interval,
27
- :capture_rails_logs, :logs_min_level
27
+ :capture_rails_logs, :logs_min_level,
28
+ :detect_performance_issues, :n_plus_one_threshold, :query_count_threshold,
29
+ :query_time_threshold_ms, :slow_request_threshold_ms, :slow_external_threshold_ms,
30
+ :capture_external_http
28
31
  attr_writer :release
29
32
  attr_reader :excluded_exceptions, :filter_parameters, :scrub_message_patterns
30
33
 
@@ -76,6 +79,17 @@ module CloseYourIt
76
79
  @capture_rails_logs = false
77
80
  @logs_min_level = :info
78
81
 
82
+ # Performance issue detection (verdetti aggregati: N+1, slow request, HTTP esterne lente).
83
+ # OPT-IN, default OFF: profila OGNI query della richiesta → overhead non trascurabile, va attivato
84
+ # consapevolmente per-app. Le soglie sono conservative (poco rumore). Vedi Performance::Rollup.
85
+ @detect_performance_issues = false
86
+ @n_plus_one_threshold = 10 # stesso fingerprint+call-site eseguito > N volte in una richiesta
87
+ @query_count_threshold = 100 # troppe query totali in una richiesta
88
+ @query_time_threshold_ms = 500 # tempo DB totale per richiesta oltre cui = high_query_count
89
+ @slow_request_threshold_ms = 1000 # durata totale della richiesta
90
+ @slow_external_threshold_ms = 1000 # singola chiamata HTTP esterna
91
+ @capture_external_http = true # strumenta Net::HTTP (solo se detect_performance_issues)
92
+
79
93
  @filter_parameters = []
80
94
  @scrub_message_patterns = []
81
95
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "../event"
5
+
6
+ module CloseYourIt
7
+ # Payload `kind=performance_issue` per la pipeline metriche (`/api/v1/projects/:id/metrics`).
8
+ # È un VERDETTO aggregato (N+1, slow request, slow external HTTP), non una metrica grezza:
9
+ # `subtype` lo qualifica, `trace_id` lo correla a log/errori della stessa richiesta. Lo SQL è già
10
+ # offuscato (è il fingerprint del profilo). I campi nil vengono omessi (slow_request non ha sql).
11
+ class PerformanceIssueEvent < Event
12
+ def initialize(attrs, configuration)
13
+ super(configuration)
14
+ @attrs = attrs
15
+ end
16
+
17
+ def to_h
18
+ compact(
19
+ "kind" => "performance_issue",
20
+ "subtype" => @attrs[:subtype],
21
+ "sample_id" => SecureRandom.uuid,
22
+ "duration_ms" => @attrs[:duration_ms]&.round(2),
23
+ "occurred_at" => @occurred_at,
24
+ "environment" => environment,
25
+ "trace_id" => @attrs[:trace_id],
26
+ "sql" => @attrs[:sql],
27
+ "source" => @attrs[:source],
28
+ "route" => @attrs[:route],
29
+ "http_host" => @attrs[:http_host],
30
+ "http_url" => @attrs[:http_url],
31
+ "query_count" => @attrs[:query_count],
32
+ "total_query_time_ms" => @attrs[:total_query_time_ms]&.round(2),
33
+ "count_in_request" => @attrs[:count_in_request],
34
+ "sdk" => sdk
35
+ )
36
+ end
37
+
38
+ def ingest_path(project_id)
39
+ "/api/v1/projects/#{project_id}/metrics"
40
+ end
41
+ end
42
+ end
@@ -3,6 +3,7 @@
3
3
  require "securerandom"
4
4
  require_relative "../event"
5
5
  require_relative "../scrubber"
6
+ require_relative "../scope"
6
7
 
7
8
  module CloseYourIt
8
9
  # Payload `kind=slow_query` per la pipeline metriche (`/api/v1/projects/:id/metrics`).
@@ -27,6 +28,7 @@ module CloseYourIt
27
28
  "cached" => @payload.fetch(:cached, false),
28
29
  "db_system" => db_system,
29
30
  "source" => @payload[:source],
31
+ "trace_id" => CloseYourIt::Scope.current.trace_id,
30
32
  "bindings" => bindings,
31
33
  "sdk" => sdk
32
34
  )
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloseYourIt
4
+ module Performance
5
+ # Accumulatore per-richiesta (vive nello Scope, resettato a fine richiesta). Conta le query, le
6
+ # raggruppa per [fingerprint SQL offuscato, call-site] (il pattern prosopite per l'N+1) e tiene le
7
+ # chiamate HTTP esterne. Puro stato in memoria: il verdetto lo calcola Performance::Rollup.
8
+ class RequestProfile
9
+ # Guard di memoria: cap ai gruppi/chiamate distinti tracciati (il conteggio totale resta esatto).
10
+ MAX_GROUPS = 1000
11
+ MAX_EXTERNAL = 500
12
+
13
+ attr_reader :query_count, :total_query_time_ms, :query_groups, :external_calls
14
+
15
+ def initialize
16
+ @query_count = 0
17
+ @total_query_time_ms = 0.0
18
+ @query_groups = {}
19
+ @external_calls = []
20
+ end
21
+
22
+ # Una query non di sistema. Le query da cache non sono round-trip DB → non contano per l'N+1.
23
+ def add_query(fingerprint:, source:, duration_ms:, cached: false)
24
+ return if cached
25
+
26
+ ms = duration_ms.to_f
27
+ @query_count += 1
28
+ @total_query_time_ms += ms
29
+
30
+ key = "#{fingerprint}\n#{source}"
31
+ group = @query_groups[key]
32
+ if group
33
+ group[:count] += 1
34
+ group[:duration_ms] += ms
35
+ elsif @query_groups.size < MAX_GROUPS
36
+ @query_groups[key] = { count: 1, duration_ms: ms, sql: fingerprint, source: source }
37
+ end
38
+ end
39
+
40
+ def add_external(host:, path:, duration_ms:)
41
+ return if @external_calls.size >= MAX_EXTERNAL
42
+
43
+ @external_calls << { host: host, path: path, duration_ms: duration_ms.to_f }
44
+ end
45
+
46
+ def empty?
47
+ @query_count.zero? && @external_calls.empty?
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../events/performance_issue_event"
4
+
5
+ module CloseYourIt
6
+ module Performance
7
+ # Trasforma un RequestProfile (+ durata/route della richiesta) in 0..N verdetti PerformanceIssueEvent.
8
+ # Le soglie vivono nella Configuration. Detection lato client; dedup/alert lato backend.
9
+ class Rollup
10
+ def self.call(...) = new(...).call
11
+
12
+ def initialize(profile:, configuration:, route: nil, request_duration_ms: nil, trace_id: nil)
13
+ @profile = profile
14
+ @config = configuration
15
+ @route = route
16
+ @request_duration_ms = request_duration_ms
17
+ @trace_id = trace_id
18
+ end
19
+
20
+ def call
21
+ events = []
22
+ events.concat(n_plus_one_events)
23
+ events << high_query_count_event if high_query_count?
24
+ events << slow_request_event if slow_request?
25
+ events.concat(slow_external_events)
26
+ events.compact
27
+ end
28
+
29
+ private
30
+
31
+ # Un verdetto per ogni gruppo [fingerprint, call-site] che ha girato più di n_plus_one_threshold volte.
32
+ def n_plus_one_events
33
+ @profile.query_groups.values.filter_map do |group|
34
+ next unless group[:count] > @config.n_plus_one_threshold
35
+
36
+ build(subtype: "n_plus_one", duration_ms: group[:duration_ms], sql: group[:sql],
37
+ source: group[:source], query_count: group[:count], count_in_request: group[:count])
38
+ end
39
+ end
40
+
41
+ def high_query_count?
42
+ (@config.query_count_threshold && @profile.query_count > @config.query_count_threshold) ||
43
+ (@config.query_time_threshold_ms && @profile.total_query_time_ms > @config.query_time_threshold_ms)
44
+ end
45
+
46
+ def high_query_count_event
47
+ build(subtype: "high_query_count", duration_ms: @profile.total_query_time_ms,
48
+ query_count: @profile.query_count, total_query_time_ms: @profile.total_query_time_ms,
49
+ route: @route)
50
+ end
51
+
52
+ def slow_request?
53
+ @request_duration_ms && @config.slow_request_threshold_ms &&
54
+ @request_duration_ms > @config.slow_request_threshold_ms
55
+ end
56
+
57
+ def slow_request_event
58
+ build(subtype: "slow_request", duration_ms: @request_duration_ms, route: @route,
59
+ query_count: @profile.query_count, total_query_time_ms: @profile.total_query_time_ms)
60
+ end
61
+
62
+ def slow_external_events
63
+ @profile.external_calls.filter_map do |call|
64
+ next unless call[:duration_ms] > @config.slow_external_threshold_ms
65
+
66
+ build(subtype: "slow_external_http", duration_ms: call[:duration_ms],
67
+ http_host: call[:host], http_url: call[:path])
68
+ end
69
+ end
70
+
71
+ def build(attrs)
72
+ PerformanceIssueEvent.new(attrs.merge(trace_id: @trace_id), @config)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require_relative "../scope"
5
+
6
+ module CloseYourIt
7
+ module Rails
8
+ # Prepended a Net::HTTP: cronometra ogni chiamata esterna e la spinge nel RequestProfile dello
9
+ # Scope, così la finestra della richiesta può rilevare le HTTP esterne lente. Trasparente
10
+ # (restituisce la risposta originale) e difensivo (no-op se la telemetria è off; mai solleva per
11
+ # colpa del profiling). Esclude le chiamate verso l'endpoint CloseYourIt stesso (niente loop).
12
+ module NetHTTPPatch
13
+ def request(req, body = nil, &block)
14
+ config = CloseYourIt.configuration
15
+ return super unless config.detect_performance_issues && config.capture_external_http
16
+
17
+ started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
18
+ begin
19
+ super
20
+ ensure
21
+ duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - started) * 1000.0
22
+ record_external(config, req, duration_ms)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def record_external(config, req, duration_ms)
29
+ host = address
30
+ return if host.nil? || own_endpoint?(config, host)
31
+
32
+ CloseYourIt::Scope.current.performance_profile.add_external(
33
+ host: host, path: templatize_path(req), duration_ms: duration_ms
34
+ )
35
+ rescue StandardError
36
+ # Il profiling non deve mai disturbare la chiamata ospite.
37
+ nil
38
+ end
39
+
40
+ def own_endpoint?(config, host)
41
+ endpoint = config.endpoint_url
42
+ return false if endpoint.nil?
43
+
44
+ URI.parse(endpoint).host == host
45
+ rescue URI::InvalidURIError
46
+ false
47
+ end
48
+
49
+ # Path senza query string, con uuid e run di ≥3 cifre → placeholder (stessa rotta = stessa
50
+ # signature). La soglia ≥3 cifre preserva le versioni API tipo "/v1" e templatizza gli id reali
51
+ # ("/v1/charges/ch_12345" → "/v1/charges/ch_<n>", "/users/123" → "/users/<n>").
52
+ def templatize_path(req)
53
+ path = req.respond_to?(:path) ? req.path.to_s : ""
54
+ path.split("?", 2).first.to_s
55
+ .gsub(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i, "<uuid>")
56
+ .gsub(/\d{3,}/, "<n>")
57
+ end
58
+ end
59
+ end
60
+ end
@@ -7,7 +7,9 @@ require_relative "error_subscriber"
7
7
  require_relative "../sidekiq/error_handler"
8
8
  require_relative "query_source"
9
9
  require_relative "log_broadcast"
10
+ require_relative "net_http_patch"
10
11
  require_relative "../subscribers/slow_query"
12
+ require_relative "../subscribers/request_performance"
11
13
 
12
14
  module CloseYourIt
13
15
  module Rails
@@ -45,9 +47,39 @@ module CloseYourIt
45
47
  duration_ms: event.duration,
46
48
  cached: event.payload.fetch(:cached, false)
47
49
  )
50
+ # Accumula la query nel profilo per-richiesta (detection N+1 a fine richiesta).
51
+ subscriber.profile(
52
+ name: event.payload[:name],
53
+ sql: event.payload[:sql],
54
+ duration_ms: event.duration,
55
+ cached: event.payload.fetch(:cached, false),
56
+ source: CloseYourIt::Rails::QuerySource.from_caller
57
+ )
58
+ end
59
+ end
60
+
61
+ # A fine richiesta: trasforma il profilo accumulato in verdetti performance_issue (N+1, slow
62
+ # request, HTTP esterne lente). Il subscriber è no-op se detect_performance_issues è OFF.
63
+ initializer "closeyourit.subscribe_request_performance" do
64
+ perf = CloseYourIt::Subscribers::RequestPerformance.new
65
+
66
+ ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*args|
67
+ event = ActiveSupport::Notifications::Event.new(*args)
68
+ payload = event.payload
69
+ perf.record(
70
+ route: "#{payload[:controller]}##{payload[:action]}",
71
+ duration_ms: event.duration
72
+ )
48
73
  end
49
74
  end
50
75
 
76
+ # Strumenta le chiamate HTTP esterne (Net::HTTP) per rilevare quelle lente nella finestra della
77
+ # richiesta. Il patch è no-op (chiama super) se la detection è OFF → overhead trascurabile.
78
+ initializer "closeyourit.instrument_net_http" do
79
+ require "net/http"
80
+ ::Net::HTTP.prepend(CloseYourIt::Rails::NetHTTPPatch) unless ::Net::HTTP.ancestors.include?(CloseYourIt::Rails::NetHTTPPatch)
81
+ end
82
+
51
83
  # Cattura gli errori di ActiveJob/Solid Queue (around_perform).
52
84
  initializer "closeyourit.active_job" do
53
85
  ActiveSupport.on_load(:active_job) do
@@ -64,7 +96,11 @@ module CloseYourIt
64
96
 
65
97
  # Broadcast opt-in di Rails.logger → CloseYourIt.log (config.capture_rails_logs, default OFF).
66
98
  # Spedisce solo i log dell'app ≥ logs_min_level. Richiede BroadcastLogger (Rails 7.1+).
67
- initializer "closeyourit.capture_rails_logs" do
99
+ # `after: :load_config_initializers`: config.capture_rails_logs è impostato in
100
+ # config/initializers/closeyourit.rb (CloseYourIt.init), che gira DOPO gli initializer dei
101
+ # railtie. Senza questo `after:` il check leggerebbe il default (false) e il broadcast non
102
+ # verrebbe mai agganciato → i log dell'app non arriverebbero a CloseYourIt.
103
+ initializer "closeyourit.capture_rails_logs", after: :load_config_initializers do
68
104
  config = CloseYourIt.configuration
69
105
  if config.capture_rails_logs && ::Rails.logger.respond_to?(:broadcast_to)
70
106
  ::Rails.logger.broadcast_to(CloseYourIt::Rails::LogBroadcast.new(config.logs_min_level))
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "breadcrumb_buffer"
4
+ require_relative "performance/request_profile"
4
5
 
5
6
  module CloseYourIt
6
7
  # Contesto per-richiesta (o per-job) isolato per execution-context (Fiber storage):
@@ -65,6 +66,12 @@ module CloseYourIt
65
66
  @breadcrumbs.add(breadcrumb)
66
67
  end
67
68
 
69
+ # Profilo di performance per-richiesta (query + HTTP esterne). Lazy: creato al primo accesso,
70
+ # azzerato da #clear a fine richiesta. Il verdetto lo calcola Subscribers::RequestPerformance.
71
+ def performance_profile
72
+ @performance_profile ||= Performance::RequestProfile.new
73
+ end
74
+
68
75
  def clear
69
76
  @user = {}
70
77
  @tags = {}
@@ -73,6 +80,7 @@ module CloseYourIt
73
80
  @request = nil
74
81
  @trace_id = nil
75
82
  @breadcrumbs = BreadcrumbBuffer.new(CloseYourIt.configuration.max_breadcrumbs)
83
+ @performance_profile = nil
76
84
  end
77
85
 
78
86
  # Sottoinsieme non vuoto in forma evento Sentry (user/tags/extra/contexts/request),
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../scope"
4
+ require_relative "../performance/rollup"
5
+
6
+ module CloseYourIt
7
+ module Subscribers
8
+ # A fine richiesta (process_action.action_controller) trasforma il RequestProfile accumulato nello
9
+ # Scope in verdetti performance_issue e li spedisce (fire-and-forget). Lo Scope — e quindi il
10
+ # profilo — viene azzerato subito dopo da RequestContext#call (ensure). Logica pura: il wiring ad
11
+ # ActiveSupport::Notifications vive nel Railtie.
12
+ class RequestPerformance
13
+ def initialize(configuration = nil)
14
+ @configuration = configuration
15
+ end
16
+
17
+ def record(route:, duration_ms:)
18
+ config = @configuration || CloseYourIt.configuration
19
+ return unless config.detect_performance_issues
20
+
21
+ profile = CloseYourIt::Scope.current.performance_profile
22
+ return if profile.empty? && !slow_request?(config, duration_ms)
23
+
24
+ events = CloseYourIt::Performance::Rollup.call(
25
+ profile: profile, configuration: config, route: route,
26
+ request_duration_ms: duration_ms, trace_id: CloseYourIt::Scope.current.trace_id
27
+ )
28
+ events.each { |event| CloseYourIt.capture_event(event) }
29
+ end
30
+
31
+ private
32
+
33
+ def slow_request?(config, duration_ms)
34
+ duration_ms && config.slow_request_threshold_ms && duration_ms > config.slow_request_threshold_ms
35
+ end
36
+ end
37
+ end
38
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../events/slow_query_event"
4
4
  require_relative "../scrubber"
5
+ require_relative "../scope"
5
6
 
6
7
  module CloseYourIt
7
8
  module Subscribers
@@ -40,13 +41,33 @@ module CloseYourIt
40
41
  CloseYourIt.add_breadcrumb(
41
42
  category: "query",
42
43
  type: "query",
43
- message: Scrubber.new(config).obfuscate_sql(sql),
44
+ message: scrubber(config).obfuscate_sql(sql),
44
45
  data: { "name" => name, "duration_ms" => duration_ms.to_f.round(2), "cached" => cached }
45
46
  )
46
47
  end
47
48
 
49
+ # Spinge OGNI query non di sistema nel RequestProfile dello Scope (per la detection N+1 a fine
50
+ # richiesta). Solo se detect_performance_issues: fingerprint = SQL offuscato + call-site.
51
+ def profile(name:, sql:, duration_ms:, cached: false, source: nil)
52
+ config = @configuration || CloseYourIt.configuration
53
+ return unless config.detect_performance_issues
54
+ return if ignored_name?(name)
55
+
56
+ CloseYourIt::Scope.current.performance_profile.add_query(
57
+ fingerprint: scrubber(config).obfuscate_sql(sql),
58
+ source: source, duration_ms: duration_ms, cached: cached
59
+ )
60
+ rescue StandardError
61
+ # La telemetria non deve mai disturbare la query ospite.
62
+ nil
63
+ end
64
+
48
65
  private
49
66
 
67
+ def scrubber(config)
68
+ @scrubber ||= Scrubber.new(config)
69
+ end
70
+
50
71
  def ignored_name?(name)
51
72
  name.nil? || IGNORED_NAMES.include?(name)
52
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloseYourIt
4
- VERSION = "0.3.3"
4
+ VERSION = "0.3.4"
5
5
  end
@@ -16,15 +16,20 @@ require_relative "closeyourit/events/message_event"
16
16
  require_relative "closeyourit/events/slow_query_event"
17
17
  require_relative "closeyourit/events/slow_method_event"
18
18
  require_relative "closeyourit/events/log_event"
19
+ require_relative "closeyourit/events/performance_issue_event"
20
+ require_relative "closeyourit/performance/request_profile"
21
+ require_relative "closeyourit/performance/rollup"
19
22
  require_relative "closeyourit/log_device"
20
23
  require_relative "closeyourit/log_buffer"
21
24
  require_relative "closeyourit/subscribers/slow_query"
25
+ require_relative "closeyourit/subscribers/request_performance"
22
26
  require_relative "closeyourit/instrumenter"
23
27
  require_relative "closeyourit/monitor"
24
28
  require_relative "closeyourit/client"
25
29
  require_relative "closeyourit/rails/capture_exceptions"
26
30
  require_relative "closeyourit/rails/request_context"
27
31
  require_relative "closeyourit/rails/log_broadcast"
32
+ require_relative "closeyourit/rails/net_http_patch"
28
33
  require_relative "closeyourit/rails/active_job_extension"
29
34
  require_relative "closeyourit/rails/error_subscriber"
30
35
  require_relative "closeyourit/sidekiq/error_handler"
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.3.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessio Bussolari
@@ -43,16 +43,20 @@ files:
43
43
  - lib/closeyourit/events/error_event.rb
44
44
  - lib/closeyourit/events/log_event.rb
45
45
  - lib/closeyourit/events/message_event.rb
46
+ - lib/closeyourit/events/performance_issue_event.rb
46
47
  - lib/closeyourit/events/slow_method_event.rb
47
48
  - lib/closeyourit/events/slow_query_event.rb
48
49
  - lib/closeyourit/instrumenter.rb
49
50
  - lib/closeyourit/log_buffer.rb
50
51
  - lib/closeyourit/log_device.rb
51
52
  - lib/closeyourit/monitor.rb
53
+ - lib/closeyourit/performance/request_profile.rb
54
+ - lib/closeyourit/performance/rollup.rb
52
55
  - lib/closeyourit/rails/active_job_extension.rb
53
56
  - lib/closeyourit/rails/capture_exceptions.rb
54
57
  - lib/closeyourit/rails/error_subscriber.rb
55
58
  - lib/closeyourit/rails/log_broadcast.rb
59
+ - lib/closeyourit/rails/net_http_patch.rb
56
60
  - lib/closeyourit/rails/query_source.rb
57
61
  - lib/closeyourit/rails/railtie.rb
58
62
  - lib/closeyourit/rails/request_context.rb
@@ -60,6 +64,7 @@ files:
60
64
  - lib/closeyourit/scrubber.rb
61
65
  - lib/closeyourit/sidekiq/error_handler.rb
62
66
  - lib/closeyourit/stats.rb
67
+ - lib/closeyourit/subscribers/request_performance.rb
63
68
  - lib/closeyourit/subscribers/slow_query.rb
64
69
  - lib/closeyourit/transport.rb
65
70
  - lib/closeyourit/version.rb