counters 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,53 +1,65 @@
1
1
  h1. Counters
2
2
 
3
- Easily record any metric from anywhere within your system. Metrics are recorded to Redis (using the provided backend), in a single Hash key. You can then extract the keys later and use them with Cacti to generate graphs about anything going on.
3
+ Easily record any metrics from anywhere within your code, using a very simple interface:
4
4
 
5
- h2. Sample Usage
5
+ * <code>ping</code>: When's the last time we saw this thing?
6
+ * <code>hit</code>: Increments a counter
7
+ * <code>magnitude</code>: Measures numerical values
8
+ * <code>latency</code>: Measures time intervals
6
9
 
7
- Let's say you have a crawler. You'd like to record the number of URLs you visit, the number of URLs you skipped due to 304 Not Modified responses, and the number of bytes you consumed, and the amount of time each page takes to process. Here's how you'd do that:
10
+ h2. Example
8
11
 
9
- <pre><code>require "counters"
10
- require "redis"
11
- require "rest_client"
12
- Counter = Counters::Redis.new(Redis.new, "counters")
12
+ Let's say you have a web crawler. There are a ton of things you can measure about your crawler: how many pages it processed (<code>#hit</code>), how many bytes you read (<code>#magnitude</code>), how long did it take to download the page (<code>#latency</code>), how long did it take to parse the raw HTML to a useable format (<code>#latency</code>).
13
13
 
14
- while url = STDIN.gets
15
- Counter.hit "crawler.urls"
14
+ <pre><code>require "uri"
15
+ require "open-uri"
16
+ require "counters"
17
+ require "nokogiri"
16
18
 
17
- response = RestClient.get(url)
18
- Counter.magnitude "crawler.bytes.read", response.length
19
- next Counter.hit "crawler.urls.skipped" if response.code == 304
19
+ Counter = Counters::Redis.new(Redis.new, :namespace => "crawler", :base_key => "counters")
20
20
 
21
- Counter.latency "crawler.processing" do
22
- # some long and complicated processing
21
+ urls_to_crawl = ["http://blog.teksol.info/", "http://techcrunch.com/", "http://www.google.com/"]
22
+
23
+ while url = urls_to_crawl.pop
24
+ Counter.ping "crawler"
25
+
26
+ begin
27
+ Counter.hit "urls_popped"
28
+
29
+ puts "Fetching #{url}"
30
+ raw_html = Counter.latency "download" do
31
+ URI.parse(url).read
32
+ end
33
+
34
+ Counter.magnitude "bytes_in", raw_html.length
35
+
36
+ parsed_html = Counter.latency "html_parsing" do
37
+ Nokogiri::HTML(raw_html)
38
+ end
39
+ rescue
40
+ Counter.hit "error"
23
41
  end
24
42
  end
25
43
  </code></pre>
26
44
 
27
- Redis will have a single key, named counters here, with the following keys and values in it (after 1 call with a 200 response code):
28
-
29
- * hits.crawler.urls = 1
30
- * magnitudes.crawler.bytes.read = 2041
31
- * latencies.crawler.processing.count = 1
32
- * latencies.crawler.processing.nanoseconds = 381000000
33
-
34
- h2. Other Implementations
45
+ h2. Other Backends
35
46
 
36
47
  For testing purposes, there also exists a <code>Counters::Memory</code>. This would be good in test mode, for example. The counters are exposed through accessor methods returning a Hash.
37
48
 
38
- For file logging, you should use <code>Counters::File</code>, which accepts a <code>String</code>, <code>IO</code> or <code>Logger</code> instance. All of these will be transformed to a <code>Logger</code> instance with a very strict format. All events will be logged to the file, one event per line.
49
+ You may log to a file, but be advised the file's size grows very quickly. Counters are stored in the file, one per line, in an easily readable format.
39
50
 
40
51
  <pre><code>$ irb -r counters
41
52
  > Counter = Counters::File.new("counters.log")
