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.
Files changed (45) hide show
  1. data/.travis.yml +3 -0
  2. data/README.markdown +7 -59
  3. data/Rakefile +17 -0
  4. data/bin/populate +25 -12
  5. data/bin/populate_for_demo +65 -0
  6. data/bin/simple_metrics_server +4 -3
  7. data/config.ru +13 -0
  8. data/default_config.yml +34 -0
  9. data/lib/simple_metrics/bucket.rb +43 -106
  10. data/lib/simple_metrics/configuration.rb +82 -0
  11. data/lib/simple_metrics/dashboard.rb +29 -0
  12. data/lib/simple_metrics/dashboard_repository.rb +59 -0
  13. data/lib/simple_metrics/data_point/base.rb +59 -0
  14. data/lib/simple_metrics/data_point/counter.rb +20 -0
  15. data/lib/simple_metrics/data_point/event.rb +16 -0
  16. data/lib/simple_metrics/data_point/gauge.rb +19 -0
  17. data/lib/simple_metrics/data_point/timing.rb +15 -0
  18. data/lib/simple_metrics/data_point.rb +45 -137
  19. data/lib/simple_metrics/data_point_repository.rb +115 -0
  20. data/lib/simple_metrics/functions.rb +5 -5
  21. data/lib/simple_metrics/graph.rb +69 -32
  22. data/lib/simple_metrics/importer.rb +64 -0
  23. data/lib/simple_metrics/instrument.rb +28 -0
  24. data/lib/simple_metrics/instrument_repository.rb +64 -0
  25. data/lib/simple_metrics/metric.rb +29 -0
  26. data/lib/simple_metrics/metric_repository.rb +54 -0
  27. data/lib/simple_metrics/repository.rb +34 -0
  28. data/lib/simple_metrics/udp_server.rb +81 -0
  29. data/lib/simple_metrics/version.rb +1 -1
  30. data/lib/simple_metrics.rb +21 -76
  31. data/simple_metrics.gemspec +4 -0
  32. data/spec/bucket_spec.rb +17 -152
  33. data/spec/dashboard_repository_spec.rb +52 -0
  34. data/spec/data_point_repository_spec.rb +77 -0
  35. data/spec/data_point_spec.rb +14 -64
  36. data/spec/graph_spec.rb +30 -19
  37. data/spec/importer_spec.rb +126 -0
  38. data/spec/instrument_repository_spec.rb +52 -0
  39. data/spec/metric_repository_spec.rb +53 -0
  40. data/spec/spec_helper.rb +4 -3
  41. metadata +93 -23
  42. data/bin/simple_metrics_client +0 -64
  43. data/lib/simple_metrics/client.rb +0 -83
  44. data/lib/simple_metrics/mongo.rb +0 -48
  45. data/lib/simple_metrics/server.rb +0 -66
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
data/README.markdown CHANGED
@@ -9,78 +9,26 @@ Technically speaking it provides a simple UDP interface to send data to an Event
9
9
 
10
10
  SimpleMetrics is written in Ruby and packaged as a gem.
11
11
 
12
- The current version is considered ALPHA.
12
+ The current version is considered ALPHA.
13
13
 
