simple_metrics 0.0.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/bin/populate ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+
6
+ require 'optparse'
7
+ require "simple_metrics"
8
+
9
+ ts = Time.now.to_i
10
+
11
+ name = "test.post.clicks2"
12
+
13
+ SimpleMetrics::Mongo.ensure_collections_exist
14
+
15
+ SimpleMetrics::Bucket.all.each do |bucket|
16
+ ts_bucket = bucket.ts_bucket(ts)
17
+
18
+ previous_value = rand(300)
19
+ (1..100).each do |index|
20
+ ts = ts_bucket - (bucket.seconds*index)
21
+ value = previous_value+rand(20)
22
+ data_point = SimpleMetrics::DataPoint.create_counter(:name => name, :value => value)
23
+ bucket.save(data_point, ts)
24
+ end
25
+ end
@@ -21,14 +21,17 @@ module SimpleMetrics
21
21
  Bucket.all.sort_by! { |r| r.seconds }[1..-1]
22
22
  end
23
23
 
24
- def flush_stats(stats)
25
- return if stats.empty?
26
- SimpleMetrics.logger.info "#{Time.now} Flushing #{stats.count} counters to MongoDB"
24
+ def flush_data_points(data_points)
25
+ return if data_points.empty?
26
+ SimpleMetrics.logger.info "#{Time.now} Flushing #{data_points.count} counters to MongoDB"
27
27
 
28
28
  ts = Time.now.utc.to_i
29
29
  bucket = Bucket.first
30
- stats.each { |data| bucket.save(data, ts) }
31
-
30
+ data_points.group_by { |data| data.name }.each_pair do |name,dps|
31
+ data = DataPoint.aggregate(dps)
32
+ bucket.save(data, ts)
33
+ end
34
+
32
35
  self.aggregate_all(ts)
33
36
  end
34
37
 
@@ -41,10 +44,10 @@ module SimpleMetrics
41
44
  SimpleMetrics.logger.debug "Aggregating #{bucket.name} #{previous_ts}....#{current_ts} (#{humanized_timestamp(previous_ts)}..#{humanized_timestamp(current_ts)})"
42
45
 
43
46
  unless bucket.stats_exist_in_previous_ts?(previous_ts)
44
- stats_coll = self.first.find_all_in_ts_range(previous_ts, current_ts)
45
- stats_coll.group_by { |stats| stats.name }.each_pair do |name,stats_array|
46
- stats = Stats.aggregate(stats_array)
47
- bucket.save(stats, previous_ts)
47
+ data_points = self.first.find_all_in_ts_range(previous_ts, current_ts)
48
+ data_points.group_by { |data| data.name }.each_pair do |name,dps|
49
+ data = DataPoint.aggregate(dps)
50
+ bucket.save(data, previous_ts)
48
51
  end
49
52
  end
50
53
  end
@@ -88,33 +91,54 @@ module SimpleMetrics
88
91
 
89
92
  def find(id)
90
93
  mongo_result = mongo_coll.find_one({ :_id => id })
91
- Stats.create_from_db(mongo_result)
94
+ DataPoint.create_from_db(mongo_result)
92
95
  end
93
96
 
94
97
  def find_all_by_name(name)
95
- mongo_result = mongo_coll.find({ :name => name })
96
- mongo_result.inject([]) { |result, a| result << Stats.create_from_db(a) }
98
+ mongo_result = mongo_coll.find({ :name => name }).to_a
99
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
97
100
  end
98
101
 
99
102
  def find_all_in_ts(ts)
100
- mongo_result = mongo_coll.find({ :ts => ts_bucket(ts) })
101
- mongo_result.inject([]) { |result, a| result << Stats.create_from_db(a) }
103
+ mongo_result = mongo_coll.find({ :ts => ts_bucket(ts) }).to_a
104
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
102
105
  end
103
106
 
104
107
  def find_all_in_ts_by_name(ts, name)
105
- mongo_result = mongo_coll.find({ :ts => ts_bucket(ts), :name => name })
106
- mongo_result.inject([]) { |result, a| result << Stats.create_from_db(a) }
108
+ mongo_result = mongo_coll.find({ :ts => ts_bucket(ts), :name => name }).to_a
109
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
107
110
  end
108
111
 
