speed_gun 0.0.4 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Rakefile +6 -1
- data/lib/speed_gun/app/views/meter.html.slim +4 -9
- data/lib/speed_gun/config.rb +33 -42
- data/lib/speed_gun/event.rb +72 -0
- data/lib/speed_gun/middleware.rb +43 -43
- data/lib/speed_gun/profile.rb +102 -0
- data/lib/speed_gun/profiler/action_controller_profiler.rb +14 -0
- data/lib/speed_gun/profiler/action_view_profiler.rb +11 -0
- data/lib/speed_gun/profiler/active_record_profiler.rb +11 -0
- data/lib/speed_gun/profiler/active_support_notifications_profiler.rb +29 -0
- data/lib/speed_gun/profiler/rack_profiler.rb +7 -0
- data/lib/speed_gun/profiler.rb +11 -118
- data/lib/speed_gun/railtie.rb +7 -16
- data/lib/speed_gun/store/elastic_search_store.rb +64 -0
- data/lib/speed_gun/store/fluent_logger_store.rb +29 -0
- data/lib/speed_gun/store/memcache_store.rb +40 -0
- data/lib/speed_gun/store/memory_store.rb +23 -0
- data/lib/speed_gun/store/multiple_store.rb +23 -0
- data/lib/speed_gun/store/redis_store.rb +41 -0
- data/lib/speed_gun/store.rb +7 -3
- data/lib/speed_gun/template.rb +1 -1
- data/lib/speed_gun/version.rb +1 -1
- data/lib/speed_gun.rb +30 -39
- data/spec/lib/speed_gun/config_spec.rb +37 -0
- data/spec/lib/speed_gun/event_spec.rb +70 -0
- data/spec/lib/speed_gun/middleware_spec.rb +65 -0
- data/spec/lib/speed_gun/profile_spec.rb +41 -0
- data/spec/lib/speed_gun_spec.rb +52 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/simplecov.rb +12 -0
- data/speed_gun.gemspec +10 -7
- metadata +102 -56
- data/app/assets/javascripts/browser.js +0 -83
- data/app/assets/javascripts/profiler.js +0 -45
- data/app/views/speed_gun/_meter.html.slim +0 -9
- data/lib/speed_gun/app/public/browser.js +0 -83
- data/lib/speed_gun/app/public/jquery-1.10.2.min.js +0 -6
- data/lib/speed_gun/app/public/profile.js +0 -5
- data/lib/speed_gun/app/public/profiler.js +0 -45
- data/lib/speed_gun/app/public/style.css +0 -170
- data/lib/speed_gun/app/views/profile.slim +0 -97
- data/lib/speed_gun/app.rb +0 -58
- data/lib/speed_gun/browser/navigation.rb +0 -23
- data/lib/speed_gun/browser/timing.rb +0 -92
- data/lib/speed_gun/browser.rb +0 -22
- data/lib/speed_gun/hook.rb +0 -25
- data/lib/speed_gun/profiler/action_controller.rb +0 -12
- data/lib/speed_gun/profiler/action_view.rb +0 -12
- data/lib/speed_gun/profiler/active_record.rb +0 -16
- data/lib/speed_gun/profiler/base.rb +0 -139
- data/lib/speed_gun/profiler/js.rb +0 -17
- data/lib/speed_gun/profiler/manual.rb +0 -14
- data/lib/speed_gun/profiler/rack.rb +0 -7
- data/lib/speed_gun/store/base.rb +0 -9
- data/lib/speed_gun/store/file.rb +0 -62
- data/lib/speed_gun/store/memcache.rb +0 -27
- data/lib/speed_gun/store/memory.rb +0 -22
- data/lib/speed_gun/store/redis.rb +0 -28
@@ -1,92 +0,0 @@
|
|
1
|
-
require 'speed_gun/browser'
|
2
|
-
|
3
|
-
class SpeedGun::Browser::Timing < Hash
|
4
|
-
# rubocop:disable SymbolName
|
5
|
-
ATTRIBUTES = [
|
6
|
-
:navigationStart,
|
7
|
-
:unloadEventStart,
|
8
|
-
:unloadEventEnd,
|
9
|
-
:redirectStart,
|
10
|
-
:redirectEnd,
|
11
|
-
:fetchStart,
|
12
|
-
:domainLookupStart,
|
13
|
-
:domainLookupEnd,
|
14
|
-
:connectStart,
|
15
|
-
:connectEnd,
|
16
|
-
:secureConnectionStart,
|
17
|
-
:requestStart,
|
18
|
-
:responseStart,
|
19
|
-
:responseEnd,
|
20
|
-
:domLoading,
|
21
|
-
:domInteractive,
|
22
|
-
:domContentLoadedEventStart,
|
23
|
-
:domContentLoadedEventEnd,
|
24
|
-
:domComplete,
|
25
|
-
:loadEventStart,
|
26
|
-
:loadEventEnd,
|
27
|
-
]
|
28
|
-
# rubocop:enable all
|
29
|
-
|
30
|
-
class Timing
|
31
|
-
def initialize(name, base, started_at, ended_at)
|
32
|
-
@name = name
|
33
|
-
@started_at = started_at - base
|
34
|
-
@elapsed_time = ended_at - started_at
|
35
|
-
end
|
36
|
-
attr_reader :name, :started_at, :elapsed_time
|
37
|
-
end
|
38
|
-
|
39
|
-
def initialize(hash)
|
40
|
-
hash.each_pair do |key, val|
|
41
|
-
self[key.to_s.to_sym] = val.to_i
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
ATTRIBUTES.each do |key|
|
46
|
-
define_method(key) { self[key] }
|
47
|
-
end
|
48
|
-
|
49
|
-
def load_time
|
50
|
-
loadEventEnd - navigationStart
|
51
|
-
end
|
52
|
-
|
53
|
-
# rubocop:disable MethodLength
|
54
|
-
def timings
|
55
|
-
@timings ||= [
|
56
|
-
(
|
57
|
-
if redirectStart > 0
|
58
|
-
Timing.new('Redirect', navigationStart, redirectStart, redirectEnd)
|
59
|
-
else
|
60
|
-
nil
|
61
|
-
end
|
62
|
-
),
|
63
|
-
Timing.new('Fetch all', navigationStart, fetchStart, responseEnd),
|
64
|
-
Timing.new(
|
65
|
-
'DNS Lookup', navigationStart, domainLookupStart, domainLookupEnd
|
66
|
-
),
|
67
|
-
Timing.new(
|
68
|
-
'TCP Connecting', navigationStart, connectStart, connectEnd
|
69
|
-
),
|
70
|
-
(
|
71
|
-
if secureConnectionStart > 0
|
72
|
-
Timing.new('SSL', navigationStart, secureConnectionStart, connectEnd)
|
73
|
-
else
|
74
|
-
nil
|
75
|
-
end
|
76
|
-
),
|
77
|
-
Timing.new('Request', navigationStart, requestStart, responseStart),
|
78
|
-
Timing.new('Response', navigationStart, responseStart, responseEnd),
|
79
|
-
Timing.new('Unload', navigationStart, unloadEventStart, unloadEventEnd),
|
80
|
-
Timing.new('DOM Load', navigationStart, domLoading, domInteractive),
|
81
|
-
Timing.new(
|
82
|
-
'DOMContentLoaded Event',
|
83
|
-
navigationStart, domContentLoadedEventStart, domContentLoadedEventEnd
|
84
|
-
),
|
85
|
-
Timing.new(
|
86
|
-
'Sub resources loading', navigationStart, domInteractive, domComplete
|
87
|
-
),
|
88
|
-
Timing.new('Load Event', navigationStart, loadEventStart, loadEventEnd),
|
89
|
-
].reject { |timing| timing.nil? }
|
90
|
-
end
|
91
|
-
# rubocop:enable
|
92
|
-
end
|
data/lib/speed_gun/browser.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'speed_gun'
|
2
|
-
require 'useragent'
|
3
|
-
|
4
|
-
class SpeedGun::Browser
|
5
|
-
def initialize(hash)
|
6
|
-
@user_agent = UserAgent.parse(hash['user_agent'] || '')
|
7
|
-
@navigation = Navigation.new(hash['navigation'] || {})
|
8
|
-
@timing = Timing.new(hash['timing'] || {})
|
9
|
-
end
|
10
|
-
attr_reader :user_agent, :navigation, :timing
|
11
|
-
|
12
|
-
def as_msgpack(*args)
|
13
|
-
{
|
14
|
-
user_agent: @user_agent.to_s,
|
15
|
-
navigation: @navigation,
|
16
|
-
timing: @timing,
|
17
|
-
}
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
require 'speed_gun/browser/navigation'
|
22
|
-
require 'speed_gun/browser/timing'
|
data/lib/speed_gun/hook.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'speed_gun'
|
2
|
-
|
3
|
-
class SpeedGun::Hook
|
4
|
-
HOOKS = []
|
5
|
-
|
6
|
-
def self.inherited(klass)
|
7
|
-
HOOKS.push(klass) unless HOOKS.include?(klass)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.invoke_all(profiler)
|
11
|
-
HOOKS.each { |hook| hook.invoke(profiler) }
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.invoke(profiler)
|
15
|
-
new(profiler).invoke
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize(profiler)
|
19
|
-
@profiler = profiler
|
20
|
-
end
|
21
|
-
attr_reader :profiler
|
22
|
-
|
23
|
-
def invoke
|
24
|
-
end
|
25
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'speed_gun/profiler/base'
|
2
|
-
|
3
|
-
class SpeedGun::Profiler::ActionController < SpeedGun::Profiler::Base
|
4
|
-
hook_method ::ActionController::Base, :process
|
5
|
-
|
6
|
-
attr_reader :action_name
|
7
|
-
alias_method :title, :action_name
|
8
|
-
|
9
|
-
def before_profile(controller, action)
|
10
|
-
@action_name = "#{controller.class.name}##{action}"
|
11
|
-
end
|
12
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'speed_gun/profiler/base'
|
2
|
-
|
3
|
-
class SpeedGun::Profiler::ActionView < SpeedGun::Profiler::Base
|
4
|
-
hook_method ::ActionView::Template, :render
|
5
|
-
|
6
|
-
attr_reader :template_path
|
7
|
-
alias_method :title, :template_path
|
8
|
-
|
9
|
-
def before_profile(action_view, *args)
|
10
|
-
@template_path = action_view.instance_variable_get(:@virtual_path)
|
11
|
-
end
|
12
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'speed_gun/profiler/base'
|
2
|
-
|
3
|
-
class SpeedGun::Profiler::ActiveRecord < SpeedGun::Profiler::Base
|
4
|
-
def title
|
5
|
-
"#{@name}"
|
6
|
-
end
|
7
|
-
|
8
|
-
def html
|
9
|
-
%Q{<pre class="sql">#{@sql}</pre>}
|
10
|
-
end
|
11
|
-
|
12
|
-
def before_profile(adapter, sql, name = nil)
|
13
|
-
@sql = sql
|
14
|
-
@name = name
|
15
|
-
end
|
16
|
-
end
|
@@ -1,139 +0,0 @@
|
|
1
|
-
require 'speed_gun/profiler'
|
2
|
-
require 'securerandom'
|
3
|
-
|
4
|
-
class SpeedGun::Profiler::Base
|
5
|
-
def self.label
|
6
|
-
name.sub(/.*::/, '')
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.profiler_type
|
10
|
-
name\
|
11
|
-
.sub(/.*::/, '')\
|
12
|
-
.gsub(/(.)([A-Z])/) { |m| "#{$1}_#{$2.downcase}" }\
|
13
|
-
.downcase\
|
14
|
-
.to_sym
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.inherited(klass)
|
18
|
-
SpeedGun::Profiler::PROFILERS[klass.profiler_type] = klass
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.hook_method(klass, method_name)
|
22
|
-
without_profiling = "#{method_name}_withtout_profile".intern
|
23
|
-
with_profiling = "#{method_name}_with_profile".intern
|
24
|
-
return unless klass.send(:method_defined?, method_name)
|
25
|
-
return if klass.send(:method_defined?, with_profiling)
|
26
|
-
|
27
|
-
profiler = profiler_type
|
28
|
-
klass.send(:alias_method, without_profiling, method_name)
|
29
|
-
klass.send(:define_method, with_profiling) do |*args, &block|
|
30
|
-
return send(without_profiling, *args, &block) unless SpeedGun.current
|
31
|
-
|
32
|
-
profile_args = [self] + args
|
33
|
-
SpeedGun.current.profile(profiler, *profile_args) do
|
34
|
-
send(without_profiling, *args, &block)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
klass.send(:alias_method, method_name, with_profiling)
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.load(data)
|
41
|
-
data.delete('label')
|
42
|
-
type = data.delete('type')
|
43
|
-
profiler = SpeedGun::Profiler::PROFILERS[type.to_sym]
|
44
|
-
profile = profiler.new
|
45
|
-
|
46
|
-
data.each_pair do |key, val|
|
47
|
-
profile.send(:instance_variable_set, :"@#{key}", val)
|
48
|
-
end
|
49
|
-
|
50
|
-
profile
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.profile(profiler, *args, &block)
|
54
|
-
profile = new
|
55
|
-
profiler.profiles << profile
|
56
|
-
profile.profile(*args, &block)
|
57
|
-
end
|
58
|
-
|
59
|
-
def initialize
|
60
|
-
@id = SecureRandom.uuid
|
61
|
-
@parent_profile_id = nil
|
62
|
-
@elapsed_time = 0
|
63
|
-
@backtrace = []
|
64
|
-
end
|
65
|
-
attr_reader :id, :elapsed_time, :parent_profile_id, :backtrace
|
66
|
-
|
67
|
-
def title
|
68
|
-
warn 'Override this method'
|
69
|
-
end
|
70
|
-
|
71
|
-
def html
|
72
|
-
''
|
73
|
-
end
|
74
|
-
|
75
|
-
def label
|
76
|
-
self.class.label
|
77
|
-
end
|
78
|
-
|
79
|
-
def type
|
80
|
-
self.class.profiler_type
|
81
|
-
end
|
82
|
-
|
83
|
-
def profile(*args, &block)
|
84
|
-
@backtrace = cleanup_caller(caller(3))
|
85
|
-
parent_profile = SpeedGun.current.now_profile
|
86
|
-
SpeedGun.current.now_profile = self
|
87
|
-
call_without_defined(:before_profile, *args, &block)
|
88
|
-
result = measure(&block)
|
89
|
-
call_without_defined(:after_profile, *args, &block)
|
90
|
-
return result
|
91
|
-
ensure
|
92
|
-
SpeedGun.current.now_profile = parent_profile
|
93
|
-
@parent_profile_id = parent_profile.id if parent_profile
|
94
|
-
end
|
95
|
-
|
96
|
-
def measure(&block)
|
97
|
-
now = Time.now
|
98
|
-
result = yield
|
99
|
-
@elapsed_time = Time.now - now
|
100
|
-
|
101
|
-
result
|
102
|
-
end
|
103
|
-
|
104
|
-
def as_msgpack(*args)
|
105
|
-
hash = {}
|
106
|
-
instance_variables.each do |key|
|
107
|
-
unless key.to_s =~ /^\@_/
|
108
|
-
hash[key.to_s.sub(/^\@/, '')] = instance_variable_get(key)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
hash['type'] = type.to_s
|
112
|
-
hash['label'] = label
|
113
|
-
hash['title'] = title
|
114
|
-
hash
|
115
|
-
end
|
116
|
-
|
117
|
-
def to_msgpack(*args)
|
118
|
-
as_msgpack(*args).to_msgpack(*args)
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def call_without_defined(method, *args)
|
124
|
-
send(method, *args) if respond_to?(method)
|
125
|
-
end
|
126
|
-
|
127
|
-
def cleanup_caller(caller)
|
128
|
-
backtraces = caller.map do |backtrace|
|
129
|
-
backtrace.sub(SpeedGun.config.backtrace_remove, '')
|
130
|
-
end
|
131
|
-
backtraces.reject! do |backtrace|
|
132
|
-
!SpeedGun.config.backtrace_includes.any? do |regexp|
|
133
|
-
backtrace =~ regexp
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
backtraces
|
138
|
-
end
|
139
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'speed_gun/profiler/base'
|
2
|
-
|
3
|
-
class SpeedGun::Profiler::Js < SpeedGun::Profiler::Base
|
4
|
-
def self.profile(profiler, title, elapsed_time, backtrace)
|
5
|
-
profile = new
|
6
|
-
profiler.profiles << profile
|
7
|
-
profile.save(title, elapsed_time, backtrace)
|
8
|
-
end
|
9
|
-
|
10
|
-
attr_reader :title
|
11
|
-
|
12
|
-
def save(title, elapsed_time, backtrace)
|
13
|
-
@title = title.to_s
|
14
|
-
@elapsed_time = elapsed_time.to_i * 0.001
|
15
|
-
@backtrace = backtrace
|
16
|
-
end
|
17
|
-
end
|
data/lib/speed_gun/store/base.rb
DELETED
data/lib/speed_gun/store/file.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'speed_gun/store/base'
|
2
|
-
require 'fileutils'
|
3
|
-
|
4
|
-
class SpeedGun::Store::File
|
5
|
-
DEFAULT_PATH = '/tmp/speed_gun'
|
6
|
-
DEFAULT_EXPIRES_IN_SECONDS = 60 * 60 * 24
|
7
|
-
CLEANUP_INTERVAL = 60 * 60
|
8
|
-
|
9
|
-
def initialize(options = {})
|
10
|
-
@path = (options[:path] || DEFAULT_PATH).to_s
|
11
|
-
@expires = options[:expires] || DEFAULT_EXPIRES_IN_SECONDS
|
12
|
-
@lock = Mutex.new
|
13
|
-
|
14
|
-
this = self
|
15
|
-
Thread.new do
|
16
|
-
begin
|
17
|
-
while true
|
18
|
-
this.cleanup
|
19
|
-
sleep(CLEANUP_INTERVAL)
|
20
|
-
end
|
21
|
-
rescue
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def [](id)
|
27
|
-
@lock.synchronize {
|
28
|
-
read(id) if exist?(id)
|
29
|
-
}
|
30
|
-
end
|
31
|
-
|
32
|
-
def []=(id, val)
|
33
|
-
@lock.synchronize {
|
34
|
-
write(id, val)
|
35
|
-
}
|
36
|
-
end
|
37
|
-
|
38
|
-
def cleanup
|
39
|
-
@lock.synchronize {
|
40
|
-
files = Dir.entries(@path)
|
41
|
-
files.each do |file|
|
42
|
-
file = File.join(@path, file)
|
43
|
-
File.delete(file) if (Time.now - File.mtime(file)) > @expires
|
44
|
-
end
|
45
|
-
}
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def read(id)
|
51
|
-
File.open(File.join(@path, id), 'rb') { |f| f.read }
|
52
|
-
end
|
53
|
-
|
54
|
-
def write(id, val)
|
55
|
-
FileUtils.mkdir_p(@path)
|
56
|
-
File.open(File.join(@path, id), 'wb+') { |f| f.write(val) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def exist?(id)
|
60
|
-
File.exist?(File.join(@path, id))
|
61
|
-
end
|
62
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'speed_gun/store/base'
|
2
|
-
|
3
|
-
class SpeedGun::Store::Memcache
|
4
|
-
DEFAULT_PREFIX = 'speed-gun-'
|
5
|
-
DEFAULT_EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
|
-
|
7
|
-
def initialize(options = {})
|
8
|
-
@prefix = options[:prefix] || DEFAULT_PREFIX
|
9
|
-
@client = options[:client] || default_dalli
|
10
|
-
@expires = options[:expires] || DEFAULT_EXPIRES_IN_SECONDS
|
11
|
-
end
|
12
|
-
|
13
|
-
def [](id)
|
14
|
-
@client.get("#{@prefix}#{id}")
|
15
|
-
end
|
16
|
-
|
17
|
-
def []=(id, val)
|
18
|
-
@client.set("#{@prefix}#{id}", val, @expires)
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def default_dalli
|
24
|
-
require 'dalli' unless defined?(Dalli)
|
25
|
-
Dalli::Client.new
|
26
|
-
end
|
27
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'speed_gun/store/base'
|
2
|
-
|
3
|
-
class SpeedGun::Store::Memory < SpeedGun::Store::Base
|
4
|
-
DEFAULT_MAX_ENTRIES = 100
|
5
|
-
|
6
|
-
def initialize(options = {})
|
7
|
-
@max_entries = options[:max_entries] || DEFAULT_MAX_ENTRIES
|
8
|
-
@store = {}
|
9
|
-
@stored_list = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def [](id)
|
13
|
-
@store[id]
|
14
|
-
end
|
15
|
-
|
16
|
-
def []=(id, val)
|
17
|
-
@store[id] = val
|
18
|
-
@stored_list.push(id)
|
19
|
-
|
20
|
-
@store.delete(@stored_list.shift) while @stored_list.length > @max_entries
|
21
|
-
end
|
22
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'speed_gun/store/base'
|
2
|
-
|
3
|
-
class SpeedGun::Store::Redis
|
4
|
-
DEFAULT_PREFIX = 'speed-gun-'
|
5
|
-
DEFAULT_EXPIRES_IN_SECONDS = 60 * 60 * 24
|
6
|
-
|
7
|
-
def initialize(options = {})
|
8
|
-
@prefix = options[:prefix] || DEFAULT_PREFIX
|
9
|
-
@client = options[:client] || default_redis(options)
|
10
|
-
@expires = options[:expires] || DEFAULT_EXPIRES_IN_SECONDS
|
11
|
-
end
|
12
|
-
|
13
|
-
def [](id)
|
14
|
-
@client.get("#{@prefix}#{id}")
|
15
|
-
end
|
16
|
-
|
17
|
-
def []=(id, val)
|
18
|
-
@client.setex("#{@prefix}#{id}", val, @expires)
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def default_redis(args)
|
24
|
-
require 'redis' unless defined? Redis
|
25
|
-
Redis.new(args)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|