app_profiler 0.1.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 340bc83668cbe8abd27780ef23456a6d98f1f8c46059f2b447a25f1e6784483e
4
- data.tar.gz: 06fcbd4abe95e53f8e37595df7ca445bcd59f61cd9ba24af8c82578cdb6bb9c4
3
+ metadata.gz: e5e9b631f4b4b68fb8a2f6f2882ee0e4259cf6151d37388b299bd55c5a0ac6c3
4
+ data.tar.gz: a31ef0b50de69b81c84d5d8ec411e82db94a9aa2690fe633239e6c01f2bd2165
5
5
  SHA512:
6
- metadata.gz: 04cf486ef95efd05af19fd241f05b45c0a942c1e701295181fdef898f59cb24ace85c86e047ea63359b5c5ddfb2f8a233afbc3958004a823e0ffcf0daf692830
7
- data.tar.gz: bab5ddd1f1dcfb4d06e23b575341a7d7a783e04191b62a74dd553cf78d5726187500931e34c49ebe5e019d60602ceec074d4071d024008c05c8bfe5e5ddb7d52
6
+ metadata.gz: 4829d7292b8cc680c6936763b8fed26c5ab8f8661ada68f6c3b6a1890cee01d3ad7fb01bf377a3ce4cd3083f8b3b554d5b048c74ac594c540096724083adfb37
7
+ data.tar.gz: 20798219dc410c7b2ef0096e437ba8e7d8793a3dabac2a66246a765c8db06fca195c5d05981cff4adb28c6df40456876174e5f1d2b0c292fdff4f9922a1c059a
@@ -27,8 +27,9 @@ module AppProfiler
27
27
  return unless autoredirect
28
28
 
29
29
  # Automatically redirect to profile if autoredirect is true.
30
- if response[0].to_i < 500
31
- response[1]["Location"] = AppProfiler.profile_url(upload)
30
+ location = AppProfiler.profile_url(upload)
31
+ if response[0].to_i < 500 && location
32
+ response[1]["Location"] = location
32
33
  response[0] = 303
33
34
  end
34
35
  end
@@ -5,6 +5,7 @@ require "rails"
5
5
  module AppProfiler
6
6
  class Railtie < Rails::Railtie
7
7
  config.app_profiler = ActiveSupport::OrderedOptions.new
8
+ config.app_profiler.profile_url_formatter = DefaultProfileFormatter
8
9
 
9
10
  initializer "app_profiler.configs" do |app|
10
11
  AppProfiler.logger = app.config.app_profiler.logger || Rails.logger
@@ -3,6 +3,9 @@
3
3
  require "socket"
4
4
  require "rack"
5
5
  require "tempfile"
6
+ require "json"
7
+ require "active_support/core_ext/hash"
8
+ require "active_support/core_ext/module"
6
9
 
7
10
  # This module provides a means to start a golang-inspired profile server
8
11
  # it is implemented using stdlib and Rack to avoid additional dependencies
@@ -49,80 +52,122 @@ module AppProfiler
49
52
  private
50
53
 
51
54
  def handle(request)
52
- response = Rack::Response.new
53
- if request.request_method != "GET"
54
- response.status = HTTP_NOT_ALLOWED
55
- response.write("Only GET requests are supported")
56
- return response
57
- end
55
+ return handle_not_allowed(request) if request.request_method != "GET"
56
+
58
57
  case request.path
59
58
  when "/profile"
60
- begin
61
- stackprof_args, duration = validate_profile_params(request.params)
62
- rescue InvalidProfileArgsError => e
63
- response.status = HTTP_BAD_REQUEST
64
- response.write("Invalid argument #{e.message}")
65
- return response
66
- end
59
+ handle_profile(request)
60
+ else
61
+ handle_not_found(request)
62
+ end
63
+ end
67
64
 
68
- if start_running
69
- start_time = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
70
- AppProfiler.start(**stackprof_args)
71
- sleep(duration)
72
- profile = AppProfiler.stop
73
- stop_running
74
- response.status = HTTP_OK
75
- response.set_header("Content-Type", "application/json")
76
- if AppProfiler::Server.cors
77
- response.set_header("Access-Control-Allow-Origin", AppProfiler::Server.cors_host)
78
- end
79
- profile_hash = profile.to_h
80
- profile_hash["start_time_nsecs"] = start_time # NOTE: this is not part of the stackprof profile spec
81
- response.write(JSON.dump(profile_hash))
82
- else
83
- response.status = HTTP_CONFLICT
84
- response.write("A profile is already running")
65
+ def handle_not_allowed(request)
66
+ response = Rack::Response.new
67
+
68
+ response.status = HTTP_NOT_ALLOWED
69
+ response.write("Only GET requests are supported")
70
+
71
+ response
72
+ end
73
+
74
+ def handle_profile(request)
75
+ begin
76
+ stackprof_args, duration = validate_profile_params(request.params)
77
+ rescue InvalidProfileArgsError => e
78
+ return handle_bad_request(request, e.message)
79
+ end
80
+
81
+ response = Rack::Response.new
82
+
83
+ if start_running(stackprof_args)
84
+ start_time = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
85
+
86
+ sleep(duration)
87
+
88
+ profile = stop_running
89
+
90
+ response.status = HTTP_OK
91
+ response.set_header("Content-Type", "application/json")
92
+
93
+ profile_hash = profile.to_h
94
+ profile_hash["start_time_nsecs"] = start_time # NOTE: this is not part of the stackprof profile spec
95
+
96
+ response.write(JSON.dump(profile_hash))
97
+
98
+ if AppProfiler::Server.cors
99
+ response.set_header("Access-Control-Allow-Origin", AppProfiler::Server.cors_host)
85
100
  end