109
- def find_all_in_ts_range(previous_ts, current_ts)
110
- mongo_result = mongo_coll.find({ :ts => { "$gte" => previous_ts, "$lt" => current_ts }}).to_a
111
- mongo_result.inject([]) { |result, a| result << Stats.create_from_db(a) }
112
+ def find_all_in_ts_range(from, to)
113
+ mongo_result = mongo_coll.find({ :ts => { "$gte" => from, "$lte" => to }}).to_a
114
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
115
+ end
116
+
117
+ def find_all_in_ts_range_by_name(from, to, name)
118
+ mongo_result = mongo_coll.find({ :name => name, :ts => { "$gte" => from, "$lte" => to }}).to_a
119
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
120
+ end
121
+
122
+ def find_all_in_ts_range_by_wildcard(from, to, target)
123
+ target = target.gsub('.', '\.')
124
+ target = target.gsub('*', '.*')
125
+ mongo_result = mongo_coll.find({ :name => /#{target}/, :ts => { "$gte" => from, "$lte" => to } }).to_a
126
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
127
+ end
128
+
129
+ def find_all_in_ts_range_by_regexp(from, to, target)
130
+ mongo_result = mongo_coll.find({ :name => /#{target}/, :ts => { "$gte" => from, "$lte" => to } }).to_a
131
+ mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
112
132
  end
113
133
 
114
134
  def stats_exist_in_previous_ts?(ts)
115
135
  mongo_coll.find({ :ts => ts }).count > 0
116
136
  end
117
137
 
138
+ def find_all_distinct_names
139
+ mongo_coll.distinct(:name).to_a
140
+ end
141
+
118
142
  def save(stats, ts)
119
143
  stats.ts = ts_bucket(ts)
120
144
  result = mongo_coll.insert(stats.attributes)
@@ -129,5 +153,36 @@ module SimpleMetrics
129
153
  @capped == true
130
154
  end
131
155
 
156
+ def fill_gaps(from, to, query_result)
157
+ return query_result if query_result.nil? || query_result.size == 0
158
+
159
+ tmp_hash = DataPoint.ts_hash(query_result)
160
+ dp_template = query_result.first
161
+
162
+ result = []
163
+ each_ts(from, to) do |current_bucket_ts|
164
+ result <<
165
+ if tmp_hash.key?(current_bucket_ts)
166
+ tmp_hash[current_bucket_ts]
167
+ else
168
+ dp = dp_template.dup
169
+ dp.value = nil
170
+ dp.ts = current_bucket_ts
171
+ dp
172
+ end
173
+ end
174
+ result
175
+ end
176
+
177
+ private
178
+
179
+ def each_ts(from, to)
180
+ current_bucket_ts = ts_bucket(from)
181
+ while (current_bucket_ts <= ts_bucket(to))
182
+ yield(current_bucket_ts)
183
+ current_bucket_ts = current_bucket_ts + seconds
184
+ end
185
+ end
186
+
132
187
  end
133
188
  end
@@ -0,0 +1,162 @@
1
+ # encoding: utf-8
2
+ module SimpleMetrics
3
+
4
+ class DataPoint
5
+
6
+ class NonMatchingTypesError < Exception; end
7
+ class ParserError < Exception; end
8
+
9
+ # examples:
10
+ # com.example.test1:1|c
11
+ # com.example.test2:-1|c
12
+ # com.example.test2:50|g
13
+ # com.example.test3:5|c|@0.1
14
+ # com.example.test4:44|ms
15
+ REGEXP = /^([\d\w_.]*):(-?[\d]*)\|(c|g|ms){1}(\|@([.\d]+))?$/i
16
+
17
+ class << self
18
+
19
+ def parse(str)
20
+ if str =~ REGEXP
21
+ name, value, type, sample_rate = $1, $2, $3, $5
22
+ if type == "ms"
23
+ # TODO: implement sample_rate handling
24
+ create_timing(:name => name, :value => value)
25
+ elsif type == "g"
26
+ create_gauge(:name => name, :value => (value.to_i || 1) * (1.0 / (sample_rate || 1).to_f) )
27
+ elsif type == "c"
28
+ create_counter(:name => name, :value => (value.to_i || 1) * (1.0 / (sample_rate || 1).to_f) )
29
+ end
30
+ else
31
+ raise ParserError, "Parser Error - Invalid Stat: #{str}"
32
+ end
33
+ end
34
+
35
+ def create_counter(attributes)
36
+ self.new(attributes.merge(:type => 'c'))
37
+ end
38
+
39
+ def create_gauge(attributes)
40
+ self.new(attributes.merge(:type => 'g'))
41
+ end
42
+
43
+ def create_timing(attributes)
44
+ self.new(attributes.merge(:type => 'ms'))
45
+ end
46
+
47
+ def aggregate(stats_array, name = nil)
48
+ raise NonMatchingTypesError unless stats_array.group_by { |stats| stats.type }.size == 1
49
+
50
+ result_stat = stats_array.first.dup
51
+ result_stat.name = name if name
52
+ if stats_array.first.counter?
53
+ result_stat.value = stats_array.map { |stats| stats.value }.inject(0) { |result, value| result += value }
54
+ result_stat
55
+ elsif stats_array.first.gauge?
56
+ total_value = stats_array.map { |stats| stats.value }.inject(0) { |result, value| result += value }
57
+ result_stat.value = total_value / stats_array.size
58
+ result_stat
59
+ elsif stats_array.first.timing?
60
+ # TODO implement timing aggregation
61
+ elsif stats_array.first.event?
62
+ # TODO implement event aggregation
63
+ else
64
+ raise ArgumentError, "Unknown data point type"
65
+ end
66
+ end
67
+
68
+ def aggregate_array(stats_array, name = nil)
69
+ raise NonMatchingTypesError unless stats_array.group_by { |stats| stats.type }.size == 1
70
+
71
+ if stats_array.first.counter?
72
+ tmp_hash = ts_hash_aggregated(stats_array) do |value1, value2|
73
+ value1 + value2
74
+ end
75
+
76
+ result = []
77
+ tmp_hash.each_pair do |key, value|
78
+ result << self.new(:name => name, :ts => key, :value => value, :type => 'c')
79
+ end
80
+ result
81
+ elsif stats_array.first.gauge?
82
+ tmp_hash = ts_hash_aggregated(stats_array) do |value1, value2|
83
+ (value1 + value2)/2
84
+ end
85
+
86
+ result = []
87
+ tmp_hash.each_pair do |key, value|
88
+ result << self.new(:name => name, :ts => key, :value => value, :type => 'g')
89
+ end
90
+ result
91
+ elsif stats_array.first.timing?
92
+ # TODO implement timing aggregation
93
+ elsif stats_array.first.event?
94
+ # TODO implement event aggregation
95
+ else
96
+ raise ArgumentError, "Unknown data point type"
97
+ end
98
+ end
99
+
100
+ def create_from_db(attributes)
101
+ self.new(:name => attributes["name"], :value => attributes["value"], :ts => attributes["ts"], :type => attributes["type"])
102
+ end
103
+
104
+ def ts_hash(query_result)
105
+ query_result.inject({}) { |result, dp| result[dp.ts] = dp; result }
106
+ end
107
+
108
+ private
109
+
110
+ def ts_hash_aggregated(data_points, &block)
111
+ tmp = {}
112
+ data_points.each do |dp|
113
+ if tmp.key?(dp.ts)
114
+ tmp[dp.ts] = block.call(tmp[dp.ts], dp.value)
115
+ else
116
+ tmp[dp.ts] = dp.value
117
+ end
118
+ end
119
+ tmp
120
+ end
121
+ end
122
+
123
+ attr_accessor :name, :ts, :type, :value
124
+
125
+ def initialize(attributes)
126
+ @name = attributes[:name]
127
+ @value = attributes[:value]
128
+ @ts = attributes[:ts]
129
+ @type = attributes[:type]
130
+ end
131
+
132
+ def counter?
133
+ type == 'c'
134
+ end
135
+
136
+ def gauge?
137
+ type == 'g'
138
+ end
139
+
140
+ def timing?
141
+ type == 'ms'
142
+ end
143
+
144
+ def timestamp
145
+ ts
146
+ end
147
+
148
+ def value
149
+ @value.to_i if @value
150
+ end
151
+
152
+ def attributes
153
+ {
154
+ :name => name,
155
+ :value => value,
156
+ :ts => ts,
157
+ :type => type
158
+ }
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,70 @@
1
+ module SimpleMetrics
2
+
3
+ module Functions
4
+
5
+ # calculate the maximum value for multiple targets
6
+ #
7
+ # params:
8
+ # data_points [1, 3, 5], [2, 1, 6]
9
+ #
10
+ # return:
11
+ # array [2, 3, 6]
12
+ def max(*data_points)
13
+ end
14
+
15
+ # calculate the minimum value for multiple targets
16
+ def min(*data_points)
17
+ end
18
+
19
+ # add offset to each value
20
+ def offset(*data_points)
21
+ end
22
+
23
+ # multiple each value
24
+ def scale(*data_points)
25
+ end
26
+
27
+ # Return sum of all databounds
28
+ #
29
+ # params:
30
+ # data_points [1, 3, 5], [2, 1, 6]
31
+ #
32
+ # return:
33
+ # array [3, 4, 11]
34
+ def sum(*data_points)
35
+ end
36
+
37
+ #
38
+ # Other ideas
39
+ #
40
+ # as_percent:
41
+ # * calculate percentage of given targets
42
+ # * sum of all data_points will be used as the total 100% marker
43
+ #
44
+ # average_above (param):
45
+ # * return data_points with average value above given param
46
+ #
47
+ # average_below (param):
48
+ # * return data_points with average value below given param
49
+ #
50
+ # average_series:
51
+ # * return average value of given multiple targets
52
+ #
53
+ # current_above (param):
54
+ # * return data_points with value above given param
55
+ #
56
+ # current_below (param):
57
+ # * return data_points with value below given param
58
+ #
59
+ # derivative:
60
+ # * take an absolute value based target and show how many requests per min were handled
61
+ #
62
+ # integral:
63
+ # * calculate sum over time for relative values collected per minute
64
+ #
65
+ # logarithm:
66
+ # * calculate the value with log n (base 10 default)
67
+ #
68
+
69
+ end # module Functions
70
+ end
@@ -0,0 +1,58 @@
1
+ module SimpleMetrics
2
+
3
+ #
4
+ # url format examples:
5
+ # * target=com.post.clicks (1 line in graph)
6
+ # * target=com.post.clicks.text&target=com.post.clicks.logo (2 lines in graph)
7
+ # * target=com.post.clicks.* (1 aggregated line in graph)
8
+ #
9
+ module Graph
10
+ extend self
11
+
12
+ def minutes
13
+ Bucket[0]
14
+ end
15
+
16
+ def hours
17
+ Bucket[1]
18
+ end
19
+
20
+ def day
21
+ Bucket[2]
22
+ end
23
+
24
+ def week
25
+ Bucket[3]
26
+ end
27
+
28
+ def query_all(bucket, from, to, *targets)
29
+ result = {}
30
+ Array(targets).each do |target|
31
+ result[target.inspect] = values_only(query(bucket, from, to, target))
32
+ end
33
+ result
34
+ end
35
+
36
+ def query(bucket, from, to, target)
37
+ if target.is_a?(Regexp)
38
+ result = bucket.find_all_in_ts_range_by_regexp(from, to, target)
39
+ result = DataPoint.aggregate_array(result, target.inspect)
40
+ bucket.fill_gaps(from, to, result)
41
+ elsif target.is_a?(String) && target.include?('*')
42
+ result = bucket.find_all_in_ts_range_by_wildcard(from, to, target)
43
+ result = DataPoint.aggregate_array(result, target)
44
+ bucket.fill_gaps(from, to, result)
45
+ elsif target.is_a?(String)
46
+ result = bucket.find_all_in_ts_range_by_name(from, to, target)
47
+ bucket.fill_gaps(from, to, result)
48
+ else
49
+ raise ArgumentError, "Unknown target: #{target.inspect}"
50
+ end
51
+ end
52
+
53
+ def values_only(data_point_array)
54
+ data_point_array.map { |data| { :ts => data.ts, :value => data.value } }
55
+ end
56
+
57
+ end
58
+ end
@@ -12,6 +12,7 @@ module SimpleMetrics
12
12
  db.create_collection(bucket.name, :capped => bucket.capped, :size => bucket.size)
13
13
  SimpleMetrics.logger.debug "SERVER: MongoDB - created collection #{bucket.name}, capped: #{bucket.capped}, size: #{bucket.size}"
14
14
  end
15
+
15
16
  db.collection(bucket.name).ensure_index([['ts', ::Mongo::ASCENDING]])
16
17
  SimpleMetrics.logger.debug "SERVER: MongoDB - ensure index on column ts for collection #{bucket.name}"
17
18
  end
@@ -36,7 +37,7 @@ module SimpleMetrics
36
37
  end
37
38
 
38
39
  def connection
39
- @@connection ||= ::Mongo::Connection.new(SimpleMetrics.db_config[:host])
40
+ @@connection ||= ::Mongo::Connection.new(SimpleMetrics.db_config[:host], SimpleMetrics.db_config[:port])
40
41
  end
41
42
 
42
43
  def db
@@ -5,18 +5,18 @@ module SimpleMetrics
5
5
 
6
6
  module ClientHandler
7
7
 
8
- @@stats = []
8
+ @@data_points = []
9
9
 
10
10
  class << self
11
- def get_and_clear_stats
12
- stats = @@stats.dup
13
- @@stats = []
14
- stats
11
+ def get_and_clear_data_points
12
+ data_points = @@data_points.dup
13
+ @@data_points = []
14
+ data_points
15
15
  end
16
16
  end
17
17
 
18
- def stats
19
- @@stats
18
+ def data_points
19
+ @@data_points
20
20
  end
21
21
 
22
22
  def post_init
@@ -26,9 +26,9 @@ module SimpleMetrics
26
26
  def receive_data(data)
27
27
  SimpleMetrics.logger.debug "received_data: #{data.inspect}"
28
28
 
29
- @@stats ||= []
30
- @@stats << Stats.parse(data)
31
- rescue Stats::ParserError => e
29
+ @@data_points ||= []
30
+ @@data_points << DataPoint.parse(data)
31
+ rescue DataPoint::ParserError => e
32
32
  SimpleMetrics.logger.debug "Invalid Data skipped: #{data}"
33
33
  end
34
34
  end
@@ -47,7 +47,7 @@ module SimpleMetrics
47
47
  EventMachine::add_periodic_timer(SimpleMetrics.config[:flush_interval]) do
48
48
  SimpleMetrics.logger.debug "SERVER: period timer triggered after #{SimpleMetrics.config[:flush_interval]} seconds"
49
49
 
50
- EM.defer { Bucket.flush_stats(ClientHandler.get_and_clear_stats) }
50
+ EM.defer { Bucket.flush_data_points(ClientHandler.get_and_clear_data_points) }
51
51
  end
52
52
  end
53
53
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module SimpleMetrics
3
- VERSION = "0.0.1"
3
+ VERSION = "0.2.2"
4
4
  end
@@ -4,8 +4,10 @@ require "logger"
4
4
  require "simple_metrics/version"
5
5
  require "simple_metrics/client"
6
6
  require "simple_metrics/server"
7
- require "simple_metrics/stats"
7
+ require "simple_metrics/data_point"
8
8
  require "simple_metrics/bucket"
9
+ require "simple_metrics/graph"
10
+ require "simple_metrics/functions"
9
11
  require "simple_metrics/mongo"
10
12
 
11
13
  module SimpleMetrics
@@ -76,12 +78,14 @@ module SimpleMetrics
76
78
 
77
79
  DB_CONFIG_DEFAULTS = {
78
80
  :host => 'localhost',
81
+ :port => 27017,
79
82
  :prefix => 'development'
80
83
  }.freeze
81
84
 
82
85
  def db_config=(options)
83
86
  @@db_config = {
84
- :host => options.delete(:host),
87
+ :host => options.delete(:host) || 'localhost',
88
+ :port => options.delete(:port) || 27017,
85
89
  :db_name => "simple_metrics_#{options.delete(:prefix)}",
86
90
  :options => MONGODB_DEFAULTS.merge(options)
87
91
  }
@@ -22,6 +22,6 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_dependency "eventmachine"
24
24
  s.add_dependency "daemons"
25
- s.add_dependency "mongo"
26
- s.add_dependency "bson_ext"
25
+ s.add_dependency "mongo", '~> 1.6'
26
+ s.add_dependency "bson", '~> 1.6'
27
27
  end
data/spec/bucket_spec.rb CHANGED
@@ -43,7 +43,7 @@ module SimpleMetrics
43
43
  end
44
44
 
45
45
  let(:stats) do
46
- Stats.create_counter(:name => "key1", :value => 5)
46
+ DataPoint.create_counter(:name => "key1", :value => 5)
47
47
  end
48
48
 
49
49
  it "saves given data in bucket" do
@@ -71,9 +71,9 @@ module SimpleMetrics
71
71
 
72
72
  describe "#find_all_by_name" do
73
73
  it "returns all stats for given name" do
74
- stats_same1 = Stats.create_counter(:name => "key1", :value => 5)
75
- stats_same2 = Stats.create_counter(:name => "key1", :value => 3)
76
- stats_different = Stats.create_counter(:name => "key2", :value => 3)
74
+ stats_same1 = DataPoint.create_counter(:name => "key1", :value => 5)
75
+ stats_same2 = DataPoint.create_counter(:name => "key1", :value => 3)
76
+ stats_different = DataPoint.create_counter(:name => "key2", :value => 3)
77
77
 
78
78
  bucket.save(stats_same1, ts)
79
79
  bucket.save(stats_same2, ts)
@@ -87,8 +87,8 @@ module SimpleMetrics
87
87
 
88
88
  describe "#find_all_in_ts" do
89
89
  it "returns all stats in given timestamp" do
90
- stats1 = Stats.create_counter(:name => "key1", :value => 5)
91
- stats2 = Stats.create_counter(:name => "key2", :value => 3)
90
+ stats1 = DataPoint.create_counter(:name => "key1", :value => 5)
91
+ stats2 = DataPoint.create_counter(:name => "key2", :value => 3)
92
92
 
93
93
  bucket.save(stats1, ts)
94
94
  bucket.save(stats2, bucket.next_ts_bucket(ts))
@@ -105,10 +105,10 @@ module SimpleMetrics
105
105
 
106
106
  describe "#find_all_in_ts_by_name" do
107
107
  it "returns all stats for given name and timestamp" do
108
- stats1a = Stats.create_counter(:name => "key1", :value => 5)
109
- stats1b = Stats.create_counter(:name => "key1", :value => 7)
110
- stats2 = Stats.create_counter(:name => "key2", :value => 7)
111
- stats1_different_ts = Stats.create_counter(:name => "key1", :value => 3)
108
+ stats1a = DataPoint.create_counter(:name => "key1", :value => 5)
109
+ stats1b = DataPoint.create_counter(:name => "key1", :value => 7)
110
+ stats2 = DataPoint.create_counter(:name => "key2", :value => 7)
111
+ stats1_different_ts = DataPoint.create_counter(:name => "key1", :value => 3)
112
112
 
113
113
  bucket.save(stats1a, ts)
114
114
  bucket.save(stats1b, ts)
@@ -122,6 +122,51 @@ module SimpleMetrics
122
122
  end
123
123
  end
124
124
 
125
+ describe "#find_all_in_ts_by_wildcard" do
126
+ it "returns all stats for given name and timestamp" do
127
+ stats1 = DataPoint.create_counter(:name => "com.test.key1", :value => 5)
128
+ stats2 = DataPoint.create_counter(:name => "com.test.key2", :value => 7)
129
+ stats_different = DataPoint.create_counter(:name => "com.test2.key1", :value => 3)
130
+
131
+ from = bucket.ts_bucket(ts)
132
+ to = from
133
+ bucket.save(stats1, ts)
134
+ bucket.save(stats2, ts)
135
+ bucket.save(stats_different, ts)
136
+
137
+ results = bucket.find_all_in_ts_range_by_wildcard(from, to, "com.test.*")
138
+
139
+ results.should have(2).items
140
+ results.first.name.should == "com.test.key1"
141
+ results.last.name.should == "com.test.key2"
142
+ end
143
+ end
144
+
145
+ describe "#fill_gaps" do
146
+ it "returns stats and fills missing gaps with null entries" do
147
+ stats = DataPoint.create_counter(:name => "com.test.key1", :value => 5)
148
+
149
+ from = ts - 10
150
+ to = ts + 10
151
+ bucket.save(stats, ts)
152
+ ts_bucket = bucket.ts_bucket(ts)
153
+
154
+ results = bucket.fill_gaps(from, to, [stats])
155
+
156
+ results.should have(3).items
157
+ results[0].name.should == "com.test.key1"
158
+ results[1].name.should == "com.test.key1"
159
+ results[2].name.should == "com.test.key1"
160
+
161
+ results[0].value.should be_nil
162
+ results[1].value.should == 5
163
+ results[2].value.should be_nil
164
+
165
+ results[0].ts.should == ts_bucket - 10
166
+ results[1].ts.should == ts_bucket
167
+ results[2].ts.should == ts_bucket + 10
168
+ end
169
+ end
125
170
  end # describe "finder methods"
126
171
 
127
172
  describe "#aggregate_all" do
@@ -130,10 +175,10 @@ module SimpleMetrics
130
175
  Mongo.ensure_collections_exist
131
176
  end
132
177
 
133
- it "aggregates all stats" do
134
- stats1a = Stats.create_counter(:name => "key1", :value => 5)
135
- stats1b = Stats.create_counter(:name => "key1", :value => 7)
136
- stats2 = Stats.create_counter(:name => "key2", :value => 3)
178
+ it "aggregates all counter data points" do
179
+ stats1a = DataPoint.create_counter(:name => "key1", :value => 5)
180
+ stats1b = DataPoint.create_counter(:name => "key1", :value => 7)
181
+ stats2 = DataPoint.create_counter(:name => "key2", :value => 3)
137
182
 
138
183
  bucket2 = Bucket[1]
139
184
  ts_at_insert = bucket2.previous_ts_bucket(ts)
@@ -154,28 +199,75 @@ module SimpleMetrics
154
199
  key2_result.value.should == 3
155
200
  key2_result.should be_counter
156
201
  end
202
+
203
+ it "aggregates all gauge data points" do
204
+ stats1a = DataPoint.create_gauge(:name => "key1", :value => 5)
205
+ stats1b = DataPoint.create_gauge(:name => "key1", :value => 7)
206
+ stats2 = DataPoint.create_gauge(:name => "key2", :value => 3)
207
+
208
+ bucket2 = Bucket[1]
209
+ ts_at_insert = bucket2.previous_ts_bucket(ts)
210
+ bucket.save(stats1a, ts_at_insert)
211
+ bucket.save(stats1b, ts_at_insert)
212
+ bucket.save(stats2, ts_at_insert)
213
+
214
+ Bucket.aggregate_all(ts)
215
+
216
+ results = bucket2.find_all_in_ts(ts_at_insert)
217
+ results.should have(2).items
218
+
219
+ key1_result = results.find {|stat| stat.name == "key1"}
220
+ key1_result.value.should == 6
221
+ key1_result.should be_gauge
222
+
223
+ key2_result = results.find {|stat| stat.name == "key2"}
224
+ key2_result.value.should == 3
225
+ key2_result.should be_gauge
226
+ end
227
+
157
228
  end # describe "#aggregate_all"
158
229
 
159
- describe "#flush_stats" do
230
+ describe "#flush_data_points" do
160
231
  before do
161
- stats1 = Stats.create_counter(:name => "key1", :value => 5)
162
- stats2 = Stats.create_counter(:name => "key1", :value => 7)
163
- stats3 = Stats.create_counter(:name => "key2", :value => 3)
232
+ Mongo.truncate_collections
233
+ Mongo.ensure_collections_exist
234
+
235
+ stats1 = DataPoint.create_counter(:name => "key1", :value => 5)
236
+ stats2 = DataPoint.create_counter(:name => "key1", :value => 7)
237
+ stats3 = DataPoint.create_counter(:name => "key2", :value => 3)
164
238
  @stats = [stats1, stats2, stats3]
165
239
  end
166
240
 
167
241
  it "saves all stats in finest/first bucket" do
168
- Bucket.flush_stats(@stats)
242
+ Bucket.flush_data_points(@stats)
169
243
 
170
244
  results = bucket.find_all_in_ts(ts)
171
- results.should have(3).items
245
+ results.should have(2).items
172
246
  end
173
247
 
174
248
  it "calls aggregate_all afterwards" do
175
249
  mock(Bucket).aggregate_all(ts)
176
- Bucket.flush_stats(@stats)
250
+ Bucket.flush_data_points(@stats)
251
+ end
252
+
253
+ it "saves all stats and aggregate if duplicates found" do
254
+ Bucket.flush_data_points(@stats)
255
+
256
+ results = bucket.find_all_in_ts(ts)
257
+ results.should have(2).items
258
+ results.first.name.should == "key1"
259
+ results.last.name.should == "key2"
260
+ results.first.value == 12
261
+ results.last.value == 3
177
262
  end
178
- end # describe "#flush_stats"
263
+
264
+ it "raises error if name matches but type does not" do
265
+ stats4 = DataPoint.create_gauge(:name => "key1", :value => 3)
266
+ input = @stats + [stats4]
267
+ expect { Bucket.flush_data_points(input) }.to raise_error(SimpleMetrics::DataPoint::NonMatchingTypesError)
268
+ end
269
+
270
+ end # describe "#flush_data_points"
179
271
 
180
272
  end
181
273
  end
@@ -0,0 +1,131 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ module SimpleMetrics
5
+
6
+ describe Bucket do
7
+
8
+ let(:ts) do
9
+ Time.now.utc.to_i
10
+ end
11
+
12
+ describe "#parse" do
13
+
14
+ it "parses increment counter" do
15
+ stats = DataPoint.parse("com.example.test1:1|c")
16
+ stats.name.should == "com.example.test1"
17
+ stats.value.should == 1
18
+ stats.should be_counter
19
+ end
20
+
21
+ it "parses decrement counter" do
22
+ stats = DataPoint.parse("com.example.test1:-1|c")
23
+ stats.name.should == "com.example.test1"
24
+ stats.value.should == -1
25
+ stats.should be_counter
26
+ end
27
+
28
+ it "parses counter with sample rate" do
29
+ stats = DataPoint.parse("com.example.test2:5|c|@0.1")
30
+ stats.name.should == "com.example.test2"
31
+ stats.value.should == 50
32
+ stats.should be_counter
33
+ end
34
+
35
+ it "parses increment gauge" do
36
+ stats = DataPoint.parse("com.example.test3:5|g")
37
+ stats.name.should == "com.example.test3"
38
+ stats.value.should == 5
39
+ stats.should be_gauge
40
+ end
41
+
42
+ it "parses increment gauge with sample rate" do
43
+ stats = DataPoint.parse("com.example.test3:5|g|@0.1")
44
+ stats.name.should == "com.example.test3"
45
+ stats.value.should == 50
46
+ stats.should be_gauge
47
+ end
48
+
49
+ it "parses increment timing" do
50
+ stats = DataPoint.parse("com.example.test4:44|ms")
51
+ stats.name.should == "com.example.test4"
52
+ stats.value.should == 44
53
+ stats.should be_timing
54
+ end
55
+
56
+ it "parses increment timing with sample rate" do
57
+ end
58
+ end
59
+
60
+
61
+ describe "#aggregate" do
62
+ it "aggregates counter data points" do
63
+ stats1 = DataPoint.create_counter(:name => "key1", :value => 5)
64
+ stats2 = DataPoint.create_counter(:name => "key1", :value => 7)
65
+ result = DataPoint.aggregate([stats1, stats2])
66
+ result.value.should == 12
67
+ result.name.should == "key1"
68
+ result.should be_counter
69
+ end
70
+
71
+ it "aggregates counter data points with custom name" do
72
+ stats1 = DataPoint.create_counter(:name => "key1", :value => 5)
73
+ stats2 = DataPoint.create_counter(:name => "key1", :value => 7)
74
+ result = DataPoint.aggregate([stats1, stats2], "new_name")
75
+ result.value.should == 12
76
+ result.name.should == "new_name"
77
+ result.should be_counter
78
+ end
79
+
80
+ it "aggregates gauge data points" do
81
+ stats1 = DataPoint.create_gauge(:name => "key1", :value => 5)
82
+ stats2 = DataPoint.create_gauge(:name => "key1", :value => 7)
83
+ result = DataPoint.aggregate([stats1, stats2])
84
+ result.value.should == 6
85
+ result.name.should == "key1"
86
+ result.should be_gauge
87
+ end
88
+
89
+ it "aggregates timing data points" do
90
+ end
91
+
92
+ it "aggregates event data points" do
93
+ end
94
+ end
95
+
96
+ describe "#aggregate_array" do
97
+ it "aggregates counter data points" do
98
+ stats1 = DataPoint.create_counter(:name => "com.test.key1", :value => 5, :ts => ts)
99
+ stats2 = DataPoint.create_counter(:name => "com.test.key1", :value => 7, :ts => ts)
100
+ stats3 = DataPoint.create_counter(:name => "com.test.key1", :value => 9, :ts => (ts + 60) )
101
+
102
+ results = DataPoint.aggregate_array([stats1, stats2, stats3], "com.test.*")
103
+ results.should have(2).data_points
104
+ results.first.name.should == "com.test.*"
105
+ results.last.name.should == "com.test.*"
106
+ results.first.value.should == 12
107
+ results.last.value.should == 9
108
+ end
109
+
110
+ it "aggregates gauge data points" do
111
+ stats1 = DataPoint.create_gauge(:name => "com.test.key1", :value => 5, :ts => ts)
112
+ stats2 = DataPoint.create_gauge(:name => "com.test.key1", :value => 7, :ts => ts)
113
+ stats3 = DataPoint.create_gauge(:name => "com.test.key1", :value => 9, :ts => (ts + 60) )
114
+
115
+ results = DataPoint.aggregate_array([stats1, stats2, stats3], "com.test.*")
116
+ results.should have(2).data_points
117
+ results.first.name.should == "com.test.*"
118
+ results.last.name.should == "com.test.*"
119
+ results.first.value.should == 6
120
+ results.last.value.should == 9
121
+ end
122
+
123
+ it "raises NonMatchingTypesError if types are different" do
124
+ stats1 = DataPoint.create_counter(:name => "com.test.key1", :value => 5, :ts => ts)
125
+ stats2 = DataPoint.create_gauge(:name => "com.test.key1", :value => 5, :ts => ts)
126
+ expect { DataPoint.aggregate_array([stats1, stats2], "com.test.*") }.to raise_error(SimpleMetrics::DataPoint::NonMatchingTypesError)
127
+ end
128
+ end
129
+ end
130
+
131
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ module SimpleMetrics
5
+
6
+ describe Graph do
7
+
8
+ let(:ts) do
9
+ Time.now.utc.to_i
10
+ end
11
+
12
+ let(:bucket) do
13
+ Bucket.first
14
+ end
15
+
16
+ before do
17
+ Mongo.truncate_collections
18
+ Mongo.ensure_collections_exist
19
+ end
20
+
21
+ describe "#query" do
22
+
23
+ it "returns string request data points as is" do
24
+ dp1 = DataPoint.create_counter(:name => "key1", :value => 5)
25
+
26
+ bucket.save(dp1, ts)
27
+
28
+ current_ts = bucket.ts_bucket(ts)
29
+ from = current_ts
30
+ to = current_ts
31
+ results = Graph.query(bucket, from, to, "key1")
32
+
33
+ results.should have(1).data_point
34
+ end
35
+
36
+ it "returns string request data points and fill graps" do
37
+ dp1 = DataPoint.create_counter(:name => "key1", :value => 5)
38
+
39
+ bucket.save(dp1, ts)
40
+
41
+ current_ts = bucket.ts_bucket(ts)
42
+ from = current_ts
43
+ to = current_ts+10
44
+ results = Graph.query(bucket, from, to, "key1")
45
+
46
+ results.should have(2).data_point
47
+ results.first.value.should == 5
48
+ results.last.value.should be_nil
49
+ end
50
+
51
+ it "returns wildcard request data points with aggregate counter" do
52
+ dp1 = DataPoint.create_counter(:name => "com.test.key1", :value => 5)
53
+ dp2 = DataPoint.create_counter(:name => "com.test.key2", :value => 7)
54
+
55
+ bucket.save(dp1, ts)
56
+ bucket.save(dp2, ts)
57
+
58
+ current_ts = bucket.ts_bucket(ts)
59
+ from = current_ts
60
+ to = current_ts
61
+ results = Graph.query(bucket, from, to, "com.test.*")
62
+ results.should have(1).data_point
63
+ result = results.first
64
+ result.name.should == "com.test.*"
65
+ result.value.should == 12
66
+ result.should be_counter
67
+ end
68
+
69
+ it "returns regexp request data points with aggregate gauge" do
70
+ dp1 = DataPoint.create_gauge(:name => "com.test.key1", :value => 5)
71
+ dp2 = DataPoint.create_gauge(:name => "com.test.key2", :value => 7)
72
+
73
+ bucket.save(dp1, ts)
74
+ bucket.save(dp2, ts)
75
+
76
+ current_ts = bucket.ts_bucket(ts)
77
+ from = current_ts
78
+ to = current_ts
79
+
80
+ results = Graph.query(bucket, from, to, /com\.test(.*)/)
81
+ results.should have(1).data_point
82
+ result = results.first
83
+ result.name.should == "/com\\.test(.*)/"
84
+ result.value.should == 6
85
+ result.should be_gauge
86
+ end
87
+ end
88
+ end
89
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.2.2
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: 2012-03-02 00:00:00.000000000 Z
12
+ date: 2012-04-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70244480135940 !ruby/object:Gem::Requirement
16
+ requirement: &70101883011000 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70244480135940
24
+ version_requirements: *70101883011000
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70244480135460 !ruby/object:Gem::Requirement
27
+ requirement: &70101883009820 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70244480135460
35
+ version_requirements: *70101883009820
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rr
38
- requirement: &70244480135020 !ruby/object:Gem::Requirement
38
+ requirement: &70101883009000 !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: *70244480135020
46
+ version_requirements: *70101883009000
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: eventmachine
49
- requirement: &70244480134600 !ruby/object:Gem::Requirement
49
+ requirement: &70101883008200 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70244480134600
57
+ version_requirements: *70101883008200
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: daemons
60
- requirement: &70244480134180 !ruby/object:Gem::Requirement
60
+ requirement: &70101883007560 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,33 +65,34 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70244480134180
68
+ version_requirements: *70101883007560
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mongo
71
- requirement: &70244480133740 !ruby/object:Gem::Requirement
71
+ requirement: &70101883006620 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
- - - ! '>='
74
+ - - ~>
75
75
  - !ruby/object:Gem::Version
76
- version: '0'
76
+ version: '1.6'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *70244480133740
79
+ version_requirements: *70101883006620
80
80
  - !ruby/object:Gem::Dependency
81
- name: bson_ext
82
- requirement: &70244480133300 !ruby/object:Gem::Requirement
81
+ name: bson
82
+ requirement: &70101883005800 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
- - - ! '>='
85
+ - - ~>
86
86
  - !ruby/object:Gem::Version
87
- version: '0'
87
+ version: '1.6'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *70244480133300
90
+ version_requirements: *70101883005800
91
91
  description: SimpleMetrics
92
92
  email:
93
93
  - fdietz@gmail.com
94
94
  executables:
95
+ - populate
95
96
  - simple_metrics_client
96
97
  - simple_metrics_server
97
98
  extensions: []
@@ -102,20 +103,24 @@ files:
102
103
  - Gemfile
103
104
  - README.markdown
104
105
  - Rakefile
106
+ - bin/populate
105
107
  - bin/simple_metrics_client
106
108
  - bin/simple_metrics_server
107
109
  - example/increment.rb
108
110
  - lib/simple_metrics.rb
109
111
  - lib/simple_metrics/bucket.rb
110
112
  - lib/simple_metrics/client.rb
113
+ - lib/simple_metrics/data_point.rb
114
+ - lib/simple_metrics/functions.rb
115
+ - lib/simple_metrics/graph.rb
111
116
  - lib/simple_metrics/mongo.rb
112
117
  - lib/simple_metrics/server.rb
113
- - lib/simple_metrics/stats.rb
114
118
  - lib/simple_metrics/version.rb
115
119
  - simple_metrics.gemspec
116
120
  - spec/bucket_spec.rb
121
+ - spec/data_point_spec.rb
122
+ - spec/graph_spec.rb
117
123
  - spec/spec_helper.rb
118
- - spec/stats_spec.rb
119
124
  homepage: ''
120
125
  licenses: []
121
126
  post_install_message:
@@ -128,19 +133,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
133
  - - ! '>='
129
134
  - !ruby/object:Gem::Version
130
135
  version: '0'
136
+ segments:
137
+ - 0
138
+ hash: 3607993347198601415
131
139
  required_rubygems_version: !ruby/object:Gem::Requirement
132
140
  none: false
133
141
  requirements:
134
142
  - - ! '>='
135
143
  - !ruby/object:Gem::Version
136
144
  version: '0'
145
+ segments:
146
+ - 0
147
+ hash: 3607993347198601415
137
148
  requirements: []
138
149
  rubyforge_project:
139
- rubygems_version: 1.8.15
150
+ rubygems_version: 1.8.17
140
151
  signing_key:
141
152
  specification_version: 3
142
153
  summary: SimpleMetrics
143
154
  test_files:
144
155
  - spec/bucket_spec.rb
156
+ - spec/data_point_spec.rb
157
+ - spec/graph_spec.rb
145
158
  - spec/spec_helper.rb
146
- - spec/stats_spec.rb
@@ -1,99 +0,0 @@
1
- # encoding: utf-8
2
- module SimpleMetrics
3
-
4
- class Stats
5
-
6
- class NonMatchingTypesError < Exception; end
7
- class ParserError < Exception; end
8
-
9
- # examples:
10
- # com.example.test1:1|c
11
- # com.example.test2:-1|c
12
- # com.example.test2:50|g
13
- # com.example.test3:5|c|@0.1
14
- # com.example.test4:44|ms
15
- REGEXP = /^([\d\w_.]*):(-?[\d]*)\|(c|g|ms){1}(\|@([.\d]+))?$/i
16
-
17
- class << self
18
-
19
- def parse(str)
20
- if str =~ REGEXP
21
- name, value, type, sample_rate = $1, $2, $3, $5
22
- if type == "ms"
23
- # TODO: implement sample_rate handling
24
- create_timing(:name => name, :value => value)
25
- elsif type == "g"
26
- create_gauge(:name => name, :value => (value.to_i || 1) * (1.0 / (sample_rate || 1).to_f) )
27
- elsif type == "c"
28
- create_counter(:name => name, :value => (value.to_i || 1) * (1.0 / (sample_rate || 1).to_f) )
29
- end
30
- else
31
- raise ParserError, "Parser Error - Invalid Stat: #{str}"
32
- end
33
- end
34
-
35
- def create_counter(attributes)
36
- Stats.new(attributes.merge(:type => 'c'))
37
- end
38
-
39
- def create_gauge(attributes)
40
- Stats.new(attributes.merge(:type => 'g'))
41
- end
42
-
43
- def create_timing(attributes)
44
- Stats.new(attributes.merge(:type => 'ms'))
45
- end
46
-
47
- def aggregate(stats_array)
48
- raise NonMatchingTypesError unless stats_array.group_by { |stats| stats.type }.size == 1
49
-
50
- result_stat = stats_array.first.dup
51
- result_stat.value = stats_array.map { |stats| stats.value }.inject(0) { |result, value| result += value }
52
- result_stat
53
- end
54
-
55
- def create_from_db(attributes)
56
- Stats.new(:name => attributes["name"], :value => attributes["value"], :ts => attributes["ts"], :type => attributes["type"])
57
- end
58
- end
59
-
60
- attr_accessor :name, :ts, :type, :value
61
-
62
- def initialize(attributes)
63
- @name = attributes[:name]
64
- @value = attributes[:value]
65
- @ts = attributes[:ts]
66
- @type = attributes[:type]
67
- end
68
-
69
- def counter?
70
- type == 'c'
71
- end
72
-
73
- def gauge?
74
- type == 'g'
75
- end
76
-
77
- def timing?
78
- type == 'ms'
79
- end
80
-
81
- def timestamp
82
- ts
83
- end
84
-
85
- def value
86
- @value.to_i
87
- end
88
-
89
- def attributes
90
- {
91
- :name => name,
92
- :value => value,
93
- :ts => ts,
94
- :type => type
95
- }
96
- end
97
-
98
- end
99
- end
data/spec/stats_spec.rb DELETED
@@ -1,61 +0,0 @@
1
- # encoding: utf-8
2
- require "spec_helper"
3
-
4
- module SimpleMetrics
5
-
6
- describe Bucket do
7
-
8
- describe "#parse" do
9
-
10
- it "parses increment counter" do
11
- stats = Stats.parse("com.example.test1:1|c")
12
- stats.name.should == "com.example.test1"
13
- stats.value.should == 1
14
- stats.should be_counter
15
- end
16
-
17
- it "parses decrement counter" do
18
- stats = Stats.parse("com.example.test1:-1|c")
19
- stats.name.should == "com.example.test1"
20
- stats.value.should == -1
21
- stats.should be_counter
22
- end
23
-
24
- it "parses counter with sample rate" do
25
- stats = Stats.parse("com.example.test2:5|c|@0.1")
26
- stats.name.should == "com.example.test2"
27
- stats.value.should == 50
28
- stats.should be_counter
29
- end
30
-
31
- it "parses increment gauge" do
32
- stats = Stats.parse("com.example.test3:5|g")
33
- stats.name.should == "com.example.test3"
34
- stats.value.should == 5
35
- stats.should be_gauge
36
- end
37
-
38
- it "parses increment gauge with sample rate" do
39
- stats = Stats.parse("com.example.test3:5|g|@0.1")
40
- stats.name.should == "com.example.test3"
41
- stats.value.should == 50
42
- stats.should be_gauge
43
- end
44
-
45
- it "parses increment timing" do
46
- stats = Stats.parse("com.example.test4:44|ms")
47
- stats.name.should == "com.example.test4"
48
- stats.value.should == 44
49
- stats.should be_timing
50
- end
51
-
52
- it "parses increment timing with sample rate" do
53
- end
54
- end
55
-
56
-
57
- describe "create_counter" do
58
- end
59
- end
60
-
61
- end