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
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo_profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pablo Cantero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -39,21 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - '='
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 1.9.2
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - '='
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 1.9.2
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bson_ext
|
42
|
+
name: mongoid
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - '>='
|
@@ -136,7 +122,21 @@ dependencies:
|
|
136
122
|
- - '>='
|
137
123
|
- !ruby/object:Gem::Version
|
138
124
|
version: '0'
|
139
|
-
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: database_cleaner
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Ruby profiling tool for MongoDB
|
140
140
|
email:
|
141
141
|
- pablo@pablocantero.com
|
142
142
|
executables: []
|
@@ -145,6 +145,7 @@ extra_rdoc_files: []
|
|
145
145
|
files:
|
146
146
|
- .gitignore
|
147
147
|
- .rspec
|
148
|
+
- .travis.yml
|
148
149
|
- Gemfile
|
149
150
|
- Gemfile.lock
|
150
151
|
- LICENSE.txt
|
@@ -154,25 +155,23 @@ files:
|
|
154
155
|
- assets/mongo_profiler_group_details.png
|
155
156
|
- assets/mongo_profiler_query_details.png
|
156
157
|
- assets/mongo_profiler_query_details_backtrace.png
|
157
|
-
- config.ru
|
158
158
|
- lib/mongo_profiler.rb
|
159
159
|
- lib/mongo_profiler/caller.rb
|
160
|
-
- lib/mongo_profiler/extensions/
|
161
|
-
- lib/mongo_profiler/
|
162
|
-
- lib/mongo_profiler/
|
163
|
-
- lib/mongo_profiler/
|
160
|
+
- lib/mongo_profiler/extensions/moped.rb
|
161
|
+
- lib/mongo_profiler/models/profile.rb
|
162
|
+
- lib/mongo_profiler/models/profile_group.rb
|
163
|
+
- lib/mongo_profiler/util.rb
|
164
164
|
- lib/mongo_profiler/version.rb
|
165
165
|
- lib/mongo_profiler/web.rb
|
166
166
|
- lib/mongo_profiler/web_helpers.rb
|
167
167
|
- mongo_profiler.gemspec
|
168
168
|
- spec/mongo_profiler/caller_spec.rb
|
169
|
-
- spec/mongo_profiler/
|
170
|
-
- spec/mongo_profiler/
|
171
|
-
- spec/mongo_profiler/profiler_spec.rb
|
172
|
-
- spec/mongo_profiler/stats_spec.rb
|
169
|
+
- spec/mongo_profiler/models/profile_spec.rb
|
170
|
+
- spec/mongo_profiler/util_spec.rb
|
173
171
|
- spec/mongo_profiler/web_helpers_spec.rb
|
174
172
|
- spec/mongo_profiler/web_spec.rb
|
175
173
|
- spec/mongo_profiler_spec.rb
|
174
|
+
- spec/mongoid.yml
|
176
175
|
- spec/spec_helper.rb
|
177
176
|
- web/assets/fonts/glyphicons-halflings-regular.eot
|
178
177
|
- web/assets/fonts/glyphicons-halflings-regular.svg
|
@@ -187,7 +186,6 @@ files:
|
|
187
186
|
- web/assets/stylesheets/bootstrap.min.css
|
188
187
|
- web/assets/stylesheets/highlight/default.css
|
189
188
|
- web/assets/stylesheets/highlight/github.css
|
190
|
-
- web/views/group_id.erb
|
191
189
|
- web/views/index.erb
|
192
190
|
- web/views/layout.erb
|
193
191
|
- web/views/show.erb
|
@@ -217,11 +215,10 @@ specification_version: 4
|
|
217
215
|
summary: Ruby profiling tool for MongoDB
|
218
216
|
test_files:
|
219
217
|
- spec/mongo_profiler/caller_spec.rb
|
220
|
-
- spec/mongo_profiler/
|
221
|
-
- spec/mongo_profiler/
|
222
|
-
- spec/mongo_profiler/profiler_spec.rb
|
223
|
-
- spec/mongo_profiler/stats_spec.rb
|
218
|
+
- spec/mongo_profiler/models/profile_spec.rb
|
219
|
+
- spec/mongo_profiler/util_spec.rb
|
224
220
|
- spec/mongo_profiler/web_helpers_spec.rb
|
225
221
|
- spec/mongo_profiler/web_spec.rb
|
226
222
|
- spec/mongo_profiler_spec.rb
|
223
|
+
- spec/mongoid.yml
|
227
224
|
- spec/spec_helper.rb
|
data/config.ru
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'pry-byebug'
|
2
|
-
require 'mongo'
|
3
|
-
require 'mongo_profiler'
|
4
|
-
|
5
|
-
require 'mongo_profiler/web'
|
6
|
-
|
7
|
-
CONNECTION = Mongo::MongoClient.new
|
8
|
-
DB = CONNECTION.db('mongo_profiler-database')
|
9
|
-
COLL = DB['example-collection']
|
10
|
-
|
11
|
-
MongoProfiler.connect('localhost', 27017, 'mongo_profiler-database')
|
12
|
-
|
13
|
-
run MongoProfiler::Web
|
@@ -1,38 +0,0 @@
|
|
1
|
-
Mongo::Cursor.class_eval do
|
2
|
-
alias_method :original_send_initial_query, :send_initial_query
|
3
|
-
|
4
|
-
def send_initial_query
|
5
|
-
beginning_time = Time.now
|
6
|
-
original_send_initial_query
|
7
|
-
total_time = Time.now - beginning_time
|
8
|
-
begin
|
9
|
-
_caller = MongoProfiler::Caller.new(caller)
|
10
|
-
|
11
|
-
return if MongoProfiler.should_skip?(instrument_payload, _caller) || MongoProfiler.disabled?
|
12
|
-
|
13
|
-
result = {}
|
14
|
-
|
15
|
-
result[:total_time] = total_time
|
16
|
-
|
17
|
-
# the payload sent to mongo
|
18
|
-
result[:instrument_payload] = JSON.dump(instrument_payload)
|
19
|
-
|
20
|
-
result[:file] = _caller.file
|
21
|
-
result[:line] = _caller.line
|
22
|
-
result[:method] = _caller.method
|
23
|
-
|
24
|
-
result[:extra_attrs] = MongoProfiler.extra_attrs
|
25
|
-
|
26
|
-
# TODO rename `_caller` object instance to something more meaningful in this context
|
27
|
-
result[:backtrace] = _caller._caller
|
28
|
-
|
29
|
-
MongoProfiler.log(result)
|
30
|
-
|
31
|
-
if stats_client = MongoProfiler.stats_client
|
32
|
-
stats_client.populate(_caller, total_time)
|
33
|
-
end
|
34
|
-
rescue => e
|
35
|
-
p "MongoProfiler: #{e.message}"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module MongoProfiler
|
2
|
-
class Payload
|
3
|
-
attr_reader :payload
|
4
|
-
|
5
|
-
def initialize(payload)
|
6
|
-
@payload = (payload || {}).dup.with_indifferent_access
|
7
|
-
end
|
8
|
-
|
9
|
-
def system_database?
|
10
|
-
!payload['database'].to_s.match(/^admin|system/).nil?
|
11
|
-
end
|
12
|
-
|
13
|
-
def system_collection?
|
14
|
-
!payload['collection'].to_s.match(/^mongo_|system/).nil?
|
15
|
-
end
|
16
|
-
|
17
|
-
def system_count?
|
18
|
-
!payload['selector'].to_h['count'].to_s.match(/^mongo_|system/).nil?
|
19
|
-
end
|
20
|
-
|
21
|
-
def system_distinct?
|
22
|
-
!payload['selector'].to_h['distinct'].to_s.match(/^mongo_|system/).nil?
|
23
|
-
end
|
24
|
-
|
25
|
-
def system_command?
|
26
|
-
payload['collection'] == '$cmd' && !(payload['selector'].to_h.has_key?('count') || payload['selector'].to_h.has_key?('distinct'))
|
27
|
-
end
|
28
|
-
|
29
|
-
def system_any?
|
30
|
-
system_database? || system_collection? || system_count? || system_distinct? || system_command?
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/lib/mongo_profiler/stats.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module MongoProfiler
|
2
|
-
class Stats
|
3
|
-
def initialize(stats_client)
|
4
|
-
@stats_client = stats_client
|
5
|
-
end
|
6
|
-
|
7
|
-
def populate(_caller, total_time)
|
8
|
-
file = sanitaze_stat_key _caller.file.split('/').last
|
9
|
-
method = sanitaze_stat_key _caller.method
|
10
|
-
|
11
|
-
stat_name = "mongo_profiler.#{MongoProfiler.application_name}.#{file}.#{method}"
|
12
|
-
|
13
|
-
total_time_ms = total_time * 1000
|
14
|
-
|
15
|
-
@stats_client.increment stat_name
|
16
|
-
@stats_client.timing stat_name, total_time_ms
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def sanitaze_stat_key(key)
|
22
|
-
key.gsub(/\W/, '_')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Mongo::Cursor' do
|
4
|
-
let(:collection) { COLL }
|
5
|
-
|
6
|
-
describe '#send_initial_query' do
|
7
|
-
context 'when enabled' do
|
8
|
-
let(:stats_client) { double('StatsD client').as_null_object }
|
9
|
-
|
10
|
-
before do
|
11
|
-
MongoProfiler.enable!
|
12
|
-
|
13
|
-
MongoProfiler.stats_client = stats_client
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'populate stats' do
|
17
|
-
expect(stats_client).to receive(:increment).with(any_args)
|
18
|
-
expect(stats_client).to receive(:timing).with(any_args)
|
19
|
-
|
20
|
-
collection.find_one
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'creates a profiler entry' do
|
24
|
-
expect {
|
25
|
-
collection.insert(test: 'test')
|
26
|
-
expect(collection.find_one['test']).to eq 'test'
|
27
|
-
}.to change { MongoProfiler.collection.count }.by(1)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'when disabled' do
|
32
|
-
before { MongoProfiler.disable! }
|
33
|
-
|
34
|
-
it 'skips profiler entry' do
|
35
|
-
expect {
|
36
|
-
collection.insert(test: 'test')
|
37
|
-
expect(collection.find_one['test']).to eq 'test'
|
38
|
-
}.to_not change { MongoProfiler.collection.count }
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module MongoProfiler
|
4
|
-
describe Payload do
|
5
|
-
subject { described_class.new(payload) }
|
6
|
-
|
7
|
-
describe '#system_database?' do
|
8
|
-
context 'when admin' do
|
9
|
-
let(:payload) { { database: 'admin' } }
|
10
|
-
|
11
|
-
its(:system_database?) { should be_true }
|
12
|
-
end
|
13
|
-
|
14
|
-
context 'when system' do
|
15
|
-
let(:payload) { { database: 'system' } }
|
16
|
-
|
17
|
-
its(:system_database?) { should be_true }
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'when other' do
|
21
|
-
let(:payload) { { database: 'other' } }
|
22
|
-
|
23
|
-
its(:system_database?) { should be_false }
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe '#system_collection?' do
|
28
|
-
context 'when admin' do
|
29
|
-
let(:payload) { { collection: 'mongo_profiler' } }
|
30
|
-
|
31
|
-
its(:system_collection?) { should be_true }
|
32
|
-
end
|
33
|
-
|
34
|
-
context 'when system' do
|
35
|
-
let(:payload) { { collection: 'system' } }
|
36
|
-
|
37
|
-
its(:system_collection?) { should be_true }
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'when other' do
|
41
|
-
let(:payload) { { collection: 'other' } }
|
42
|
-
|
43
|
-
its(:system_collection?) { should be_false }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
describe '#system_count?' do
|
48
|
-
context 'when admin' do
|
49
|
-
let(:payload) { { selector: { count: 'mongo_profiler' } } }
|
50
|
-
|
51
|
-
its(:system_count?) { should be_true }
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'when system' do
|
55
|
-
let(:payload) { { selector: { count: 'system' } } }
|
56
|
-
|
57
|
-
its(:system_count?) { should be_true }
|
58
|
-
end
|
59
|
-
|
60
|
-
context 'when other' do
|
61
|
-
let(:payload) { { selector: { count: 'other' } } }
|
62
|
-
|
63
|
-
its(:system_count?) { should be_false }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe '#system_distinct?' do
|
68
|
-
context 'when admin' do
|
69
|
-
let(:payload) { { selector: { distinct: 'mongo_profiler' } } }
|
70
|
-
|
71
|
-
its(:system_distinct?) { should be_true }
|
72
|
-
end
|
73
|
-
|
74
|
-
context 'when system' do
|
75
|
-
let(:payload) { { selector: { distinct: 'system' } } }
|
76
|
-
|
77
|
-
its(:system_distinct?) { should be_true }
|
78
|
-
end
|
79
|
-
|
80
|
-
context 'when other' do
|
81
|
-
let(:payload) { { selector: { distinct: 'other' } } }
|
82
|
-
|
83
|
-
its(:system_distinct?) { should be_false }
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
describe '#system_command?' do
|
88
|
-
context 'when count' do
|
89
|
-
let(:payload) { { collection: '$cmd', selector: { count: 'something' } } }
|
90
|
-
|
91
|
-
its(:system_command?) { should be_false }
|
92
|
-
end
|
93
|
-
|
94
|
-
context 'when distinct' do
|
95
|
-
let(:payload) { { collection: '$cmd', selector: { distinct: 'something' } } }
|
96
|
-
|
97
|
-
its(:system_command?) { should be_false }
|
98
|
-
end
|
99
|
-
|
100
|
-
context 'when other' do
|
101
|
-
let(:payload) { { collection: '$cmd', selector: { getnonce: -1 } } }
|
102
|
-
|
103
|
-
its(:system_command?) { should be_true }
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
describe '#system_any?' do
|
108
|
-
pending
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module MongoProfiler
|
4
|
-
describe Stats do
|
5
|
-
let(:stats_client) { double 'StatsD Client' }
|
6
|
-
subject { Stats.new(stats_client) }
|
7
|
-
|
8
|
-
before { MongoProfiler.application_name = 'project' }
|
9
|
-
|
10
|
-
describe '#populate' do
|
11
|
-
let(:_caller) { MongoProfiler::Caller.new(backtrace) }
|
12
|
-
let(:backtrace) {
|
13
|
-
[
|
14
|
-
"/Users/pablo/workspace/project/spec/mongo_ruby_profiler_spec.rb:7:in `new'",
|
15
|
-
"/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'",
|
16
|
-
"/Users/pablo/.gem/ruby/2.0.0/gems/rspec-core-2.14.4/lib/rspec/core/memoized_helpers.rb:199:in `fetch'",
|
17
|
-
"/Users/pablo/.gem/ruby/2.0.0/gems/rspec-core-2.14.4/lib/rspec/core/memoized_helpers.rb:199:in `block in let'"
|
18
|
-
]
|
19
|
-
}
|
20
|
-
|
21
|
-
it 'populates increment and timing' do
|
22
|
-
expect(stats_client).to receive(:increment).with('mongo_profiler.project.mongo_ruby_profiler_spec_rb.new')
|
23
|
-
expect(stats_client).to receive(:timing).with('mongo_profiler.project.mongo_ruby_profiler_spec_rb.new', 5000)
|
24
|
-
|
25
|
-
subject.populate(_caller, 5)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/web/views/group_id.erb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
<h2 class="page-header">Group Id: <%= @group_id %></h2>
|
2
|
-
|
3
|
-
<% if @sample_profile %>
|
4
|
-
<div class="row">
|
5
|
-
<table class="table table-bordered">
|
6
|
-
<thead>
|
7
|
-
<th>Application</th>
|
8
|
-
<th>Total Queries</th>
|
9
|
-
<th>Total Time</th>
|
10
|
-
<th>Avg Total Time</th>
|
11
|
-
<% @sample_profile['extra_attrs'].to_h.keys.each do |key| %>
|
12
|
-
<th><%= key %></th>
|
13
|
-
<% end %>
|
14
|
-
</thead>
|
15
|
-
<tbody>
|
16
|
-
<tr>
|
17
|
-
<td><%= @sample_profile['application_name'] %></td>
|
18
|
-
<td><%= @profiles_count %></td>
|
19
|
-
<td><%= @profiles_total_time %></td>
|
20
|
-
<td><%= @profiles_total_time / @profiles_count %></td>
|
21
|
-
<% @sample_profile['extra_attrs'].to_h.keys.each do |key| %>
|
22
|
-
<td><%= @sample_profile['extra_attrs'][key.to_s] %></td>
|
23
|
-
<% end %>
|
24
|
-
</tr>
|
25
|
-
</tbody>
|
26
|
-
</table>
|
27
|
-
</div>
|
28
|
-
<% end %>
|
29
|
-
|
30
|
-
<div class="row">
|
31
|
-
<table class="table table-bordered">
|
32
|
-
<thead>
|
33
|
-
<th>File Name</th>
|
34
|
-
<th>Method</th>
|
35
|
-
<th>Total Time</th>
|
36
|
-
<th>Instrument Payload</th>
|
37
|
-
<th> </th>
|
38
|
-
</thead>
|
39
|
-
<% @grouped_profiles.each_with_index do |(method, profiles), grouped_index| %>
|
40
|
-
<tbody class="<%= (grouped_index % 2 == 0) ? 'tbody-gray' : '' %>">
|
41
|
-
<% profiles.each do |profile| %>
|
42
|
-
<tr>
|
43
|
-
<td><%= profile['file'].split('/').last %>:<%= profile['line'] %></td>
|
44
|
-
<td><%= profile['method'] %></td>
|
45
|
-
<td><%= profile['total_time'] %></td>
|
46
|
-
<td><pre><code><%= profile['instrument_payload'] %></code></pre></td>
|
47
|
-
<td><a href="<%= root_path %>profiler/<%= profile['_id'] %>" class="btn btn-default"><span class="glyphicon glyphicon-eye-open"></span></td>
|
48
|
-
</tr>
|
49
|
-
<% end %>
|
50
|
-
</tbody>
|
51
|
-
<% end %>
|
52
|
-
</table>
|
53
|
-
</div>
|