42
53
  => #<Counters::File:0x00000101a18f18 @logger=#<Logger:0x00000101a18ef0 @progname=nil, @level=0, @default_formatter=#<Logger::Formatter:0x00000101a18ea0 @datetime_format=nil>, @formatter=#<Proc:0x00000101a18bd0@/Users/francois/Projects/counters/lib/counters/file.rb:15 (lambda)>, @logdev=#<Logger::LogDevice:0x00000101a18e28 @shift_size=1048576, @shift_age=0, @filename="counters.log", @dev=#<File:counters.log>, @mutex=#<Logger::LogDevice::LogDeviceMutex:0x00000101a18e00 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x00000101a18d88>>>>>
43
54
  > Counter.hit "crawler.page_read"
44
- => true
55
+ => true
45
56
  > Counter.magnitude "crawler.bytes_in", 9_921
46
- => true
57
+ => true
47
58
  > Counter.latency "crawler.processing" do sleep 0.3 ; end
48
- => true
59
+ => true
49
60
  > Counter.ping "crawler.alive"
50
- => true
61
+ => true
62
+ > exit
51
63
 
52
64
  $ cat counters.log
53
65
  2011-02-21T09:46:21.296326000 - hit: crawler.page_read
@@ -59,6 +71,10 @@ $ cat counters.log
59
71
  2011-02-21T09:46:27.989183000 - latency: crawler.processing 0.3123122982101s
60
72
  </code></pre>
61
73
 
74
+ You may also output your counters to "StatsD":https://github.com/etsy/statsd. The only change that might be surprising is magnitudes are output as timer events. Magnitudes are used to record
75
+
76
+ See the file "samples/crawler.rb":blob/master/samples/crawler.rb for a more detailed example.
77
+
62
78
  h2. LICENSE
63
79
 
64
80
  (The MIT License)
data/lib/counters.rb CHANGED
@@ -11,4 +11,5 @@ module Counters
11
11
  autoload :Redis, "counters/redis"
12
12
  autoload :Memory, "counters/memory"
13
13
  autoload :File, "counters/file"
14
+ autoload :StatsD, "counters/stats_d"
14
15
  end
data/lib/counters/base.rb CHANGED
@@ -1,13 +1,21 @@
1
+ require "benchmark"
2
+
1
3
  module Counters
2
4
  class Base
5
+ attr_accessor :namespace
6
+
7
+ def initialize(options={})
8
+ @namespace = options[:namespace]
9
+ end
10
+
3
11
  def hit(key)
4
12
  validate(key)
5
- record_hit(key)
13
+ record_hit(namespaced_key(key))
6
14
  end
7
15
 
8
16
  def magnitude(key, value)
9
17
  validate(key)
10
- record_magnitude(key, value)
18
+ record_magnitude(namespaced_key(key), value)
11
19
  end
12
20
 
13
21
  def latency(key, time_in_seconds=nil)
@@ -18,13 +26,13 @@ module Counters
18
26
  time_in_seconds = Benchmark.measure { result = yield }.real
19
27
  end
20
28
 
21
- record_latency(key, time_in_seconds)
29
+ record_latency(namespaced_key(key), time_in_seconds)
22
30
  result
23
31
  end
24
32
 
25
33
  def ping(key)
26
34
  validate(key)
27
- record_ping(key)
35
+ record_ping(namespaced_key(key))
28
36
  end
29
37
 
30
38
  def record_hit(key)
@@ -51,5 +59,11 @@ module Counters
51
59
  key.to_s =~ /\A[.\w]+\Z/i or raise ArgumentError, "Keys can contain only letters, numbers, the underscore (_) and fullstop (.), received #{key.inspect}"
52
60
  end
53
61
  private :validate
62
+
63
+ def namespaced_key(key)
64
+ return key if namespace.nil?
65
+ "#{namespace}.#{key}"
66
+ end
67
+ private :namespaced_key
54
68
  end
55
69
  end
