statsn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ spec/argv.txt
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - ree
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ gem "bump"
5
+ gem "rake"
6
+ gem "rspec", "~>2"
7
+ gem "json"
8
+ gem "faraday"
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ statsn (0.1.0)
5
+ newrelic_rpm (~> 3.5)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ bump (0.3.7)
11
+ diff-lcs (1.1.3)
12
+ faraday (0.8.4)
13
+ multipart-post (~> 1.1)
14
+ json (1.7.5)
15
+ multipart-post (1.1.5)
16
+ newrelic_rpm (3.5.3.25)
17
+ rake (0.9.2)
18
+ rspec (2.6.0)
19
+ rspec-core (~> 2.6.0)
20
+ rspec-expectations (~> 2.6.0)
21
+ rspec-mocks (~> 2.6.0)
22
+ rspec-core (2.6.4)
23
+ rspec-expectations (2.6.0)
24
+ diff-lcs (~> 1.1.2)
25
+ rspec-mocks (2.6.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bump
32
+ faraday
33
+ json
34
+ rake
35
+ rspec (~> 2)
36
+ statsn!
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "bump/tasks"
3
+
4
+ task :default do
5
+ sh "rspec spec/"
6
+ end
data/Readme.md ADDED
@@ -0,0 +1,55 @@
1
+ StatsN: Aggregate statistics using NewRelic's custom metrics.
2
+
3
+ Install
4
+ =======
5
+
6
+ gem install statsn
7
+
8
+ Usage
9
+ =====
10
+
11
+ ### Track
12
+
13
+ require "statsn"
14
+
15
+ Statsn.increment("Custom/Xyz") -> tracked as "Custom/Xyz"
16
+ Statsn.increment([Abc::Xyz, "update"]) -> tracked as "Custom/Abc/Xyz/update"
17
+ Statsn.increment([self, "update"]) -> tracked as "Custom/<class-you-are-in>/update"
18
+
19
+ Statsn.increment("Custom/Xyz", 5) -> increment by 5
20
+
21
+ Statsn.time("Custom/Abc") do
22
+ sleep 1
23
+ end
24
+
25
+ ### Analyze
26
+
27
+ # show all metrics and fields
28
+ curl -H "x-api-key:API_KEY" https://api.newrelic.com/api/v1/applications/12345/metrics.json -> list of all metrics and fields
29
+
30
+ gem install faraday json
31
+
32
+ # statsn-calls API-KEY ACCOUNT-ID APPLICATION-ID METRIC-NAME
33
+ statsn-calls asdhgjasdgjhdasjgahsdasdjghsdjhg 123 1234567 Controller/users/index
34
+
35
+ call_count: 274909.0
36
+ total_call_time: 37585.77862974794
37
+ response time: 0.1367208008095331
38
+ call per second: 3.18181712962963
39
+
40
+ # statsn-calls API-KEY ACCOUNT-ID APPLICATION-ID METRIC-NAME-PREFIX
41
+ statsn-calls asdhgjasdgjhdasjgahsdasdjghsdjhg 123 1234567 Controller/users/*
42
+
43
+ call_count: 287300.0
44
+ total_call_time: 39330.15688060733
45
+ response time: 0.13689577751690682
46
+ call per second: 3.3252314814814814
47
+
48
+ Author
49
+ ======
50
+ Original version by [Morten Primdahl](https://github.com/morten)<br/>
51
+
52
+ [Michael Grosser](http://grosser.it)<br/>
53
+ michael@grosser.it<br/>
54
+ License: MIT<br/>
55
+ [![Build Status](https://travis-ci.org/grosser/statsn.png)](https://travis-ci.org/grosser/statsn)
data/bin/statsn-calls ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ raise "Needs 1.9+" if RUBY_VERSION < "1.9.0"
4
+ PERIOD = 24*60*60
5
+ API_METRICS_LIMIT = 100
6
+
7
+ def stats(options)
8
+ response = Faraday.new(
9
+ :url => "https://api.newrelic.com/api/v1/accounts/#{ACCOUNT_ID}/applications/#{APP_ID}/data.json",
10
+ :params => options,
11
+ :headers => {"x-api-key" => API_KEY}
12
+ ).get
13
+ raise response.body unless response.status == 200
14
+ JSON.parse(response.body)
15
+ end
16
+
17
+ def aggregate(names, field)
18
+ names.each_slice(API_METRICS_LIMIT).map do |group|
19
+ stats(:metrics => group, :field => field, :begin => Time.now - PERIOD, :end => Time.now)
20
+ end.flatten(1)
21
+ end
22
+
23
+ def metrics
24
+ response = `curl -H "x-api-key:#{API_KEY}" https://api.newrelic.com/api/v1/applications/#{APP_ID}/metrics.json`
25
+ JSON.parse(response)
26
+ end
27
+
28
+ def parse_options
29
+ banner = nil
30
+ OptionParser.new do |opts|
31
+ banner = opts
32
+ opts.banner = <<-BANNER.gsub(" "*6, "")
33
+ Analyze newrelic data.
34
+
35
+ Usage:
36
+ statsn-calls API-KEY ACCOUNT-ID APPLICATION-ID METRIC-NAME
37
+ statsn-calls asdhgjasdgjhdasjgahsdasdjghsdjhg 123 1234567 Controller/users/index
38
+
39
+ statsn-calls API-KEY ACCOUNT-ID APPLICATION-ID METRIC-PREFIX
40
+ statsn-calls asdhgjasdgjhdasjgahsdasdjghsdjhg 123 1234567 Controller/users/*
41
+
42
+ Options:
43
+ BANNER
44
+ opts.on("-h", "--help", "Show this.") { puts opts; exit }
45
+ end.parse!
46
+ banner
47
+ end
48
+
49
+ def metric_names(namespace)
50
+ if namespace.end_with?("*")
51
+ namespace = namespace.sub("*", "")
52
+ metrics.select { |m| m["name"].start_with?(namespace) }.map { |m| m["name"] }
53
+ else
54
+ [namespace]
55
+ end
56
+ end
57
+
58
+ # Dependencies
59
+ gem "json"
60
+ gem "faraday"
61
+
62
+ require "json"
63
+ require "faraday"
64
+ require "optparse"
65
+
66
+ # ARGS
67
+ options = parse_options
68
+ unless ARGV.size == 4
69
+ puts options
70
+ exit 1
71
+ end
72
+ API_KEY, ACCOUNT_ID, APP_ID, namespace = ARGV
73
+
74
+ # Data
75
+ names = metric_names(namespace)
76
+ results = ["call_count", "total_call_time"].map do |field|
77
+ metric_data = aggregate(names, field).group_by{ |x| x["name"] }
78
+ sums = metric_data.map{ |_, values| values.inject(0){ |sum,x| sum + x[field] } }.inject(:+)
79
+ [field, sums]
80
+ end
81
+
82
+ # Display results
83
+ results = Hash[results]
84
+ if results.values.first
85
+ call_time = results["total_call_time"]
86
+ call_count = results["call_count"]
87
+ puts results.map{|x|x.join(": ")}
88
+ puts "response time: #{call_time / call_count}"
89
+ puts "call per second: #{call_count / PERIOD}"
90
+ else
91
+ puts "No data found for #{names.join(", ")} in last #{PERIOD} seconds"
92
+ exit 1
93
+ end
@@ -0,0 +1,3 @@
1
+ module Statsn
2
+ VERSION = "0.1.0"
3
+ end
data/lib/statsn.rb ADDED
@@ -0,0 +1,36 @@
1
+ require "statsn/version"
2
+ require "newrelic_rpm"
3
+
4
+ module Statsn
5
+ PREFIX = "Custom"
6
+
7
+ class << self
8
+ attr_accessor :api_key
9
+
10
+ # Increments the count of the key by the given amount, default 1
11
+ def increment(who, amount = 1)
12
+ stat(key(who)).record_data_point(amount)
13
+ end
14
+
15
+ # Times the given block and returns the output of the block
16
+ def time(who, &block)
17
+ trace_execution_unscoped(key(who), {}, &block)
18
+ end
19
+
20
+ private
21
+
22
+ def stat(key)
23
+ NewRelic::Agent.agent.stats_engine.get_stats_no_scope(key)
24
+ end
25
+
26
+ def key(who)
27
+ if who.is_a?(Array)
28
+ model = who.first
29
+ model = (model.is_a?(Class) ? model.name : model.class.name).gsub("::", "/")
30
+ [PREFIX, model, *who[1..-1]].compact.join("/")
31
+ else
32
+ who
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1 @@
1
+ asdadsasddasads 123 123123
@@ -0,0 +1,2 @@
1
+ require "bundler/setup"
2
+ require "statsn"
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+
3
+ describe "statsn-calls" do
4
+ let(:argv){ File.read("#{Bundler.root}/spec/argv.txt").strip }
5
+
6
+ it "gives us some numbers" do
7
+ result = run("#{argv} Controller/api/v2/users/update")
8
+ result.should =~ /^call_count: \d+/
9
+ result.should =~ /^total_call_time: \d+/
10
+ result.should =~ /^response time: \d+/
11
+ result.should =~ /^call per second: \d+/
12
+ end
13
+
14
+ it "gives us some numbers for a prefix call" do
15
+ result = run("#{argv} Controller/api/v2/users/*")
16
+ result.should =~ /^call_count: \d+/
17
+ result.should =~ /^total_call_time: \d+/
18
+ result.should =~ /^response time: \d+/
19
+ result.should =~ /^call per second: \d+/
20
+ end
21
+
22
+ it "shows help" do
23
+ result = run("--help")
24
+ result.should include("statsn-calls API-KEY")
25
+ end
26
+
27
+ it "shows help and fail with invalid args" do
28
+ result = run("asdadsasdasd", :fail => true)
29
+ result.should include("statsn-calls API-KEY")
30
+ end
31
+
32
+ it "tell us if the metric is missing" do
33
+ result = run("#{argv} Controller/xxxxxxx", :fail => true)
34
+ result.should include("No data found for Controller/xxxxxxx")
35
+ end
36
+
37
+ private
38
+
39
+ def run(argv, options={})
40
+ result = `ruby -I #{Bundler.root}/lib #{Bundler.root}/bin/statsn-calls #{argv} 2>&1`
41
+ raise "FAILED: #{result}" if $?.success? == !!options[:fail]
42
+ result
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ require "spec_helper"
2
+
3
+ module Foobar
4
+ class TestClass
5
+ end
6
+ end
7
+
8
+ describe Statsn do
9
+ it "has a VERSION" do
10
+ Statsn::VERSION.should =~ /^[\.\da-z]+$/
11
+ end
12
+
13
+ describe ".increment" do
14
+ it "calls count api with 1" do
15
+ NewRelic::MethodTraceStats.any_instance.should_receive(:record_data_point).with(1)
16
+ Statsn.increment("xxx")
17
+ end
18
+
19
+ it "calls count api with given number" do
20
+ NewRelic::MethodTraceStats.any_instance.should_receive(:record_data_point).with(3)
21
+ Statsn.increment("xxx", 3)
22
+ end
23
+ end
24
+
25
+ describe ".time" do
26
+ before do
27
+ NewRelic::Agent.is_execution_traced?.should == true
28
+ end
29
+
30
+ it "returns block result" do
31
+ Statsn.time("xxx"){ 4 }.should == 4
32
+ end
33
+
34
+ it "raises errors" do
35
+ expect{
36
+ Statsn.time("xxx"){ raise EOFError }
37
+ }.to raise_error(EOFError)
38
+ end
39
+
40
+ it "records used time" do
41
+ NewRelic::MethodTraceStats.any_instance.should_receive(:trace_call).with{|x| x.should be_within(0.05).of(0.1) }
42
+ Statsn.time("xxx"){ sleep 0.1 }
43
+ end
44
+ end
45
+
46
+ describe ".data" do
47
+ it "returns data for metrics" do
48
+
49
+ end
50
+ end
51
+
52
+ describe ".key" do
53
+ it "builds with a string" do
54
+ Statsn.send(:key, "xxx").should == "xxx"
55
+ end
56
+
57
+ it "builds with a class and name" do
58
+ Statsn.send(:key, [Foobar::TestClass, "xxx"]).should == "Custom/Foobar/TestClass/xxx"
59
+ end
60
+
61
+ it "builds with an instance and name" do
62
+ Statsn.send(:key, [Foobar::TestClass.new, "xxx"]).should == "Custom/Foobar/TestClass/xxx"
63
+ end
64
+
65
+ it "ignores nil" do
66
+ Statsn.send(:key, [Foobar::TestClass, nil, nil, "xxx"]).should == "Custom/Foobar/TestClass/xxx"
67
+ end
68
+ end
69
+ end
data/statsn.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ name = "statsn"
3
+ require "#{name}/version"
4
+
5
+ Gem::Specification.new name, Statsn::VERSION do |s|
6
+ s.summary = "StatsN: Aggregate statistics using newrelics custom metrics"
7
+ s.authors = ["Michael Grosser"]
8
+ s.email = "michael@grosser.it"
9
+ s.homepage = "http://github.com/grosser/#{name}"
10
+ s.files = `git ls-files`.split("\n")
11
+ s.license = "MIT"
12
+ s.executables = ["statsn-calls"]
13
+ s.add_runtime_dependency "newrelic_rpm", "~> 3.5"
14
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: statsn
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Michael Grosser
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '3.5'
20
+ none: false
21
+ prerelease: false
22
+ name: newrelic_rpm
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '3.5'
28
+ none: false
29
+ type: :runtime
30
+ description:
31
+ email: michael@grosser.it
32
+ executables:
33
+ - statsn-calls
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .travis.yml
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - Rakefile
42
+ - Readme.md
43
+ - bin/statsn-calls
44
+ - lib/statsn.rb
45
+ - lib/statsn/version.rb
46
+ - spec/argv.example.txt
47
+ - spec/spec_helper.rb
48
+ - spec/statsn_calls_spec.rb
49
+ - spec/statsn_spec.rb
50
+ - statsn.gemspec
51
+ homepage: http://github.com/grosser/statsn
52
+ licenses:
53
+ - MIT
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ segments:
64
+ - 0
65
+ hash: -3608192158582391302
66
+ none: false
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ segments:
73
+ - 0
74
+ hash: -3608192158582391302
75
+ none: false
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 1.8.24
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: ! 'StatsN: Aggregate statistics using newrelics custom metrics'
82
+ test_files: []