simple_metrics 0.0.1 → 0.2.2

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