mongo_profiler 0.0.1 → 0.0.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 +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>
|