├── .gitignore ├── Gemfile ├── README.md ├── Rakefile ├── lib ├── platform.rb └── slowgrowl.rb └── slowgrowl.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | .gem 2 | .swp 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | require 'lib/platform' 2 | 3 | source :gemcutter 4 | 5 | SlowGrowl::GEMS.each do |dep| 6 | gem dep[:name], :require => (dep[:require] || dep[:name]) 7 | end 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SlowGrowl 2 | ========= 3 | 4 | Rails 3 plugin which surfaces slow code paths in your Rails application by integrating with the new Notifications API in Rails 3 with your system Growl (OSX) or libnotify (Linux) notification service. By default, any activity which takes longer than one second, will generate a growl alert, with the description of the action, time taken, and other meta data. A preview in action: 5 | 6 | ![slowgrowl notification](http://img.skitch.com/20100804-8w1wte8bad7tby418kmucs4hsm.png) 7 | 8 | Integrating with Rails 3 9 | ------------------------ 10 | 11 | # in your Gemfile 12 | group :development do 13 | gem 'slowgrowl' 14 | end 15 | 16 | That's it. 17 | 18 | Optional Configuration for SlowGrowl 19 | ------------------------------------ 20 | 21 | # in your config/environments/development.rb 22 | config.slowgrowl.warn = 1000 # growl any action which takes > 1000ms (1s) 23 | config.slowgrowl.sticky = true # make really slow (2x warn) alerts sticky 24 | 25 | Resources 26 | --------- 27 | 28 | * [Rails 3 Internals: Railtie & Creating Plugins](http://www.igvita.com/2010/08/04/rails-3-internals-railtie-creating-plugins/) 29 | * [Rails 3 Notifications API](http://edgeapi.rubyonrails.org/classes/ActiveSupport/Notifications.html) 30 | * [Railties](http://edgeapi.rubyonrails.org/classes/Rails/Railtie.html) 31 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | 3 | Bundler::GemHelper.install_tasks -------------------------------------------------------------------------------- /lib/platform.rb: -------------------------------------------------------------------------------- 1 | module SlowGrowl 2 | if RUBY_PLATFORM =~ /linux/i 3 | NOTIFIER = :libnotify 4 | GEMS = [{:name => 'gtk2'}, {:name => 'ruby-libnotify', :require => 'rnotify'}] 5 | else 6 | NOTIFIER = :growl 7 | GEMS = [{:name => 'growl'}] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/slowgrowl.rb: -------------------------------------------------------------------------------- 1 | require 'platform' 2 | 3 | SlowGrowl::GEMS.each do |dep| 4 | require (dep[:require] || dep[:name]) 5 | end 6 | 7 | module SlowGrowl 8 | class Railtie < Rails::Railtie 9 | config.slowgrowl = ActiveSupport::OrderedOptions.new 10 | config.slowgrowl.warn = 1000 # default slow alert set to 1000ms 11 | config.slowgrowl.sticky = false # should error warnings be sticky? 12 | config.slowgrowl.debug = false # print debug information 13 | 14 | initializer "slowgrowl.initialize" do |app| 15 | ActiveSupport::Notifications.subscribe do |*args| 16 | 17 | if NOTIFIER 18 | event = ActiveSupport::Notifications::Event.new(*args) 19 | 20 | sticky = false 21 | action, type = event.name.split('.') 22 | alert = case event.duration 23 | when (0...config.slowgrowl.warn) then 24 | false 25 | when (config.slowgrowl.warn..config.slowgrowl.warn*2) then 26 | :warning 27 | else 28 | sticky = config.slowgrowl.sticky 29 | :error 30 | end 31 | 32 | begin 33 | e = event.payload 34 | message = case type 35 | when 'action_controller' then 36 | case action 37 | when 'process_action' then 38 | # {:controller=>"WidgetsController", :action=>"index", :params=>{"controller"=>"widgets", "action"=>"index"}, 39 | # :formats=>[:html], :method=>"GET", :path=>"/widgets", :status=>200, :view_runtime=>52.25706100463867, 40 | # :db_runtime=>0} 41 | 42 | 43 | if e[:exception] 44 | "%s#%s.\n\n%s" % [ 45 | e[:controller], e[:action], e[:exception].join(', ') 46 | ] 47 | else 48 | "%s#%s (%s).\nDB: %.1f, View: %.1f" % [ 49 | e[:controller], e[:action], e[:status], (e[:db_runtime] || 0), (e[:view_runtime] || 0) 50 | ] 51 | end 52 | 53 | else 54 | '%s#%s (%s)' % [e[:controller], e[:action], e[:status]] 55 | end 56 | 57 | when 'action_view' then 58 | # {:identifier=>"text template", :layout=>nil } 59 | '%s, layout: %s' % [e[:identifier], e[:layout].nil? ? 'none' : e[:layout]] 60 | 61 | when 'active_record' then 62 | # {:sql=>"SELECT "widgets".* FROM "widgets", :name=>"Widget Load", :connection_id=>2159415800} 63 | "%s\n\n%s" % [e[:name], e[:sql].gsub("\n", ' ').squeeze(' ')] 64 | else 65 | 'Duration: %.1f' % [event.duration] if event.respond_to? :duration 66 | end 67 | 68 | if alert 69 | title = "%1.fms - %s : %s" % [event.duration, action.humanize, type.camelize] 70 | 71 | case NOTIFIER 72 | when :growl 73 | if Growl.installed? 74 | Growl.send("notify_#{alert}", message, {:title => title, :sticky => sticky}) 75 | end 76 | 77 | when :libnotify 78 | Notify::Notification.new(title, message, nil, nil).show 79 | end 80 | end 81 | rescue Exception => e 82 | if config.slowgrowl.debug 83 | puts e 84 | puts event.inspect 85 | puts e.backtrace.join("\n") 86 | puts "-- If you're seeing this, you should probably open a ticket on github :-)" 87 | end 88 | end 89 | 90 | end 91 | end 92 | 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /slowgrowl.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = "slowgrowl" 5 | s.version = "0.1.3" 6 | 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ["Ilya Grigorik", "Milan Dobrota"] 9 | s.email = ["ilya@igvita.com"] 10 | s.homepage = "http://github.com/igrigorik/slowgrowl" 11 | s.summary = "Surface slow code paths in your Rails 3 app via Growl / libnotify" 12 | s.description = s.summary 13 | s.rubyforge_project = "slowgrowl" 14 | 15 | s.files = `git ls-files`.split("\n") 16 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 17 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 18 | s.require_paths = ["lib"] 19 | end --------------------------------------------------------------------------------