app_profiler 0.1.9 → 0.2.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: 6411e547488321b7d7de4fb7cc2d19c48ac462559331ac79b1e161ac11339fe7
4
- data.tar.gz: 8f78938dfcdf37ee184371c6772db42cbca73e547aaab2e904ec51135cb4b69a
3
+ metadata.gz: 1c36371d12c24dfed23e6e492b5c3912e01480bfd7a5973bf1f311a86adc86d3
4
+ data.tar.gz: 1b78da495c55cee24ab6b0d6f8d8194ca6cfb27030de7a4ba90db37fa60e3988
5
5
  SHA512:
6
- metadata.gz: 992474693598d4ff50a51d703143d593cb32156fa12f1e9d65fedd6489d5ff5a8d4d6a823a608dca814a64d505a85dc74a36000a07c0c4803377b9ba28cf5457
7
- data.tar.gz: f184c40a906cc4cf2989becfd0cf5e8dab28c17a3f2a7cf6ad51da3e244a69d58a60bfc8c8b5011d3bcf955602cce24f36dd2122990104e1d2645b3e76a4fc01
6
+ metadata.gz: bd4e66293f491f25caba2a352b509012344bdc6e04c880cb4c3d60d00f3dc38cab50b9c59cf0cbe14c36adc2591610bd004addc28521cf077b7d3b7c91585947
7
+ data.tar.gz: 036c9de32a86b5e99e1d244fe1200aa3e0dabdf6d610d91c34aa6621bf8a194b9a769a8a9bae254432d2e2bfae67b137a1209c4214bb2c2ce774aa9db9bea907
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppProfiler
4
+ module Backend
5
+ class BaseBackend
6
+ def self.name
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def run(params = {}, &block)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def start(params = {})
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def stop
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def results
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def running?
27
+ raise NotImplementedError
28
+ end
29
+
30
+ class << self
31
+ def run_lock
32
+ @run_lock ||= Mutex.new
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def acquire_run_lock
39
+ self.class.run_lock.try_lock
40
+ end
41
+
42
+ def release_run_lock
43
+ self.class.run_lock.unlock
44
+ rescue ThreadError
45
+ AppProfiler.logger.warn("[AppProfiler] run lock not released as it was never acquired")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,13 +3,23 @@
3
3
  require "stackprof"
4
4
 
5
5
  module AppProfiler
6
- module Profiler
7
- DEFAULTS = {
8
- mode: :cpu,
9
- raw: true,
10
- }.freeze
6
+ module Backend
7
+ class StackprofBackend < BaseBackend
8
+ DEFAULTS = {
9
+ mode: :cpu,
10
+ raw: true,
11
+ }.freeze
12
+
13
+ AVAILABLE_MODES = [
14
+ :wall,
15
+ :cpu,
16
+ :object,
17
+ ].freeze
18
+
19
+ def self.name
20
+ :stackprof
21
+ end
11
22
 
12
- class << self
13
23
  def run(params = {})
14
24
  started = start(params)
15
25
 
@@ -27,6 +37,7 @@ module AppProfiler
27
37
  def start(params = {})
28
38
  # Do not start the profiler if StackProf was started somewhere else.
29
39
  return false if running?
40
+ return false unless acquire_run_lock
30
41
 
31
42
  clear
32
43
 
@@ -35,6 +46,7 @@ module AppProfiler
35
46
  AppProfiler.logger.info(
36
47
  "[Profiler] failed to start the profiler error_class=#{error.class} error_message=#{error.message}"
37
48
  )
49
+ release_run_lock
38
50
  # This is a boolean instead of nil because StackProf#start returns a
39
51
  # boolean as well.
40
52
  false
@@ -42,14 +54,16 @@ module AppProfiler
42
54
 
43
55
  def stop
44
56
  StackProf.stop
57
+ ensure
58
+ release_run_lock
45
59
  end
46
60
 
47
61
  def results
48
- stackprof_profile = stackprof_results
62
+ stackprof_profile = backend_results
49
63
 
50
64
  return unless stackprof_profile
51
65
 
52
- Profile.from_stackprof(stackprof_profile)
66
+ BaseProfile.from_stackprof(stackprof_profile)
53
67
  rescue => error
