Module: AppProfiler

Defined in:
lib/app_profiler/server.rb,
lib/app_profiler.rb,
lib/app_profiler/exec.rb,
lib/app_profiler/backend.rb,
lib/app_profiler/railtie.rb,
lib/app_profiler/sampler.rb,
lib/app_profiler/version.rb,
lib/app_profiler/middleware.rb,
lib/app_profiler/parameters.rb,
lib/app_profiler/profile_id.rb,
lib/app_profiler/base_profile.rb,
lib/app_profiler/yarn/command.rb,
lib/app_profiler/sampler/config.rb,
lib/app_profiler/vernier_profile.rb,
lib/app_profiler/stackprof_profile.rb,
lib/app_profiler/request_parameters.rb,
lib/app_profiler/viewer/base_viewer.rb,
lib/app_profiler/backend/base_backend.rb,
lib/app_profiler/storage/base_storage.rb,
lib/app_profiler/storage/file_storage.rb,
lib/app_profiler/yarn/with_speedscope.rb,
lib/app_profiler/viewer/firefox_viewer.rb,
lib/app_profiler/middleware/base_action.rb,
lib/app_profiler/middleware/view_action.rb,
lib/app_profiler/sampler/vernier_config.rb,
lib/app_profiler/viewer/base_middleware.rb,
lib/app_profiler/backend/vernier_backend.rb,
lib/app_profiler/middleware/upload_action.rb,
lib/app_profiler/sampler/stackprof_config.rb,
lib/app_profiler/viewer/speedscope_viewer.rb,
lib/app_profiler/backend/stackprof_backend.rb,
lib/app_profiler/yarn/with_firefox_profiler.rb,
lib/app_profiler/storage/google_cloud_storage.rb,
lib/app_profiler/viewer/firefox_remote_viewer.rb,
lib/app_profiler/viewer/speedscope_remote_viewer.rb,
lib/app_profiler/viewer/firefox_remote_viewer/middleware.rb,
lib/app_profiler/viewer/speedscope_remote_viewer/middleware.rb

Overview

This module provides a means to start a golang-inspired profile server it is implemented using stdlib and Rack to avoid additional dependencies

Defined Under Namespace

Modules: Backend, Exec, Sampler, Server, Storage, Viewer, Yarn Classes: BackendError, BaseProfile, ConfigurationError, Middleware, Parameters, ProfileId, Railtie, RequestParameters, StackprofProfile, VernierProfile

Constant Summary collapse

PROFILE_ID_METADATA_KEY =
:profile_id
PROFILE_BACKEND_METADATA_KEY =
:profiler
DefaultProfileFormatter =
proc do |upload|
  "#{AppProfiler.speedscope_host}#profileURL=#{upload.url}"
end
DefaultProfilePrefix =
proc do
  Time.zone.now.strftime("%Y%m%d-%H%M%S")
end
VERSION =
"0.3.0"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.otel_instrumentation_enabledObject

Returns the value of attribute otel_instrumentation_enabled.



79
80
81
# File 'lib/app_profiler.rb', line 79

def otel_instrumentation_enabled
  @otel_instrumentation_enabled
end

Class Method Details

.after_process_queue=(handler) ⇒ Object



229
230
231
232
233
234
235
# File 'lib/app_profiler.rb', line 229

def after_process_queue=(handler)
  if handler && (!handler.is_a?(Proc) || (handler.lambda? && handler.arity != 2))
    raise ArgumentError, "after_process_queue must be a proc or a lambda that accepts two arguments"
  end

  @@after_process_queue = handler # rubocop:disable Style/ClassVars
end

.backendObject



187
188
189
# File 'lib/app_profiler.rb', line 187

def backend
  profiler_backend.name
end

.backend=(new_backend) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/app_profiler.rb', line 119

def backend=(new_backend)
  return if (new_profiler_backend = backend_for(new_backend)) == profiler_backend

  if running?
    raise BackendError,
      "cannot change backend to #{new_backend} while #{backend} backend is running"
  end

  clear
  @profiler_backend = new_profiler_backend
end

.backend_for(backend_name) ⇒ Object



176
177
178
179
180
181
182
183
184
185
# File 'lib/app_profiler.rb', line 176

def backend_for(backend_name)
  if vernier_supported? &&
      backend_name&.to_sym == AppProfiler::VernierProfile::BACKEND_NAME
    AppProfiler::Backend::VernierBackend
  elsif backend_name&.to_sym == AppProfiler::Backend::StackprofBackend.name
    AppProfiler::Backend::StackprofBackend
  else
    raise BackendError, "unknown backend #{backend_name.inspect}"
  end
end

.deprecatorObject

:nodoc:



81
82
83
# File 'lib/app_profiler.rb', line 81

def deprecator # :nodoc:
  @deprecator ||= ActiveSupport::Deprecation.new("in future releases", "app_profiler")
end

.profile_data_headerObject



205
206
207
# File 'lib/app_profiler.rb', line 205

def profile_data_header
  @@profile_data_header ||= profile_header.dup << "-Data" # rubocop:disable Style/ClassVars
end

.profile_enqueue_failure=(handler) ⇒ Object



221
222
223
224
225
226
227
# File 'lib/app_profiler.rb', line 221

def profile_enqueue_failure=(handler)
  if handler && (!handler.is_a?(Proc) || (handler.lambda? && handler.arity != 1))
    raise ArgumentError, "profile_enqueue_failure must be a proc or a lambda that accepts one argument"
  end

  @@profile_enqueue_failure = handler # rubocop:disable Style/ClassVars
