feldtruby 0.3.16 → 0.3.18
Sign up to get free protection for your applications and to get access to all the features.
- 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
|