mongo_profiler 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +22 -1
- data/.travis.yml +13 -0
- data/Gemfile.lock +32 -20
- data/README.md +53 -64
- data/lib/mongo_profiler.rb +9 -95
- data/lib/mongo_profiler/caller.rb +4 -6
- data/lib/mongo_profiler/extensions/moped.rb +17 -0
- data/lib/mongo_profiler/models/profile.rb +107 -0
- data/lib/mongo_profiler/models/profile_group.rb +39 -0
- data/lib/mongo_profiler/util.rb +21 -0
- data/lib/mongo_profiler/version.rb +1 -1
- data/lib/mongo_profiler/web.rb +8 -55
- data/lib/mongo_profiler/web_helpers.rb +0 -56
- data/mongo_profiler.gemspec +3 -4
- data/spec/mongo_profiler/caller_spec.rb +5 -50
- data/spec/mongo_profiler/models/profile_spec.rb +60 -0
- data/spec/mongo_profiler/util_spec.rb +49 -0
- data/spec/mongo_profiler/web_helpers_spec.rb +0 -44
- data/spec/mongo_profiler/web_spec.rb +0 -30
- data/spec/mongo_profiler_spec.rb +0 -109
- data/spec/mongoid.yml +20 -0
- data/spec/spec_helper.rb +13 -12
- data/web/views/index.erb +29 -25
- data/web/views/layout.erb +6 -10
- data/web/views/show.erb +74 -74
- metadata +29 -32
- data/config.ru +0 -13
- data/lib/mongo_profiler/extensions/mongo/cursor.rb +0 -38
- data/lib/mongo_profiler/payload.rb +0 -33
- data/lib/mongo_profiler/profiler.rb +0 -4
- data/lib/mongo_profiler/stats.rb +0 -25
- data/spec/mongo_profiler/extensions/mongo/cursor_spec.rb +0 -42
- data/spec/mongo_profiler/payload_spec.rb +0 -111
- data/spec/mongo_profiler/profiler_spec.rb +0 -8
- data/spec/mongo_profiler/stats_spec.rb +0 -29
- data/web/views/group_id.erb +0 -53
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MongoProfiler
|
4
|
+
describe Util do
|
5
|
+
|
6
|
+
describe '.deep_keys' do
|
7
|
+
it 'iterates over arrays' do
|
8
|
+
hash = {
|
9
|
+
key1: [{ 'key1_sub1' => 'key1_sub1_value', key1_sub2: [ 'key1_sub2_sub1' => {}] }, 'test'],
|
10
|
+
key2: nil
|
11
|
+
}
|
12
|
+
|
13
|
+
expect(described_class.deep_keys(hash)).to eq([:key1, 'key1_sub1', :key1_sub2, 'key1_sub2_sub1', :key2])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns all keys' do
|
17
|
+
hash = {
|
18
|
+
key1: {
|
19
|
+
key1_sub1: {
|
20
|
+
key1_sub1_sub1: 'key1_sub1_sub1_value'
|
21
|
+
},
|
22
|
+
key1_sub2: 'key1_sub2_value',
|
23
|
+
key1_sub3: {}
|
24
|
+
},
|
25
|
+
key2: {
|
26
|
+
key2_sub1: {
|
27
|
+
key2_sub1_sub1: 'key1_sub1_sub1_value',
|
28
|
+
key2_sub1_sub2: {
|
29
|
+
key2_sub1_sub2_sub1: {
|
30
|
+
key2_sub1_sub2_sub1_sub1: {}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
},
|
34
|
+
key2_sub2: 'key2_sub2_value',
|
35
|
+
key2_sub3: 'key2_sub3_value',
|
36
|
+
key2_sub4: 'key2_sub4_value',
|
37
|
+
key2_sub5: 'key2_sub5_value'
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
expect(described_class.deep_keys(hash)).to eq(%i[key1 key1_sub1 key1_sub1_sub1
|
42
|
+
key1_sub2 key1_sub3 key2 key2_sub1
|
43
|
+
key2_sub1_sub1 key2_sub1_sub2
|
44
|
+
key2_sub1_sub2_sub1 key2_sub1_sub2_sub1_sub1
|
45
|
+
key2_sub2 key2_sub3 key2_sub4 key2_sub5])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -4,49 +4,5 @@ require 'mongo_profiler/web_helpers'
|
|
4
4
|
module MongoProfiler
|
5
5
|
describe WebHelpers do
|
6
6
|
subject { Class.new { include MongoProfiler::WebHelpers }.new }
|
7
|
-
|
8
|
-
|
9
|
-
describe '#graphite_graph_url' do
|
10
|
-
before { MongoProfiler.graphite_url = 'http://graphite' }
|
11
|
-
|
12
|
-
let(:profile) { { 'method' => 'load_schedulers',
|
13
|
-
'file' => '/workspace/project/config/schedule.rb',
|
14
|
-
'line' => 11,
|
15
|
-
'method' => 'load_schedulers' } }
|
16
|
-
|
17
|
-
context 'when -1h and 1min' do
|
18
|
-
xit 'generates graphite url' do
|
19
|
-
expect(subject.graphite_graph_url(profile, '-1h', '1min', 'hello')).to eq("http://graphite/render?from=-1h&until=now&width=400&height=250&target=alias(summarize(stats_counts.mongo_profiler..schedule_rb.load_schedulers,%20'1min',%20'sum'),%20'schedule.rb%23load_schedulers')&title=hello")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#print_backtrace_entry' do
|
25
|
-
it 'prints project entry' do
|
26
|
-
c = %{/Users/pablo/workspace/project/app/controllers/queues_controller.rb:34:in `show'}
|
27
|
-
expect(subject.print_backtrace_entry(c)).to eq %{<span class="btn-info">#{c}</span>}
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'when gem/ruby' do
|
31
|
-
it 'prints dependency entry' do
|
32
|
-
c = %{/Users/pablo/.gem/ruby/2.0.0/gems/rack-1.4.5/lib/rack/session/abstract/id.rb:210:in `context'}
|
33
|
-
expect(subject.print_backtrace_entry(c)).to eq c
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'when rubies/ruby' do
|
38
|
-
it 'prints dependency entry' do
|
39
|
-
c = %{/Users/pablo/.rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'}
|
40
|
-
expect(subject.print_backtrace_entry(c)).to eq c
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'when bundle/ruby' do
|
45
|
-
it 'prints dependency entry' do
|
46
|
-
c = %{/Users/pablo/bundle/ruby/2.0.0/gems/rspec-core-2.14.4/lib/rspec/core/memoized_helpers.rb:199:in `block (2 levels) in let'}
|
47
|
-
expect(subject.print_backtrace_entry(c)).to eq c
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
7
|
end
|
52
8
|
end
|
@@ -9,35 +9,5 @@ module MongoProfiler
|
|
9
9
|
def app
|
10
10
|
described_class
|
11
11
|
end
|
12
|
-
|
13
|
-
describe 'POST /profiler/enable' do
|
14
|
-
it 'enables profiler' do
|
15
|
-
MongoProfiler.disable!
|
16
|
-
|
17
|
-
post '/profiler/enable'
|
18
|
-
|
19
|
-
expect(last_response.status).to eq 302
|
20
|
-
expect(MongoProfiler.enabled?).to be_true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe 'POST /profiler/disable' do
|
25
|
-
it 'disables profiler' do
|
26
|
-
MongoProfiler.enable!
|
27
|
-
|
28
|
-
post '/profiler/disable'
|
29
|
-
|
30
|
-
expect(last_response.status).to eq 302
|
31
|
-
expect(MongoProfiler.disabled?).to be_true
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
describe 'GET /profiler/groups/:group_id' do
|
36
|
-
pending
|
37
|
-
end
|
38
|
-
|
39
|
-
describe 'GET /profiler/:_id/explain' do
|
40
|
-
pending
|
41
|
-
end
|
42
12
|
end
|
43
13
|
end
|
data/spec/mongo_profiler_spec.rb
CHANGED
@@ -1,113 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe MongoProfiler do
|
4
|
-
describe '.log' do
|
5
|
-
it 'merges required keys' do
|
6
|
-
expect(MongoProfiler.collection).to receive(:insert).with({ application_name: MongoProfiler.application_name, group_id: MongoProfiler.group_id})
|
7
|
-
|
8
|
-
described_class.log({})
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '.connect' do
|
13
|
-
context 'without any argument' do
|
14
|
-
it 'connects to a mongo database' do
|
15
|
-
expect {
|
16
|
-
described_class.connect
|
17
|
-
|
18
|
-
expect(described_class.connected?).to be_true
|
19
|
-
}.to_not raise_error
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'with arguments' do
|
24
|
-
it 'connects with host, port and database' do
|
25
|
-
expect {
|
26
|
-
described_class.connect('localhost', 27017, 'project_development')
|
27
|
-
|
28
|
-
expect(described_class.connected?).to be_true
|
29
|
-
}.to_not raise_error
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'when invalid config' do
|
34
|
-
it 'connects with host, port and database' do
|
35
|
-
expect {
|
36
|
-
described_class.connect('xxx')
|
37
|
-
}.to raise_error
|
38
|
-
|
39
|
-
expect(described_class.connected?).to be_false
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe '.should_skip?' do
|
45
|
-
let(:_caller) {
|
46
|
-
[
|
47
|
-
"/Users/pablo/workspace/project/file.rb:7:in `new'",
|
48
|
-
"/Users/pablo/.gem/ruby/2.0.0/gems/rspec-core-2.14.4/lib/rspec/core/memoized_helpers.rb:199:in `block (2 levels) in let'",
|
49
|
-
"/Users/pablo/.gem/ruby/2.0.0/gems/rspec-core-2.14.4/lib/rspec/core/memoized_helpers.rb:199:in `fetch'",
|
50
|
-
"/Users/pablo/.gem/ruby/2.0.0/gems/rspec-core-2.14.4/lib/rspec/core/memoized_helpers.rb:199:in `block in let'"
|
51
|
-
]
|
52
|
-
}
|
53
|
-
|
54
|
-
it 'accepts valid payload' do
|
55
|
-
payload = { :database => 'project',
|
56
|
-
:collection => 'stores',
|
57
|
-
:selector => { '_id' => BSON::ObjectId('5282d125755b1c7f2c000005') },
|
58
|
-
:limit => -1 }
|
59
|
-
|
60
|
-
expect(described_class.should_skip?(payload, MongoProfiler::Caller.new(_caller))).to be_false
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'skips $cmd for unknown collections' do
|
64
|
-
payload = { :database => 'project',
|
65
|
-
:collection => '$cmd',
|
66
|
-
:selector => { :getnonce => 1 },
|
67
|
-
:limit => -1 }
|
68
|
-
|
69
|
-
expect(described_class.should_skip?(payload, MongoProfiler::Caller.new(_caller))).to be_true
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe '.extra_attrs' do
|
74
|
-
it 'sets an extra attr' do
|
75
|
-
described_class.extra_attrs['test'] = 'test_value'
|
76
|
-
|
77
|
-
expect(described_class.extra_attrs['test']).to eq 'test_value'
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe '.group_id' do
|
82
|
-
it 'uses default keys by default' do
|
83
|
-
expect(described_class.group_id).to match /process_pid/
|
84
|
-
expect(described_class.group_id).to match /thread_object_id/
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
describe 'enabled and disable' do
|
89
|
-
context 'when disabled' do
|
90
|
-
before { described_class.disable! }
|
91
|
-
|
92
|
-
it(:disabled?) { be_true }
|
93
|
-
it(:enabled?) { be_false }
|
94
|
-
end
|
95
|
-
|
96
|
-
context 'when enabled' do
|
97
|
-
before { described_class.enable! }
|
98
|
-
|
99
|
-
it(:enabled?) { be_true }
|
100
|
-
it(:disabled?) { be_true }
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
describe '#collection' do
|
105
|
-
it { expect(described_class.collection).to be_a(Mongo::Collection) }
|
106
|
-
it { expect(described_class.collection.name).to eq 'mongo_profiler' }
|
107
|
-
end
|
108
|
-
|
109
|
-
describe '#collection_config' do
|
110
|
-
it { expect(described_class.collection_config).to be_a(Mongo::Collection) }
|
111
|
-
it { expect(described_class.collection_config.name).to eq 'mongo_profiler_config' }
|
112
|
-
end
|
113
4
|
end
|
data/spec/mongoid.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
development:
|
2
|
+
sessions:
|
3
|
+
default:
|
4
|
+
database: mongo_profiler_development
|
5
|
+
hosts:
|
6
|
+
- localhost:27017
|
7
|
+
options:
|
8
|
+
pool_size: 50
|
9
|
+
options:
|
10
|
+
include_root_in_json: false
|
11
|
+
raise_not_found_error: true
|
12
|
+
test:
|
13
|
+
sessions:
|
14
|
+
default:
|
15
|
+
database: mongo_profiler_test
|
16
|
+
hosts:
|
17
|
+
- localhost:27017
|
18
|
+
options:
|
19
|
+
include_root_in_json: false
|
20
|
+
raise_not_found_error: false
|
data/spec/spec_helper.rb
CHANGED
@@ -1,28 +1,29 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
require 'pry-byebug'
|
3
|
+
require 'database_cleaner'
|
4
|
+
require 'mongo_profiler'
|
3
5
|
|
4
6
|
Bundler.require(:default, :test)
|
5
7
|
|
6
|
-
|
7
|
-
require_relative '../lib/mongo_profiler/extensions/mongo/cursor'
|
8
|
+
ENV['MONGOID_ENV'] = 'test'
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
Mongoid.load!(File.join(File.dirname(__FILE__), 'mongoid.yml'))
|
11
|
+
|
12
|
+
class TestModel
|
13
|
+
include Mongoid::Document
|
14
|
+
include Mongoid::Timestamps
|
15
|
+
include Mongoid::Attributes::Dynamic
|
16
|
+
end
|
12
17
|
|
13
18
|
Dir['./spec/support/**/*.rb'].each &method(:require)
|
14
19
|
|
15
20
|
RSpec.configure do |config|
|
16
21
|
config.before do
|
17
|
-
|
18
|
-
|
19
|
-
# creates capped collections
|
20
|
-
MongoProfiler.create_collections
|
22
|
+
DatabaseCleaner.strategy = :truncation
|
23
|
+
DatabaseCleaner.start
|
21
24
|
end
|
22
25
|
|
23
26
|
config.after do
|
24
|
-
|
25
|
-
collection.drop unless collection.name.match(/^system\./)
|
26
|
-
end
|
27
|
+
DatabaseCleaner.clean
|
27
28
|
end
|
28
29
|
end
|
data/web/views/index.erb
CHANGED
@@ -1,32 +1,36 @@
|
|
1
1
|
<h2 class="page-header">Profile Groups</h2>
|
2
2
|
|
3
3
|
<div class="row">
|
4
|
-
<table class="table table-bordered">
|
4
|
+
<table class="table table-bordered table-striped">
|
5
5
|
<thead>
|
6
|
-
<th>
|
7
|
-
<th>
|
8
|
-
<th>
|
9
|
-
<th>
|
10
|
-
<th>
|
11
|
-
<th>
|
12
|
-
<th>
|
6
|
+
<th>Group name</th>
|
7
|
+
<th>Total time ms</th>
|
8
|
+
<th>AVG time ms</th>
|
9
|
+
<th>Min time ms</th>
|
10
|
+
<th>Max time ms</th>
|
11
|
+
<th>Queries</th>
|
12
|
+
<th>perfect</th>
|
13
|
+
<th>had_to_order</th>
|
14
|
+
<th>scanned_more_than_returned</th>
|
15
|
+
<th>no_index</th>
|
16
|
+
<th>no_docs_found</th>
|
13
17
|
</thead>
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
<td
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
18
|
+
<tbody class="header">
|
19
|
+
<% @groups.each do |group| %>
|
20
|
+
<tr>
|
21
|
+
<td><a href="<%= root_path %>groups/<%= group.id %>"><%= group.name %></a></td>
|
22
|
+
<td><%= group.total_time %></td>
|
23
|
+
<td><%= group.avg_time %></td>
|
24
|
+
<td><%= group.min_time %></td>
|
25
|
+
<td><%= group.max_time %></td>
|
26
|
+
<td><%= group.profiles.count %></td>
|
27
|
+
<td><%= group.count_by_score :perfect %></td>
|
28
|
+
<td><%= group.count_by_score :had_to_order %></td>
|
29
|
+
<td><%= group.count_by_score :scanned_more_than_returned %></td>
|
30
|
+
<td><%= group.count_by_score :no_index %></td>
|
31
|
+
<td><%= group.count_by_score :no_docs_found %></td>
|
32
|
+
</tr>
|
33
|
+
<% end %>
|
34
|
+
</tbody>
|
31
35
|
</table>
|
32
36
|
</div>
|
data/web/views/layout.erb
CHANGED
@@ -36,20 +36,16 @@
|
|
36
36
|
<li><a href="<%= root_path %>">Dashboard</a></li>
|
37
37
|
</ul>
|
38
38
|
-->
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
<form action="<%= root_path %>profiler/enable" method="post" class="navbar-form navbar-right">
|
45
|
-
<button class="btn btn-primary">Enable Profiler</button>
|
46
|
-
</form>
|
47
|
-
<% end %>
|
39
|
+
|
40
|
+
<form action="<%= root_path %>clear" method="post" class="navbar-form navbar-left" role="search">
|
41
|
+
<button class="btn btn-danger">Clear History</button>
|
42
|
+
</form>
|
43
|
+
|
48
44
|
</div><!--/.nav-collapse -->
|
49
45
|
</div>
|
50
46
|
</div>
|
51
47
|
|
52
|
-
<div class="container">
|
48
|
+
<div class="container-fluid">
|
53
49
|
<div class="starter-template">
|
54
50
|
<% if @error_alert %>
|
55
51
|
<div class="alert alert-danger alert-dismissable">
|
data/web/views/show.erb
CHANGED
@@ -1,83 +1,83 @@
|
|
1
|
-
|
1
|
+
<% group = @group %>
|
2
|
+
|
3
|
+
<h2 class="page-header">Profile Groups <%= group.name %></h2>
|
4
|
+
|
5
|
+
<style>
|
6
|
+
tr, td, pre
|
7
|
+
{
|
8
|
+
height:60px;
|
9
|
+
max-height:60px;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
|
13
|
+
<script>
|
14
|
+
$(function(){
|
15
|
+
$('tr pre').click(function(){
|
16
|
+
var height = '400px';
|
17
|
+
|
18
|
+
if($(this).height() > 300){
|
19
|
+
height = '60px';
|
20
|
+
}
|
21
|
+
|
22
|
+
$(this).animate({ 'height': height, 'max-height': height });
|
23
|
+
})
|
24
|
+
});
|
25
|
+
</script>
|
2
26
|
|
3
27
|
<div class="row">
|
4
|
-
<table class="table table-bordered">
|
28
|
+
<table class="table table-bordered table-striped">
|
5
29
|
<thead>
|
6
|
-
<th>
|
7
|
-
<th>
|
8
|
-
<th>
|
9
|
-
<th>
|
10
|
-
<th>
|
30
|
+
<th>Group name</th>
|
31
|
+
<th>Total time ms</th>
|
32
|
+
<th>AVG time ms</th>
|
33
|
+
<th>Min time ms</th>
|
34
|
+
<th>Max time ms</th>
|
35
|
+
<th>Queries</th>
|
36
|
+
<th>perfect</th>
|
37
|
+
<th>had_to_order</th>
|
38
|
+
<th>scanned_more_than_returned</th>
|
39
|
+
<th>no_index</th>
|
40
|
+
<th>no_docs_found</th>
|
11
41
|
</thead>
|
12
|
-
|
42
|
+
<tbody>
|
13
43
|
<tr>
|
14
|
-
<td><%=
|
15
|
-
<td><%=
|
16
|
-
<td><%=
|
17
|
-
<td
|
18
|
-
<td
|
44
|
+
<td><%= group.name %></td>
|
45
|
+
<td><%= group.total_time %></td>
|
46
|
+
<td><%= group.avg_time %></td>
|
47
|
+
<td><%= group.min_time %></td>
|
48
|
+
<td><%= group.max_time %></td>
|
49
|
+
<td><%= group.profiles.count %></td>
|
50
|
+
<td><%= group.count_by_score :perfect %></td>
|
51
|
+
<td><%= group.count_by_score :had_to_order %></td>
|
52
|
+
<td><%= group.count_by_score :scanned_more_than_returned %></td>
|
53
|
+
<td><%= group.count_by_score :no_index %></td>
|
54
|
+
<td><%= group.count_by_score :no_docs_found %></td>
|
19
55
|
</tr>
|
20
|
-
|
56
|
+
</tbody>
|
21
57
|
</table>
|
22
|
-
</div>
|
23
|
-
|
24
|
-
<% if MongoProfiler.graphite_url %>
|
25
|
-
<div class="row">
|
26
|
-
<h4>Graphite / Timers</h4>
|
27
|
-
<div class="col-sm-6 col-md-4">
|
28
|
-
<div class="thumbnail">
|
29
|
-
<img data-src="holder.js/300x200" alt="..." src="<%= graphite_graph_timers_url(@profile, '-1h', '1min', 'Last hour/min') %>">
|
30
|
-
</div>
|
31
|
-
</div>
|
32
|
-
<div class="col-sm-6 col-md-4">
|
33
|
-
<div class="thumbnail">
|
34
|
-
<img data-src="holder.js/300x200" alt="..." src="<%= graphite_graph_timers_url(@profile, '-1day', '1h', 'Last day/hour') %>">
|
35
|
-
</div>
|
36
|
-
</div>
|
37
|
-
<div class="col-sm-6 col-md-4">
|
38
|
-
<div class="thumbnail">
|
39
|
-
<img data-src="holder.js/300x200" alt="..." src="<%= graphite_graph_timers_url(@profile, '-1month', '1d', 'Last month/day') %>">
|
40
|
-
</div>
|
41
|
-
</div>
|
42
|
-
</div>
|
43
|
-
|
44
|
-
<div class="row">
|
45
|
-
<h4>Graphite / Count</h4>
|
46
|
-
<div class="col-sm-6 col-md-4">
|
47
|
-
<div class="thumbnail">
|
48
|
-
<img data-src="holder.js/300x200" alt="..." src="<%= graphite_graph_count_url(@profile, '-1h', '1min', 'Last hour/min') %>">
|
49
|
-
</div>
|
50
|
-
</div>
|
51
|
-
<div class="col-sm-6 col-md-4">
|
52
|
-
<div class="thumbnail">
|
53
|
-
<img data-src="holder.js/300x200" alt="..." src="<%= graphite_graph_count_url(@profile, '-1day', '1h', 'Last day/hour') %>">
|
54
|
-
</div>
|
55
|
-
</div>
|
56
|
-
<div class="col-sm-6 col-md-4">
|
57
|
-
<div class="thumbnail">
|
58
|
-
<img data-src="holder.js/300x200" alt="..." src="<%= graphite_graph_count_url(@profile, '-1month', '1d', 'Last month/day') %>">
|
59
|
-
</div>
|
60
|
-
</div>
|
61
|
-
</div>
|
62
|
-
<% end %>
|
63
58
|
|
64
|
-
<
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
<
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
59
|
+
<table class="table table-bordered table-striped">
|
60
|
+
<thead class="header">
|
61
|
+
<th>Total Time</th>
|
62
|
+
<th>Query score</th>
|
63
|
+
<th>File Name</th>
|
64
|
+
<th>Database</th>
|
65
|
+
<th>Collection</th>
|
66
|
+
<th>Command</th>
|
67
|
+
<th>Explain</th>
|
68
|
+
</thead>
|
69
|
+
<tbody>
|
70
|
+
<% group.profiles.sort_by(&:total_time).reverse.each do |profile| %>
|
71
|
+
<tr>
|
72
|
+
<td><%= profile['total_time'] %></td>
|
73
|
+
<td><%= profile.score %></td>
|
74
|
+
<td><%= "#{profile['file'].split('/').last}:#{profile['line']}" %></td>
|
75
|
+
<td><%= profile['command_database'] %></td>
|
76
|
+
<td><%= profile['command_collection'] %></td>
|
77
|
+
<td><pre style="width: 300px" title="Click to toggle"><code><%= profile['command'] %></code></pre></td>
|
78
|
+
<td><pre style="width: 300px" title="Click to toggle"><code><%= profile['explain'] %></code></pre></td>
|
79
|
+
</tr>
|
80
|
+
<% end %>
|
81
|
+
</tbody>
|
82
|
+
</table>
|
83
83
|
</div>
|