feldtruby 0.3.16 → 0.3.18
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/Gemfile.lock +9 -2
- data/Rakefile +8 -0
- data/feldtruby.gemspec +6 -0
- data/lib/feldtruby/annotations.rb +10 -0
- data/lib/feldtruby/array/basic_stats.rb +3 -1
- data/lib/feldtruby/array/permutations_and_subsets.rb +17 -0
- data/lib/feldtruby/float.rb +23 -0
- data/lib/feldtruby/logger.rb +216 -30
- data/lib/feldtruby/minitest_extensions.rb +0 -1
- data/lib/feldtruby/mongodb.rb +16 -0
- data/lib/feldtruby/mongodb_logger.rb +245 -0
- data/lib/feldtruby/optimize/differential_evolution.rb +29 -5
- data/lib/feldtruby/optimize/elite_archive.rb +91 -0
- data/lib/feldtruby/optimize/max_steps_termination_criterion.rb +1 -1
- data/lib/feldtruby/optimize/objective.rb +343 -222
- data/lib/feldtruby/optimize/optimizer.rb +138 -60
- data/lib/feldtruby/optimize/search_space.rb +10 -0
- data/lib/feldtruby/optimize.rb +1 -26
- data/lib/feldtruby/statistics.rb +74 -3
- data/lib/feldtruby/time.rb +19 -0
- data/lib/feldtruby/version.rb +1 -1
- data/old/event_logger.rb +682 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/analyze_sampler_comparison_results.R +78 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/compare_samplers.rb +264 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_130405_175934.csv +561 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder.csv +11201 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder_all_radii_4_to_30.csv +44801 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_omnitest.csv +1401 -0
- data/spikes/mongodb_logger.rb +47 -0
- data/spikes/simple_de_run.rb +32 -0
- data/test/helper.rb +17 -1
- data/test/test_array_basic_stats.rb +5 -1
- data/test/test_array_permutations_and_subsets.rb +23 -0
- data/test/test_float.rb +15 -0
- data/test/test_html_doc_getter.rb +1 -1
- data/test/test_logger.rb +86 -48
- data/test/test_mongodb_logger.rb +116 -0
- data/test/test_object_annotations.rb +14 -0
- data/test/test_optimize.rb +7 -6
- data/test/test_optimize_differential_evolution.rb +21 -19
- data/test/test_optimize_elite_archive.rb +85 -0
- data/test/test_optimize_objective.rb +237 -74
- data/test/test_optimize_populationbasedoptimizer.rb +72 -6
- data/test/test_optimize_random_search.rb +0 -17
- data/test/test_optimize_search_space.rb +15 -0
- data/test/test_statistics.rb +30 -4
- data/test/test_time.rb +22 -0
- data/test/tmp_shorter.csv +200 -0
- metadata +62 -21
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1c76bb7e979470ff22539fc9543ddf008dfe34a0
|
4
|
+
data.tar.gz: b9215aa49df906e914bd69611fefd0eddd46ae03
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ff95c9ebbca79724d25133f970d3a638d118f624eb72b6601605f697a53c1132d7b7923c670319b547d70006b6dd8f81416aba5498ec4dc75d85e6fe8c16ce63
|
7
|
+
data.tar.gz: 78b95306f0af7db720989945452304e4bd6977209cb3eb84d4d7faf43807c72b28776bc140be95ea1c5bcb9d9857ecffb0bb5fbf3b04f68fb162715479812496
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
feldtruby (0.3.
|
4
|
+
feldtruby (0.3.18)
|
5
|
+
bson_ext
|
5
6
|
json
|
7
|
+
mongo
|
6
8
|
nokogiri
|
7
9
|
rinruby
|
8
10
|
|
9
11
|
GEM
|
10
12
|
remote: https://rubygems.org/
|
11
13
|
specs:
|
14
|
+
bson (1.8.4)
|
15
|
+
bson_ext (1.8.4)
|
16
|
+
bson (~> 1.8.4)
|
12
17
|
json (1.7.7)
|
13
|
-
|
18
|
+
mongo (1.8.4)
|
19
|
+
bson (~> 1.8.4)
|
20
|
+
nokogiri (1.5.9)
|
14
21
|
rinruby (2.0.3)
|
15
22
|
|
16
23
|
PLATFORMS
|
data/Rakefile
CHANGED
@@ -13,6 +13,14 @@ def run_tests(testFiles)
|
|
13
13
|
psys "ruby -Ilib:. -e '#{require_files}' --"
|
14
14
|
end
|
15
15
|
|
16
|
+
desc "Run tests separately to identify problematic ones"
|
17
|
+
task :test_sep do
|
18
|
+
Dir["test/**/test*.rb"].each do |fn|
|
19
|
+
puts "RUNNING: #{fn}"
|
20
|
+
run_tests [fn]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
desc "Run all tests"
|
17
25
|
task :test do
|
18
26
|
run_tests Dir["test/**/test*.rb"]
|
data/feldtruby.gemspec
CHANGED
@@ -22,4 +22,10 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_dependency('json')
|
23
23
|
|
24
24
|
gem.add_dependency('nokogiri')
|
25
|
+
|
26
|
+
# For mongodb_logger:
|
27
|
+
gem.add_dependency('mongo')
|
28
|
+
|
29
|
+
# bson_ext does not work with macruby but good for MongoDB performance so:
|
30
|
+
gem.add_dependency('bson_ext')
|
25
31
|
end
|
@@ -131,7 +131,9 @@ module BasicStatistics
|
|
131
131
|
|
132
132
|
# Return summary stats for an array of numbers.
|
133
133
|
def summary_stats
|
134
|
-
|
134
|
+
return "" if length == 0
|
135
|
+
vals = [mean, self.min, self.max, median, stdev]
|
136
|
+
"%.3g (min = %.3g, max = %.3g, median = %.3g, stdev = %.3g)" % vals
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
@@ -8,4 +8,21 @@ class Array
|
|
8
8
|
rest.map {|e| [x, e]} + rest.all_pairs
|
9
9
|
|
10
10
|
end
|
11
|
+
|
12
|
+
# Create all combinations of values from an array of sub-arrays, with
|
13
|
+
# each combination picking one value from each sub-array.
|
14
|
+
#
|
15
|
+
# Examples:
|
16
|
+
# [[1,2], [3]].all_combinations_one_from_each => [[1,3], [2,3]]
|
17
|
+
#
|
18
|
+
# [[1,2], [3, 7]].all_combinations_one_from_each => [[1,3], [2,3], [1,7], [2,7]]
|
19
|
+
def all_combinations_one_from_each
|
20
|
+
return [] if length == 0
|
21
|
+
|
22
|
+
return self.first.map {|v| [v]} if length == 1
|
23
|
+
|
24
|
+
self[1..-1].all_combinations_one_from_each.map do |c|
|
25
|
+
self.first.map {|v| [v] + c}
|
26
|
+
end.flatten(1)
|
27
|
+
end
|
11
28
|
end
|
data/lib/feldtruby/float.rb
CHANGED
@@ -14,4 +14,27 @@ class Numeric
|
|
14
14
|
def ratio_diff_vs(other)
|
15
15
|
(self - other).protected_division_with(other)
|
16
16
|
end
|
17
|
+
|
18
|
+
# Change to a float with a given number of significant digits.
|
19
|
+
def signif(numDigits = 3)
|
20
|
+
self.to_f.signif(numDigits)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Change to a float with a given number of significant digits.
|
24
|
+
def to_significant_digits(numDigits = 3)
|
25
|
+
self.to_f.to_significant_digits(numDigits)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Float
|
30
|
+
# Change to a float with a given number of significant digits.
|
31
|
+
def signif(numDigits = 3)
|
32
|
+
return self if self == INFINITY || self == -INFINITY
|
33
|
+
Float("%.#{numDigits}g" % self)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Change to a float with a given number of significant digits.
|
37
|
+
def to_significant_digits(numDigits = 3)
|
38
|
+
signif(numDigits)
|
39
|
+
end
|
17
40
|
end
|
data/lib/feldtruby/logger.rb
CHANGED
@@ -1,62 +1,248 @@
|
|
1
|
-
require '
|
1
|
+
require 'time'
|
2
|
+
require 'feldtruby/array/basic_stats'
|
3
|
+
require 'feldtruby/time'
|
4
|
+
require 'feldtruby/float'
|
2
5
|
|
3
6
|
module FeldtRuby
|
4
7
|
|
8
|
+
# Simplest possible logger only prints to STDOUT.
|
5
9
|
class Logger
|
6
|
-
|
10
|
+
DefaultParams = {
|
11
|
+
:verbose => true,
|
12
|
+
:print_frequency => 0.3 # Minimum seconds between consecutive messages printed for the same event type
|
13
|
+
}
|
7
14
|
|
8
|
-
|
15
|
+
UnixEpoch = Time.at(0)
|
9
16
|
|
10
|
-
|
11
|
-
@events = Hash.new {|h,k| h[k] = Array.new}
|
17
|
+
attr_reader :start_time
|
12
18
|
|
19
|
+
def initialize(io = STDOUT, params = DefaultParams)
|
20
|
+
|
21
|
+
@start_time = Time.now
|
22
|
+
|
23
|
+
@params = DefaultParams.clone.update(params)
|
24
|
+
|
25
|
+
self.verbose = @params[:verbose]
|
26
|
+
|
27
|
+
self.print_frequency = @params[:print_frequency]
|
28
|
+
|
29
|
+
@ios = []
|
30
|
+
|
31
|
+
add_io io
|
32
|
+
|
33
|
+
setup_data_store
|
34
|
+
|
35
|
+
@last_time_printed_for_event_type = Hash.new(UnixEpoch)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set up the internal data store.
|
40
|
+
def setup_data_store
|
41
|
+
# Nothing is saved by this simplest Logger, we just count them
|
42
|
+
@counts = Hash.new(0)
|
13
43
|
end
|
14
44
|
|
15
|
-
#
|
45
|
+
# Number of events of _eventType_ we have seen so far.
|
16
46
|
def num_events eventType = nil
|
17
|
-
|
47
|
+
if eventType == nil
|
48
|
+
@counts.values.sum
|
49
|
+
else
|
50
|
+
@counts[eventType]
|
51
|
+
end
|
18
52
|
end
|
19
53
|
|
20
|
-
|
54
|
+
# Return the elapsed time since the logger was started.
|
55
|
+
def elapsed_time t = Time.now
|
56
|
+
t - @start_time
|
57
|
+
end
|
21
58
|
|
22
|
-
|
23
|
-
|
24
|
-
@events[eventType] << Event.new(description, Time.now)
|
59
|
+
def verbose=(flag)
|
60
|
+
@verbose = @params[:verbose] = flag
|
25
61
|
end
|
26
62
|
|
27
|
-
#
|
28
|
-
def
|
29
|
-
@
|
63
|
+
# Set the minimum time between printing successive messages of the same type.
|
64
|
+
def print_frequency=(seconds = 1.0)
|
65
|
+
@print_frequency = @params[:print_frequency] = seconds
|
30
66
|
end
|
31
67
|
|
32
|
-
#
|
33
|
-
def
|
34
|
-
@
|
68
|
+
# Add one more _io_ stream to which events are logged.
|
69
|
+
def add_io io
|
70
|
+
@ios << io
|
71
|
+
@ios.uniq
|
35
72
|
end
|
36
73
|
|
37
|
-
|
38
|
-
|
39
|
-
|
74
|
+
def add_output_file(filename)
|
75
|
+
@output_ios ||= []
|
76
|
+
@output_ios << File.open(filename, "w")
|
77
|
+
add_io @output_ios.last
|
78
|
+
ObjectSpace.define_finalizer(self) do
|
79
|
+
@output_ios.each {|fh| fh.close}
|
80
|
+
end
|
81
|
+
end
|
40
82
|
|
41
|
-
|
83
|
+
# Events:
|
84
|
+
#
|
85
|
+
# An event is a hash with the keys:
|
86
|
+
# "t" => time of event in UTC
|
87
|
+
# "v" => optional value of the event, this is singled out for perf reasons,
|
88
|
+
# it could also be saved in the data ("d")
|
89
|
+
# "d" => optional hash with additional data for the event
|
90
|
+
#
|
91
|
+
# An event which has a value but no data is called a value event.
|
92
|
+
# A value event where the value is a number is called a number event.
|
93
|
+
# An event with data is called a data event.
|
94
|
+
# An event with only a message is called a message event. This is saved
|
95
|
+
# as a data event of type "___default___", with the message in e["d"]["m"].
|
96
|
+
# An event can also be a counter event. Counter events are not logged, we just
|
97
|
+
# count how many they are.
|
98
|
+
|
99
|
+
# Log a counter event, i.e. update the (local) count of how many times
|
100
|
+
# this event has happened.
|
101
|
+
def log_counter eventType, message = nil
|
102
|
+
if message
|
103
|
+
log_event eventType, nil, message
|
104
|
+
else
|
105
|
+
# We count it even if should not log it
|
106
|
+
@counts[eventType] += 1
|
107
|
+
end
|
108
|
+
end
|
42
109
|
|
43
|
-
|
110
|
+
# Log a value event.
|
111
|
+
def log_value eventType, value, message = nil
|
112
|
+
log_event eventType, {"v" => value}, message
|
113
|
+
end
|
44
114
|
|
45
|
-
|
115
|
+
# Log a data event.
|
116
|
+
def log_data eventType, data, message = nil
|
117
|
+
log_event eventType, {"d" => data}, message
|
118
|
+
end
|
46
119
|
|
120
|
+
# Log a message event.
|
121
|
+
def log message
|
122
|
+
log_event "___default___", {"d" => {"m" => message}}, message
|
47
123
|
end
|
48
124
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
125
|
+
# Log the event and print the message, if any. This simplest logger
|
126
|
+
# only prints, it never saves the event.
|
127
|
+
def log_event eventType, event, message = nil
|
128
|
+
|
129
|
+
@counts[eventType] += 1
|
130
|
+
|
131
|
+
if message
|
132
|
+
print_message_if_needed message, eventType, (eventType == "___default___")
|
133
|
+
end
|
134
|
+
|
135
|
+
event
|
136
|
+
|
52
137
|
end
|
53
138
|
|
54
|
-
|
55
|
-
|
139
|
+
def print_message_if_needed message, eventType, skipCheck = false
|
140
|
+
time = Time.now.utc
|
141
|
+
|
142
|
+
# We only print if enough time since last time we printed. This way
|
143
|
+
# we avoid "flooding" the user with log messages of the same type.
|
144
|
+
if skipCheck || (time - @last_time_printed_for_event_type[eventType]) >= @print_frequency
|
145
|
+
|
146
|
+
io_puts message, time
|
147
|
+
|
148
|
+
@last_time_printed_for_event_type[eventType] = time
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Puts the given _message_ on the io stream(s) stamped with the given time.
|
154
|
+
def io_puts message, time = Time.now
|
155
|
+
|
156
|
+
return unless @verbose
|
157
|
+
|
158
|
+
elapsed_str = Time.human_readable_timestr elapsed_time(time)
|
159
|
+
|
160
|
+
s = time.strftime("\n%H:%M.%S%3N (#{elapsed_str}), ") + message
|
161
|
+
|
162
|
+
@ios.each {|io| io.puts s}
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
# A simple logging interface front end for classes that need basic logging.
|
169
|
+
# Just include and call log methods on logger. Uses a single common logger
|
170
|
+
# unless a new one is been explicitly specified..
|
171
|
+
module Logging
|
172
|
+
attr_accessor :logger
|
173
|
+
|
174
|
+
def setup_logger_and_distribute_to_instance_variables(logger = nil)
|
175
|
+
|
176
|
+
# Precedence for loggers if several has been setup:
|
177
|
+
# 1. One specified as parameter to this method
|
178
|
+
# 2. One that has already been set on this object
|
179
|
+
# 3. First one found on an instance var
|
180
|
+
# 4. Create a new standard one
|
181
|
+
self.logger = logger || self.logger || __find_logger_set_on_instance_vars() ||
|
182
|
+
new_default_logger()
|
183
|
+
|
184
|
+
# Now distribute the preferred logger to all instance vars, recursively.
|
185
|
+
self.instance_variables.each do |ivar_name|
|
186
|
+
|
187
|
+
ivar = self.instance_variable_get ivar_name
|
188
|
+
|
189
|
+
if ivar.respond_to?(:setup_logger_and_distribute_to_instance_variables)
|
190
|
+
ivar.setup_logger_and_distribute_to_instance_variables self.logger
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
# Override to use another logger as default if no logger is found.
|
198
|
+
def new_default_logger
|
199
|
+
FeldtRuby::Logger.new
|
200
|
+
end
|
201
|
+
|
202
|
+
# Find a logger if one has been set on any of my instance vars or their
|
203
|
+
# instance vars (recursively).
|
204
|
+
def __find_logger_set_on_instance_vars
|
205
|
+
|
206
|
+
# First see if we find it in the immediate ivars
|
207
|
+
self.instance_variables.each do |ivar_name|
|
208
|
+
|
209
|
+
ivar = self.instance_variable_get ivar_name
|
210
|
+
|
211
|
+
if ivar.respond_to?(:logger)
|
212
|
+
|
213
|
+
begin
|
214
|
+
|
215
|
+
l = ivar.send(:logger)
|
216
|
+
return l if l.is_a?(FeldtRuby::Logger)
|
217
|
+
|
218
|
+
rescue Exception => e
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
# If we come here it means we did NOT find a logger in immediate
|
226
|
+
# ivar's. So we recurse.
|
227
|
+
self.instance_variables.each do |ivar_name|
|
228
|
+
|
229
|
+
ivar = self.instance_variable_get ivar_name
|
230
|
+
|
231
|
+
if ivar.respond_to?(:find_logger_set_on_instance_vars)
|
232
|
+
|
233
|
+
begin
|
234
|
+
|
235
|
+
l = ivar.send(:find_logger_set_on_instance_vars)
|
236
|
+
return l if l.is_a?(FeldtRuby::Logger)
|
237
|
+
|
238
|
+
rescue Exception => e
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
56
242
|
|
57
|
-
|
243
|
+
end
|
58
244
|
|
59
|
-
|
245
|
+
nil
|
60
246
|
|
61
247
|
end
|
62
248
|
end
|
@@ -6,7 +6,6 @@ module MiniTest::Assertions
|
|
6
6
|
# Ensure that that are (statistically) the same number of each type
|
7
7
|
# of value in an array.
|
8
8
|
def assert_similar_proportions(values, expectedPValue = 0.01, msg = nil)
|
9
|
-
#pvalue = FeldtRuby.probability_of_same_proportions(values)
|
10
9
|
pvalue = FeldtRuby.chi_squared_test(values)
|
11
10
|
assert(pvalue > expectedPValue, msg || "Proportions differ! p-value is #{pvalue} (<0.05), counts: #{values.counts.inspect}")
|
12
11
|
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'feldtruby/version'
|
2
|
+
require 'feldtruby/logger'
|
3
|
+
require 'feldtruby/mongodb'
|
4
|
+
|
5
|
+
if FeldtRuby.is_mongo_running?
|
6
|
+
|
7
|
+
module FeldtRuby
|
8
|
+
|
9
|
+
# This is a class to access the main directory of all MongoDBLogger db's
|
10
|
+
# saved in a mongodb.
|
11
|
+
class AllMongoDBLoggers
|
12
|
+
def initialize host, port
|
13
|
+
@host, @port = host, port
|
14
|
+
|
15
|
+
@client = Mongo::MongoClient.new("localhost", 27017)
|
16
|
+
|
17
|
+
@main_db = @client.db("MongoDBLoggers")
|
18
|
+
end
|
19
|
+
|
20
|
+
def all_logs
|
21
|
+
@main_db["all_logs"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a logger to the main dir of logger dbs.
|
25
|
+
def add_logger(logger)
|
26
|
+
|
27
|
+
db_logger_data = {
|
28
|
+
"db_name" => logger.db_name,
|
29
|
+
"logger_class" => logger.class.inspect,
|
30
|
+
"gem" => "FeldtRuby",
|
31
|
+
"gem_version" => FeldtRuby::VERSION,
|
32
|
+
"start_time" => logger.start_time
|
33
|
+
}
|
34
|
+
|
35
|
+
all_logs.insert db_logger_data
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def all_log_infos
|
40
|
+
all_logs.find.to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_logger_dbs skipLatestDb = true
|
44
|
+
|
45
|
+
infos = all_log_infos.sort_by {|i| i["start_time"]}
|
46
|
+
|
47
|
+
if skipLatestDb
|
48
|
+
infos = infos - [infos.last]
|
49
|
+
end
|
50
|
+
|
51
|
+
infos.each {|i| drop_db_named(i["db_name"])}
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Drop the logger db with given name.
|
56
|
+
def drop_db_named name
|
57
|
+
|
58
|
+
@client.drop_database name
|
59
|
+
|
60
|
+
all_logs.remove( {"db_name" => name} )
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# This is an EventLogger that logs to a MongoDB database. It caches
|
66
|
+
# the last two events per event type for quicker access to them.
|
67
|
+
class MongoDBLogger < Logger
|
68
|
+
OurParams = {
|
69
|
+
:host => "localhost",
|
70
|
+
:port => 27017,
|
71
|
+
}
|
72
|
+
|
73
|
+
DefaultParams = FeldtRuby::Logger::DefaultParams.clone.update(OurParams)
|
74
|
+
|
75
|
+
# I think this is needed since we have redefined DefaultParams but should
|
76
|
+
# investigate...
|
77
|
+
def initialize(io = STDOUT, params = DefaultParams)
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
def unique_mongodb_name
|
82
|
+
"MongoDBLogger_" + @start_time.utc.strftime("%Y%m%d_%H%M%S") + "_#{object_id}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Reader methods for the db and mongo client and db name.
|
86
|
+
attr_reader :db, :mongo_client, :db_name
|
87
|
+
|
88
|
+
# Set up the internal data store.
|
89
|
+
def setup_data_store
|
90
|
+
|
91
|
+
super
|
92
|
+
|
93
|
+
# To handle the main "directory" of logger dbs in the mongodb.
|
94
|
+
@all_dbs = FeldtRuby::AllMongoDBLoggers.new @params[:host], @params[:port]
|
95
|
+
|
96
|
+
@mongo_client = Mongo::MongoClient.new @params[:host], @params[:port]
|
97
|
+
|
98
|
+
@db_name = unique_mongodb_name()
|
99
|
+
|
100
|
+
# Always creates a new db based on unique timestamp and object_id. The
|
101
|
+
# latter is used to ensure each db has a unique name.
|
102
|
+
@db = @mongo_client.db(@db_name)
|
103
|
+
|
104
|
+
@all_dbs.add_logger self
|
105
|
+
|
106
|
+
# Will map each event type to a collection in the db were we save
|
107
|
+
# events for that type.
|
108
|
+
@collections = Hash.new
|
109
|
+
|
110
|
+
# Caches for the last and second_last events per type so we need
|
111
|
+
# not lookup in db for most common requests.
|
112
|
+
@cache_last = Hash.new
|
113
|
+
@cache_2ndlast = Hash.new
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
# Reader methods for the main db and its all_logs collection. The latter is
|
118
|
+
# the main "directory" for listing all available logs.
|
119
|
+
attr_reader :main_db, :all_logs
|
120
|
+
|
121
|
+
# Events are saved in the mongodb db, using one collection per event type.
|
122
|
+
# The type itself is not saved in the event since it is implicit in the
|
123
|
+
# collection in which the event is saved.
|
124
|
+
def save_event event, type
|
125
|
+
|
126
|
+
collection_for_type(type).insert event
|
127
|
+
|
128
|
+
update_cache event, type
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
def update_cache event, type
|
133
|
+
|
134
|
+
@cache_2ndlast[type] = @cache_last[type]
|
135
|
+
|
136
|
+
@cache_last[type] = event
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
# Number of events of _eventType_ we have seen so far.
|
141
|
+
def num_events eventType
|
142
|
+
@counts[eventType]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Log the event and print the message, if any.
|
146
|
+
def log_event eventType, event, message = nil
|
147
|
+
|
148
|
+
@counts[eventType] += 1
|
149
|
+
|
150
|
+
if event
|
151
|
+
|
152
|
+
event["t"] = Time.now.utc
|
153
|
+
|
154
|
+
save_event(event, eventType)
|
155
|
+
|
156
|
+
if message
|
157
|
+
print_message_if_needed message, eventType, (eventType == "___default___")
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
event
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
def collection_for_type(t)
|
167
|
+
ts = t || "___default___"
|
168
|
+
@collections[ts] ||= @db[ts.to_s]
|
169
|
+
end
|
170
|
+
|
171
|
+
def last_event eventType
|
172
|
+
@cache_last[eventType]
|
173
|
+
end
|
174
|
+
|
175
|
+
def prev_event eventType
|
176
|
+
@cache_2ndlast[eventType]
|
177
|
+
end
|
178
|
+
|
179
|
+
def last_value eventType
|
180
|
+
current_value eventType
|
181
|
+
end
|
182
|
+
|
183
|
+
# Return the current (latest) value for a given eventType and metric.
|
184
|
+
# Return nil if no value has been set.
|
185
|
+
def previous_value eventType, metric = "v"
|
186
|
+
@cache_2ndlast[eventType][metric]
|
187
|
+
end
|
188
|
+
|
189
|
+
# Return the current (latest) value for a given eventType and metric.
|
190
|
+
# Return nil if no value has been set.
|
191
|
+
def current_value eventType, metric = "v"
|
192
|
+
@cache_last[eventType][metric]
|
193
|
+
end
|
194
|
+
|
195
|
+
# Return all the events for a given _eventType_.
|
196
|
+
def events(eventType = nil)
|
197
|
+
c = collection_for_type eventType
|
198
|
+
c.find.to_a
|
199
|
+
end
|
200
|
+
|
201
|
+
# Return all events, for a given _eventType_, between the _start_ and _stop_
|
202
|
+
# times. If _includePreEvent_ is true we include the event that comes directly
|
203
|
+
# before the start time.
|
204
|
+
def events_between start, stop, eventType = nil, includePreEvent = false
|
205
|
+
|
206
|
+
all_events = events(eventType)
|
207
|
+
|
208
|
+
es = all_events.select do |e|
|
209
|
+
|
210
|
+
t = e.time
|
211
|
+
|
212
|
+
t >= start && t <= stop
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
if includePreEvent
|
217
|
+
|
218
|
+
index_to_first_selected_event = all_events.index(es.first)
|
219
|
+
|
220
|
+
if index_to_first_selected_event && index_to_first_selected_event > 0
|
221
|
+
# There is a pre-event so add it
|
222
|
+
es.unshift all_events[index_to_first_selected_event - 1]
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
es
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
# Get an array of values for the metric named _metric_ in events
|
232
|
+
# of type _eventType_.
|
233
|
+
def values_for_event_and_metric eventType, metric = "v"
|
234
|
+
events(eventType).map {|e| e.data[metric]}
|
235
|
+
end
|
236
|
+
|
237
|
+
# Shortcut method to get the value saved for a certain _eventType_.
|
238
|
+
def values_for eventType
|
239
|
+
values_for_event_and_metric eventType
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|