54
68
  AppProfiler.logger.info(
55
69
  "[Profiler] failed to obtain the profile error_class=#{error.class} error_message=#{error.message}"
@@ -57,9 +71,13 @@ module AppProfiler
57
71
  nil
58
72
  end
59
73
 
74
+ def running?
75
+ StackProf.running?
76
+ end
77
+
60
78
  private
61
79
 
62
- def stackprof_results
80
+ def backend_results
63
81
  StackProf.results
64
82
  end
65
83
 
@@ -73,14 +91,8 @@ module AppProfiler
73
91
  # Ref: https://github.com/tmm1/stackprof/blob/0ded6c/ext/stackprof/stackprof.c#L118-L123
74
92
  #
75
93
  def clear
76
- stackprof_results
77
- end
78
-
79
- def running?
80
- StackProf.running?
94
+ backend_results
81
95
  end
82
96
  end
83
97
  end
84
-
85
- private_constant :Profiler
86
98
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem("vernier", ">= 0.7.0")
4
+ require "vernier"
5
+
6
+ module AppProfiler
7
+ module Backend
8
+ class VernierBackend < BaseBackend
9
+ DEFAULTS = {
10
+ mode: :wall,
11
+ }.freeze
12
+
13
+ AVAILABLE_MODES = [
14
+ :wall,
15
+ :retained,
16
+ ].freeze
17
+
18
+ def self.name
19
+ :vernier
20
+ end
21
+
22
+ def run(params = {})
23
+ started = start(params)
24
+
25
+ yield
26
+
27
+ return unless started
28
+
29
+ stop
30
+ results
31
+ ensure
32
+ # Only stop the profiler if profiling was started in this context.
33
+ stop if started
34
+ end
35
+
36
+ def start(params = {})
37
+ # Do not start the profiler if we already have a collector started somewhere else.
38
+ return false if running?
39
+ return false unless acquire_run_lock
40
+
41
+ @mode = params.delete(:mode) || DEFAULTS[:mode]
42
+ raise ArgumentError unless AVAILABLE_MODES.include?(@mode)
43
+
44
+ @metadata = params.delete(:metadata)
45
+ clear
46
+
47
+ @collector ||= ::Vernier::Collector.new(@mode, **params)
48
+ @collector.start
49
+ rescue => error
50
+ AppProfiler.logger.info(
51
+ "[Profiler] failed to start the profiler error_class=#{error.class} error_message=#{error.message}"
52
+ )
53
+ release_run_lock
54
+ # This is a boolean instead of nil to be consistent with the stackprof backend behaviour
55
+ # boolean as well.
56
+ false
57
+ end
58
+
59
+ def stop
60
+ return false unless running?
61
+
62
+ @results = @collector&.stop
63
+ @collector = nil
64
+ !@results.nil?
65
+ ensure
66
+ release_run_lock
67
+ end
68
+
69
+ def results
70
+ vernier_profile = backend_results
71
+ clear
72
+
73
+ return unless vernier_profile
74
+
75
+ # HACK: - "data" is private, but we want to avoid serializing to JSON then
76
+ # parsing back from JSON by just directly getting the hash
77
+ data = ::Vernier::Output::Firefox.new(vernier_profile).send(:data)
78
+ data[:meta][:mode] = @mode # TODO: https://github.com/jhawthorn/vernier/issues/30
79
+ data[:meta].merge!(@metadata) if @metadata
80
+ @mode = nil
81
+ @metadata = nil
82
+
83
+ BaseProfile.from_vernier(data)
84
+ rescue => error
85
+ AppProfiler.logger.info(
86
+ "[Profiler] failed to obtain the profile error_class=#{error.class} error_message=#{error.message}"
87
+ )
88
+ nil
89
+ end
90
+
91
+ def running?
92
+ @collector != nil
93
+ end
94
+
95
+ private
96
+
97
+ def backend_results
98
+ @results
99
+ end
100
+
101
+ def clear
102
+ @results = nil
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppProfiler
4
+ module Backend
5
+ autoload :BaseBackend, "app_profiler/backend/base_backend"
6
+ autoload :StackprofBackend, "app_profiler/backend/stackprof_backend"
7
+ autoload :VernierBackend, "app_profiler/backend/vernier_backend"
8
+ end
9
+ end
@@ -9,7 +9,7 @@ module AppProfiler
9
9
  end
10
10
 
11
11
  def cleanup
12
- profile = Profiler.results
12
+ profile = AppProfiler.profiler.results
13
13
  call(profile) if profile
14
14
  end
15
15
  end
@@ -4,8 +4,8 @@ module AppProfiler
4
4
  class Middleware
5
5
  class ViewAction < BaseAction
6
6
  class << self
7
- def call(profile, _params = {})
8
- profile.view
7
+ def call(profile, params = {})
8
+ profile.view(**params)
9
9
  end
10
10
  end
11
11
  end
@@ -31,7 +31,7 @@ module AppProfiler
31
31
 
32
32
  return yield unless before_profile(env, params_hash)
33
33
 
34
- profile = AppProfiler.run(params_hash) do
34
+ profile = AppProfiler.run(**params_hash) do
35
35
  response = yield
36
36
  end
37
37
 
@@ -4,17 +4,18 @@ require "rack"
4
4
 
5
5
  module AppProfiler
6
6
  class Parameters
7
- DEFAULT_INTERVALS = { "cpu" => 1000, "wall" => 1000, "object" => 2000 }.freeze
8
- MIN_INTERVALS = { "cpu" => 200, "wall" => 200, "object" => 400 }.freeze
9
- MODES = DEFAULT_INTERVALS.keys.freeze
7
+ DEFAULT_INTERVALS = { "cpu" => 1000, "wall" => 1000, "object" => 2000, "retained" => 0 }.freeze
8
+ MIN_INTERVALS = { "cpu" => 200, "wall" => 200, "object" => 400, "retained" => 0 }.freeze
10
9
 
11
- attr_reader :autoredirect, :async
10
+ attr_reader :autoredirect, :async, :backend
12
11
 
13
- def initialize(mode: :wall, interval: nil, ignore_gc: false, autoredirect: false, async: false, metadata: {})
12
+ def initialize(mode: :wall, interval: nil, ignore_gc: false, autoredirect: false,
13
+ async: false, backend: nil, metadata: {})
14
14
  @mode = mode.to_sym
15
15
  @interval = [interval&.to_i || DEFAULT_INTERVALS.fetch(@mode.to_s), MIN_INTERVALS.fetch(@mode.to_s)].max
16
16
  @ignore_gc = !!ignore_gc
17
17
  @autoredirect = autoredirect
18
+ @backend = backend || AppProfiler::Backend::StackprofBackend.name
18
19
  @metadata = { context: AppProfiler.context }.merge(metadata)
19
20
  @async = async
20
21
  end
@@ -29,6 +30,7 @@ module AppProfiler
29
30
  interval: @interval,
30
31
  ignore_gc: @ignore_gc,
31
32
  metadata: @metadata,
33
+ backend: @backend,
32
34
  }