14
- SimpleMetrics Client
15
- --------------------
16
-
17
- Commandline client:
18
-
19
- Send a count of 5 for data point "module.test1":
20
-
21
- simple_metrics_client module.test1 -counter 5
22
-
23
- Send a timing of 100ms:
24
-
25
- simple_metrics_client module.test1 -timing 100
26
-
27
- doing the same, but since we expect a lot of calls we sample the data (10%):
28
-
29
- simple_metrics_client module.test1 -timing 100 --sample_rate 0.1
30
-
31
- more info:
32
-
33
- simple_metrics_client --help
34
-
35
- Ruby client API
36
- ---------------
37
-
38
- Initialize client:
39
-
40
- client = SimpleMetrics::Client.new("localhost")
41
-
42
- sends "com.example.test1:1|c" via UDP:
43
-
44
- client.increment("com.example.test1")
45
-
46
- sends "com.example.test1:-1|c":
47
-
48
- client.decrement("com.example.test1")
49
-
50
- sends "com.example.test1:5|c" (a counter with a relative value of 5):
51
-
52
- client.count("com.example.test1", 5)
53
-
54
- sends "com.example.test1:5|c|@0.1" with a sample rate of 10%:
55
-
56
- client.count("com.example.test1", 5, 0.1)
57
-
58
- sends "com.example.test1:5|g" (meaning gauge, an absolute value of 5):
59
-
60
- client.count("com.example.test1", 5)
61
-
62
- sends "com.example.test1:100|ms":
63
-
64
- client.timing("com.example.test1")
65
-
66
- More examples in the examples/ directory.
14
+ There is a rails app provided to visualize the graphs in dashboards.
67
15
 
68
16
  SimpleMetrics Server
69
17
  --------------------
70
18
 