data/lib/counters/file.rb CHANGED
@@ -1,9 +1,10 @@
1
- require "benchmark"
2
1
  require "logger"
3
2
 
4
3
  module Counters
5
4
  class File < Counters::Base
6
- def initialize(path_or_io_or_logger)
5
+ def initialize(path_or_io_or_logger, options={})
6
+ super(options)
7
+
7
8
  @logger = if path_or_io_or_logger.kind_of?(Logger) then
8
9
  path_or_io_or_logger
9
10
  elsif path_or_io_or_logger.respond_to?(:<<) then
@@ -4,7 +4,9 @@ module Counters
4
4
  class Memory < Counters::Base
5
5
  attr_reader :hits, :latencies, :magnitudes, :pings
6
6
 
7
- def initialize
7
+ def initialize(options={})
8
+ super(options)
9
+
8
10
  @hits = Hash.new {|h,k| h[k] = 0}
9
11
  @magnitudes = Hash.new {|h,k| h[k] = 0}
10
12
  @latencies = Hash.new {|h,k| h[k] = Array.new}
@@ -16,7 +18,7 @@ module Counters
16
18
  end
17
19
 
18
20
  def record_ping(key)
19
- @pings[key] = Time.now
21
+ @pings[key] = Time.now.utc
20
22
  end
21
23
 
22
24
  def record_latency(key, time_in_seconds)
@@ -1,12 +1,13 @@
1
- require "benchmark"
2
-
3
1
  # The redis gem must already be required - we don't require it.
4
2
  # This allows callers / users to use any implementation that has the right API.
5
3
 
6
4
  module Counters
7
5
  class Redis < Counters::Base
8
- def initialize(redis=Redis.new, base_key="counters")
9
- @redis, @base_key = redis, base_key
6
+ def initialize(redis=Redis.new, options={})
7
+ super(options)
8
+
9
+ @redis = redis
10
+ @base_key = options.fetch(:base_key) { raise "Missing :base_key from #{options.inspect}" }
10
11
  end
11
12
 
12
13
  def record_hit(key)
@@ -20,8 +21,8 @@ module Counters
20
21
  end
21
22
  end
22
23
 
23
- def ping(key)
24
- @redis.hset(@base_key, "pings.#{key}", Time.now.to_i)
24
+ def record_ping(key)
25
+ @redis.hset(@base_key, "pings.#{key}", Time.now.utc.to_i)
25
26
  end
26
27
 
27
28
  # Redis requires integer keys, thus we scale all latencies to the nanosecond precision
@@ -0,0 +1,35 @@
1
+ require "socket"
2
+
3
+ module Counters
4
+ class StatsD < Counters::Base
5
+ attr_reader :host, :port, :socket
6
+
7
+ def initialize(host, port, options={})
8
+ super(options)
9
+
10
+ @host, @port = host, port
11
+ @socket = options.fetch(:socket) { UDPSocket.new }
12
+ end
13
+
14
+ def record_ping(key)
15
+ socket.send("pings.#{key}:1|c", 0, host, port)
16
+ end
17
+
18
+ def record_hit(key)
19
+ socket.send("hits.#{key}:1|c", 0, host, port)
20
+ end
21
+
22
+ # StatsD expects millisecond resolution
23
+ SCALING_FACTOR = 1_000
24
+
25
+ def record_latency(key, time_in_seconds)
26
+ value = "latencies.%s:%d|ms" % [key, time_in_seconds * SCALING_FACTOR]
27
+ socket.send(value, 0, host, port)
28
+ end
29
+
30
+ def record_magnitude(key, amount)
31
+ value = "magnitudes.%s:%d|ms" % [key, amount]
32
+ socket.send(value, 0, host, port)
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Counters
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,37 @@
1
+ require "uri"
2
+ require "open-uri"
3
+ require "counters"
4
+ require "nokogiri"
5
+ require "redis"
6
+ require "pp"
7
+
8
+ # Counter = Counters::Redis.new(Redis.new, :namespace => "crawler", :base_key => "counters")
9
+ Counter = Counters::StatsD.new("127.0.0.1", 8125, :namespace => "crawler")
10
+ # Counter = Counters::Memory.new(:namespace => "crawler")
11
+ # Counter = Counters::File.new(STDOUT, :namespace => "crawler")
12
+
13
+ urls_to_crawl = ["http://blog.teksol.info/", "http://techcrunch.com/", "http://www.google.com/"]
14
+
15
+ while url = urls_to_crawl.pop
16
+ Counter.ping "crawler"
17
+
18
+ begin
19
+ Counter.hit "urls_popped"
20
+
21
+ puts "Fetching #{url}"
22
+ raw_html = Counter.latency "download" do
23
+ URI.parse(url).read
24
+ end
25
+
26
+ Counter.magnitude "bytes_in", raw_html.length
27
+
28
+ parsed_html = Counter.latency "html_parsing" do
29
+ Nokogiri::HTML(raw_html)
30
+ end
31
+ rescue
32
+ Counter.hit "error"
33
+ end
34
+ end
35
+
36
+ pp Counter if Counter.instance_of?(Counters::Memory)
37
+ pp Redis.new.hgetall("counters") if Counter.instance_of?(Counters::Redis)
@@ -15,6 +15,43 @@ describe Counters::File do
15
15
 
