simple_metrics 0.4.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/README.markdown +7 -59
- data/Rakefile +17 -0
- data/bin/populate +25 -12
- data/bin/populate_for_demo +65 -0
- data/bin/simple_metrics_server +4 -3
- data/config.ru +13 -0
- data/default_config.yml +34 -0
- data/lib/simple_metrics/bucket.rb +43 -106
- data/lib/simple_metrics/configuration.rb +82 -0
- data/lib/simple_metrics/dashboard.rb +29 -0
- data/lib/simple_metrics/dashboard_repository.rb +59 -0
- data/lib/simple_metrics/data_point/base.rb +59 -0
- data/lib/simple_metrics/data_point/counter.rb +20 -0
- data/lib/simple_metrics/data_point/event.rb +16 -0
- data/lib/simple_metrics/data_point/gauge.rb +19 -0
- data/lib/simple_metrics/data_point/timing.rb +15 -0
- data/lib/simple_metrics/data_point.rb +45 -137
- data/lib/simple_metrics/data_point_repository.rb +115 -0
- data/lib/simple_metrics/functions.rb +5 -5
- data/lib/simple_metrics/graph.rb +69 -32
- data/lib/simple_metrics/importer.rb +64 -0
- data/lib/simple_metrics/instrument.rb +28 -0
- data/lib/simple_metrics/instrument_repository.rb +64 -0
- data/lib/simple_metrics/metric.rb +29 -0
- data/lib/simple_metrics/metric_repository.rb +54 -0
- data/lib/simple_metrics/repository.rb +34 -0
- data/lib/simple_metrics/udp_server.rb +81 -0
- data/lib/simple_metrics/version.rb +1 -1
- data/lib/simple_metrics.rb +21 -76
- data/simple_metrics.gemspec +4 -0
- data/spec/bucket_spec.rb +17 -152
- data/spec/dashboard_repository_spec.rb +52 -0
- data/spec/data_point_repository_spec.rb +77 -0
- data/spec/data_point_spec.rb +14 -64
- data/spec/graph_spec.rb +30 -19
- data/spec/importer_spec.rb +126 -0
- data/spec/instrument_repository_spec.rb +52 -0
- data/spec/metric_repository_spec.rb +53 -0
- data/spec/spec_helper.rb +4 -3
- metadata +93 -23
- data/bin/simple_metrics_client +0 -64
- data/lib/simple_metrics/client.rb +0 -83
- data/lib/simple_metrics/mongo.rb +0 -48
- data/lib/simple_metrics/server.rb +0 -66
@@ -0,0 +1,59 @@
|
|
1
|
+
module SimpleMetrics
|
2
|
+
class DashboardRepository
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def find_one(id)
|
7
|
+
dashboard(collection.find_one(BSON::ObjectId.from_string(id)))
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_one_by_name(name)
|
11
|
+
result = collection.find({ :name => name }).to_a.first
|
12
|
+
dashboard(result) if result
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_all
|
16
|
+
results = collection.find.sort([['name', ::Mongo::ASCENDING]]).to_a
|
17
|
+
dashboards(results) if results
|
18
|
+
end
|
19
|
+
|
20
|
+
def save(dashboard)
|
21
|
+
collection.insert(dashboard.attributes.merge(:created_at => Time.now.utc, :updated_at => Time.now.utc))
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(dashboard)
|
25
|
+
collection.update({ "_id" => dashboard.id }, { "$set" => dashboard.attributes.merge(:updated_at => Time.now.utc).reject { |k, v| k == 'id' } })
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove(id)
|
29
|
+
collection.remove("_id" => BSON::ObjectId.from_string(id))
|
30
|
+
end
|
31
|
+
|
32
|
+
def truncate_collections
|
33
|
+
collection.remove
|
34
|
+
end
|
35
|
+
|
36
|
+
def ensure_index
|
37
|
+
collection.ensure_index([['created_at', ::Mongo::ASCENDING]])
|
38
|
+
collection.ensure_index([['updated_at', ::Mongo::ASCENDING]])
|
39
|
+
collection.ensure_index([['name', ::Mongo::ASCENDING]])
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def collection
|
45
|
+
Repository.db.collection('dashboards')
|
46
|
+
end
|
47
|
+
|
48
|
+
def dashboard(result)
|
49
|
+
Dashboard.new(:id => result["_id"], :name => result["name"], :instruments => result["instruments"], :created_at => result["created_at"], :updated_at => result["updated_at"])
|
50
|
+
end
|
51
|
+
|
52
|
+
def dashboards(results)
|
53
|
+
results.inject([]) { |result, a| result << dashboard(a); }
|
54
|
+
end
|
55
|
+
|
56
|
+
end # class << self
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module SimpleMetrics
|
3
|
+
module DataPoint
|
4
|
+
class Base
|
5
|
+
|
6
|
+
attr_accessor :name, :ts, :type, :value, :total, :sum
|
7
|
+
attr_reader :id
|
8
|
+
|
9
|
+
def initialize(attributes)
|
10
|
+
@id = attributes[:id]
|
11
|
+
@name = attributes[:name]
|
12
|
+
@value = attributes[:value]
|
13
|
+
@ts = attributes[:ts]
|
14
|
+
@sample_rate = attributes[:sample_rate]
|
15
|
+
@sum = attributes[:sum]
|
16
|
+
@total = attributes[:total]
|
17
|
+
end
|
18
|
+
|
19
|
+
def counter?
|
20
|
+
@type == 'c'
|
21
|
+
end
|
22
|
+
|
23
|
+
def gauge?
|
24
|
+
@type == 'g'
|
25
|
+
end
|
26
|
+
|
27
|
+
def timing?
|
28
|
+
@type == 'ms'
|
29
|
+
end
|
30
|
+
|
31
|
+
def event?
|
32
|
+
@type == 'ev'
|
33
|
+
end
|
34
|
+
|
35
|
+
def timestamp
|
36
|
+
ts
|
37
|
+
end
|
38
|
+
|
39
|
+
def value
|
40
|
+
@value.to_i if @value
|
41
|
+
end
|
42
|
+
|
43
|
+
def attributes
|
44
|
+
{
|
45
|
+
:name => @name,
|
46
|
+
:value => @value,
|
47
|
+
:ts => @ts,
|
48
|
+
:type => @type,
|
49
|
+
:total => @total,
|
50
|
+
:sum => @sum
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
attributes.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SimpleMetrics
|
2
|
+
module DataPoint
|
3
|
+
class Counter < Base
|
4
|
+
|
5
|
+
def initialize(attributes)
|
6
|
+
super(attributes)
|
7
|
+
@type = 'c'
|
8
|
+
@value = (@value.to_i || 1) * (1.0 / (@sample_rate || 1).to_f)
|
9
|
+
end
|
10
|
+
|
11
|
+
def combine(dp)
|
12
|
+
@total += 1
|
13
|
+
@value += dp.value
|
14
|
+
@sum += dp.value
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SimpleMetrics
|
2
|
+
module DataPoint
|
3
|
+
class Gauge < Base
|
4
|
+
|
5
|
+
def initialize(attributes)
|
6
|
+
super(attributes)
|
7
|
+
@type = 'g'
|
8
|
+
@value = (@value.to_i || 1) * (1.0 / (@sample_rate || 1).to_f)
|
9
|
+
end
|
10
|
+
|
11
|
+
def combine(dp)
|
12
|
+
@total += 1
|
13
|
+
@sum += dp.value
|
14
|
+
@value = @sum / @total
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
module SimpleMetrics
|
3
|
-
|
4
|
-
|
2
|
+
module DataPoint
|
3
|
+
extend self
|
5
4
|
|
6
5
|
class NonMatchingTypesError < Exception; end
|
7
6
|
class ParserError < Exception; end
|
7
|
+
class UnknownTypeError < Exception; end
|
8
8
|
|
9
9
|
# examples:
|
10
10
|
# com.example.test1:1|c
|
@@ -14,149 +14,57 @@ module SimpleMetrics
|
|
14
14
|
# com.example.test4:44|ms
|
15
15
|
REGEXP = /^([\d\w_.]*):(-?[\d]*)\|(c|g|ms){1}(\|@([.\d]+))?$/i
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
17
|
+
def parse(str)
|
18
|
+
if str =~ REGEXP
|
19
|
+
name, value, type, sample_rate = $1, $2, $3, $5
|
20
|
+
build(:name => name, :value => value, :type => type, :sample_rate => sample_rate)
|
21
|
+
else
|
22
|
+
raise ParserError, "Parser Error - Invalid data point: #{str}"
|
120
23
|
end
|
121
24
|
end
|
122
25
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
def gauge?
|
137
|
-
type == 'g'
|
26
|
+
def build(attributes)
|
27
|
+
case attributes[:type]
|
28
|
+
when 'c'
|
29
|
+
Counter.new(attributes)
|
30
|
+
when 'g'
|
31
|
+
Gauge.new(attributes)
|
32
|
+
when 'ms'
|
33
|
+
Timing.new(attributes)
|
34
|
+
when 'ev'
|
35
|
+
Event.new(attributes)
|
36
|
+
else
|
37
|
+
raise UnknownTypeError, "Unknown Type Error: #{attributes[:type]}"
|
38
|
+
end
|
138
39
|
end
|
139
|
-
|
140
|
-
def
|
141
|
-
|
40
|
+
|
41
|
+
def aggregate_values(dps)
|
42
|
+
raise SimpleMetrics::DataPoint::NonMatchingTypesError if has_non_matching_types?(dps)
|
43
|
+
|
44
|
+
dp = dps.first.dup
|
45
|
+
dp.value = if dp.counter?
|
46
|
+
sum(dps)
|
47
|
+
elsif dp.gauge?
|
48
|
+
sum(dps) / dps.size
|
49
|
+
elsif dp.event?
|
50
|
+
raise "Implement me!"
|
51
|
+
elsif dp.timing?
|
52
|
+
raise "Implement me!"
|
53
|
+
else
|
54
|
+
raise ArgumentError("Unknown data point type: #{dp}")
|
55
|
+
end
|
56
|
+
dp
|
142
57
|
end
|
143
58
|
|
144
|
-
|
145
|
-
ts
|
146
|
-
end
|
59
|
+
private
|
147
60
|
|
148
|
-
def
|
149
|
-
|
61
|
+
def sum(dps)
|
62
|
+
dps.map { |dp| dp.value }.inject(0) { |result, value| result += value }
|
150
63
|
end
|
151
64
|
|
152
|
-
def
|
153
|
-
{
|
154
|
-
:name => name,
|
155
|
-
:value => value,
|
156
|
-
:ts => ts,
|
157
|
-
:type => type
|
158
|
-
}
|
65
|
+
def has_non_matching_types?(dps)
|
66
|
+
dps.group_by { |dp| dp.type }.size != 1
|
159
67
|
end
|
160
68
|
|
161
69
|
end
|
162
|
-
end
|
70
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "mongo"
|
2
|
+
|
3
|
+
module SimpleMetrics
|
4
|
+
class DataPointRepository
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
@@collection = {}
|
9
|
+
|
10
|
+
def for_retention(name)
|
11
|
+
self.new(collection(name))
|
12
|
+
end
|
13
|
+
|
14
|
+
def collection(name)
|
15
|
+
raise ArgumentError, "Unknown retention: #{name}" unless retention_names.include?(name)
|
16
|
+
@@collection[name] ||= db.collection(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ensure_collections_exist
|
20
|
+
SimpleMetrics.logger.debug "SERVER: MongoDB - found following collections: #{db.collection_names.inspect}"
|
21
|
+
buckets.each do |retention|
|
22
|
+
unless db.collection_names.include?(retention.fetch(:name))
|
23
|
+
db.create_collection(retention.fetch(:name), :capped => retention.fetch(:capped), :size => retention.fetch(:size))
|
24
|
+
SimpleMetrics.logger.debug "SERVER: MongoDB - created collection #{retention.fetch(:name)}, capped: #{retention.fetch(:capped)}, size: #{retention.fetch(:size)}"
|
25
|
+
end
|
26
|
+
|
27
|
+
db.collection(retention.fetch(:name)).ensure_index([['ts', ::Mongo::ASCENDING]])
|
28
|
+
db.collection(retention.fetch(:name)).ensure_index([['_id', ::Mongo::ASCENDING]])
|
29
|
+
SimpleMetrics.logger.debug "SERVER: MongoDB - ensure index on column ts for collection #{retention.fetch(:name)}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def truncate_collections
|
34
|
+
buckets.each do |retention|
|
35
|
+
if db.collection_names.include?(retention.fetch(:name))
|
36
|
+
if retention.fetch(:capped)
|
37
|
+
collection(retention.fetch(:name)).drop # capped collections can't remove elements, drop it instead
|
38
|
+
else
|
39
|
+
collection(retention.fetch(:name)).remove
|
40
|
+
end
|
41
|
+
SimpleMetrics.logger.debug "SERVER: MongoDB - truncated collection #{retention.fetch(:name)}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def db
|
49
|
+
Repository.db
|
50
|
+
end
|
51
|
+
|
52
|
+
def retention_names
|
53
|
+
buckets.map { |r| r.fetch(:name) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def buckets
|
57
|
+
SimpleMetrics.config.buckets
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(collection)
|
62
|
+
@collection = collection
|
63
|
+
end
|
64
|
+
|
65
|
+
def save(result)
|
66
|
+
@collection.insert(result.attributes.reject { |k, v| k == 'id' })
|
67
|
+
end
|
68
|
+
|
69
|
+
def update(dp, ts)
|
70
|
+
@collection.update({ "_id" => dp.id }, { "$set" => { :value => dp.value, :sum => dp.sum, :total => dp.total }})
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_all_at_ts(ts)
|
74
|
+
results = @collection.find({ :ts => ts }).to_a
|
75
|
+
data_points(results)
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_data_point_at_ts(ts, name)
|
79
|
+
result = @collection.find_one({ :ts => ts, :name => name })
|
80
|
+
data_point(result) if result
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_all_in_ts_range_by_name(from, to, name)
|
84
|
+
results = @collection.find({ :name => name }.merge(range_query(from, to))).to_a
|
85
|
+
data_points(results)
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_all_in_ts_range_by_wildcard(from, to, target)
|
89
|
+
results = @collection.find({ :name => regexp(target) }.merge(range_query(from, to))).to_a
|
90
|
+
data_points(results)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def regexp(target)
|
96
|
+
/#{wildcard_replace(target)}/
|
97
|
+
end
|
98
|
+
|
99
|
+
def wildcard_replace(target)
|
100
|
+
target.gsub('.', '\.').gsub('*', '.*')
|
101
|
+
end
|
102
|
+
|
103
|
+
def range_query(from, to)
|
104
|
+
{ :ts => { "$gte" => from, "$lte" => to } }
|
105
|
+
end
|
106
|
+
|
107
|
+
def data_point(result)
|
108
|
+
DataPoint.build(:id => result["_id"], :name => result["name"], :value => result["value"], :ts => result["ts"], :type => result["type"], :sum => result["sum"], :total => result["total"])
|
109
|
+
end
|
110
|
+
|
111
|
+
def data_points(results)
|
112
|
+
results.inject([]) { |result, a| result << data_point(a); }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -2,14 +2,14 @@ module SimpleMetrics
|
|
2
2
|
|
3
3
|
module Functions
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# calculate the maximum value for multiple targets
|
6
|
+
#
|
7
|
+
# params:
|
8
8
|
# data_points [1, 3, 5], [2, 1, 6]
|
9
9
|
#
|
10
10
|
# return:
|
11
11
|
# array [2, 3, 6]
|
12
|
-
|
12
|
+
def max(*data_points)
|
13
13
|
end
|
14
14
|
|
15
15
|
# calculate the minimum value for multiple targets
|
@@ -17,7 +17,7 @@ module SimpleMetrics
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# add offset to each value
|
20
|
-
|
20
|
+
def offset(*data_points)
|
21
21
|
end
|
22
22
|
|
23
23
|
# multiple each value
|
data/lib/simple_metrics/graph.rb
CHANGED
@@ -1,58 +1,95 @@
|
|
1
1
|
module SimpleMetrics
|
2
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
3
|
module Graph
|
10
4
|
extend self
|
11
5
|
|
12
|
-
def minutes
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
def hours
|
17
|
-
Bucket[1]
|
18
|
-
end
|
6
|
+
def minutes; Bucket[0]; end
|
7
|
+
def hours; Bucket[1]; end
|
8
|
+
def day; Bucket[2]; end
|
9
|
+
def week; Bucket[3]; end
|
19
10
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
def time_range(time)
|
12
|
+
case time
|
13
|
+
when 'minute'
|
14
|
+
5 * one_minute
|
15
|
+
when 'hour'
|
16
|
+
one_hour
|
17
|
+
when 'day'
|
18
|
+
one_day
|
19
|
+
when 'week'
|
20
|
+
one_week
|
21
|
+
else
|
22
|
+
raise "Unknown time param: #{time}"
|
23
|
+
end
|
26
24
|
end
|
27
|
-
|
25
|
+
|
28
26
|
def query_all(bucket, from, to, *targets)
|
29
|
-
|
27
|
+
results = []
|
30
28
|
Array(targets).each do |target|
|
31
|
-
|
29
|
+
results << { :name => target, :data => query(bucket, from, to, target).map { |data| { :x => data.ts, :y => data.value || 0 } } }
|
32
30
|
end
|
33
|
-
|
31
|
+
results
|
34
32
|
end
|
35
33
|
|
36
34
|
def query(bucket, from, to, target)
|
37
|
-
if
|
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?('*')
|
35
|
+
if wild_card_query?(target)
|
42
36
|
result = bucket.find_all_in_ts_range_by_wildcard(from, to, target)
|
43
|
-
result =
|
37
|
+
result = aggregate(result, target)
|
44
38
|
bucket.fill_gaps(from, to, result)
|
45
39
|
elsif target.is_a?(String)
|
46
40
|
result = bucket.find_all_in_ts_range_by_name(from, to, target)
|
47
41
|
bucket.fill_gaps(from, to, result)
|
48
42
|
else
|
49
|
-
raise ArgumentError, "Unknown target: #{target.inspect}"
|
43
|
+
raise ArgumentError, "Unknown target format: #{target.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def aggregate(dps, target)
|
50
|
+
raise SimpleMetrics::DataPoint::NonMatchingTypesError if has_non_matching_types?(dps)
|
51
|
+
|
52
|
+
tmp = {}
|
53
|
+
dps.each do |dp|
|
54
|
+
dp.name = target
|
55
|
+
if tmp.key?(dp.ts)
|
56
|
+
tmp[dp.ts] << dp
|
57
|
+
else
|
58
|
+
tmp[dp.ts] = [dp]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
tmp
|
62
|
+
|
63
|
+
result = []
|
64
|
+
tmp.each_pair do |ts, dps|
|
65
|
+
result << DataPoint.aggregate_values(dps)
|
50
66
|
end
|
67
|
+
result
|
51
68
|
end
|
52
69
|
|
53
|
-
def
|
54
|
-
|
70
|
+
def has_non_matching_types?(dps)
|
71
|
+
dps.group_by { |dp| dp.type }.size != 1
|
55
72
|
end
|
56
73
|
|
74
|
+
def one_minute
|
75
|
+
60
|
76
|
+
end
|
77
|
+
|
78
|
+
def one_hour
|
79
|
+
one_minute * 60
|
80
|
+
end
|
81
|
+
|
82
|
+
def one_day
|
83
|
+
one_hour * 24
|
84
|
+
end
|
85
|
+
|
86
|
+
def one_week
|
87
|
+
one_day * 7
|
88
|
+
end
|
89
|
+
|
90
|
+
def wild_card_query?(target)
|
91
|
+
target.is_a?(String) && target.include?('*')
|
92
|
+
end
|
93
|
+
|
57
94
|
end
|
58
95
|
end
|