33
35
  end
34
36
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppProfiler
4
+ class StackprofProfile < BaseProfile
5
+ FILE_EXTENSION = ".json"
6
+
7
+ def mode
8
+ @data[:mode]
9
+ end
10
+
11
+ def format
12
+ FILE_EXTENSION
13
+ end
14
+
15
+ def view(params = {})
16
+ AppProfiler.viewer.view(self, **params)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppProfiler
4
+ class VernierProfile < BaseProfile
5
+ FILE_EXTENSION = ".gecko.json"
6
+
7
+ def mode
8
+ @data[:meta][:mode]
9
+ end
10
+
11
+ def format
12
+ FILE_EXTENSION
13
+ end
14
+
15
+ def view(params = {})
16
+ raise NotImplementedError
17
+ end
18
+ end
19
+ end
@@ -1,42 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation/constant_accessor"
4
+
3
5
  module AppProfiler
4
- class Profile
6
+ autoload :StackprofProfile, "app_profiler/profile/stackprof"
7
+ autoload :VernierProfile, "app_profiler/profile/vernier"
8
+
9
+ class BaseProfile
5
10
  INTERNAL_METADATA_KEYS = [:id, :context]
6
11
  private_constant :INTERNAL_METADATA_KEYS
7
12
  class UnsafeFilename < StandardError; end
8
13
 
9
- delegate :[], to: :@data
10
14
  attr_reader :id, :context
11
15
 
16
+ delegate :[], to: :@data
17
+
12
18
  # This function should not be called if `StackProf.results` returns nil.
13
19
  def self.from_stackprof(data)
14
20
  options = INTERNAL_METADATA_KEYS.map { |key| [key, data[:metadata]&.delete(key)] }.to_h
15
21
 