16
16
  it_should_behave_like "all counters"
17
17
 
18
+ context "#initialize" do
19
+ it "should accept a namepsace in options" do
20
+ counter = Counters::File.new(tempfile, :namespace => "wine")
21
+ counter.namespace.should == "wine"
22
+ end
23
+ end
24
+
25
+ context "given the counter is namespaced" do
26
+ it "should namespace the key from #hit" do
27
+ counter.namespace = "juice"
28
+ counter.hit "foxglove"
29
+ tempfile.rewind
30
+ tempfile.read.should =~ /^#{TIMESTAMP_RE}\s-\shit:\sjuice\.foxglove$/
31
+ end
32
+
33
+ it "should namespace the key from #latency" do
34
+ counter.namespace = "cocktail"
35
+ counter.latency "angelica", 200
36
+ tempfile.rewind
37
+ tempfile.read.should =~ /^#{TIMESTAMP_RE}\s-\slatency:\scocktail\.angelica\s200s$/
38
+ end
39
+
40
+ it "should namespace the key from #magnitude" do
41
+ counter.namespace = "brew"
42
+ counter.magnitude "crocus", 100
43
+ tempfile.rewind
44
+ tempfile.read.should =~ /^#{TIMESTAMP_RE}\s-\smagnitude:\sbrew\.crocus 100$/
45
+ end
46
+
47
+ it "should namespace the key from #ping" do
48
+ counter.namespace = "beer"
49
+ counter.ping "tulip"
50
+ tempfile.rewind
51
+ tempfile.read.should =~ /^#{TIMESTAMP_RE}\s-\sping:\sbeer\.tulip$/
52
+ end
53
+ end
54
+
18
55
  it "should log a message to the logfile when a hit is recorded" do
19
56
  counter.hit "urls.visited"
20
57
  tempfile.rewind
@@ -7,6 +7,41 @@ describe Counters::Memory do
7
7
 
8
8
  it_should_behave_like "all counters"
9
9
 
