fozzie 0.0.27 → 1.0.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.
Files changed (42) hide show
  1. data/.gitignore +2 -1
  2. data/Guardfile +2 -2
  3. data/README.md +22 -11
  4. data/Rakefile +1 -1
  5. data/fozzie.gemspec +8 -14
  6. data/lib/core_ext/module/monitor.rb +4 -3
  7. data/lib/fozzie.rb +6 -7
  8. data/lib/fozzie/adapter.rb +1 -0
  9. data/lib/fozzie/adapter/statsd.rb +86 -0
  10. data/lib/fozzie/bulk_dsl.rb +28 -0
  11. data/lib/fozzie/configuration.rb +32 -13
  12. data/lib/fozzie/dsl.rb +19 -0
  13. data/lib/fozzie/exception.rb +5 -0
  14. data/lib/fozzie/interface.rb +30 -15
  15. data/lib/fozzie/rack/middleware.rb +3 -1
  16. data/lib/fozzie/sniff.rb +28 -33
  17. data/lib/fozzie/version.rb +1 -1
  18. data/spec/config/fozzie.yml +2 -1
  19. data/spec/lib/core_ext/module/monitor_spec.rb +9 -0
  20. data/spec/lib/fozzie/adapter/statsd_spec.rb +82 -0
  21. data/spec/lib/fozzie/bulk_dsl_spec.rb +47 -0
  22. data/spec/lib/fozzie/configuration_spec.rb +34 -20
  23. data/spec/lib/fozzie/dsl_spec.rb +16 -0
  24. data/spec/lib/fozzie/rack/middleware_spec.rb +6 -36
  25. data/spec/lib/fozzie/rack/sinatra_spec.rb +31 -0
  26. data/spec/lib/fozzie/sniff_spec.rb +37 -37
  27. data/spec/lib/fozzie/version_spec.rb +1 -1
  28. data/spec/lib/fozzie_spec.rb +19 -3
  29. data/spec/shared_examples/fozzie_adapter.rb +7 -0
  30. data/spec/shared_examples/interface.rb +160 -0
  31. data/spec/spec_helper.rb +20 -10
  32. metadata +31 -74
  33. data/lib/core_ext/hash.rb +0 -16
  34. data/lib/fozzie/mill.rb +0 -50
  35. data/lib/fozzie/rails/engine.rb +0 -15
  36. data/lib/fozzie/rails/middleware.rb +0 -39
  37. data/lib/fozzie/railtie.rb +0 -15
  38. data/lib/fozzie/socket.rb +0 -53
  39. data/spec/lib/core_ext/hash_spec.rb +0 -33
  40. data/spec/lib/fozzie/interface_spec.rb +0 -180
  41. data/spec/lib/fozzie/mill_spec.rb +0 -43
  42. data/spec/lib/fozzie/rails/middleware_spec.rb +0 -125