16
- new(data, **options).tap do |profile|
22
+ StackprofProfile.new(data, **options).tap do |profile|
23
+ raise ArgumentError, "invalid profile data" unless profile.valid?
24
+ end
25
+ end
26
+
27
+ def self.from_vernier(data)
28
+ options = INTERNAL_METADATA_KEYS.map { |key| [key, data[:meta]&.delete(key)] }.to_h
29
+
30
+ VernierProfile.new(data, **options).tap do |profile|
17
31
  raise ArgumentError, "invalid profile data" unless profile.valid?
18
32
  end
19
33
  end
20
34
 
21
- # `data` is assumed to be a Hash.
35
+ # `data` is assumed to be a Hash for Stackprof,
36
+ # a vernier "result" object for vernier
22
37
  def initialize(data, id: nil, context: nil)
23
38
  @id = id.presence || SecureRandom.hex
24
39
  @context = context
25
40
  @data = data
26
41
  end
27
42
 
28
- def valid?
29
- mode.present?
30
- end
31
-
32
- def mode
33
- @data[:mode]
34
- end
35
-
36
- def view
37
- AppProfiler.viewer.view(self)
38
- end
39
-
40
43
  def upload
41
44
  AppProfiler.storage.upload(self).tap do |upload|
42
45
  if upload && defined?(upload.url)
@@ -60,6 +63,10 @@ module AppProfiler
60
63
  AppProfiler.storage.enqueue_upload(self)
61
64
  end
62
65
 
66
+ def valid?
67
+ mode.present?
68
+ end
69
+
63
70
  def file
64
71
  @file ||= path.tap do |p|
65
72
  p.dirname.mkpath
@@ -71,6 +78,18 @@ module AppProfiler
71
78
  @data
72
79
  end
73
80
 
81
+ def mode
82
+ raise NotImplementedError
83
+ end
84
+
85
+ def format
86
+ raise NotImplementedError
87
+ end
88
+
89
+ def view(params = {})
90
+ raise NotImplementedError
91
+ end
92
+
74
93
  private
75
94
 
76
95
  def path
@@ -79,11 +98,14 @@ module AppProfiler
79
98
  mode,
80
99
  id,
81
100
  Socket.gethostname,
82
- ].compact.join("-") << ".json"
101
+ ].compact.join("-") << format
83
102
 
84
103
  raise UnsafeFilename if /[^0-9A-Za-z.\-\_]/.match?(filename)
85
104
 
86
105
  AppProfiler.profile_root.join(filename)
87
106
  end
88
107
  end
108
+
109
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
110
+ deprecate_constant "Profile", "AppProfiler::BaseProfile", deprecator: ActiveSupport::Deprecation.new
89
111
  end
@@ -40,6 +40,7 @@ module AppProfiler
40
40
  AppProfiler.profile_enqueue_success = app.config.app_profiler.profile_enqueue_success
41
41
  AppProfiler.profile_enqueue_failure = app.config.app_profiler.profile_enqueue_failure
42
42
  AppProfiler.after_process_queue = app.config.app_profiler.after_process_queue
43
+ AppProfiler.backend = app.config.app_profiler.profiler_backend || :stackprof
43
44
  end
44
45
 
45
46
  initializer "app_profiler.add_middleware" do |app|
@@ -16,17 +16,30 @@ module AppProfiler
16
16
  query_param("async")
17
17
  end
18
18
 
19
+ def backend
20
+ backend = query_param("backend") || profile_header_param("backend") ||
21
+ AppProfiler.backend
22
+ backend.to_sym
23
+ end
24
+
19
25
  def valid?
20
26
  if mode.blank?
21
27
  return false
22
28
  end
23
29
 
24
- unless Parameters::MODES.include?(mode)
25
- AppProfiler.logger.info("[Profiler] unsupported profiling mode=#{mode}")
30
+ return false if backend != AppProfiler::Backend::StackprofBackend.name && !AppProfiler.vernier_supported?
31
+
32
+ if AppProfiler.vernier_supported? && backend == AppProfiler::Backend::VernierBackend.name &&
33
+ !AppProfiler::Backend::VernierBackend::AVAILABLE_MODES.include?(mode.to_sym)
34
+ AppProfiler.logger.info("[AppProfiler] unsupported profiling mode=#{mode} for backend #{backend}")
35
+ return false
36
+ elsif backend == AppProfiler::Backend::StackprofBackend.name &&
37
+ !AppProfiler::Backend::StackprofBackend::AVAILABLE_MODES.include?(mode.to_sym)
38
+ AppProfiler.logger.info("[AppProfiler] unsupported profiling mode=#{mode} for backend #{backend}")
26
39
  return false
