nunes 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +150 -0
- data/lib/nunes.rb +39 -0
- data/lib/nunes/adapter.rb +56 -0
- data/lib/nunes/adapters/default.rb +10 -0
- data/lib/nunes/adapters/memory.rb +62 -0
- data/lib/nunes/adapters/timing_aliased.rb +17 -0
- data/lib/nunes/instrumentable.rb +102 -0
- data/lib/nunes/subscriber.rb +69 -0
- data/lib/nunes/subscribers/action_controller.rb +80 -0
- data/lib/nunes/subscribers/action_mailer.rb +33 -0
- data/lib/nunes/subscribers/action_view.rb +44 -0
- data/lib/nunes/subscribers/active_record.rb +33 -0
- data/lib/nunes/subscribers/active_support.rb +58 -0
- data/lib/nunes/subscribers/nunes.rb +24 -0
- data/lib/nunes/version.rb +3 -0
- data/nunes.gemspec +22 -0
- data/script/bootstrap +21 -0
- data/script/test +25 -0
- data/script/watch +30 -0
- data/test/adapter_test.rb +40 -0
- data/test/adapters/default_test.rb +26 -0
- data/test/adapters/timing_aliased_test.rb +26 -0
- data/test/cache_instrumentation_test.rb +79 -0
- data/test/controller_instrumentation_test.rb +88 -0
- data/test/fake_udp_socket_test.rb +26 -0
- data/test/helper.rb +25 -0
- data/test/instrumentable_test.rb +100 -0
- data/test/mailer_instrumentation_test.rb +26 -0
- data/test/model_instrumentation_test.rb +55 -0
- data/test/nunes_test.rb +20 -0
- data/test/rails_app/.gitignore +15 -0
- data/test/rails_app/Rakefile +7 -0
- data/test/rails_app/app/assets/images/rails.png +0 -0
- data/test/rails_app/app/assets/javascripts/application.js +15 -0
- data/test/rails_app/app/assets/stylesheets/application.css +13 -0
- data/test/rails_app/app/controllers/application_controller.rb +3 -0
- data/test/rails_app/app/controllers/posts_controller.rb +28 -0
- data/test/rails_app/app/helpers/application_helper.rb +2 -0
- data/test/rails_app/app/mailers/.gitkeep +0 -0
- data/test/rails_app/app/mailers/post_mailer.rb +11 -0
- data/test/rails_app/app/models/.gitkeep +0 -0
- data/test/rails_app/app/models/post.rb +2 -0
- data/test/rails_app/app/views/layouts/application.html.erb +14 -0
- data/test/rails_app/app/views/post_mailer/created.text.erb +1 -0
- data/test/rails_app/app/views/posts/_post.html.erb +1 -0
- data/test/rails_app/app/views/posts/index.html.erb +5 -0
- data/test/rails_app/config.ru +4 -0
- data/test/rails_app/config/application.rb +67 -0
- data/test/rails_app/config/boot.rb +6 -0
- data/test/rails_app/config/database.yml +6 -0
- data/test/rails_app/config/environment.rb +5 -0
- data/test/rails_app/config/environments/development.rb +31 -0
- data/test/rails_app/config/environments/production.rb +64 -0
- data/test/rails_app/config/environments/test.rb +35 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/force_test_schema_load.rb +3 -0
- data/test/rails_app/config/initializers/inflections.rb +15 -0
- data/test/rails_app/config/initializers/mime_types.rb +5 -0
- data/test/rails_app/config/initializers/secret_token.rb +7 -0
- data/test/rails_app/config/initializers/session_store.rb +8 -0
- data/test/rails_app/config/initializers/wrap_parameters.rb +10 -0
- data/test/rails_app/config/locales/en.yml +5 -0
- data/test/rails_app/config/routes.rb +8 -0
- data/test/rails_app/db/migrate/20130417154459_create_posts.rb +8 -0
- data/test/rails_app/db/schema.rb +22 -0
- data/test/rails_app/db/seeds.rb +7 -0
- data/test/rails_app/lib/assets/.gitkeep +0 -0
- data/test/rails_app/lib/tasks/.gitkeep +0 -0
- data/test/rails_app/public/404.html +26 -0
- data/test/rails_app/public/422.html +26 -0
- data/test/rails_app/public/500.html +25 -0
- data/test/rails_app/public/favicon.ico +0 -0
- data/test/rails_app/public/index.html +241 -0
- data/test/rails_app/public/robots.txt +5 -0
- data/test/rails_app/script/rails +6 -0
- data/test/subscriber_test.rb +50 -0
- data/test/support/adapter_test_helpers.rb +33 -0
- data/test/support/fake_udp_socket.rb +50 -0
- data/test/view_instrumentation_test.rb +30 -0
- metadata +205 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require "active_support/notifications"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
class Subscriber
|
5
|
+
# Private: The bang character that is the first char of some events.
|
6
|
+
BANG = '!'
|
7
|
+
|
8
|
+
# Public: Setup a subscription for the subscriber using the
|
9
|
+
# provided adapter.
|
10
|
+
#
|
11
|
+
# adapter - The adapter instance to send instrumentation to.
|
12
|
+
def self.subscribe(adapter, options = {})
|
13
|
+
subscriber = options.fetch(:subscriber) { ActiveSupport::Notifications }
|
14
|
+
subscriber.subscribe pattern, new(adapter)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.pattern
|
18
|
+
raise "Not Implemented, override in subclass and provide a regex or string."
|
19
|
+
end
|
20
|
+
|
21
|
+
# Private: The adapter to send instrumentation to.
|
22
|
+
attr_reader :adapter
|
23
|
+
|
24
|
+
# Internal: Initializes a new instance.
|
25
|
+
#
|
26
|
+
# adapter - The adapter instance to send instrumentation to.
|
27
|
+
def initialize(adapter)
|
28
|
+
@adapter = Nunes::Adapter.wrap(adapter)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Private: Dispatcher that converts incoming events to method calls.
|
32
|
+
def call(name, start, ending, transaction_id, payload)
|
33
|
+
# rails doesn't recommend instrumenting methods that start with bang
|
34
|
+
# when in production
|
35
|
+
return if name.starts_with?(BANG)
|
36
|
+
|
37
|
+
method_name = name.split('.').first
|
38
|
+
|
39
|
+
if respond_to?(method_name)
|
40
|
+
send(method_name, start, ending, transaction_id, payload)
|
41
|
+
else
|
42
|
+
$stderr.puts "#{self.class.name} did not respond to #{method_name} therefore it cannot instrument the event named #{name}."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Internal: Increment a metric for the client.
|
47
|
+
#
|
48
|
+
# metric - The String name of the metric to increment.
|
49
|
+
#
|
50
|
+
# Returns nothing.
|
51
|
+
def increment(metric)
|
52
|
+
if @adapter
|
53
|
+
@adapter.increment metric
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Internal: Track the timing of a metric for the client.
|
58
|
+
#
|
59
|
+
# metric - The String name of the metric.
|
60
|
+
# duration_in_ms - The Integer duration of the event in milliseconds.
|
61
|
+
#
|
62
|
+
# Returns nothing.
|
63
|
+
def timing(metric, duration_in_ms)
|
64
|
+
if @adapter
|
65
|
+
@adapter.timing metric, duration_in_ms
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "nunes/subscriber"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
module Subscribers
|
5
|
+
class ActionController < ::Nunes::Subscriber
|
6
|
+
# Private
|
7
|
+
Pattern = /\.action_controller\Z/
|
8
|
+
|
9
|
+
# Private: The namespace for events to subscribe to.
|
10
|
+
def self.pattern
|
11
|
+
Pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_action(start, ending, transaction_id, payload)
|
15
|
+
controller = payload[:controller].to_s.gsub('Controller', '').underscore
|
16
|
+
action = payload[:action]
|
17
|
+
status = payload[:status]
|
18
|
+
exception_info = payload[:exception]
|
19
|
+
|
20
|
+
format = payload[:format] || "all"
|
21
|
+
format = "all" if format == "*/*"
|
22
|
+
|
23
|
+
db_runtime = payload[:db_runtime]
|
24
|
+
db_runtime = db_runtime.round if db_runtime
|
25
|
+
|
26
|
+
view_runtime = payload[:view_runtime]
|
27
|
+
view_runtime = view_runtime.round if view_runtime
|
28
|
+
|
29
|
+
runtime = ((ending - start) * 1_000).round
|
30
|
+
|
31
|
+
timing "action_controller.runtime", runtime if runtime
|
32
|
+
timing "action_controller.view_runtime", view_runtime if view_runtime
|
33
|
+
timing "action_controller.db_runtime", db_runtime if db_runtime
|
34
|
+
|
35
|
+
increment "action_controller.format.#{format}" if format
|
36
|
+
increment "action_controller.status.#{status}" if status
|
37
|
+
|
38
|
+
if controller && action
|
39
|
+
namespace = "action_controller.#{controller}.#{action}"
|
40
|
+
|
41
|
+
timing "#{namespace}.runtime", runtime if runtime
|
42
|
+
timing "#{namespace}.view_runtime", view_runtime if view_runtime
|
43
|
+
timing "#{namespace}.db_runtime", db_runtime if db_runtime
|
44
|
+
end
|
45
|
+
|
46
|
+
if exception_info
|
47
|
+
exception_class, exception_message = exception_info
|
48
|
+
|
49
|
+
increment "action_controller.exception.#{exception_class}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##########################################################################
|
54
|
+
# All of the events below don't really matter. Most of them also go #
|
55
|
+
# through process_action. The only value that could be pulled from them #
|
56
|
+
# would be topk related which graphite doesn't do. #
|
57
|
+
##########################################################################
|
58
|
+
|
59
|
+
def start_processing(*)
|
60
|
+
# noop
|
61
|
+
end
|
62
|
+
|
63
|
+
def halted_callback(*)
|
64
|
+
# noop
|
65
|
+
end
|
66
|
+
|
67
|
+
def redirect_to(*)
|
68
|
+
# noop
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_file(*)
|
72
|
+
# noop
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_data(*)
|
76
|
+
# noop
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "nunes/subscriber"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
module Subscribers
|
5
|
+
class ActionMailer < ::Nunes::Subscriber
|
6
|
+
# Private
|
7
|
+
Pattern = /\.action_mailer\Z/
|
8
|
+
|
9
|
+
# Private: The namespace for events to subscribe to.
|
10
|
+
def self.pattern
|
11
|
+
Pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def deliver(start, ending, transaction_id, payload)
|
15
|
+
runtime = ((ending - start) * 1_000).round
|
16
|
+
mailer = payload[:mailer]
|
17
|
+
|
18
|
+
if mailer
|
19
|
+
timing "action_mailer.deliver.#{mailer.to_s.underscore}", runtime
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive(start, ending, transaction_id, payload)
|
24
|
+
runtime = ((ending - start) * 1_000).round
|
25
|
+
mailer = payload[:mailer]
|
26
|
+
|
27
|
+
if mailer
|
28
|
+
timing "action_mailer.receive.#{mailer.to_s.underscore}", runtime
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "nunes/subscriber"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
module Subscribers
|
5
|
+
class ActionView < ::Nunes::Subscriber
|
6
|
+
# Private
|
7
|
+
Pattern = /\.action_view\Z/
|
8
|
+
|
9
|
+
# Private: The namespace for events to subscribe to.
|
10
|
+
def self.pattern
|
11
|
+
Pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def render_template(start, ending, transaction_id, payload)
|
15
|
+
instrument_identifier payload[:identifier], start, ending
|
16
|
+
end
|
17
|
+
|
18
|
+
def render_partial(start, ending, transaction_id, payload)
|
19
|
+
instrument_identifier payload[:identifier], start, ending
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Private: Sends timing information about identifier event.
|
25
|
+
def instrument_identifier(identifier, start, ending)
|
26
|
+
if identifier.present?
|
27
|
+
runtime = ((ending - start) * 1_000).round
|
28
|
+
timing identifier_to_metric(identifier), runtime
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Private: Converts an identifier to a metric name. Strips out the rails
|
33
|
+
# root from the full path.
|
34
|
+
#
|
35
|
+
# identifier - The String full path to the template or partial.
|
36
|
+
def identifier_to_metric(identifier)
|
37
|
+
rails_root = ::Rails.root.to_s + '/'
|
38
|
+
view_path = identifier.gsub(rails_root, '')
|
39
|
+
metric = view_path.gsub('/', '.')
|
40
|
+
"action_view.#{metric}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "nunes/subscriber"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
module Subscribers
|
5
|
+
class ActiveRecord < ::Nunes::Subscriber
|
6
|
+
# Private
|
7
|
+
Pattern = /\.active_record\Z/
|
8
|
+
|
9
|
+
# Private: The namespace for events to subscribe to.
|
10
|
+
def self.pattern
|
11
|
+
Pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def sql(start, ending, transaction_id, payload)
|
15
|
+
runtime = ((ending - start) * 1_000).round
|
16
|
+
name = payload[:name]
|
17
|
+
sql = payload[:sql].to_s.strip
|
18
|
+
operation = sql.split(' ', 2).first.to_s.downcase
|
19
|
+
|
20
|
+
timing "active_record.sql", runtime
|
21
|
+
|
22
|
+
case operation
|
23
|
+
when "begin"
|
24
|
+
timing "active_record.sql.transaction_begin", runtime
|
25
|
+
when "commit"
|
26
|
+
timing "active_record.sql.transaction_commit", runtime
|
27
|
+
else
|
28
|
+
timing "active_record.sql.#{operation}", runtime
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "nunes/subscriber"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
module Subscribers
|
5
|
+
class ActiveSupport < ::Nunes::Subscriber
|
6
|
+
# Private
|
7
|
+
Pattern = /\.active_support\Z/
|
8
|
+
|
9
|
+
# Private: The namespace for events to subscribe to.
|
10
|
+
def self.pattern
|
11
|
+
Pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache_read(start, ending, transaction_id, payload)
|
15
|
+
super_operation = payload[:super_operation]
|
16
|
+
runtime = ((ending - start) * 1_000).round
|
17
|
+
|
18
|
+
case super_operation
|
19
|
+
when Symbol
|
20
|
+
timing "active_support.cache_#{super_operation}", runtime
|
21
|
+
else
|
22
|
+
timing "active_support.cache_read", runtime
|
23
|
+
end
|
24
|
+
|
25
|
+
hit = payload[:hit]
|
26
|
+
unless hit.nil?
|
27
|
+
hit_type = hit ? :hit : :miss
|
28
|
+
increment "active_support.cache_#{hit_type}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cache_generate(start, ending, transaction_id, payload)
|
33
|
+
runtime = ((ending - start) * 1_000).round
|
34
|
+
timing "active_support.cache_generate", runtime
|
35
|
+
end
|
36
|
+
|
37
|
+
def cache_fetch_hit(start, ending, transaction_id, payload)
|
38
|
+
runtime = ((ending - start) * 1_000).round
|
39
|
+
timing "active_support.cache_fetch_hit", runtime
|
40
|
+
end
|
41
|
+
|
42
|
+
def cache_write(start, ending, transaction_id, payload)
|
43
|
+
runtime = ((ending - start) * 1_000).round
|
44
|
+
timing "active_support.cache_write", runtime
|
45
|
+
end
|
46
|
+
|
47
|
+
def cache_delete(start, ending, transaction_id, payload)
|
48
|
+
runtime = ((ending - start) * 1_000).round
|
49
|
+
timing "active_support.cache_delete", runtime
|
50
|
+
end
|
51
|
+
|
52
|
+
def cache_exist?(start, ending, transaction_id, payload)
|
53
|
+
runtime = ((ending - start) * 1_000).round
|
54
|
+
timing "active_support.cache_exist", runtime
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "nunes/subscriber"
|
2
|
+
|
3
|
+
module Nunes
|
4
|
+
module Subscribers
|
5
|
+
class Nunes < ::Nunes::Subscriber
|
6
|
+
# Private
|
7
|
+
Pattern = /\.nunes\Z/
|
8
|
+
|
9
|
+
# Private: The namespace for events to subscribe to.
|
10
|
+
def self.pattern
|
11
|
+
Pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def instrument_method_time(start, ending, transaction_id, payload)
|
15
|
+
runtime = ((ending - start) * 1_000).round
|
16
|
+
metric = payload[:metric]
|
17
|
+
|
18
|
+
if metric
|
19
|
+
timing "#{metric}", runtime
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/nunes.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'nunes/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "nunes"
|
8
|
+
spec.version = Nunes::VERSION
|
9
|
+
spec.authors = ["John Nunemaker"]
|
10
|
+
spec.email = ["nunemaker@gmail.com"]
|
11
|
+
spec.description = %q{The friendly gem that instruments everything for you, like I would if I could.}
|
12
|
+
spec.summary = %q{The friendly gem that instruments everything for you, like I would if I could.}
|
13
|
+
spec.homepage = "https://github.com/jnunemaker/nunes"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
end
|
data/script/bootstrap
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#/ Usage: bootstrap [bundle options]
|
3
|
+
#/
|
4
|
+
#/ Bundle install the dependencies.
|
5
|
+
#/
|
6
|
+
#/ Examples:
|
7
|
+
#/
|
8
|
+
#/ bootstrap
|
9
|
+
#/ bootstrap --local
|
10
|
+
#/
|
11
|
+
|
12
|
+
set -e
|
13
|
+
cd $(dirname "$0")/..
|
14
|
+
|
15
|
+
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
|
16
|
+
grep '^#/' <"$0"| cut -c4-
|
17
|
+
exit 0
|
18
|
+
}
|
19
|
+
|
20
|
+
rm -rf .bundle/{binstubs,config}
|
21
|
+
bundle install --binstubs .bundle/binstubs --path .bundle --quiet "$@"
|
data/script/test
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#/ Usage: test [individual test file]
|
3
|
+
#/
|
4
|
+
#/ Bootstrap and run all tests or an individual test.
|
5
|
+
#/
|
6
|
+
#/ Examples:
|
7
|
+
#/
|
8
|
+
#/ # run all tests
|
9
|
+
#/ test
|
10
|
+
#/
|
11
|
+
#/ # run individual test
|
12
|
+
#/ test test/controller_instrumentation_test.rb
|
13
|
+
#/
|
14
|
+
|
15
|
+
set -e
|
16
|
+
cd $(dirname "$0")/..
|
17
|
+
|
18
|
+
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
|
19
|
+
grep '^#/' <"$0"| cut -c4-
|
20
|
+
exit 0
|
21
|
+
}
|
22
|
+
|
23
|
+
script/bootstrap && ruby -I lib -I test -r rubygems \
|
24
|
+
-e 'require "bundler/setup"' \
|
25
|
+
-e '(ARGV.empty? ? Dir["test/**/*_test.rb"] : ARGV).each { |f| load f }' -- "$@"
|
data/script/watch
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#/ Usage: watch
|
3
|
+
#/
|
4
|
+
#/ Run the tests whenever any relevant files change.
|
5
|
+
#/
|
6
|
+
|
7
|
+
require "pathname"
|
8
|
+
require "rubygems"
|
9
|
+
require "bundler"
|
10
|
+
Bundler.setup :watch
|
11
|
+
|
12
|
+
# Put us where we belong, in the root dir of the project.
|
13
|
+
Dir.chdir Pathname.new(__FILE__).realpath + "../.."
|
14
|
+
|
15
|
+
# Run the tests to start.
|
16
|
+
system "clear; script/test"
|
17
|
+
|
18
|
+
require "rb-fsevent"
|
19
|
+
|
20
|
+
IgnoreRegex = /\/log|db/
|
21
|
+
|
22
|
+
fs = FSEvent.new
|
23
|
+
fs.watch ["lib", "test"], latency: 1 do |args|
|
24
|
+
unless args.first =~ IgnoreRegex
|
25
|
+
system "clear"
|
26
|
+
puts "#{args.first} changed..."
|
27
|
+
system "script/test"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
fs.run
|