data/.gitignore CHANGED
@@ -2,4 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
- coverage/*
5
+ coverage/*
6
+ .DS_Store
data/Guardfile CHANGED
@@ -1,8 +1,8 @@
1
1
  # A sample Guardfile
2
2
  # More info at https://github.com/guard/guard#readme
3
3
 
4
- guard 'rspec', :version => 2 do
4
+ guard 'rspec', :cli => "--color" do
5
5
  watch(%r{^spec/.+_spec\.rb$})
6
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
7
  watch('spec/spec_helper.rb') { "spec" }
8
- end
8
+ end
data/README.md CHANGED
@@ -109,12 +109,31 @@ class FooBar
109
109
  # my code here...
110
110
  end
111
111
 
112
+ _monitor("my.awesome.bucket.name")
113
+ def quux
114
+ # something
115
+ end
116
+
112
117
  end
113
118
  ```
114
119
  This will register the processing time for this method, everytime it is called, under the Graphite bucket `foo_bar.zar`.
115
120
 
116
121
  This will work on both Class and Instance methods.
117
122
 
123
+ ## Bulk
124
+
125
+ You can send a bulk of metrics using the `bulk` method:
126
+ ``` ruby
127
+ Stats.bulk do
128
+ increment 'wat'
129
+ decrement 'wot'
130
+ gauge 'foo', rand
131
+ time_to_do 'wat_timer' { sleep 4 }
132
+ end
133
+ ```
134
+
135
+ This will send all the given metrics in a single packet to the statistics server.
136
+
118
137
  ## Namespaces
119
138
 
120
139
  Fozzie supports the following namespaces as default
@@ -185,13 +204,13 @@ Prefixes are cached on first use, therefore any changes to the Fozzie configure
185
204
 
186
205
  ## Middleware
187
206
 
188
- To time and register the controller actions within your Rails application, Fozzie provides some middleware.
207
+ To time and register the controller actions within your Rack and Rails application, Fozzie provides some middleware.
189
208
 
190
209
  ### Rack
191
210
 
192
211
  ``` ruby
193
212
  require 'rack'
194
- require 'fozzie'
213
+ require 'fozzie/rack/middleware'
195
214
 
196
215
  app = Rack::Builder.new {
197
216
  use Fozzie::Rack::Middleware
@@ -201,9 +220,7 @@ app = Rack::Builder.new {
201
220
 
202
221
  ### Rails
203
222
 
204
- Based on the Rack middleware above, but is more involved in its construction of the bucket value.
205
-
206
- Fozzie::Rails::Middleware will automatically be invoked on Rails initialization.
223
+ See [Fozzie Rails](http://github.com/lonelyplanet/fozzie_rails).
207
224
 
208
225
  ## Bucket name prefixes
209
226
 
@@ -242,18 +259,12 @@ Fozzie.logger = Logger.new 'log/fozzie.log'
242
259
 
243
260
  This may change, depending on feedback and more production experience.
244
261
 
245
- ## Rails User Interface Performance Measuring
246
-
247
- If you also require UI metrics, you can also include the Mill script in the bottom of any page you would like to measure (see `resources/mill.js` and `resources/mill.min.js`) and you start receiving measurements on page performance.
248
-
249
262
  ## Credits
250
263
 
251
264
  Currently supported and maintained by [Marc Watts](marc.watts@lonelyplanet.co.uk) @ Lonely Planet Online.
252
265
 
253
266
  Big thanks and Credits:
254
267
 
255
- * [Mark Barger](mark.barger@lonelyplanet.co.uk) for support in trying to make this Gem useful.
256
-
257
268
  * [Dave Nolan](https://github.com/textgoeshere)
258
269
 
259
270
  * [Etsy](http://codeascraft.etsy.com/) whose [Statsd](https://github.com/etsy/statsd) product has enabled us to come such a long way in a very short period of time. We love Etsy.
data/Rakefile CHANGED
@@ -4,4 +4,4 @@ Bundler::GemHelper.install_tasks
4
4
  require 'rspec/core/rake_task'
5
5
  RSpec::Core::RakeTask.new(:spec)
6
6
 
7
- task :default => :spec
7
+ task :default => :spec
@@ -5,18 +5,18 @@ require "fozzie/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "fozzie"
7
7
  s.version = Fozzie::VERSION
8
- s.authors = ["Marc Watts", "Dave Nolan"]
8
+ s.authors = ["Marc Watts"]
9
9
  s.email = ["marc.watts@lonelyplanet.co.uk"]
10
- s.summary = %q{Statsd Ruby gem from Lonely Planet Online}
10
+ s.summary = %q{Ruby gem from Lonely Planet Online to register statistics. Currently supports Statsd.}
11
11
  s.description = %q{
12
- Gem to make statistics sending to Statsd from Ruby applications simple and efficient as possible.
13
- Inspired by the original ruby-statsd gem by Etsy, currently used by Lonely Planet Online.
12
+ Gem to make statistics sending from Ruby applications simple and efficient as possible.
13
+ Currently supports Statsd, and is inspired by the original ruby-statsd gem by Etsy.
14
14
  }
15
15
 
16
16
  s.rubyforge_project = "fozzie"
17
17
 
18
18
  s.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
20
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
21
  s.require_paths = ["lib"]
22
22
 
@@ -25,15 +25,9 @@ Gem::Specification.new do |s|
25
25
 
26
26
  s.add_development_dependency 'rake'
27
27
  s.add_development_dependency 'rspec'
28
- s.add_development_dependency 'mocha'
29
- s.add_development_dependency 'syntax'
30
- s.add_development_dependency 'simplecov'
31
-
32
- s.add_development_dependency 'sinatra'
33
- s.add_development_dependency 'rack-test'
34
- s.add_development_dependency 'actionpack', '2.3.14'
35
-
36
28
  s.add_development_dependency 'guard'
37
29
  s.add_development_dependency 'guard-rspec'
38
-
30
+ s.add_development_dependency 'rb-fsevent'
31
+ s.add_development_dependency 'sinatra'
32
+ s.add_development_dependency 'rack-test'
39
33
  end
@@ -2,10 +2,11 @@ require 'fozzie/sniff'
2
2
 
3
3
  class Module
4
4
 
5
- def _monitor
5
+ def _monitor(bucket_name = nil)
6
6
  return unless Fozzie.c.sniff?
7
- self.class_eval { include Fozzie::Sniff }
7
+ self.class_eval { extend Fozzie::Sniff }
8
8
  @_monitor_flag = true
9
+ @_bucket_name = bucket_name
9
10
  end
10
11
 
11
- end
12
+ end
@@ -11,15 +11,14 @@ module Fozzie
11
11
 
12
12
  require 'core_ext/module/monitor'
13
13
 
14
+ require 'fozzie/adapter'
15
+
16
+ require "fozzie/exception"
14
17
  require 'fozzie/configuration'
15
- require "fozzie/interface"
18
+ require "fozzie/dsl"
19
+ require "fozzie/bulk_dsl"
16
20
  require "fozzie/version"
17
21
 
18
- require "fozzie/rack/middleware"
19
- require "fozzie/rails/middleware"
20
-
21
- require 'fozzie/railtie' if defined?(::Rails)
22
-
23
22
  class << self
24
23
 
25
24
  # Shortcut for `Fozzie.config`
@@ -58,7 +57,7 @@ module Fozzie
58
57
 
59
58
  # Loads each namespace for registering statistics
60
59
  self.c.namespaces.each do |klas|
61
- Kernel.const_set(klas, Interface.instance) unless const_defined?(klas)
60
+ Kernel.const_set(klas, Dsl.instance) unless const_defined?(klas)
62
61
  end
63
62
 
64
63
  end
@@ -0,0 +1 @@
1
+ %w{statsd}.each {|r| require "fozzie/adapter/#{r}" }
@@ -0,0 +1,86 @@
1
+ require 'socket'
2
+
3
+ module Fozzie
4
+ module Adapter
5
+
6
+ class Statsd
7
+
8
+ RESERVED_CHARS_REGEX = /[\:\|\@\s]/
9
+ RESERVED_CHARS_REPLACEMENT = '_'
10
+ DELIMETER = '.'
11
+ SAFE_SEPARATOR = '-'
12
+ TYPES = { :gauge => 'g', :count => 'c', :timing => 'ms' }
13
+ BULK_DELIMETER = "\n"
14
+
15
+ # Send the statistic to the server
16
+ #
17
+ # Creates the Statsd key from the given values, and sends to socket (depending on sample rate)
18
+ def register(*stats)
19
+ metrics = stats.flatten.collect do |stat|
20
+ next if sampled?(stat[:sample_rate])
21
+
22
+ bucket = format_bucket(stat[:bin])
23
+ value = format_value(stat[:value], stat[:type], stat[:sample_rate])
24
+
25
+ [bucket, value].join(':')
26
+ end.compact.join(BULK_DELIMETER)
27
+
28
+ send_to_socket(metrics)
29
+ end
30
+
31
+ def format_bucket(stat)
32
+ bucket = [stat].flatten.compact.collect(&:to_s).join(DELIMETER).downcase
33
+ bucket = bucket.gsub('::', DELIMETER).gsub(RESERVED_CHARS_REGEX, RESERVED_CHARS_REPLACEMENT)
34
+ bucket = [Fozzie.c.data_prefix, bucket].compact.join(DELIMETER)
35
+
36
+ bucket
37
+ end
38
+
39
+ def format_value(val, type, sample_rate)
40
+ converted_type = TYPES[type.to_sym]
41
+ converted_type ||= TYPES[:gauge]
42
+
43
+ value = [val, converted_type].join('|')
44
+ value << '@%s' % sample_rate.to_s if sample_rate < 1
45
+
46
+ value
47
+ end
48
+
49
+ # If the statistic is sampled, generate a condition to check if it's good to send
50
+ def sampled(sample_rate)
51
+ yield unless sampled?(sample_rate)
52
+ end
53
+
54
+ def sampled?(sample_rate)
55
+ sample_rate < 1 and rand > sample_rate
56
+ end
57
+
58
+ # Send data to the server via the socket
59
+ def send_to_socket(message)
60
+ Fozzie.logger.debug {"Statsd: #{message}"} if Fozzie.logger
61
+ Timeout.timeout(Fozzie.c.timeout) {
62
+ res = socket.send(message, 0, Fozzie.c.host, Fozzie.c.port)
63
+ Fozzie.logger.debug {"Statsd sent: #{res}"} if Fozzie.logger
64
+ (res.to_i == message.length)
65
+ }
66
+ rescue => exc
67
+ Fozzie.logger.debug {"Statsd Failure: #{exc.message}\n#{exc.backtrace}"} if Fozzie.logger
68
+ false
69
+ end
70
+
71
+ # The Socket we want to use to send data
72
+ def socket
73
+ @socket ||= ::UDPSocket.new
74
+ end
75
+
76
+ def delimeter
77
+ DELIMETER
78
+ end
79
+
80
+ def safe_separator
81
+ SAFE_SEPARATOR
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,28 @@
1
+ module Fozzie
2
+ class BulkDsl
3
+ include Fozzie::Interface
4
+
5
+ def initialize(&block)
6
+ @metrics = []
7
+ block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
8
+ send_bulk
9
+ end
10
+
11
+ private
12
+
13
+ # Cache the requested metrics for bulk sending
14
+ #
15
+ def send(stat, value, type, sample_rate = 1)
16
+ val = { :bin => stat, :value => value, :type => type, :sample_rate => sample_rate }
17
+
18
+ @metrics.push(val)
19
+ end
20
+
21
+ def send_bulk
22
+ return if @metrics.empty?
23
+
24
+ adapter.register(@metrics)
25
+ end
26
+
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  require 'yaml'
2
- require 'core_ext/hash'
2
+ require 'facets/hash/symbolize_keys'
3
3
  require 'sys/uname'
4
4
  require 'timeout'
5
5
 
@@ -7,16 +7,32 @@ module Fozzie
7
7
 
8
8
  # Fozzie configuration allows assignment of global properties
9
9
  # that will be used within the Fozzie codebase.
10
+
10
11
  class Configuration
11
12
  include Sys
13
+ extend Forwardable
14
+
15
+ def_delegators :adapter, :delimeter, :safe_separator
12
16
 
13
- attr_accessor :env, :config_path, :host, :port, :appname, :namespaces, :timeout, :monitor_classes, :sniff_envs, :ignore_prefix, :prefix
17
+ attr_accessor :env, :config_path, :host, :port, :appname, :namespaces,
18
+ :timeout, :monitor_classes, :sniff_envs, :ignore_prefix, :prefix
14
19
 
15
20
  def initialize(args = {})
16
21
  merge_and_assign_config(args)
22
+ self.adapter
17
23
  self.origin_name
18
24
  end
19
25
 
26
+ def adapter=(adapter)
27
+ @adapter = eval("Fozzie::Adapter::#{adapter}").new
28
+ rescue NoMethodError
29
+ raise AdapterMissing, "Adapter could not be found for given provider #{@provider}"
30
+ end
31
+
32
+ def adapter
33
+ @adapter || default_configuration[:adapter]
34
+ end
35
+
20
36
  def disable_prefix
21
37
  @ignore_prefix = true
22
38
  end
@@ -26,15 +42,17 @@ module Fozzie
26
42
  return nil if @ignore_prefix
27
43
  return @data_prefix if @data_prefix
28
44
 
29
- data_prefix = @prefix.collect do |me|
30
- (me.kind_of?(Symbol) && self.respond_to?(me.to_sym) ? self.send(me) : me.to_s)
31
- end
32
-
33
- data_prefix = data_prefix.collect do |s|
34
- s.empty? ? nil : s.gsub(Socket::DELIMETER, '-')
35
- end.compact.join(Socket::DELIMETER).strip
36
-
37
- @data_prefix ||= (data_prefix.empty? ? nil : data_prefix)
45
+ escaped_prefix_with_dynamically_resolved_parts = prefix.map do |part|
46
+ resolved_part = (part.kind_of?(Symbol) && self.respond_to?(part) ? self.send(part) : part.to_s)
47
+ escaped_resolved_part = resolved_part.gsub(delimeter, safe_separator)
48
+ escaped_resolved_part == "" ? nil : escaped_resolved_part
49
+ end.compact
50
+
51
+ @data_prefix = if escaped_prefix_with_dynamically_resolved_parts.any?
52
+ escaped_prefix_with_dynamically_resolved_parts.join(delimeter).strip
53
+ else
54
+ nil
55
+ end
38
56
  end
39
57
 
40
58
  # Returns the origin name of the current machine to register the stat against
@@ -70,7 +88,8 @@ module Fozzie
70
88
  :timeout => 0.5,
71
89
  :monitor_classes => [],
72
90
  :sniff_envs => [:development, :staging, :production],
73
- :ignore_prefix => false
91
+ :ignore_prefix => false,
92
+ :adapter => :Statsd
74
93
  }.dup
75
94
  end
76
95
 
@@ -89,4 +108,4 @@ module Fozzie
89
108
 
90
109
  end
91
110
 
92
- end
111
+ end
@@ -0,0 +1,19 @@
1
+ require 'singleton'
2
+ require "fozzie/interface"
3
+
4
+ module Fozzie
5
+ class Dsl
6
+ include Fozzie::Interface, Singleton
7
+
8
+ private
9
+
10
+ # Send the statistic to the chosen provider
11
+ #
12
+ def send(stat, value, type, sample_rate = 1)
13
+ val = { :bin => stat, :value => value, :type => type, :sample_rate => sample_rate }
14
+
15
+ adapter.register(val)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Fozzie
2
+
3
+ class AdapterMissing < StandardError; end
4
+
5
+ end
@@ -1,9 +1,7 @@
1
- require 'singleton'
2
- require 'fozzie/socket'
1
+ require 'fozzie/adapter/statsd'
3
2
 
4
3
  module Fozzie
5
- class Interface
6
- include Fozzie::Socket, Singleton
4
+ module Interface
7
5
 
8
6
  # Increments the given stat by one, with an optional sample rate
9
7
  #
@@ -23,14 +21,14 @@ module Fozzie
23
21
  #
24
22
  # `Stats.count 'wat', 500`
25
23
  def count(stat, count, sample_rate=1)
26
- send(stat, count, 'c', sample_rate)
24
+ send(stat, count, :count, sample_rate)
27
25
  end
28
26
 
29
27
  # Registers a timing (in ms) for the given stat, with an optional sample rate
30
28
  #
31
29
  # `Stats.timing 'wat', 500`
32
30
  def timing(stat, ms, sample_rate=1)
33
- send(stat, ms, 'ms', sample_rate)
31
+ send(stat, ms, :timing, sample_rate)
34
32
  end
35
33
 
36
34
  # Registers the time taken to complete a given block (in ms), with an optional sample rate
@@ -47,7 +45,7 @@ module Fozzie
47
45
  #
48
46
  # `Stats.time_to_do 'wat' { # Do something, again... }`
49
47
  def time_to_do(stat, sample_rate=1, &block)
50
- time_for(stat, sample_rate, &block)
48
+ time(stat, sample_rate, &block)
51
49
  end
52
50
 
53
51
  # Registers the time taken to complete a given block (in ms), with an optional sample rate
@@ -99,6 +97,13 @@ module Fozzie
99
97
  deployed(app)
100
98
  end
101
99
 
100
+ # Register an event of any type
101
+ #
102
+ # `Stats.event 'wat', 'app'`
103
+ def event(type, app = nil)
104
+ gauge ["event", type.to_s, app], Time.now.usec
105
+ end
106
+
102
107
  # Registers an increment on the result of the given boolean
103
108
  #
104
109
  # `Stats.increment_on 'wat', wat.random?`
@@ -108,18 +113,28 @@ module Fozzie
108
113
  perf
109
114
  end
110
115
 
111
- # Register an event of any type
112
- #
113
- # `Stats.event 'wat', 'app'`
114
- def event(type, app = nil)
115
- gauge ["event", type.to_s, app], Time.now.usec
116
- end
117
-
118
116
  # Register an arbitrary value
119
117
  #
120
118
  # `Stats.gauge 'wat', 'app'`
121
119
  def gauge(stat, value, sample_rate = 1)
122
- send(stat, value, "g", sample_rate)
120
+ send(stat, value, :gauge, sample_rate)
123
121
  end
122
+
123
+ # Register multiple statistics in a single call
124
+ #
125
+ # `Stats.bulk do
126
+ # increment 'wat'
127
+ # decrement 'wot'
128
+ # end`
129
+ def bulk(&block)
130
+ Fozzie::BulkDsl.new(&block)
131
+ end
132
+
133
+ private
134
+
135
+ def adapter
136
+ Fozzie.c.adapter
137
+ end
138
+
124
139
  end
125
140
  end