27
40
  end
28
41
 
29
- if interval.to_i < Parameters::MIN_INTERVALS[mode]
42
+ if interval.to_i < Parameters::MIN_INTERVALS[mode.to_s]
30
43
  return false
31
44
  end
32
45
 
@@ -38,6 +51,7 @@ module AppProfiler
38
51
  mode: mode.to_sym,
39
52
  interval: interval.to_i,
40
53
  ignore_gc: !!ignore_gc,
54
+ backend: backend,
41
55
  metadata: {
42
56
  id: request_id,
43
57
  context: context,
@@ -56,7 +70,7 @@ module AppProfiler
56
70
  end
57
71
 
58
72
  def interval
59
- query_param("interval") || profile_header_param("interval") || Parameters::DEFAULT_INTERVALS[mode]
73
+ query_param("interval") || profile_header_param("interval") || Parameters::DEFAULT_INTERVALS[mode.to_s]
60
74
  end
61
75
 
62
76
  def request_id
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppProfiler
4
- VERSION = "0.1.9"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -4,12 +4,12 @@ module AppProfiler
4
4
  module Viewer
5
5
  class BaseViewer
6
6
  class << self
7
- def view(profile)
8
- new(profile).view
7
+ def view(profile, params = {})
8
+ new(profile).view(**params)
9
9
  end
10
10
  end
11
11
 
12
- def view(_profile)
12
+ def view(_params = {})
13
13
  raise NotImplementedError
14
14
  end
15
15
  end
@@ -7,8 +7,8 @@ module AppProfiler
7
7
  module Viewer
8
8
  class SpeedscopeRemoteViewer < BaseViewer
9
9
  class << self
10
- def view(profile)
11
- new(profile).view
10
+ def view(profile, params = {})
11
+ new(profile).view(**params)
12
12
  end
13
13
  end
14
14
 
@@ -17,9 +17,15 @@ module AppProfiler
17
17
  @profile = profile
18
18
  end
19
19
 
20
- def view
20
+ def view(response: nil, autoredirect: nil, async: false)
21
21
  id = Middleware.id(@profile.file)
22
- AppProfiler.logger.info("[Profiler] Profile available at /app_profiler/#{id}\n")
22
+
23
+ if response && response[0].to_i < 500
24
+ response[1]["Location"] = "/app_profiler/#{id}"
25
+ response[0] = 303
26
+ else
27
+ AppProfiler.logger.info("[Profiler] Profile available at /app_profiler/#{id}\n")
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -9,8 +9,8 @@ module AppProfiler
9
9
  include Yarn::WithSpeedscope
10
10
 
11
11
  class << self
12
- def view(profile)
13
- new(profile).view
12
+ def view(profile, params = {})
13
+ new(profile).view(**params)
14
14
  end
15
15
  end
16
16
 
@@ -19,7 +19,7 @@ module AppProfiler
19
19
  @profile = profile
20
20
  end
21
21
 
22
- def view
22
+ def view(_params = {})
23
23
  yarn("run", "speedscope", @profile.file.to_s)
24
24
  end
25
25
  end
data/lib/app_profiler.rb CHANGED
@@ -9,6 +9,9 @@ module AppProfiler
9
9
  class ConfigurationError < StandardError
10
10
  end
11
11
 
12
+ class BackendError < StandardError
13
+ end
14
+
12
15
  DefaultProfileFormatter = proc do |upload|
13
16
  "#{AppProfiler.speedscope_host}#profileURL=#{upload.url}"
14
17
  end
@@ -32,8 +35,8 @@ module AppProfiler
32
35
  require "app_profiler/middleware"
33
36
  require "app_profiler/parameters"
34
37
  require "app_profiler/request_parameters"
35
- require "app_profiler/profiler"
36
38
  require "app_profiler/profile"
39
+ require "app_profiler/backend"
37
40
  require "app_profiler/server"
38
41
 
39
42
  mattr_accessor :logger, default: Logger.new($stdout)
@@ -60,17 +63,73 @@ module AppProfiler
60
63
  mattr_reader :after_process_queue, default: nil
61
64
 
62
65
  class << self
63
- def run(*args, &block)
64
- Profiler.run(*args, &block)
66
+ def run(*args, backend: nil, **kwargs, &block)
67
+ orig_backend = self.backend
68
+ begin
69
+ self.backend = backend if backend
70
+ profiler.run(*args, **kwargs, &block)
71
+ rescue BackendError => e
72
+ logger.error(
73
+ "[AppProfiler.run] exception #{e} configuring backend #{backend}: #{e.message}"
74
+ )
75
+ yield
76
+ end
77
+ ensure
78
+ AppProfiler.backend = orig_backend
65
79
  end
66
80
 
67
81
  def start(*args)
68
- Profiler.start(*args)
82
+ profiler.start(*args)
69
83
  end
70
84
 
71
85
  def stop
72
- Profiler.stop
73
- Profiler.results
86
+ profiler.stop
87
+ profiler.results
88
+ end
89
+
90
+ def running?
91
+ @backend&.running?
92
+ end
93
+
94
+ def profiler
95
+ backend
96
+ @backend ||= @profiler_backend.new
97
+ end
98
+
99
+ def backend=(new_backend)
100
+ return if new_backend == backend
101
+
102
+ new_profiler_backend = backend_for(new_backend)
103
+
104
+ if running?
105
+ raise BackendError,
106
+ "cannot change backend to #{new_backend} while #{backend} backend is running"
107
+ end
108
+
109
+ return if @profiler_backend == new_profiler_backend
110
+
111
+ clear
112
+ @profiler_backend = new_profiler_backend
113
+ end
114
+
115
+ def backend_for(backend_name)
116
+ if vernier_supported? &&
117
+ backend_name == AppProfiler::Backend::VernierBackend.name
118
+ AppProfiler::Backend::VernierBackend
119
+ elsif backend_name == AppProfiler::Backend::StackprofBackend.name
120
+ AppProfiler::Backend::StackprofBackend
121
+ else
122
+ raise BackendError, "unknown backend #{backend_name}"
123
+ end
124
+ end
125
+
126
+ def backend
127
+ @profiler_backend ||= Backend::StackprofBackend
128
+ @profiler_backend.name
129
+ end
130
+
131
+ def vernier_supported?
132
+ defined?(AppProfiler::Backend::VernierBackend.name)
74
133
  end
75
134
 
76
135
  def profile_header=(profile_header)
@@ -120,6 +179,13 @@ module AppProfiler
120
179
 
121
180
  AppProfiler.profile_url_formatter.call(upload)
122
181
  end
182
+
183
+ private
184
+
185
+ def clear
186
+ @backend.stop if @backend&.running?
187
+ @backend = nil
188
+ end
123
189
  end
124
190
 
125
191
  require "app_profiler/railtie" if defined?(Rails::Railtie)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gannon McGibbon
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2023-12-08 00:00:00.000000000 Z
16
+ date: 2024-06-18 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: activesupport
@@ -135,13 +135,18 @@ extensions: []
135
135
  extra_rdoc_files: []
136
136
  files:
137
137
  - lib/app_profiler.rb
138
+ - lib/app_profiler/backend.rb
139
+ - lib/app_profiler/backend/base_backend.rb
140
+ - lib/app_profiler/backend/stackprof_backend.rb
141
+ - lib/app_profiler/backend/vernier_backend.rb
138
142
  - lib/app_profiler/middleware.rb
139
143
  - lib/app_profiler/middleware/base_action.rb
140
144
  - lib/app_profiler/middleware/upload_action.rb
141
145
  - lib/app_profiler/middleware/view_action.rb
142
146
  - lib/app_profiler/parameters.rb
143
147
  - lib/app_profiler/profile.rb
144
- - lib/app_profiler/profiler.rb
148
+ - lib/app_profiler/profile/stackprof.rb
149
+ - lib/app_profiler/profile/vernier.rb
145
150
  - lib/app_profiler/railtie.rb
146
151
  - lib/app_profiler/request_parameters.rb
147
152
  - lib/app_profiler/server.rb
@@ -175,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
180
  - !ruby/object:Gem::Version
176
181
  version: '0'
177
182
  requirements: []
178
- rubygems_version: 3.4.22
183
+ rubygems_version: 3.5.11
179
184
  signing_key:
180
185
  specification_version: 4
181
186
  summary: Collect performance profiles for your Rails application.