metric_system 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +15 -0
- data/lib/metric_system/benchmark.rb +11 -0
- data/lib/metric_system/core_extensions.rb +17 -0
- data/lib/metric_system/sqlite3_extensions.rb +158 -0
- data/lib/metric_system/version.rb +3 -0
- data/lib/metric_system.rb +168 -0
- data/test/counters_test.rb +109 -0
- data/test/gauges_test.rb +26 -0
- data/test/mixed_test.rb +52 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c76a2843de05d5ccaaa70865f23e76b09b287dc3
|
4
|
+
data.tar.gz: 34156de4921e7fb09874a3d47bedc4174ca4985b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 13f1207dee0f2cfb89850d1b637f4015abc0507231b332640848f88d6295e30ca222d59c8df2ae972139f01a184c04c1af997f4752151eb6a2cb709701e06cc9
|
7
|
+
data.tar.gz: 89948cf352c4e282b5e0bb7c91dc3f6bca221312767bec1a84f72c5254fa3aa7952d0f2abef10366750a101b2e1f0891526df258d4cb7f60c4f8bf6717090eeb
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# metric_system
|
2
|
+
|
3
|
+
A simple metric collector
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
gem install metric_system
|
8
|
+
|
9
|
+
## Getting started
|
10
|
+
|
11
|
+
TBD
|
12
|
+
|
13
|
+
## License
|
14
|
+
|
15
|
+
The metric_system gem is (c) radiospiel, 2012; it is distributed under the terms of the Modified BSD License, see LICENSE.BSD for details.
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
|
3
|
+
# manage SQLite3::Records
|
4
|
+
#
|
5
|
+
# The SQLite3::Record module is able to generate classes that are optimized
|
6
|
+
# for a specific set of columns. It is build on top of Struct, which is way
|
7
|
+
# faster than Hashes, for example.
|
8
|
+
module SQLite3::Record
|
9
|
+
module ClassMethods
|
10
|
+
attr :columns, true
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def to_time(s)
|
15
|
+
case s
|
16
|
+
when String then Time.parse(s)
|
17
|
+
when Fixnum then Time.at(s)
|
18
|
+
else s
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_date(s)
|
23
|
+
return unless time = to_time(s)
|
24
|
+
time.to_date
|
25
|
+
end
|
26
|
+
|
27
|
+
public
|
28
|
+
|
29
|
+
def build(*attrs)
|
30
|
+
attrs = columns.zip(attrs).map do |key, value|
|
31
|
+
case key
|
32
|
+
when /_at$/ then to_time(value)
|
33
|
+
when /_on$/ then to_date(value)
|
34
|
+
else value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
new *attrs
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_a
|
43
|
+
self.class.columns.map do |column| send(column) end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.for_columns(columns)
|
47
|
+
columns = columns.map(&:to_sym)
|
48
|
+
|
49
|
+
@@classes ||= {}
|
50
|
+
@@classes[columns] ||= begin
|
51
|
+
struct = Struct.new(*columns)
|
52
|
+
struct.extend SQLite3::Record::ClassMethods
|
53
|
+
struct.include SQLite3::Record
|
54
|
+
|
55
|
+
struct.columns = columns
|
56
|
+
struct
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class SQLite3::Query
|
62
|
+
def initialize(sql, statement)
|
63
|
+
expect! statement => SQLite3::Statement
|
64
|
+
|
65
|
+
@sql, @statement = sql, statement
|
66
|
+
end
|
67
|
+
|
68
|
+
def run(*args)
|
69
|
+
# STDERR.puts "Q: #{@sql} #{args.map(&:inspect).join(", ")}"
|
70
|
+
@statement.execute *args
|
71
|
+
end
|
72
|
+
|
73
|
+
def select(*args)
|
74
|
+
@klass ||= SQLite3::Record.for_columns(@statement.columns)
|
75
|
+
|
76
|
+
run(*args).map do |rec|
|
77
|
+
@klass.build *rec
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ask(*args)
|
82
|
+
results = run(*args)
|
83
|
+
row = results.first
|
84
|
+
results.reset
|
85
|
+
|
86
|
+
if !row then nil
|
87
|
+
elsif row.length == 1 then row.first
|
88
|
+
else row
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class SQLite3::Database
|
94
|
+
# execute multiple SQL statements at once.
|
95
|
+
def exec(sql, *args)
|
96
|
+
args = prepare_arguments(args)
|
97
|
+
|
98
|
+
while sql =~ /\S/ do
|
99
|
+
statement = prepare(sql)
|
100
|
+
|
101
|
+
sql = statement.remainder
|
102
|
+
if statement.active?
|
103
|
+
statement.execute!(*args)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
rescue
|
108
|
+
STDERR.puts "#{sql}: #{$!}"
|
109
|
+
raise
|
110
|
+
end
|
111
|
+
|
112
|
+
# -- cached queries ---------------------------------------------------------
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def query(sql)
|
117
|
+
@queries ||= {}
|
118
|
+
@queries[sql] ||= SQLite3::Query.new sql, prepare(sql)
|
119
|
+
end
|
120
|
+
|
121
|
+
def prepare_arguments(args)
|
122
|
+
args.map do |arg|
|
123
|
+
case arg
|
124
|
+
when Time then arg.to_i
|
125
|
+
when Date then arg.to_time.to_i
|
126
|
+
else arg
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
public
|
132
|
+
|
133
|
+
def run(sql, *args)
|
134
|
+
query(sql).run *prepare_arguments(args)
|
135
|
+
end
|
136
|
+
|
137
|
+
def ask(sql, *args)
|
138
|
+
query(sql).ask *prepare_arguments(args)
|
139
|
+
end
|
140
|
+
|
141
|
+
# run a select like query. Returns an array of records.
|
142
|
+
def select(sql, *args)
|
143
|
+
query(sql).select *prepare_arguments(args)
|
144
|
+
end
|
145
|
+
|
146
|
+
def print(sql, *args)
|
147
|
+
results = select sql, *args
|
148
|
+
log_sql = sql.gsub(/\n/, " ").gsub(/\s+/, " ")
|
149
|
+
puts "=" * log_sql.length
|
150
|
+
puts log_sql
|
151
|
+
puts "-" * log_sql.length
|
152
|
+
|
153
|
+
results.each do |result|
|
154
|
+
pp result.to_a
|
155
|
+
end
|
156
|
+
puts "=" * log_sql.length
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require "expectation"
|
2
|
+
require_relative "metric_system/core_extensions"
|
3
|
+
require_relative "metric_system/sqlite3_extensions"
|
4
|
+
|
5
|
+
# -- The MetricSystem module --------------------------------------------------
|
6
|
+
|
7
|
+
require "forwardable"
|
8
|
+
class MetricSystem
|
9
|
+
extend Forwardable
|
10
|
+
delegate [:exec, :select, :transaction, :rollback] => :@db
|
11
|
+
|
12
|
+
PERIODS = [
|
13
|
+
[ :year, 31536000, "strftime('%Y-01-01', starts_at, 'unixepoch')" ],
|
14
|
+
[ :month, 2592000, "strftime('%Y-%m-01', starts_at, 'unixepoch')" ],
|
15
|
+
[ :week, 604800, "strftime('%Y-%m-%d', starts_at, 'unixepoch', 'weekday 1', '-7 days')" ],
|
16
|
+
[ :day, 86400, "strftime('%Y-%m-%d', starts_at, 'unixepoch')" ],
|
17
|
+
[ :hour, 3600, "strftime('%Y-%m-%d %H:00:00', starts_at, 'unixepoch')" ],
|
18
|
+
[ :minute, 60, "strftime('%Y-%m-%d %H:%M:00', starts_at, 'unixepoch')" ],
|
19
|
+
[ :second, 1, "strftime('%Y-%m-%d %H:%M:%S', starts_at, 'unixepoch')" ],
|
20
|
+
]
|
21
|
+
|
22
|
+
def initialize(path)
|
23
|
+
@db = SQLite3::Database.new(path)
|
24
|
+
|
25
|
+
[ :counters, :gauges ].each do |name|
|
26
|
+
exec <<-SQL
|
27
|
+
CREATE TABLE IF NOT EXISTS #{name}(
|
28
|
+
id INTEGER PRIMARY KEY,
|
29
|
+
|
30
|
+
name NOT NULL, -- the event name
|
31
|
+
value NOT NULL, -- the value
|
32
|
+
starts_at TIMESTAMP NOT NULL DEFAULT (strftime('%s','now')) -- the timestamp
|
33
|
+
);
|
34
|
+
|
35
|
+
CREATE INDEX IF NOT EXISTS #{name}_idx1 ON #{name}(name, starts_at);
|
36
|
+
|
37
|
+
CREATE TABLE IF NOT EXISTS aggregated_#{name}(
|
38
|
+
id INTEGER PRIMARY KEY,
|
39
|
+
|
40
|
+
name NOT NULL, -- the event name
|
41
|
+
starts_at TIMESTAMP NOT NULL, -- the start-at timestamp
|
42
|
+
duration NOT NULL, -- the duration (estimate, in secs.)
|
43
|
+
period, -- the name of the period (year, day, etc.)
|
44
|
+
sum, -- the sum of event values
|
45
|
+
count, -- the count of events
|
46
|
+
value -- the aggregated value
|
47
|
+
);
|
48
|
+
|
49
|
+
CREATE UNIQUE INDEX IF NOT EXISTS aggregated_#{name}_uidx1 ON aggregated_#{name}(name, starts_at, duration);
|
50
|
+
CREATE INDEX IF NOT EXISTS aggregated_#{name}_idx2 ON aggregated_#{name}(starts_at);
|
51
|
+
CREATE INDEX IF NOT EXISTS aggregated_#{name}_idx3 ON aggregated_#{name}(duration);
|
52
|
+
SQL
|
53
|
+
end
|
54
|
+
|
55
|
+
exec <<-SQL
|
56
|
+
PRAGMA synchronous = NORMAL;
|
57
|
+
|
58
|
+
CREATE VIEW IF NOT EXISTS aggregates AS
|
59
|
+
SELECT * FROM aggregated_gauges
|
60
|
+
UNION
|
61
|
+
SELECT * FROM aggregated_counters
|
62
|
+
SQL
|
63
|
+
end
|
64
|
+
|
65
|
+
def gauge(name, value, starts_at = nil)
|
66
|
+
add_event :gauges, name, value, starts_at
|
67
|
+
end
|
68
|
+
|
69
|
+
def count(name, value, starts_at = nil)
|
70
|
+
add_event :counters, name, value, starts_at
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def add_event(table, name, value, starts_at)
|
76
|
+
# get names of all related events. An event "a.b.c" is actually
|
77
|
+
# 3 events: "a", "a.b", "a.b.c"
|
78
|
+
names = begin
|
79
|
+
parts = name.split(".")
|
80
|
+
parts.length.downto(1).map do |cnt|
|
81
|
+
parts[0,cnt].join(".")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if starts_at
|
86
|
+
starts_at = Time.parse(starts_at) if starts_at.is_a?(String)
|
87
|
+
|
88
|
+
names.each do |name|
|
89
|
+
@db.run "INSERT INTO #{table}(name, value, starts_at) VALUES(?, ?, ?)", name, value, starts_at.to_i
|
90
|
+
end
|
91
|
+
else
|
92
|
+
names.each do |name|
|
93
|
+
@db.run "INSERT INTO #{table}(name, value) VALUES(?, ?)", name, value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
public
|
99
|
+
|
100
|
+
PERIODS_BY_KEY = PERIODS.by(&:first)
|
101
|
+
|
102
|
+
def aggregate(*keys)
|
103
|
+
if keys.empty?
|
104
|
+
keys = PERIODS.map(&:first)
|
105
|
+
end
|
106
|
+
|
107
|
+
transaction do
|
108
|
+
keys.each do |period|
|
109
|
+
aggregate_for_period :period => period, :source => :counters, :dest => :aggregated_counters, :aggregate => "sum"
|
110
|
+
end
|
111
|
+
|
112
|
+
@db.exec "DELETE FROM counters"
|
113
|
+
end
|
114
|
+
|
115
|
+
transaction do
|
116
|
+
keys.each do |period|
|
117
|
+
aggregate_for_period :period => period, :source => :gauges, :dest => :aggregated_gauges, :aggregate => "CAST(sum AS FLOAT) / count"
|
118
|
+
end
|
119
|
+
|
120
|
+
@db.exec "DELETE FROM gauges"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def aggregate_for_period(options)
|
125
|
+
expect! options => {
|
126
|
+
:period => PERIODS.map(&:first)
|
127
|
+
}
|
128
|
+
period, source, dest, aggregate = options.values_at :period, :source, :dest, :aggregate
|
129
|
+
|
130
|
+
_, duration, starts_at = PERIODS_BY_KEY[period]
|
131
|
+
|
132
|
+
# sql expression to calculate value from sum and count of event values
|
133
|
+
aggregate = source == :gauges ? "CAST(sum AS FLOAT) / count" : "sum"
|
134
|
+
|
135
|
+
@db.exec <<-SQL
|
136
|
+
CREATE TEMPORARY TABLE batch AS
|
137
|
+
SELECT name, starts_at, SUM(sum) AS sum, SUM(count) AS count FROM
|
138
|
+
(
|
139
|
+
SELECT name AS name,
|
140
|
+
#{starts_at} AS starts_at,
|
141
|
+
SUM(value) AS sum,
|
142
|
+
COUNT(value) AS count
|
143
|
+
FROM #{source}
|
144
|
+
GROUP BY name, starts_at
|
145
|
+
|
146
|
+
UNION
|
147
|
+
|
148
|
+
SELECT #{dest}.name AS name,
|
149
|
+
#{dest}.starts_at AS starts_at,
|
150
|
+
sum AS sum,
|
151
|
+
count AS count
|
152
|
+
FROM #{dest}
|
153
|
+
INNER JOIN #{source} ON #{dest}.name=#{source}.name
|
154
|
+
WHERE duration=#{duration}
|
155
|
+
AND #{dest}.starts_at >= (SELECT MIN(starts_at) FROM #{source})
|
156
|
+
)
|
157
|
+
GROUP BY name, starts_at;
|
158
|
+
|
159
|
+
INSERT OR REPLACE INTO #{dest}(name, starts_at, period, duration, sum, count, value)
|
160
|
+
SELECT name, starts_at, '#{period}', #{duration}, sum, count, #{aggregate}
|
161
|
+
FROM batch;
|
162
|
+
SQL
|
163
|
+
|
164
|
+
@db.exec <<-SQL
|
165
|
+
DROP TABLE batch;
|
166
|
+
SQL
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
$: << "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
require "metric_system"
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
class MetricSystem::TestCounters < Test::Unit::TestCase
|
6
|
+
def db
|
7
|
+
@db ||= MetricSystem.new ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_two_events
|
11
|
+
db.count "foo", 1, "2014-03-02 12:10:11"
|
12
|
+
db.count "foo", 1, "2014-03-02 14:10:11"
|
13
|
+
db.aggregate :minute, :hour, :day
|
14
|
+
|
15
|
+
r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name, starts_at")
|
16
|
+
r = r.map(&:to_a)
|
17
|
+
|
18
|
+
assert_equal(r, [
|
19
|
+
["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
20
|
+
["foo", 1, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
|
21
|
+
["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
22
|
+
["foo", 1, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
|
23
|
+
["foo", 2, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
24
|
+
])
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_conversion
|
28
|
+
db.exec "CREATE TABLE tmp(value, starts_at, starts_on)"
|
29
|
+
now = Time.parse("2014-03-02 11:10:11 +0100")
|
30
|
+
day = Date.parse("2014-03-02")
|
31
|
+
|
32
|
+
db.exec "INSERT INTO tmp (value, starts_at, starts_on) VALUES(?, ?, ?)", "one", now, now
|
33
|
+
rows = db.select("SELECT value, starts_at, starts_on FROM tmp")
|
34
|
+
assert_equal rows.map(&:to_a), [
|
35
|
+
[ "one", now, day ]
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_single_aggregate
|
40
|
+
db.count "foo", 1, "2014-03-02 12:10:11"
|
41
|
+
db.aggregate
|
42
|
+
|
43
|
+
r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name")
|
44
|
+
assert_equal(r.map(&:to_a), [
|
45
|
+
["foo", 1, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
46
|
+
["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
47
|
+
["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
48
|
+
["foo", 1, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
49
|
+
["foo", 1, "week", Time.parse("2014-02-24 00:00:00 +0100")],
|
50
|
+
["foo", 1, "month", Time.parse("2014-03-01 00:00:00 +0100")],
|
51
|
+
["foo", 1, "year", Time.parse("2014-01-01 00:00:00 +0100")]
|
52
|
+
])
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_store_combined_name
|
56
|
+
db.count "foo", 1, "2014-03-02 12:10:11"
|
57
|
+
db.count "foo.bar", 2, "2014-03-02 12:10:11"
|
58
|
+
r = db.select("SELECT name, value, starts_at FROM counters ORDER BY name")
|
59
|
+
assert_equal(r.map(&:to_a), [
|
60
|
+
["foo" , 1, Time.parse("2014-03-02 12:10:11 +0100")],
|
61
|
+
["foo" , 2, Time.parse("2014-03-02 12:10:11 +0100")],
|
62
|
+
["foo.bar", 2, Time.parse("2014-03-02 12:10:11 +0100")]
|
63
|
+
])
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_combined_name
|
67
|
+
db.count "foo", 3, "2014-03-02 12:10:11"
|
68
|
+
db.count "foo.bar", 2, "2014-03-02 12:10:11"
|
69
|
+
db.aggregate :second, :minute, :hour
|
70
|
+
|
71
|
+
r = db.select <<-SQL
|
72
|
+
SELECT name, value, period, starts_at
|
73
|
+
FROM aggregates
|
74
|
+
ORDER BY duration, name
|
75
|
+
SQL
|
76
|
+
|
77
|
+
assert_equal(r.map(&:to_a), [
|
78
|
+
["foo" , 5, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
79
|
+
["foo.bar", 2, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
80
|
+
["foo" , 5, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
81
|
+
["foo.bar", 2, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
82
|
+
["foo" , 5, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
83
|
+
["foo.bar", 2, "hour", Time.parse("2014-03-02 11:00:00 +0100")]
|
84
|
+
])
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_double_aggregate
|
88
|
+
db.count "foo", 3, "2014-03-02 12:10:11"
|
89
|
+
db.count "foo.bar", 2, "2014-03-02 12:10:11"
|
90
|
+
db.aggregate :second, :minute, :hour
|
91
|
+
db.aggregate :second, :minute, :hour
|
92
|
+
|
93
|
+
r = db.select <<-SQL
|
94
|
+
SELECT name, value, period, starts_at
|
95
|
+
FROM aggregates
|
96
|
+
ORDER BY duration, name
|
97
|
+
SQL
|
98
|
+
|
99
|
+
assert_equal(r.map(&:to_a), [
|
100
|
+
["foo" , 5, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
101
|
+
["foo.bar", 2, "second", Time.parse("2014-03-02 11:10:11 +0100")],
|
102
|
+
["foo" , 5, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
103
|
+
["foo.bar", 2, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
104
|
+
["foo" , 5, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
105
|
+
["foo.bar", 2, "hour", Time.parse("2014-03-02 11:00:00 +0100")]
|
106
|
+
])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
data/test/gauges_test.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$: << "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
require "metric_system"
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
class MetricSystem::TestGauging < Test::Unit::TestCase
|
6
|
+
def db
|
7
|
+
@db ||= MetricSystem.new ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_two_events
|
11
|
+
db.gauge "foo", 1, "2014-03-02 12:10:11"
|
12
|
+
db.gauge "foo", 2, "2014-03-02 14:10:11"
|
13
|
+
db.aggregate :minute, :hour, :day
|
14
|
+
|
15
|
+
r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY duration, name, starts_at")
|
16
|
+
r = r.map(&:to_a)
|
17
|
+
|
18
|
+
assert_equal(r, [
|
19
|
+
["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
20
|
+
["foo", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
|
21
|
+
["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
22
|
+
["foo", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
|
23
|
+
["foo", 1.5, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
24
|
+
])
|
25
|
+
end
|
26
|
+
end
|
data/test/mixed_test.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
$: << "#{File.dirname(__FILE__)}/../lib"
|
2
|
+
require "metric_system"
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
class MetricSystem::TestMixed < Test::Unit::TestCase
|
6
|
+
def db
|
7
|
+
@db ||= MetricSystem.new ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def xtest_two_events
|
11
|
+
db.gauge "foo", 1, "2014-03-02 12:10:11"
|
12
|
+
db.gauge "foo", 2, "2014-03-02 14:10:11"
|
13
|
+
db.count "bar", 1, "2014-03-02 12:10:11"
|
14
|
+
db.count "bar", 2, "2014-03-02 14:10:11"
|
15
|
+
db.aggregate :minute, :hour, :day
|
16
|
+
|
17
|
+
r = db.select("SELECT name, value, period, starts_at FROM aggregates ORDER BY name, duration, starts_at")
|
18
|
+
r = r.map(&:to_a)
|
19
|
+
|
20
|
+
assert_equal(r, [
|
21
|
+
["bar", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
22
|
+
["bar", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
|
23
|
+
["bar", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
24
|
+
["bar", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
|
25
|
+
["bar", 3, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
26
|
+
["foo", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
27
|
+
["foo", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
|
28
|
+
["foo", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
29
|
+
["foo", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
|
30
|
+
["foo", 1.5, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
31
|
+
])
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_two_events_x
|
35
|
+
db.count "bar", 1, "2014-03-02 12:10:11"
|
36
|
+
db.count "bar", 2, "2014-03-02 14:10:11"
|
37
|
+
db.gauge "foo", 1, "2014-03-02 12:10:11"
|
38
|
+
|
39
|
+
db.aggregate :minute, :hour, :day
|
40
|
+
|
41
|
+
r = db.select("SELECT name, value, period, starts_at FROM aggregates WHERE name='bar' ORDER BY name, duration, starts_at")
|
42
|
+
r = r.map(&:to_a)
|
43
|
+
|
44
|
+
assert_equal(r, [
|
45
|
+
["bar", 1, "minute", Time.parse("2014-03-02 11:10:00 +0100")],
|
46
|
+
["bar", 2, "minute", Time.parse("2014-03-02 13:10:00 +0100")],
|
47
|
+
["bar", 1, "hour", Time.parse("2014-03-02 11:00:00 +0100")],
|
48
|
+
["bar", 2, "hour", Time.parse("2014-03-02 13:00:00 +0100")],
|
49
|
+
["bar", 3, "day", Time.parse("2014-03-02 00:00:00 +0100")],
|
50
|
+
])
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metric_system
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- radiospiel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: expectation
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sqlite3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A simple metrics aggregator
|
42
|
+
email: eno@radiospiel.org
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- README.md
|
48
|
+
- lib/metric_system.rb
|
49
|
+
- lib/metric_system/benchmark.rb
|
50
|
+
- lib/metric_system/core_extensions.rb
|
51
|
+
- lib/metric_system/sqlite3_extensions.rb
|
52
|
+
- lib/metric_system/version.rb
|
53
|
+
- test/counters_test.rb
|
54
|
+
- test/gauges_test.rb
|
55
|
+
- test/mixed_test.rb
|
56
|
+
homepage: http://github.com/radiospiel/metric_system
|
57
|
+
licenses: []
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.2.2
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: A simple metrics aggregator
|
79
|
+
test_files: []
|