71
19
  We provide a simple commandline wrapper using daemons gem (http://daemons.rubyforge.org/).
72
20
 
73
- Start Server as background daemond:
21
+ Start Server as background daemon:
74
22
 
75
- simple_metrics_server start
23
+ simple_metrics_server start
76
24
 
77
25
  Start in foreground:
78
26
 
79
- simple_metrics_server start -t
27
+ simple_metrics_server start -t
80
28
 
81
29
  Show Help:
82
30
 
83
- simple_metrics_server --help
31
+ simple_metrics_server --help
84
32
 
85
33
  Round Robin Database Principles in MongoDB
86
34
  ------------------------------------------
@@ -91,7 +39,7 @@ We use 4 collections in MongoDB each with more coarse timestamp buckets:
91
39
  * 10 min
92
40
  * 1 day
93
41
 
94
- The 10s and 1m collections are capped collections and have a fixed size. The other will store the data as long as we have sufficient disc space.
42
+ The 10sec and 1min collections are capped collections and have a fixed size. The others will store the data as long as there is sufficient disc space.
95
43
 
96
44
  How can we map these times to graphs?
97
45
 
data/Rakefile CHANGED
@@ -2,7 +2,24 @@ require "bundler/gem_tasks"
2
2
  require 'rake'
3
3
  require 'rspec/core/rake_task'
4
4
 
5
+ require 'simple_metrics'
6
+
5
7
  RSpec::Core::RakeTask.new
6
8
  task :default => :spec
7
9
  task :test => :spec
8
10
 
11
+ namespace :simple_metrics do
12
+
13
+ desc "Ensure collections and index exist"
14
+ task :ensure_collections_exist do
15
+ SimpleMetrics.logger = Logger.new($stdout)
16
+ SimpleMetrics::DataPointRepository.ensure_collections_exist
17
+ end
18
+
19
+ desc "Truncate all collections"
20
+ task :truncate_collections do
21
+ SimpleMetrics.logger = Logger.new($stdout)
22
+ SimpleMetrics::DataPointRepository.truncate_collections
23
+ end
24
+
25
+ end
data/bin/populate CHANGED
@@ -6,20 +6,33 @@ require "bundler/setup"
6
6
  require 'optparse'
7
7
  require "simple_metrics"
8
8
 
9
- ts = Time.now.to_i
9
+ SimpleMetrics.logger = Logger.new("/dev/null")
10
+ SimpleMetrics::DataPointRepository.truncate_collections
11
+ SimpleMetrics::DataPointRepository.ensure_collections_exist
10
12
 
11
- name = "test.post.clicks2"
13
+ bucket = SimpleMetrics::Bucket.first
12
14
 
13
- SimpleMetrics::Mongo.ensure_collections_exist
15
+ name = ENV['NAME'] || "test.page.visits.1"
16
+ now = Time.now.to_i
17
+ minute = 60
18
+ hour = minute * 60
14
19
 
15
- SimpleMetrics::Bucket.all.each do |bucket|
16
- ts_bucket = bucket.ts_bucket(ts)
20
+ def create_dps(name)
21
+ previous_value = 20
22
+ (1..1).inject([]) do |result, index|
23
+ value = previous_value+rand(20)
24
+ result << SimpleMetrics::DataPoint::Counter.new(:name => name, :value => value)
25
+ previous_value = value
26
+ result
27
+ end
28
+ end
17
29
 
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
30
+ counter = 1
31
+ current = now - 1 * hour
32
+ while (current < now)
33
+ dps = create_dps(name)
34
+ puts "flush data for #{Time.at(current)}, #{counter}"
35
+ SimpleMetrics::Importer.flush_data_points(dps, current)
36
+ current += 10
37
+ counter += 1
25
38
  end
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+
6
+ require 'optparse'
7
+ require "simple_metrics"
8
+
9
+ SimpleMetrics.logger = Logger.new("/dev/null")
10
+ SimpleMetrics::DataPointRepository.truncate_collections
11
+ SimpleMetrics::DataPointRepository.ensure_collections_exist
12
+ SimpleMetrics::MetricRepository.truncate_collections
13
+
14
+ def create_dps(name)
15
+ previous_value = 20
16
+ (1..1).inject([]) do |result, index|
17
+ value = previous_value+rand(20)
18
+ result << SimpleMetrics::DataPoint::Counter.new(:name => name, :value => value)
19
+ previous_value = value
20
+ result
21
+ end
22
+ end
23
+
24
+ def flush_data_points(name)
25
+ #bucket = SimpleMetrics::Bucket.first
26
+ now = Time.now.to_i
27
+ minute = 60
28
+ hour = minute * 60
29
+
30
+ counter = 1
31
+ current = now - 1 * hour
32
+ while (current < now)
33
+ dps = create_dps(name)
34
+ puts "flush data for #{Time.at(current)}, #{counter}"
35
+ SimpleMetrics::Importer.flush_data_points(dps, current)
36
+ current += 10
37
+ counter += 1
38
+ end
39
+ end
40
+
41
+
42
+ name1 = "test.page.visits"
43
+ flush_data_points(name1)
44
+ name2 = "test.invoices.send"
45
+ flush_data_points(name2)
46
+
47
+ metric1 = SimpleMetrics::MetricRepository.find_one_by_name(name1)
48
+ metric2 = SimpleMetrics::MetricRepository.find_one_by_name(name2)
49
+
50
+ SimpleMetrics::InstrumentRepository.truncate_collections
51
+ instrument1 = SimpleMetrics::Instrument.new(:name => "Example Instrument 1", :metrics => [{ :name => metric1.name }, { :name => metric2.name}])
52
+ instrument2 = SimpleMetrics::Instrument.new(:name => "Example Instrument 2", :metrics => [{ :name => metric1.name} ])
53
+ instrument1.id = SimpleMetrics::InstrumentRepository.save(instrument1)
54
+ instrument2.id = SimpleMetrics::InstrumentRepository.save(instrument2)
55
+
56
+ puts instrument1.inspect
57
+ puts instrument2.inspect
58
+ 10.times do |i|
59
+ instrument = SimpleMetrics::Instrument.new(:name => "Example Instrument Randmon #{i}", :metrics => [{ :name => metric1.name}])
60
+ SimpleMetrics::InstrumentRepository.save(instrument)
61
+ end
62
+
63
+ SimpleMetrics::DashboardRepository.truncate_collections
64
+ SimpleMetrics::DashboardRepository.save(SimpleMetrics::Dashboard.new(:name => "Example Dashboard 1", :instruments => [instrument1.id.to_s, instrument2.id.to_s]))
65
+ SimpleMetrics::DashboardRepository.save(SimpleMetrics::Dashboard.new(:name => "Example Dashboard 2", :instruments => [instrument1.id.to_s]))
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+
3
5
  require "rubygems"
4
- require "bundler/setup"
5
6
  require "simple_metrics"
6
7
  require "daemons"
7
8
 
8
9
  options = {
9
10
  :backtrace => true,
10
11
  :log_output => true,
11
- :dir_mode => :script
12
+ :dir_mode => :script,
12
13
  }
13
14
 
14
15
  Daemons.run_proc("simple_metrics", options) do
15
- SimpleMetrics::Server.new.start
16
+ SimpleMetrics::UDPServer.new.start
16
17
  end
17
18
 
18
19
 
data/config.ru ADDED
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift ::File.expand_path(::File.dirname(__FILE__) + '/lib')
2
+
3
+ require "rubygems"
4
+ require "simple_metrics"
5
+ require "simple_metrics/app"
6
+
7
+ map "/assets" do
8
+ run SimpleMetrics::App.sprockets
9
+ end
10
+
11
+ map "/" do
12
+ run SimpleMetrics::App
13
+ end
@@ -0,0 +1,34 @@
1
+ server:
2
+ host: 'localhost'
3
+ port: 8125
4
+ flush_interval: 10
5
+
6
+ db:
7
+ host: 'localhost'
8
+ port: 27017
9
+ prefix: 'development'
10
+ options:
11
+ pool_size: 5
12
+ timeout: 5
13
+
14
+ web:
15
+ host: 'localhost'
16
+ port: 5678
17
+
18
+ buckets:
19
+ - name: 'stats_per_10s'
20
+ seconds: 10
21
+ capped: true
22
+ size: 180_000 # 6*30 = 180 (30 min ttl), 180 * 100 = 18.000 (100 data points), 18.000 * 10 (bytes each) = 180.000
23
+ - name: 'stats_per_1min'
24
+ seconds: 60
25
+ capped: true
26
+ size: 120_000 # 60*3 = 180 (3h ttl), 120 * 100 = 18.000 (100 data points), 18.000 * 10 (bytes each) = 180.000
27
+ - name: 'stats_per_10min'
28
+ seconds: 600
29
+ size: 0
30
+ capped: false
31
+ - name: 'stats_per_day'
32
+ seconds: 86400 # 600*6*24
33
+ size: 0
34
+ capped: false
@@ -5,68 +5,39 @@ module SimpleMetrics
5
5
  class << self
6
6
 
7
7
  def all
8
- @@all ||= SimpleMetrics.buckets_config.map { |r| Bucket.new(r) }
8
+ @@all ||= SimpleMetrics.config.buckets.map { |r| Bucket.new(r) }
9
9
  end
10
10
 
11
11
  def first
12
12
  all.first
13
13
  end
14
- alias :finest :first
15
14
 
16
15
  def [](index)
17
16
  all[index]
18
17
  end
19
18
 
20
- def coarse_buckets
21
- Bucket.all.sort_by! { |r| r.seconds }[1..-1]
22
- end
23
-
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
-
28
- ts = Time.now.utc.to_i
29
- bucket = Bucket.first
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
-
35
- self.aggregate_all(ts)
36
- end
37
-
38
- def aggregate_all(ts)
39
- ts_bucket = self.first.ts_bucket(ts)
40
-
41
- coarse_buckets.each do |bucket|
42
- current_ts = bucket.ts_bucket(ts_bucket)
43
- previous_ts = bucket.previous_ts_bucket(ts_bucket)
44
- SimpleMetrics.logger.debug "Aggregating #{bucket.name} #{previous_ts}....#{current_ts} (#{humanized_timestamp(previous_ts)}..#{humanized_timestamp(current_ts)})"
45
-
46
- unless bucket.stats_exist_in_previous_ts?(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)
51
- end
52
- end
19
+ def for_time(time)
20
+ case time
21
+ when 'minute'
22
+ self[0]
23
+ when 'hour'
24
+ self[1]
25
+ when 'day'
26
+ self[2]
27
+ when 'week'
28
+ self[3]
53
29
  end
54
30
  end
55
31
 
56
- private
57
-
58
- def humanized_timestamp(ts)
59
- Time.at(ts).utc
60
- end
61
32
  end
62
33
 
63
34
  attr_reader :name, :capped
64
35
 
65
36
  def initialize(attributes)
66
- @name = attributes[:name]
67
- @seconds = attributes[:seconds]
68
- @capped = attributes[:capped]
69
- @size = attributes[:size]
37
+ @name = attributes.fetch(:name)
38
+ @seconds = attributes.fetch(:seconds)
39
+ @capped = attributes.fetch(:capped)
40
+ @size = attributes.fetch(:size)
70
41
  end
71
42
 
72
43
  def seconds
@@ -78,7 +49,7 @@ module SimpleMetrics
78
49
  end
79
50
 
80
51
  def ts_bucket(ts)
81
- ts / seconds * seconds
52
+ (ts / seconds) * seconds
82
53
  end
83
54
 
84
55
  def next_ts_bucket(ts)
@@ -89,64 +60,33 @@ module SimpleMetrics
89
60
  ts_bucket(ts) - seconds
90
61
  end
91
62
 
92
- def find(id)
93
- mongo_result = mongo_coll.find_one({ :_id => id })
94
- DataPoint.create_from_db(mongo_result)
63
+ # TODO: only used in tests, do we need it?
64
+ def find_all_at_ts(ts)
65
+ repository.find_all_at_ts(ts_bucket(ts))
95
66
  end
96
67
 
97
- def find_all_by_name(name)
98
- mongo_result = mongo_coll.find({ :name => name }).to_a
99
- mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
100
- end
101
-
102
- def find_all_in_ts(ts)
103
- mongo_result = mongo_coll.find({ :ts => ts_bucket(ts) }).to_a
104
- mongo_result.inject([]) { |result, a| result << DataPoint.create_from_db(a) }
105
- end
106
-
107
- def find_all_in_ts_by_name(ts, name)
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) }
110
- end
111
-
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) }
68
+ def find_data_point_at_ts(ts, name)
69
+ repository.find_data_point_at_ts(ts_bucket(ts), name)
115
70
  end