10
+ context "#initialize" do
11
+ it "should accept a namepsace in options" do
12
+ counter = Counters::Memory.new(:namespace => "wine")
13
+ counter.namespace.should == "wine"
14
+ end
15
+ end
16
+
17
+ context "given the counter is namespaced" do
18
+ it "should namespace the key from #hit" do
19
+ counter.namespace = "juice"
20
+ counter.hit "foxglove"
21
+ counter.hits["juice.foxglove"].should == 1
22
+ end
23
+
24
+ it "should namespace the key from #latency" do
25
+ counter.namespace = "cocktail"
26
+ counter.latency "angelica", 200
27
+ counter.latencies["cocktail.angelica"].should == [200]
28
+ end
29
+
30
+ it "should namespace the key from #magnitude" do
31
+ counter.namespace = "brew"
32
+ counter.magnitude "crocus", 100
33
+ counter.magnitudes["brew.crocus"].should == 100
34
+ end
35
+
36
+ it "should namespace the key from #ping" do
37
+ counter.namespace = "beer"
38
+ Timecop.freeze(Time.now.utc) do
39
+ counter.ping "tulip"
40
+ counter.pings["beer.tulip"].should == Time.now.utc
41
+ end
42
+ end
43
+ end
44
+
10
45
  it "should record a hit with key 'pages.read'" do
11
46
  counter.hit "pages.read"
12
47
  counter.hits.should have_key("pages.read")
@@ -20,14 +55,14 @@ describe Counters::Memory do
20
55
  Timecop.freeze do
21
56
  counter.ping "processor.alive"
22
57
  counter.pings.should have_key("processor.alive")
23
- counter.pings["processor.alive"].strftime("%Y-%m-%d %H:%M:%S").should == Time.now.strftime("%Y-%m-%d %H:%M:%S")
58
+ counter.pings["processor.alive"].strftime("%Y-%m-%d %H:%M:%S").should == Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")
24
59
  end
25
60
 
26
61
  target_time = Time.now + 9
27
62
  Timecop.travel(target_time) do
28
63
  counter.ping "processor.alive"
29
64
  counter.pings.should have_key("processor.alive")
30
- counter.pings["processor.alive"].strftime("%Y-%m-%d %H:%M:%S").should == target_time.strftime("%Y-%m-%d %H:%M:%S")
65
+ counter.pings["processor.alive"].strftime("%Y-%m-%d %H:%M:%S").should == target_time.utc.strftime("%Y-%m-%d %H:%M:%S")
31
66
  end
32
67
  end
33
68
 
@@ -7,7 +7,7 @@ describe Counters::Redis, "integration tests" do
7
7
  end
8
8
 
9
9
  let :counter do
10
- Counters::Redis.new(redis, "counters")
10
+ Counters::Redis.new(redis, :base_key => "counters")
11
11
  end
12
12
 
13
13
  before(:each) do
@@ -16,6 +16,41 @@ describe Counters::Redis, "integration tests" do
16
16
 
17
17
  it_should_behave_like "all counters"
18
18
 
19
+ context "#initialize" do
20
+ it "should accept a namepsace in options" do
21
+ counter = Counters::Redis.new(redis, :namespace => "wine", :base_key => "counters")
22
+ counter.namespace.should == "wine"
23
+ end
24
+ end
25
+
26
+ context "given the counter is namespaced" do
27
+ it "should namespace the key from #hit" do
28
+ counter.namespace = "juice"
29
+ counter.hit "foxglove"
30
+ redis.hget("counters", "hits.juice.foxglove").should == "1"
31
+ end
32
+
33
+ it "should namespace the key from #latency" do
34
+ counter.namespace = "cocktail"
35
+ counter.latency "angelica", 200
36
+ redis.hget("counters", "latencies.cocktail.angelica.nanoseconds").should == (200 * Counters::Redis::SCALING_FACTOR).to_s
37
+ end
38
+
39
+ it "should namespace the key from #magnitude" do
40
+ counter.namespace = "brew"
41
+ counter.magnitude "crocus", 100
42
+ redis.hget("counters", "magnitudes.brew.crocus.value").should == "100"
43
+ end
44
+
45
+ it "should namespace the key from #ping" do
46
+ counter.namespace = "beer"
47
+ Timecop.freeze(Time.now.utc) do
48
+ counter.ping "tulip"
49
+ redis.hget("counters", "pings.beer.tulip").should == Time.now.utc.to_i.to_s
50
+ end
51
+ end
52
+ end
53
+
19
54
  it "should record 2 hits on 'pages.read'" do
