rack_jruby_profiling 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jason Rogers
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = rack_jruby_profiling
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Jason Rogers. See LICENSE for details.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,106 @@
1
+ require 'jruby-prof'
2
+
3
+ module Rack
4
+ # Based on rack/contrib/profiling
5
+ #
6
+ # Set the profile=call_tree query parameter to view a calltree profile of the request.
7
+ #
8
+ # Set the download query parameter to download the result locally
9
+ #
10
+ class JRubyProfiler
11
+ DEFAULT_CONTENT_TYPE = 'text/html'
12
+
13
+ PRINTER_CONTENT_TYPE = {
14
+ :print_flat_text => 'text/plain',
15
+ :print_graph_text => 'text/plain',
16
+ :print_call_tree => 'text/plain',
17
+ :print_graph_html => 'text/html',
18
+ :print_tree_html => 'text/html'
19
+ }
20
+
21
+ DEFAULT_PRINTER = :print_tree_html
22
+
23
+ PRINTER_METHODS = {
24
+ :flat => :print_flat_text,
25
+ :graph => :print_graph_text,
26
+ :call_tree => :print_call_tree,
27
+ :graph_html => :print_graph_html,
28
+ :tree_html => :print_tree_html
29
+ }
30
+
31
+ FILE_NAMING = {
32
+ :print_flat_text => 'flat',
33
+ :print_graph_text => 'graph',
34
+ :print_call_tree => 'call_tree',
35
+ :print_graph_html => 'graph',
36
+ :print_tree_html => 'call_tree'
37
+ }
38
+
39
+ # Accepts a :times => [Fixnum] option defaulting to 1.
40
+ def initialize(app, options = {})
41
+ @app = app
42
+ @times = (options[:times] || 1).to_i
43
+ end
44
+
45
+ def call(env)
46
+ profile(env)
47
+ end
48
+
49
+ def profile_file
50
+ @profile_file
51
+ end
52
+
53
+ private
54
+ def profile(env)
55
+ request = Rack::Request.new(env)
56
+ @printer = parse_printer(request.params.delete('profile'))
57
+ if JRubyProf.running?
58
+ @app.call(env)
59
+ else
60
+ count = (request.params.delete('times') || @times).to_i
61
+ result = JRubyProf.profile do
62
+ count.times { @app.call(env) }
63
+ end
64
+ @uniq_id = Java::java.lang.System.nano_time
65
+ @profile_file = ::File.expand_path( filename(@printer, env) )
66
+ [200, headers(@printer, request, env), print(@printer, request, env, result)]
67
+ end
68
+ end
69
+
70
+ def filename(printer, env)
71
+ extension = printer.to_s.include?("html") ? "html" : "txt"
72
+ "#{::File.basename(env['PATH_INFO'])}_#{FILE_NAMING[printer]}_#{@uniq_id}.#{extension}"
73
+ end
74
+
75
+ def print(printer, request, env, result)
76
+ return result if printer.nil?
77
+ filename = filename(printer, env)
78
+ JRubyProf.send(printer, result, filename)
79
+ ::File.read filename
80
+ end
81
+
82
+ def headers(printer, request, env)
83
+ headers = { 'Content-Type' => PRINTER_CONTENT_TYPE[printer] || DEFAULT_CONTENT_TYPE }
84
+ if boolean(request.params['download'])
85
+ filename = filename(printer, env)
86
+ headers['Content-Disposition'] = %(attachment; filename="#{filename}")
87
+ end
88
+ headers
89
+ end
90
+
91
+ def boolean(parameter)
92
+ return false if parameter.nil?
93
+ return true if %w{t true y yes}.include?(parameter.downcase)
94
+ false
95
+ end
96
+
97
+ def parse_printer(printer)
98
+ printer = printer.to_sym rescue nil
99
+ if printer.nil?
100
+ DEFAULT_PRINTER
101
+ else
102
+ PRINTER_METHODS[printer] || DEFAULT_PRINTER
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module TestHelpers
4
+ def basic_application(code)
5
+ Proc.new { |env|
6
+ 500.times {|t| "construct a string for #{t} just so that we have some work to do in this application"}
7
+ 500.times {|t| [t, t/4.0].max }
8
+ [@code, {"Content-Type" => "text/plain", "Content-Length" => "3"}, [@code.to_s]]
9
+ }
10
+ end
11
+
12
+ def error_application(error)
13
+ Proc.new { |env| raise error }
14
+ end
15
+
16
+ def response_for(app, request)
17
+ @profiler = Rack::JRubyProfiler.new(app)
18
+ @profiler.call(request)
19
+ end
20
+
21
+ def all_profiles
22
+ Dir["./#{@path}*"]
23
+ end
24
+
25
+ def first_profile
26
+ all_profiles.first
27
+ end
28
+ end
29
+
30
+ describe Rack::JRubyProfiler do
31
+ include Rack::Test::Methods
32
+ include TestHelpers
33
+
34
+ before :each do
35
+ @path = 'profile_test'
36
+ @request = Rack::MockRequest.env_for("/#{@path}")
37
+ end
38
+
39
+ after :each do
40
+ if @profiler
41
+ File.delete @profiler.profile_file
42
+ end
43
+ end
44
+
45
+ [200, 301, 404, 500].each do | code |
46
+ it "should successfully respond with 200 if down-stream app returns #{code}" do
47
+ response = response_for( basic_application(code), @request )
48
+ response[0].should == 200
49
+ response[1].should include("Content-Type")
50
+ response[1]["Content-Type"].should == 'text/html'
51
+ @profiler.profile_file.should_not be_nil
52
+ response[2].class.should == String
53
+ response[2].length.should > 0
54
+ response[2].should == File.read(@profiler.profile_file)
55
+ end
56
+ end
57
+
58
+ it "should NOT mask when a down-stream app throws an error" do
59
+ @request = Rack::MockRequest.env_for("/profile_test")
60
+ lambda{ response_for( error_application('ka-boom!'), request ) }.should raise_error
61
+ end
62
+
63
+ class Tuple
64
+ TRUISH = %w{true t yes y}
65
+ attr_reader :profile_type, :content_type, :partial_filename, :extension
66
+
67
+ def initialize(profile_type, content_type, partial_filename=nil)
68
+ @profile_type = profile_type
69
+ @content_type = content_type
70
+ @partial_filename = (partial_filename || profile_type)
71
+ @extension = content_type == :plain ? 'txt' : 'html'
72
+ end
73
+
74
+ def download
75
+ TRUISH[rand(TRUISH.length)]
76
+ end
77
+ end
78
+
79
+ [
80
+ Tuple.new(:flat, :plain),
81
+ Tuple.new(:graph, :plain),
82
+ Tuple.new(:call_tree, :plain),
83
+ Tuple.new(:graph_html, :html, :graph),
84
+ Tuple.new(:tree_html, :html, :call_tree)
85
+ ].each do |tuple|
86
+ it "should construct a #{tuple.profile_type} profile with content type of #{tuple.content_type} and download it" do
87
+ @request = Rack::MockRequest.env_for("/profile_test?profile=#{tuple.profile_type}&download=#{tuple.download}")
88
+ response = response_for( basic_application(200), @request )
89
+ response[0].should == 200
90
+ response[1].should include("Content-Type")
91
+ response[1]["Content-Type"].should == "text/#{tuple.content_type}"
92
+
93
+ @profiler.profile_file.should_not be_nil
94
+ @profiler.profile_file.should =~ /profile_test_#{tuple.partial_filename}_\d+\.#{tuple.extension}$/
95
+ response[1].should include("Content-Disposition")
96
+ response[1]["Content-Disposition"].should =~ Regexp.compile(%(attachment; filename="#{File.basename(@profiler.profile_file)}"))
97
+ response[2].class.should == String
98
+ response[2].should == File.read(@profiler.profile_file)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,13 @@
1
+ raise 'specs can only be run with JRuby' unless Object.const_defined?('Java')
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'rubygems'
6
+ require 'rack_jruby_profiling'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'rack/test'
10
+
11
+ Spec::Runner.configure do |config|
12
+
13
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack_jruby_profiling
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Jason Rogers
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-13 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
31
+ version: 1.2.9
32
+ type: :development
33
+ version_requirements: *id001
34
+ description: Rack middleware for running profiling JRuby applications
35
+ email: jacaetevha@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - LICENSE
42
+ - README.rdoc
43
+ files:
44
+ - LICENSE
45
+ - README.rdoc
46
+ - VERSION
47
+ - lib/rack_jruby_profiling.rb
48
+ - spec/rack_jruby_profiling_spec.rb
49
+ - spec/spec.opts
50
+ - spec/spec_helper.rb
51
+ has_rdoc: true
52
+ homepage: http://github.com/jacaetevha/rack_jruby_profiling
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.6
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Rack middleware for running profiling JRuby applications
81
+ test_files:
82
+ - spec/rack_jruby_profiling_spec.rb
83
+ - spec/spec_helper.rb