116
71
 
117
72
  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) }
73
+ repository.find_all_in_ts_range_by_name(from, to, name)
120
74
  end
121
75
 
122
76
  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) }
77
+ repository.find_all_in_ts_range_by_wildcard(from, to, target)
127
78
  end
128
79
 
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) }
80
+ def save(dp, ts)
81
+ dp.ts = ts_bucket(ts)
82
+ dp.sum = dp.value
83
+ dp.total = 1
84
+ repository.save(dp)
132
85
  end
133
86
 
134
- def stats_exist_in_previous_ts?(ts)
135
- mongo_coll.find({ :ts => ts }).count > 0
136
- end
137
-
138
- def find_all_distinct_names
139
- mongo_coll.distinct(:name).to_a
140
- end
141
-
142
- def save(stats, ts)
143
- stats.ts = ts_bucket(ts)
144
- result = mongo_coll.insert(stats.attributes)
145
- SimpleMetrics.logger.debug "SERVER: MongoDB - insert in #{name}: #{stats.inspect}, result: #{result}"
146
- end
147
-
148
- def mongo_coll
149
- Mongo.collection(name)
87
+ def update(dp, ts)
88
+ dp.ts = ts_bucket(ts)
89
+ repository.update(dp, ts)
150
90
  end
151
91
 