20
55
  2.times { counter.hit "pages.read" }
21
56
  redis.hkeys("counters").should == ["hits.pages.read"]
data/spec/spec_helper.rb CHANGED
@@ -9,13 +9,13 @@ end
9
9
 
10
10
  shared_examples_for "all counters" do
11
11
  it "should raise a ArgumentError when the key includes invalid chars" do
12
- lambda { counter.hit "hit!" } .should raise_error(ArgumentError)
13
- lambda { counter.hit "hit counter" } .should raise_error(ArgumentError)
14
- lambda { counter.hit "boy.hit?" } .should raise_error(ArgumentError)
15
- lambda { counter.hit "hit/a" } .should raise_error(ArgumentError)
16
- lambda { counter.hit "hit-a" } .should raise_error(ArgumentError)
17
- lambda { counter.hit "" } .should raise_error(ArgumentError)
18
- lambda { counter.hit nil } .should raise_error(ArgumentError)
12
+ lambda { counter.hit "hit!" }.should raise_error(ArgumentError)
13
+ lambda { counter.hit "hit counter" }.should raise_error(ArgumentError)
14
+ lambda { counter.hit "boy.hit?" }.should raise_error(ArgumentError)
15
+ lambda { counter.hit "hit/a" }.should raise_error(ArgumentError)
16
+ lambda { counter.hit "hit-a" }.should raise_error(ArgumentError)
17
+ lambda { counter.hit "" }.should raise_error(ArgumentError)
18
+ lambda { counter.hit nil }.should raise_error(ArgumentError)
19
19
  end
20
20
 
21
21
  it "should not raise ArgumentError when the key includes a number" do
@@ -0,0 +1,80 @@
1
+ require "spec_helper"
2
+
3
+ describe Counters::StatsD do
4
+ let :socket do
5
+ double("socket")
6
+ end
7
+
8
+ let :host do
9
+ "127.0.0.1"
10
+ end
11
+
12
+ let :port do
13
+ 8125
14
+ end
15
+
16
+ let :counter do
17
+ Counters::StatsD.new(host, port, :socket => socket)
18
+ end
19
+
20
+ context "" do
21
+ before(:each) do
22
+ socket.as_null_object
23
+ end
24
+
25
+ it_should_behave_like "all counters"
26
+ end
27
+
28
+ it "should record a hit as an increment of the key" do
29
+ socket.should_receive(:send).with("hits.tweets_received:1|c", 0, host, port)
30
+ counter.hit "tweets_received"
31
+ end
32
+
33
+ it "should record a magnitude as a timer" do
34
+ socket.should_receive(:send).with("magnitudes.bytes_in:381|ms", 0, host, port)
35
+ counter.magnitude "bytes_in", 381
36
+ end
37
+
38
+ it "should record a latency as a timer" do
39
+ socket.should_receive(:send).with("latencies.json_parsing:9|ms", 0, host, port)
40
+ counter.latency "json_parsing", 0.009
41
+ end
42
+
43
+ it "should record a ping as a counter" do
44
+ socket.should_receive(:send).with("pings.tweet_processor:1|c", 0, host, port)
45
+ counter.ping "tweet_processor"
46
+ end
47
+
48
+ context "#initialize" do
49
+ it "should accept a namepsace in options" do
50
+ counter = Counters::StatsD.new(host, port, :namespace => "wine")
51
+ counter.namespace.should == "wine"
52
+ end
53
+ end
54
+
55
+ context "given the counter is namespaced" do
56
+ it "should namespace the key from #hit" do
57
+ counter.namespace = "juice"
58
+ socket.should_receive(:send).with(/^hits\.juice\.foxglove\b/, anything, anything, anything)
59
+ counter.hit "foxglove"
60
+ end
61
+
62
+ it "should namespace the key from #latency" do
63
+ counter.namespace = "cocktail"
64
+ socket.should_receive(:send).with(/^latencies\.cocktail\.angelica\b/, anything, anything, anything)
65
+ counter.latency "angelica", 200
66
+ end
67
+
68
+ it "should namespace the key from #magnitude" do
69
+ counter.namespace = "brew"
70
+ socket.should_receive(:send).with(/^magnitudes\.brew\.crocus\b/, anything, anything, anything)
71
+ counter.magnitude "crocus", 100
72
+ end
73
+
74
+ it "should namespace the key from #ping" do
75
+ counter.namespace = "beer"
76
+ socket.should_receive(:send).with(/^pings\.beer\.tulip\b/, anything, anything, anything)
77
+ counter.ping "tulip"
78
+ end
79
+ end
80
+ end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: counters
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 1
7
- - 0
8
- - 2
9
- version: 1.0.2
4
+ prerelease:
5
+ version: 1.1.0
10
6
  platform: ruby
