nunes 0.1.0
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.
- 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
|