152
92
  def capped?
@@ -156,31 +96,28 @@ module SimpleMetrics
156
96
  def fill_gaps(from, to, query_result)
157
97
  return query_result if query_result.nil? || query_result.size == 0
158
98
 
159
- tmp_hash = DataPoint.ts_hash(query_result)
99
+ existing_ts_entries = query_result.inject({}) { |result, dp| result[dp.ts] = dp; result }
160
100
  dp_template = query_result.first
161
101
 
162
102
  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
103
+ each_ts(from, to) do |ts_bucket|
104
+ dp = existing_ts_entries[ts_bucket] || DataPoint::Base.new(:name => dp_template.name, :ts => ts_bucket)
105
+ result << dp
173
106
  end
174
107
  result
175
108
  end
176
109
 
177
110
  private
178
111
 
112
+ def repository
113
+ DataPointRepository.for_retention(name)
114
+ end
115
+
179
116
  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
117
+ ts_bucket = ts_bucket(from)
118
+ while (ts_bucket <= ts_bucket(to))
119
+ yield(ts_bucket)
120
+ ts_bucket = ts_bucket + seconds
184
121
  end
185
122
  end
186
123
 
@@ -0,0 +1,82 @@
1
+ require 'yaml'
2
+
3
+ module SimpleMetrics
4
+ class Configuration
5
+
6
+ attr_reader :config
7
+
8
+ def initialize(hash = {}, &block)
9
+ @config = load_defaults.merge(hash.symbolize_keys)
10
+ end
11
+
12
+ def configure(hash = {}, &block)
13
+ yield self if block_given?
14
+ self
15
+ end
16
+
17
+ def db
18
+ @db ||= config.fetch(:db).symbolize_keys
19
+ end
20
+
21
+ def db=(db)
22
+ @db = db
23
+ end
24
+
25
+ def buckets
26
+ @buckets ||= begin
27
+ tmp = config.fetch(:buckets)
28
+ tmp.map { |b| b.symbolize_keys }
29
+ end
30
+ end
31
+
32
+ def buckets=(buckets)
33
+ @buckets = buckets
34
+ end
35
+
36
+ def server
37
+ @server ||= config.fetch(:server).symbolize_keys
38
+ end
39
+
40
+ def server=(server)
41
+ @server = server
42
+ end
43
+
44
+ def web
45
+ @web ||= config.fetch(:web).symbolize_keys
46
+ end
47
+
48
+ def web=(web)
49
+ @web = web
50
+ end
51
+
52
+ private
53
+
54
+ def load_defaults
55
+ @config = load_config
56
+ rescue Errno::ENOENT # not found error
57
+ logger.info "Creating initial config file: #{config_file}"
58
+ FileUtils.cp(default_config_file, config_file)
59
+ @config = load_config
60
+ end
61
+
62
+ def config_file
63
+ File.expand_path('~/.simple_metrics.conf')
64
+ end
65
+
66
+ def default_config_file
67
+ File.expand_path('../../../default_config.yml', __FILE__)
68
+ end
69
+
70
+ def load_config
71
+ YAML.load_file(config_file).symbolize_keys
72
+ rescue ArgumentError => e
73
+ logger.error "Error parsing config file: #{e}"
74
+ rescue IOError => e
75
+ logger.error "Error reading config file: #{e}"
76
+ end
77
+
78
+ def logger
79
+ SimpleMetrics.logger
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,29 @@
1
+ module SimpleMetrics
2
+ class Dashboard
3
+
4
+ attr_reader :id, :created_at, :updated_at
5
+ attr_accessor :name, :instruments
6
+
7
+ def initialize(attributes)
8
+ @id = attributes[:id]
9
+ @name = attributes[:name]
10
+ @created_at = attributes[:created_at]
11
+ @updated_at = attributes[:updated_at]
12
+ @instruments = attributes[:instruments] || []
13
+ end
14
+
15
+ def attributes
16
+ {
17
+ :id => @id.to_s, # convert bson id to str
18
+ :name => @name,
19
+ :instruments => @instruments,
20
+ :created_at => @created_at,
21
+ :updated_at => @updated_at
22
+ }
23
+ end
24
+
25
+ def to_s
26
+ attributes.to_s
27
+ end
28
+ end
29
+ end