11
7
  authors:
12
8
  - "Fran\xC3\xA7ois Beausoleil"
@@ -14,7 +10,7 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2011-03-21 00:00:00 -04:00
13
+ date: 2011-03-29 00:00:00 -04:00
18
14
  default_executable:
19
15
  dependencies:
20
16
  - !ruby/object:Gem::Dependency
@@ -25,8 +21,6 @@ dependencies:
25
21
  requirements:
26
22
  - - ">="
27
23
  - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
24
  version: "0"
31
25
  type: :development
32
26
  version_requirements: *id001
@@ -38,8 +32,6 @@ dependencies:
38
32
  requirements:
39
33
  - - ">="
40
34
  - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
35
  version: "0"
44
36
  type: :development
45
37
  version_requirements: *id002
@@ -51,8 +43,6 @@ dependencies:
51
43
  requirements:
52
44
  - - ">="
53
45
  - !ruby/object:Gem::Version
54
- segments:
55
- - 0
56
46
  version: "0"
57
47
  type: :development
58
48
  version_requirements: *id003
@@ -64,8 +54,6 @@ dependencies:
64
54
  requirements:
65
55
  - - ">="
66
56
  - !ruby/object:Gem::Version
67
- segments:
68
- - 0
69
57
  version: "0"
70
58
  type: :development
71
59
  version_requirements: *id004
@@ -92,11 +80,14 @@ files:
92
80
  - lib/counters/file.rb
93
81
  - lib/counters/memory.rb
94
82
  - lib/counters/redis.rb
83
+ - lib/counters/stats_d.rb
95
84
  - lib/counters/version.rb
85
+ - samples/crawler.rb
96
86
  - spec/file_counter_spec.rb
97
87
  - spec/memory_counter_spec.rb
98
88
  - spec/redis_counter_spec.rb
99
89
  - spec/spec_helper.rb
90
+ - spec/statsd_counter_spec.rb
100
91
  has_rdoc: true
101
92
  homepage: https://github.com/francois/counters
102
93
  licenses: []
@@ -111,21 +102,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
102
  requirements:
112
103
  - - ">="
113
104
  - !ruby/object:Gem::Version
114
- segments:
115
- - 0
116
105
  version: "0"
117
106
  required_rubygems_version: !ruby/object:Gem::Requirement
118
107
  none: false
119
108
  requirements:
120
109
  - - ">="
121
110
  - !ruby/object:Gem::Version
122
- segments:
123
- - 0
124
111
  version: "0"
125
112
  requirements: []
126
113
 
127
114
  rubyforge_project:
128
- rubygems_version: 1.3.7
115
+ rubygems_version: 1.6.2
129
116
  signing_key:
130
117
  specification_version: 3
131
118
  summary: Provides an API to record any kind of metrics within your system
@@ -134,3 +121,4 @@ test_files:
134
121
  - spec/memory_counter_spec.rb
135
122
  - spec/redis_counter_spec.rb
136
123
  - spec/spec_helper.rb
124
+ - spec/statsd_counter_spec.rb