end

.profile_enqueue_success=(handler) ⇒ Object



213
214
215
216
217
218
219
# File 'lib/app_profiler.rb', line 213

def profile_enqueue_success=(handler)
  if handler && (!handler.is_a?(Proc) || (handler.lambda? && handler.arity != 0))
    raise ArgumentError, "profile_enqueue_success must be proc or a lambda that accepts no argument"
  end

  @@profile_enqueue_success = handler # rubocop:disable Style/ClassVars
end

.profile_file_name=(value) ⇒ Object

Raises:

  • (ArgumentError)


139
140
141
142
143
# File 'lib/app_profiler.rb', line 139

def profile_file_name=(value)
  raise ArgumentError, "profile_file_name must be a proc" if value && !value.is_a?(Proc)

  @@profile_file_name = value # rubocop:disable Style/ClassVars
end

.profile_header=(profile_header) ⇒ Object



195
196
197
198
199
# File 'lib/app_profiler.rb', line 195

def profile_header=(profile_header)
  @@profile_header = profile_header # rubocop:disable Style/ClassVars
  @@request_profile_header = nil    # rubocop:disable Style/ClassVars
  @@profile_data_header = nil       # rubocop:disable Style/ClassVars
end

.profile_sampler_enabledObject



156
157
158
159
160
161
162
163
164
165
# File 'lib/app_profiler.rb', line 156

def profile_sampler_enabled
  return false unless defined?(@profile_sampler_enabled)

  @profile_sampler_enabled.is_a?(Proc) ? @profile_sampler_enabled.call : @profile_sampler_enabled
rescue => e
  logger.error(
    "[AppProfiler.profile_sampler_enabled] exception: #{e}, message: #{e.message}",
  )
  false
end

.profile_sampler_enabled=(value) ⇒ Object



145
146
147
148
149
150
151
152
153
154
# File 'lib/app_profiler.rb', line 145

def profile_sampler_enabled=(value)
  if value.is_a?(Proc)
    raise ArgumentError,
      "profile_sampler_enabled must be a proc or a lambda that accepts no argument" if value.arity != 0
  else
    raise ArgumentError, "Must be TrueClass or FalseClass" unless [TrueClass, FalseClass].include?(value.class)
  end

  @profile_sampler_enabled = value
end

.profile_url(upload) ⇒ Object



237
238
239
240
241
# File 'lib/app_profiler.rb', line 237

def profile_url(upload)
  return unless AppProfiler.profile_url_formatter

  AppProfiler.profile_url_formatter.call(upload)
end

.profile_url_formatter=(block) ⇒ Object



209
210
211
# File 'lib/app_profiler.rb', line 209

def profile_url_formatter=(block)
  @@profile_url_formatter = block # rubocop:disable Style/ClassVars
end

.profilerObject



115
116
117
# File 'lib/app_profiler.rb', line 115

def profiler
  @profiler ||= profiler_backend.new
end

.request_profile_headerObject



201
202
203
# File 'lib/app_profiler.rb', line 201

def request_profile_header
  @@request_profile_header ||= profile_header.upcase.tr("-", "_").prepend("HTTP_") # rubocop:disable Style/ClassVars
end

.run(*args, backend: nil, **kwargs, &block) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/app_profiler.rb', line 85

def run(*args, backend: nil, **kwargs, &block)
  if backend
    original_backend = self.backend
    self.backend = backend
  end
  profiler.run(*args, **kwargs, &block)
rescue BackendError => e
  logger.error(
    "[AppProfiler.run] exception #{e} configuring backend #{backend}: #{e.message}",
  )
  yield
ensure
  self.backend = original_backend if backend
  ProfileId::Current.reset
end

.running?Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/app_profiler.rb', line 111

def running?
  @profiler&.running?
end

.stackprof_viewerObject



131
132
133
# File 'lib/app_profiler.rb', line 131

def stackprof_viewer
  @@stackprof_viewer ||= Viewer::SpeedscopeViewer # rubocop:disable Style/ClassVars
end

.start(*args, backend: nil, **kwargs) ⇒ Object



101
102
103
104
# File 'lib/app_profiler.rb', line 101

def start(*args, backend: nil, **kwargs)
  self.backend = backend if backend
  profiler.start(*args, **kwargs)
end

.stopObject



106
107
108
109
# File 'lib/app_profiler.rb', line 106

def stop
  profiler.stop
  profiler.results.tap { clear }
end

.vernier_supported?Boolean

Returns:

  • (Boolean)


191
192
193
# File 'lib/app_profiler.rb', line 191

def vernier_supported?
  RUBY_VERSION >= "3.2.1" && defined?(AppProfiler::VernierProfile::BACKEND_NAME)
end

.vernier_viewerObject



135
136
137
# File 'lib/app_profiler.rb', line 135

def vernier_viewer
  @@vernier_viewer ||= Viewer::FirefoxViewer # rubocop:disable Style/ClassVars
end

.viewerObject



243
244
245
246
# File 'lib/app_profiler.rb', line 243

def viewer
  deprecator.warn("AppProfiler.viewer is deprecated, please use stackprof_viewer instead.")
  stackprof_viewer
end

.viewer=(viewer) ⇒ Object



248
249
250
251
# File 'lib/app_profiler.rb', line 248

def viewer=(viewer)
  deprecator.warn("AppProfiler.viewer= is deprecated, please use stackprof_viewer= instead.")
  self.stackprof_viewer = viewer
end