├── Rakefile ├── screenshot.png ├── lib ├── peek-moped │ ├── version.rb │ └── railtie.rb ├── peek-moped.rb └── peek │ └── views │ └── moped.rb ├── Gemfile ├── .gitignore ├── CHANGELOG.md ├── README.md ├── peek-moped.gemspec ├── LICENSE.txt └── app └── views └── peek └── views └── _moped.html.erb /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preact/peek-moped/master/screenshot.png -------------------------------------------------------------------------------- /lib/peek-moped/version.rb: -------------------------------------------------------------------------------- 1 | module Peek 2 | module Moped 3 | VERSION = '1.0.3' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in peek-moped.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/peek-moped.rb: -------------------------------------------------------------------------------- 1 | require 'peek-moped/version' 2 | require 'peek/views/moped' 3 | require 'peek-moped/railtie' 4 | -------------------------------------------------------------------------------- /lib/peek-moped/railtie.rb: -------------------------------------------------------------------------------- 1 | module Peek 2 | module Moped 3 | class Railtie < ::Rails::Engine 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | *.sassc 3 | .sass-cache 4 | capybara-*.html 5 | .rspec 6 | .rvmrc 7 | /.bundle 8 | /vendor/bundle 9 | /log/* 10 | /tmp/* 11 | /db/*.sqlite3 12 | /public/system/* 13 | /coverage/ 14 | /spec/tmp/* 15 | **.orig 16 | rerun.txt 17 | pickle-email-*.html 18 | .project 19 | config/initializers/secret_token.rb 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.3 2 | - Fix undefined method 'database' for Moped::Protocol::KillCursors 3 | 4 | # 1.0.2 5 | 6 | - Write ObjectId in mongodb query format (previously was in binary) 7 | - Replace $cmd on proper command name which sending to Mongo 8 | - Colorize and write system DB in collection names 9 | 10 | # 1.0.1 11 | 12 | - Clearify collection names 13 | - Add white background 14 | 15 | # 1.0.0 16 | 17 | - Initial release. 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Peek::Moped 2 | 3 | Take a peek into the Mongoid and Moped commands made within your Rails application. 4 | 5 | ![Preview](/screenshot.png) 6 | 7 | Things this peek view provides: 8 | 9 | - Total number of Mongo commands called during the request 10 | - The duration of the calls made during the request 11 | - Profiling each command with app stacktrace 12 | 13 | ## Installation 14 | 15 | Add this line to your application's Gemfile: 16 | 17 | gem 'peek-moped' 18 | 19 | And then execute: 20 | 21 | $ bundle 22 | 23 | Or install it yourself as: 24 | 25 | $ gem install peek-moped 26 | 27 | ## Usage 28 | 29 | Add the following to your `config/initializers/peek.rb`: 30 | 31 | ```ruby 32 | Peek.into Peek::Views::Moped 33 | ``` 34 | 35 | ## Contributing 36 | 37 | 1. Fork it 38 | 2. Create your feature branch (`git checkout -b my-new-feature`) 39 | 3. Commit your changes (`git commit -am 'Add some feature'`) 40 | 4. Push to the branch (`git push origin my-new-feature`) 41 | 5. Create new Pull Request 42 | -------------------------------------------------------------------------------- /peek-moped.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'peek-moped/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = 'peek-moped' 8 | gem.version = Peek::Moped::VERSION 9 | gem.authors = ['Pavel Chertorogov'] 10 | gem.email = ['pavel.chertorogov@gmail.com'] 11 | gem.description = %q{Take a peek into the Moped and Mongoid commands made within your Rails application.} 12 | gem.summary = %q{Take a peek into the Moped and Mongoid commands made within your Rails application.} 13 | gem.homepage = 'https://github.com/nodkz/peek-moped' 14 | 15 | gem.files = `git ls-files`.split($/) 16 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 17 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 18 | gem.require_paths = ['lib'] 19 | 20 | gem.add_dependency 'peek' 21 | gem.add_dependency 'moped', '>= 1.5.1' 22 | gem.add_dependency 'atomic', '>= 1.0.0' 23 | end 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Pavel Chertorogov 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /app/views/peek/views/_moped.html.erb: -------------------------------------------------------------------------------- 1 | 2 | ... 3 | / 4 | ... 5 | moped 6 | 7 | 8 | 48 |
49 | 50 | <% duration = view.duration %> 51 | <% cmd_num = 0 %> 52 | <% view.operations.each do |row| %> 53 | 54 | 55 | 56 | 71 | 72 | <% end %> 73 |
<%= row[0] %> 0.1 ? 'style=color:red':'' %>><%= "%.2fms" % (row[1] * 1000) %> 57 | <% row[2].each do |cmd| %> 58 |
59 | <% code_desc = view.op_code_info cmd.op_code %> 60 | <%= code_desc[0] %> 61 | 62 | <%= raw view.cmd_collection_name cmd %> 63 | 64 |
<%= 65 | cmd.to_yaml.gsub(/\!ruby\/object\:Moped\:\:BSON\:\:ObjectId\s*raw_data:.*\n\s*([^\n]*)/) {|capture| "ObjectId(\"#{$1.unpack("m")[0].unpack("H*")[0].force_encoding(Moped::BSON::UTF8_ENCODING)}\")"} rescue "" 66 | %>
67 |
APP STACK:<%= "\n#{row[3].join("\n")}" %>
68 |
69 | <% end %> 70 |
74 |
75 | <% if false %> 76 | ... 77 | <% end %> 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /lib/peek/views/moped.rb: -------------------------------------------------------------------------------- 1 | require 'moped' 2 | require 'atomic' 3 | 4 | class Moped::Node 5 | class << self 6 | attr_accessor :command_time, :command_count, :command_operations 7 | end 8 | self.command_count = Atomic.new(0) 9 | self.command_time = Atomic.new(0) 10 | self.command_operations = Atomic.new([]) 11 | 12 | def logging_with_peek(*args, &block) 13 | start = Time.now 14 | logging_without_peek(*args, &block) 15 | ensure 16 | duration = (Time.now - start) 17 | Moped::Node.command_time.update { |value| value + duration } 18 | Moped::Node.command_count.update { |value| value + 1 } 19 | Moped::Node.command_operations.update { |value| value << [Moped::Node.command_count.get, duration, args[0], peek_app_backtrace] } 20 | end 21 | alias_method_chain :logging, :peek 22 | 23 | def peek_app_backtrace 24 | root_path = Rails.root.to_s 25 | caller.grep(%r{^#{root_path}/(app|lib)}) { |v| v.sub(root_path, '') } 26 | end 27 | end 28 | 29 | module Peek 30 | module Views 31 | class Moped < View 32 | OP_CODES = { 33 | '1' => ['*', 'Reply to a client request. responseTo is set'], 34 | '1000' => ['*', 'Generic msg command followed by a string'], 35 | '2001' => ['U', 'Update document'], 36 | '2002' => ['I', 'Insert new document'], 37 | '2003' => ['*', 'Formerly used for OP_GET_BY_OID'], 38 | '2004' => ['R', 'Query a collection'], 39 | '2005' => ['RR','Get more data from a query'], 40 | '2006' => ['D', 'Delete documents'], 41 | '2007' => ['*', 'Tell database client is done with a cursor'] 42 | } 43 | 44 | def duration 45 | ::Moped::Node.command_time.value 46 | end 47 | 48 | def op_code_info op_code 49 | key = op_code.to_s 50 | if OP_CODES.has_key? key 51 | OP_CODES[key] 52 | else 53 | [key, 'Undescribed op_code of mongodb wired protocol'] 54 | end 55 | end 56 | 57 | def cmd_collection_name cmd 58 | if not cmd.respond_to? :database 59 | name = "#{cmd.class.name} " 60 | elsif %w(admin config local).include? cmd.database 61 | name = %Q(#{cmd.database}.) 62 | else 63 | name = '' 64 | end 65 | 66 | if cmd.respond_to? :collection 67 | if cmd.collection == '$cmd' 68 | if not cmd.respond_to? :selector 69 | name << '$cmd' 70 | elsif cmd.selector.has_key? :getlasterror 71 | name << 'getLastError()' 72 | elsif cmd.selector.has_key? :count 73 | name << "#{cmd.selector[:count]}.count()" 74 | elsif cmd.selector.has_key? :aggregate 75 | name << "#{cmd.selector[:aggregate]}.aggregate(#{cmd.selector[:pipeline].map{|p| p.keys}.flatten.join(', ')})" 76 | elsif cmd.selector.has_key? :mapreduce 77 | name << "#{cmd.selector[:mapreduce]}.mapReduce(#{cmd.selector[:out].to_s})" 78 | elsif cmd.selector.has_key? :findAndModify 79 | name << "#{cmd.selector[:findAndModify]}.findAndModify()" 80 | elsif cmd.selector.has_key? :geoNear 81 | name << "#{cmd.selector[:geoNear]}.geoNear()" 82 | elsif cmd.selector.has_key? :ismaster 83 | name << 'isMaster()' 84 | else 85 | name << '$cmd' 86 | end 87 | else 88 | name << cmd.collection 89 | end 90 | end 91 | 92 | name 93 | end 94 | 95 | def formatted_duration 96 | ms = duration * 1000 97 | if ms >= 1000 98 | "%.2fms" % ms 99 | else 100 | "%.0fms" % ms 101 | end 102 | end 103 | 104 | def calls 105 | ::Moped::Node.command_count.value 106 | end 107 | 108 | def operations 109 | ::Moped::Node.command_operations.value 110 | end 111 | 112 | def results 113 | { :duration => formatted_duration, :calls => calls, :operations => operations.inspect } 114 | end 115 | 116 | private 117 | 118 | def setup_subscribers 119 | # Reset each counter when a new request starts 120 | before_request do 121 | ::Moped::Node.command_time.value = 0 122 | ::Moped::Node.command_count.value = 0 123 | ::Moped::Node.command_operations.value = [] 124 | end 125 | end 126 | end 127 | end 128 | end --------------------------------------------------------------------------------