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.
@@ -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
@@ -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
@@ -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
@@ -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
- require_relative '../lib/mongo_profiler'
7
- require_relative '../lib/mongo_profiler/extensions/mongo/cursor'
8
+ ENV['MONGOID_ENV'] = 'test'
8
9
 
9
- CONNECTION = Mongo::MongoClient.new
10
- DB = CONNECTION.db('mongo_profiler-database-test')
11
- COLL = DB['example-collection']
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
- MongoProfiler.connect('localhost', 27017, 'mongo_profiler-database-test')
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
- DB.collections.each do |collection|
25
- collection.drop unless collection.name.match(/^system\./)
26
- end
27
+ DatabaseCleaner.clean
27
28
  end
28
29
  end
@@ -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>Application</th>
7
- <th>File Name</th>
8
- <th>Method</th>
9
- <th>Total Queries</th>
10
- <th>Total Time</th>
11
- <th>Avg Total Time</th>
12
- <th>Group Id</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
- <% @grouped_profiles.each_with_index do |(group_id, profiles), grouped_index| %>
15
- <tbody class="<%= (grouped_index % 2 == 0) ? 'tbody-gray' : '' %>">
16
- <% profiles.each_with_index do |profile, profiles_index| %>
17
- <tr>
18
- <td><%= profile['application_name'] %></td>
19
- <td><%= profile['file'].split('/').last %></td>
20
- <td><%= profile['method'] %></td>
21
- <td><%= profile['total'] %></td>
22
- <td><%= profile['total_time'] %></td>
23
- <td><%= profile['total_time'] / profile['total'] %></td>
24
- <% if profiles_index == 0 %>
25
- <td rowspan="<%= profiles.size %>"><a href="<%= root_path %>profiler/groups/<%= group_id %>"><%= group_id %></a></td>
26
- <% end %>
27
- </tr>
28
- <% end %>
29
- </tbody>
30
- <% end %>
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>
@@ -36,20 +36,16 @@
36
36
  <li><a href="<%= root_path %>">Dashboard</a></li>
37
37
  </ul>
38
38
  -->
39
- <% if MongoProfiler.enabled? %>
40
- <form action="<%= root_path %>profiler/disable" method="post" class="navbar-form navbar-right">
41
- <button class="btn btn-danger">Disable Profiler</button>
42
- </form>
43
- <% else %>
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">
@@ -1,83 +1,83 @@
1
- <h2 class="page-header">Profile Id: <%= @profile_id %></h2>
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>File Name</th>
7
- <th>Method</th>
8
- <th>Total Time</th>
9
- <th>Instrument Payload</th>
10
- <th>Group Id</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
- <tbody>
42
+ <tbody>
13
43
  <tr>
14
- <td><%= @profile['file'].split('/').last %>:<%= @profile['line'] %></td>
15
- <td><%= @profile['method'] %></td>
16
- <td><%= @profile['total_time'] %></td>
17
- <td><pre><code><%= @profile['instrument_payload'] %></code></pre></td>
18
- <td><a href="<%= root_path %>profiler/groups/<%= @profile['group_id'] %>"><%= @profile['group_id'] %></a></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
- </tbody>
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
- <div class="row">
65
- <div class="col-md-5">
66
- <h4>Selector</h4>
67
- <pre><code><%= @selector.to_json %></code></pre>
68
- </div>
69
-
70
- <div class="col-md-5">
71
- <h4>Explain</h4>
72
- <pre><code><%= @explain.to_json %></code></pre>
73
- </div>
74
- </div>
75
-
76
- <div class="row">
77
- <h4>Backtrace</h4>
78
- <ol>
79
- <% @profile['backtrace'].to_a.each do |c| %>
80
- <li><%= print_backtrace_entry(c) %></li>
81
- <% end %>
82
- </ol>
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>