tronprint 1.2.0 → 1.2.1

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/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