tronprint 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/tronprint.rb CHANGED
@@ -82,12 +82,20 @@ module Tronprint
82
82
 
83
83
  # The Tronprint::Aggregator instance.
84
84
  def aggregator
85
- @aggregator ||= Aggregator.new aggregator_options.dup
85
+ return @aggregator unless @aggregator.nil?
86
+ begin
87
+ @aggregator = Aggregator.new aggregator_options.dup
88
+ rescue => e
89
+ backtrace = ([e] + e.backtrace).join("\n")
90
+ log_error "Failed to connect to aggregator: #{backtrace}"
91
+ @aggregator = nil
92
+ end
93
+ @aggregator
86
94
  end
87
95
 
88
96
  # The Tronprint::CPUMonitor instance.
89
97
  def cpu_monitor
90
- @cpu_monitor ||= CPUMonitor.new aggregator, application_name
98
+ @cpu_monitor ||= CPUMonitor.new application_name
91
99
  end
92
100
 
93
101
  # The Tronprint::TrafficMonitor instance
@@ -158,4 +166,7 @@ module Tronprint
158
166
  def log_info(message)
159
167
  logger.info message if logger
160
168
  end
169
+ def log_error(message)
170
+ logger.error message if logger
171
+ end
161
172
  end
@@ -21,6 +21,9 @@ module Tronprint
21
21
  self.adapter = options.delete :adapter
22
22
  self.adapter ||= 'pstore'
23
23
  self.adapter = self.adapter.to_s.downcase
24
+
25
+ options = process_options(options)
26
+
24
27
  begin
25
28
  require "moneta/#{self.adapter}"
26
29
  klass = Moneta.const_get adapter_constant
@@ -34,6 +37,14 @@ module Tronprint
34
37
  super instance
35
38
  end
36
39
 
40
+ # Handle initializer options for some special cases
41
+ def process_options(options)
42
+ if adapter == 'mongodb'
43
+ options[:pool_size] = 3
44
+ end
45
+ options
46
+ end
47
+
37
48
  def __getobj__ # :nodoc:
38
49
  @delegate_sd_obj
39
50
  end
@@ -133,10 +144,22 @@ module Tronprint
133
144
  end
134
145
 
135
146
  def update_entry(key, value)
136
- old_value = self[key]
147
+ begin
148
+ old_value = self[key]
149
+ rescue => e
150
+ backtrace = ([e] + e.backtrace).join("\n")
151
+ Tronprint.log_error "Tronprint: Unable to fetch statistics from datastore: #{backtrace}"
152
+ end
153
+
137
154
  new_value = old_value ? old_value + value : value
138
155
  Tronprint.log_debug "Tronprint.aggregator[#{key.inspect}] = #{new_value}"
139
- self[key] = new_value
156
+
157
+ begin
158
+ self[key] = new_value
159
+ rescue => e
160
+ backtrace = ([e] + e.backtrace).join("\n")
161
+ Tronprint.log_error "Tronprint: Unable to update datastore with statistics: #{backtrace}"
162
+ end
140
163
  end
141
164
  end
142
165
  end
@@ -2,34 +2,47 @@ module Tronprint
2
2
 
3
3
  # Tronprint::CPUMonitor is a thread that monitors aggregate CPU usage.
4
4
  class CPUMonitor < Thread
5
- attr_accessor :total_recorded_cpu_time, :aggregator, :application_name
5
+ attr_accessor :total_recorded_cpu_time, :application_name, :run_continuously
6
6
 
7
7
  # Parameters:
8
- # +aggregator+:: A Tronprint::Aggregator instance.
9
8
  # +application_name+:: A unique application name.
10
9
  # +options+:: Extra options, such as:
11
10
  # --+run+:: Whether to fork the thread and run continuously. +true+ by default.
12
- def initialize(aggregator, application_name, options = {})
13
- self.aggregator = aggregator
11
+ def initialize(application_name, options = {})
14
12
  self.application_name = application_name
15
13
  options[:run] = true if options[:run].nil?
16
14
  if options[:run]
17
- super do
18
- while(true) do
19
- monitor
20
- sleep(5)
21
- end
22
- end
15
+ super &method(:thread_loop)
23
16
  else
24
17
  super() {} # Ruby 1.8 hack
25
18
  end
26
19
  end
27
20
 
