app_profiler 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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