86
101
  else
87
- response.status = HTTP_NOT_FOUND
88
- response.write("Unsupported endpoint #{request.path}")
102
+ response.status = HTTP_CONFLICT
103
+ response.write("A profile is already running")
89
104
  end
105
+
106
+ response
107
+ end
108
+
109
+ def handle_not_found(request)
110
+ response = Rack::Response.new
111
+
112
+ response.status = HTTP_NOT_FOUND
113
+ response.write("Unsupported endpoint #{request.path}")
114
+
115
+ response
116
+ end
117
+
118
+ def handle_bad_request(request, message)
119
+ response = Rack::Response.new
120
+
121
+ response.status = HTTP_BAD_REQUEST
122
+ response.write("Invalid argument #{message}")
123
+
90
124
  response
91
125
  end
92
126
 
93
127
  def validate_profile_params(params)
94
128
  params = params.symbolize_keys
95
129
  stackprof_args = {}
130
+
96
131
  begin
97
132
  duration = Float(params.key?(:duration) ? params[:duration] : AppProfiler::Server.duration)
98
133
  rescue ArgumentError
99
- raise InvalidProfileArgsError, "invalid duration #{params[:duration]}"
134
+ raise InvalidProfileArgsError, "duration: #{params[:duration]}"
100
135
  end
136
+
101
137
  if params.key?(:mode)
102
138
  if ["cpu", "wall", "object"].include?(params[:mode])
103
139
  stackprof_args[:mode] = params[:mode].to_sym
104
140
  else
105
- raise InvalidProfileArgsError, "invalid mode #{params[:mode]}"
141
+ raise InvalidProfileArgsError, "mode: #{params[:mode]}"
106
142
  end
107
143
  end
144
+
108
145
  if params.key?(:interval)
109
146
  stackprof_args[:interval] = params[:interval].to_i
110
- raise InvalidProfileArgsError, "invalid interval #{params[:interval]}" if stackprof_args[:interval] <= 0
147
+
148
+ raise InvalidProfileArgsError, "interval: #{params[:interval]}" if stackprof_args[:interval] <= 0
111
149
  end
150
+
112
151
  [stackprof_args, duration]
113
152
  end
114
153
 
115
154
  # Prevent multiple concurrent profiles by synchronizing between threads
116
- def start_running
155
+ def start_running(stackprof_args)
117
156
  @semaphore.synchronize do
118
157
  return false if @profile_running
119
158
 
120
159
  @profile_running = true
160
+
161
+ AppProfiler.start(**stackprof_args)
121
162
  end
122
163
  end
123
164
 
124
165
  def stop_running
125
- @semaphore.synchronize { @profile_running = false }
166
+ @semaphore.synchronize do
167
+ AppProfiler.stop.tap do
168
+ @profile_running = false
169
+ end
170
+ end
126
171
  end
127
172
  end
128
173
 
@@ -147,6 +192,7 @@ module AppProfiler
147
192
  class UNIX < Transport
148
193
  def initialize
149
194
  super
195
+
150
196
  FileUtils.mkdir_p(PROFILER_TEMPFILE_PATH)
151
197
  @socket_file = File.join(PROFILER_TEMPFILE_PATH, "app-profiler-#{Process.pid}.sock")
152
198
  File.unlink(@socket_file) if File.exist?(@socket_file) && File.socket?(@socket_file)
@@ -216,10 +262,13 @@ module AppProfiler
216
262
  loop do
217
263
  Thread.new(@transport.socket.accept) do |session|
218
264
  request = session.gets
265
+
219
266
  if request.nil?
220
267
  session.close
268
+
221
269
  next
222
270
  end
271
+
223
272
  method, path, http_version = request.split(" ")
224
273
  path_info, query_string = path.split("?")
225
274
  env = { # an extremely minimal rack env hash, just enough to get the job done
@@ -233,10 +282,13 @@ module AppProfiler
233
282
 
234
283
  begin
235
284
  session.print("#{http_version} #{status}\r\n")
285
+
236
286
  headers.each do |header, value|
237
287
  session.print("#{header}: #{value}\r\n")
238
288
  end
289
+
239
290
  session.print("\r\n")
291
+
240
292
  body.each do |part|
241
293
  session.print(part)
242
294
  end
@@ -265,6 +317,7 @@ module AppProfiler
265
317
  class << self
266
318
  def reset
267
319
  profile_servers.clear
320
+
268
321
  DEFAULTS.each do |config, value|
269
322
  class_variable_set(:"@@#{config}", value) # rubocop:disable Style/ClassVars
270
323
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppProfiler
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/app_profiler.rb CHANGED
@@ -4,7 +4,6 @@ require "active_support/core_ext/class"
4
4
  require "active_support/core_ext/module"
5
5
  require "logger"
6
6
  require "app_profiler/version"
7
- require "app_profiler/railtie" if defined?(Rails::Railtie)
8
7
 
9
8
  module AppProfiler
10
9
  class ConfigurationError < StandardError
@@ -81,7 +80,11 @@ module AppProfiler
81
80
  end
82
81
 
83
82
  def profile_url(upload)
83
+ return unless AppProfiler.profile_url_formatter
84
+
84
85
  AppProfiler.profile_url_formatter.call(upload)
85
86
  end
86
87
  end
88
+
89
+ require "app_profiler/railtie" if defined?(Rails::Railtie)
87
90
  end
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.1
4
+ version: 0.1.2
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: 2022-06-16 00:00:00.000000000 Z
16
+ date: 2022-09-23 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: activesupport