21
+ def run_continuously?
22
+ @run_continuously = true if @run_continuously.nil?
23
+ @run_continuously
24
+ end
25
+
26
+ # The main thread loop, monitors CPU usage every 5 seconds if
27
+ # a connection to the aggregator is available
28
+ def thread_loop
29
+ first_run = true
30
+ begin
31
+ monitor if aggregator
32
+ sleep(5) if run_continuously?
33
+ first_run = false
34
+ end while(first_run or run_continuously?)
35
+ end
36
+
28
37
  # The key used to store CPU data in the Aggregator.
29
38
  def key
30
39
  [application_name, 'application', 'cpu_time'].join('/')
31
40
  end
32
41
 
42
+ def aggregator
43
+ Tronprint.aggregator
44
+ end
45
+
33
46
  # The process used to collect CPU uptime data and store it.
34
47
  def monitor
35
48
  elapsed_time = elapsed_cpu_time
@@ -21,31 +21,37 @@ module TronprintHelper
21
21
 
22
22
  # An informational badge displaying total energy, footprint, CO2/minute
23
23
  def footprint_badge(options = {})
24
- footprint = pounds_with_precision total_estimate
24
+ begin
25
+ footprint = pounds_with_precision total_estimate
25
26
 
26
- two_hr_emissions = Tronprint.statistics.
27
- emission_estimate(Time.now - 7200, Time.now).to_f
28
- rate = two_hr_emissions / 120 # kgs CO2 per minute over last 2 hours
29
- rate = rate < 0.0001 ? "< 0.0001" : pounds_with_precision(rate)
27
+ two_hr_emissions = Tronprint.statistics.
28
+ emission_estimate(Time.now - 7200, Time.now).to_f
29
+ rate = two_hr_emissions / 120 # kgs CO2 per minute over last 2 hours
30
+ rate = rate < 0.0001 ? "< 0.0001" : pounds_with_precision(rate)
30
31
 
31
- text = <<-HTML
32
- <p class="tronprint-footprint">
33
- <span class="tronprint-total-footprint">
34
- <span class="tronprint-label">Total app footprint:</span>
35
- <span class="tronprint-value">#{total_electricity.to_i}</span>
36
- <span class="tronprint-units">W</span>,
37
- <span class="tronprint-value">#{footprint}</span>
38
- <span class="tronprint-units">lbs. CO<sub>2</sub>e</span>
39
- </span>
40
- <span class="tronprint-separator">&middot;</span>
41
- <span class="tronprint-current-footprint">
42
- <span class="tronprint-label">Current footprint:</span>
43
- <span class="tronprint-value">#{rate}</span>
44
- <span class="tronprint-units">lbs. CO<sub>2</sub>e/min.</span>
45
- </span>
46
- <span class="tronprint-attribution">#{tronprint_attribution if options[:attribution]}</span>
47
- </p>
48
- HTML
32
+ text = <<-HTML
33
+ <p class="tronprint-footprint">
34
+ <span class="tronprint-total-footprint">
35
+ <span class="tronprint-label">Total app footprint:</span>
36
+ <span class="tronprint-value">#{total_electricity.to_i}</span>
37
+ <span class="tronprint-units">W</span>,
38
+ <span class="tronprint-value">#{footprint}</span>
39
+ <span class="tronprint-units">lbs. CO<sub>2</sub>e</span>
40
+ </span>
41
+ <span class="tronprint-separator">&middot;</span>
42
+ <span class="tronprint-current-footprint">
43
+ <span class="tronprint-label">Current footprint:</span>
44
+ <span class="tronprint-value">#{rate}</span>
45
+ <span class="tronprint-units">lbs. CO<sub>2</sub>e/min.</span>
46
+ </span>
47
+ <span class="tronprint-attribution">#{tronprint_attribution if options[:attribution]}</span>
48
+ </p>
49
+ HTML
50
+ rescue => e
51
+ text = <<-HTML
52
+ <p class="tronprint-footprint">App footprint unavailable</p>
53
+ HTML
54
+ end
49
55
 
50
56
  text.html_safe
51
57
  end
@@ -57,7 +63,7 @@ module TronprintHelper
57
63
 
58
64
  # A link to more information about Tronprint
59
65
  def tronprint_badge
60
- %Q{<p class="tronprint-link"><%= tronprint_attribution %></p>}.html_safe
66
+ %Q{<p class="tronprint-link">#{tronprint_attribution}</p>}.html_safe
61
67
  end
62
68
 
63
69
  # Let the world know that your app is powered by CM1
@@ -1,3 +1,3 @@
1
1
  module Tronprint
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -73,6 +73,22 @@ describe Tronprint::Aggregator do
73
73
  end
74
74
  end
75
75
 
76
+ describe '#process_options' do
77
+ it 'sets a pool size if the adapter is mongodb' do
78
+ aggregator.adapter = 'mongodb'
79
+ options = aggregator.process_options :hostname => 'example.com'
80
+ options[:pool_size].should > 1
81
+ end
82
+ end
83
+
84
+ describe '#update_entry' do
85
+ it 'logs any connection errors' do
86
+ aggregator.stub!(:[]).and_raise StandardError
87
+ Tronprint.should_receive(:log_error).once
88
+ aggregator.send :update_entry, 'foo', 77
89
+ end
90
+ end
91
+
76
92
  describe '#range_total' do
77
93
  before :each do
78
94
  [
@@ -2,7 +2,32 @@ require 'spec_helper'
2
2
 
3
3
  describe Tronprint::CPUMonitor do
4
4
  let(:aggregator) { Tronprint::Aggregator.new :adapter => :memory }
5
- let(:cpu_monitor) { Tronprint::CPUMonitor.new aggregator, 'my_app', :run => false }
5
+ let(:cpu_monitor) { Tronprint::CPUMonitor.new 'my_app', :run => false }
6
+
7
+ before :each do
8
+ Tronprint.stub!(:aggregator).and_return aggregator
9
+ cpu_monitor.run_continuously = false
10
+ end
11
+
12
+ describe '#thread_loop' do
13
+ it 'monitors CPU usage' do
14
+ cpu_monitor.should_receive :monitor
15
+ cpu_monitor.thread_loop
16
+ end
17
+ it 'does not monitor if no aggregator is available' do
18
+ cpu_monitor.should_not_receive :monitor
19
+ Tronprint.stub!(:aggregator)
20
+ cpu_monitor.thread_loop
21
+ end
22
+ it 'eventually monitors when it can connect to an aggregator' do
23
+ cpu_monitor.stub!(:sleep)
24
+ cpu_monitor.stub!(:run_continuously?).and_return(true, true, true, true, true, false)
25
+ Tronprint.stub!(:aggregator).and_return(nil, nil, aggregator)
26
+
27
+ cpu_monitor.should_receive :monitor
28
+ cpu_monitor.thread_loop
29
+ end
30
+ end
6
31
 
7
32
  describe '#monitor' do
8
33
  before :each do
@@ -14,5 +14,19 @@ describe TronprintHelper do
14
14
  helper.total_footprint.should == 89.4
15
15
  end
16
16
  end
17
+
18
+ describe '#footprint_badge' do
19
+ it 'outputs a badge' do
20
+ helper.stub!(:pounds_with_precision).and_return "100.0"
21
+ helper.stub!(:total_estimate).and_return 100.0
22
+ Tronprint.statistics.stub!(:emission_estimate).and_return 100.0
23
+ helper.stub!(:total_electricity).and_return 20
24
+ helper.footprint_badge.should =~ /Total app footprint/
25
+ end
26
+ it 'gracefully handles lost aggregator connections' do
27
+ helper.stub!(:total_estimate).and_raise StandardError
28
+ helper.footprint_badge.should =~ /App footprint unavailable/
29
+ end
30
+ end
17
31
  end
18
32
 
@@ -37,6 +37,12 @@ describe Tronprint do
37
37
  end
38
38
  end
39
39
 
40
+ describe '.traffic_monitor' do
41
+ it 'returns a traffic monitor' do
42
+ Tronprint.traffic_monitor.should be_a(Tronprint::TrafficMonitor)
43
+ end
44
+ end
45
+
40
46
  describe '.config' do
41
47
  it 'returns a configuration loaded from disk' do
42
48
  Tronprint.stub!(:load_config).and_return :foo => :bar
@@ -89,6 +95,25 @@ describe Tronprint do
89
95
  end
90
96
  end
91
97
 
98
+ describe '.aggregator' do
99
+ it 'returns an aggregator' do
100
+ Tronprint.aggregator.should be_a(Tronprint::Aggregator)
101
+ end
102
+ context 'when the aggregator fails to load a datastore' do
103
+ before :each do
104
+ Tronprint::Aggregator.stub!(:new).and_raise(StandardError)
105
+ Tronprint.instance_variable_set :@aggregator, nil
106
+ end
107
+ it 'logs an error' do
108
+ Tronprint.should_receive :log_error
109
+ Tronprint.aggregator
110
+ end
111
+ it 'returns nil' do
112
+ Tronprint.aggregator.should be_nil
113
+ end
114
+ end
115
+ end
116
+
92
117
  describe '.aggregator_options' do
93
118
  before :each do
94
119
  ENV['MONGOHQ_URL'] = nil
data/tronprint.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Tronprint::VERSION
8
8
 
9
9
  s.authors = ['Derek Kastner']
10
- s.date = "2011-07-06"
10
+ s.date = "2011-07-20"
11
11
  s.description = %q{A gem for monitoring the carbon footprint of your ruby app}
12
12
  s.email = %q{dkastner@gmail.com}
13
13
  s.homepage = %q{http://github.com/brighterplanet/tronprint}
@@ -38,8 +38,8 @@ Gem::Specification.new do |s|
38
38
  s.add_development_dependency 'rspec', '~> 2.0'
39
39
  s.add_development_dependency 'sandbox'
40
40
  s.add_development_dependency 'timecop'
41
- s.add_runtime_dependency 'carbon', '~> 1.1.1'
41
+ s.add_runtime_dependency 'carbon', '~> 1.1.1'
42
42
  s.add_runtime_dependency 'i18n'
43
- s.add_runtime_dependency 'dkastner-moneta', '~> 1.1.0'
43
+ s.add_runtime_dependency 'dkastner-moneta', '~> 1.1.1'
44
44
  end
45
45
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tronprint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-07-06 00:00:00.000000000Z
12
+ date: 2011-07-20 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
16
- requirement: &2169219780 !ruby/object:Gem::Requirement
16
+ requirement: &2164332000 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2169219780
24
+ version_requirements: *2164332000
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &2169219280 !ruby/object:Gem::Requirement
27
+ requirement: &2164331500 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '3'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2169219280
35
+ version_requirements: *2164331500
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: cucumber
38
- requirement: &2169218900 !ruby/object:Gem::Requirement
38
+ requirement: &2164331120 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2169218900
46
+ version_requirements: *2164331120
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bueller
49
- requirement: &2169173640 !ruby/object:Gem::Requirement
49
+ requirement: &2164330580 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.0.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2169173640
57
+ version_requirements: *2164330580
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rake
60
- requirement: &2169173020 !ruby/object:Gem::Requirement
60
+ requirement: &2164330160 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2169173020
68
+ version_requirements: *2164330160
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
- requirement: &2169172440 !ruby/object:Gem::Requirement
71
+ requirement: &2164329620 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '2.0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2169172440
79
+ version_requirements: *2164329620
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: sandbox
82
- requirement: &2169172020 !ruby/object:Gem::Requirement
82
+ requirement: &2164329200 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *2169172020
90
+ version_requirements: *2164329200
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: timecop
93
- requirement: &2169171560 !ruby/object:Gem::Requirement
93
+ requirement: &2164328740 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *2169171560
101
+ version_requirements: *2164328740
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: carbon
104
- requirement: &2169171060 !ruby/object:Gem::Requirement
104
+ requirement: &2164328240 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: 1.1.1
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *2169171060
112
+ version_requirements: *2164328240
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: i18n
115
- requirement: &2169170640 !ruby/object:Gem::Requirement
115
+ requirement: &2164327820 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,18 +120,18 @@ dependencies:
120
120
  version: '0'
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *2169170640
123
+ version_requirements: *2164327820
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: dkastner-moneta
126
- requirement: &2169170100 !ruby/object:Gem::Requirement
126
+ requirement: &2164327280 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ~>
130
130
  - !ruby/object:Gem::Version
131
- version: 1.1.0
131
+ version: 1.1.1
132
132
  type: :runtime
133
133
  prerelease: false
134
- version_requirements: *2169170100
134
+ version_requirements: *2164327280
135
135
  description: A gem for monitoring the carbon footprint of your ruby app
136
136
  email: dkastner@gmail.com
137
137
  executables: []
@@ -188,7 +188,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
188
188
  version: '0'
189
189
  segments:
190
190
  - 0
191
- hash: 2285782564370156017
191
+ hash: -3333556664513962669
192
192
  required_rubygems_version: !ruby/object:Gem::Requirement
193
193
  none: false
194
194
  requirements:
@@ -197,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
197
  version: '0'
198
198
  segments:
199
199
  - 0
200
- hash: 2285782564370156017
200
+ hash: -3333556664513962669
201
201
  requirements: []
202
202
  rubyforge_project:
203
203
  rubygems_version: 1.8.5