logging 0.9.2 → 0.9.3
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.
- data/History.txt +6 -0
- data/Manifest.txt +2 -0
- data/Rakefile +1 -1
- data/lib/logging.rb +4 -1
- data/lib/logging/appender.rb +0 -2
- data/lib/logging/logger.rb +0 -2
- data/lib/logging/stats.rb +278 -0
- data/lib/logging/utils.rb +25 -0
- data/test/test_stats.rb +274 -0
- metadata +5 -2
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -25,6 +25,7 @@ lib/logging/log_event.rb
|
|
25
25
|
lib/logging/logger.rb
|
26
26
|
lib/logging/repository.rb
|
27
27
|
lib/logging/root_logger.rb
|
28
|
+
lib/logging/stats.rb
|
28
29
|
lib/logging/utils.rb
|
29
30
|
tasks/ann.rake
|
30
31
|
tasks/bones.rake
|
@@ -57,4 +58,5 @@ test/test_logger.rb
|
|
57
58
|
test/test_logging.rb
|
58
59
|
test/test_repository.rb
|
59
60
|
test/test_root_logger.rb
|
61
|
+
test/test_stats.rb
|
60
62
|
test/test_utils.rb
|
data/Rakefile
CHANGED
@@ -16,7 +16,7 @@ PROJ.rdoc.dir = 'doc/rdoc'
|
|
16
16
|
#PROJ.rdoc.remote_dir = 'rdoc'
|
17
17
|
PROJ.rdoc.remote_dir = ''
|
18
18
|
PROJ.version = Logging::VERSION
|
19
|
-
PROJ.release_name = %q{
|
19
|
+
PROJ.release_name = %q{Log-O-Stat}
|
20
20
|
|
21
21
|
PROJ.exclude << %w[^tags$ ^tasks/archive ^coverage]
|
22
22
|
PROJ.rdoc.exclude << '^data'
|
data/lib/logging.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
# Used to prevent the class/module from being loaded more than once
|
4
4
|
unless defined? Logging
|
5
5
|
|
6
|
+
require 'thread'
|
7
|
+
begin require 'fastthread'; rescue LoadError; end
|
8
|
+
|
6
9
|
# TODO: Windows Log Service appender
|
7
10
|
|
8
11
|
#
|
@@ -10,7 +13,7 @@ unless defined? Logging
|
|
10
13
|
module Logging
|
11
14
|
|
12
15
|
# :stopdoc:
|
13
|
-
VERSION = '0.9.
|
16
|
+
VERSION = '0.9.3'
|
14
17
|
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
15
18
|
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
16
19
|
WIN32 = %r/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM
|
data/lib/logging/appender.rb
CHANGED
data/lib/logging/logger.rb
CHANGED
@@ -0,0 +1,278 @@
|
|
1
|
+
# Simple statistics collection and logging module.
|
2
|
+
#
|
3
|
+
module Logging::Stats
|
4
|
+
|
5
|
+
# A very simple little class for doing some basic fast statistics
|
6
|
+
# sampling. You feed it either samples of numeric data you want measured
|
7
|
+
# or you call Sampler#tick to get it to add a time delta between the last
|
8
|
+
# time you called it. When you're done either call sum, sumsq, num, min,
|
9
|
+
# max, mean or sd to get the information. The other option is to just
|
10
|
+
# call to_s and see everything.
|
11
|
+
#
|
12
|
+
# It does all of this very fast and doesn't take up any memory since the
|
13
|
+
# samples are not stored but instead all the values are calculated on the
|
14
|
+
# fly.
|
15
|
+
#
|
16
|
+
class Sampler
|
17
|
+
|
18
|
+
attr_reader :name, :sum, :sumsq, :num, :min, :max, :last
|
19
|
+
|
20
|
+
# Create a new sampler.
|
21
|
+
#
|
22
|
+
def initialize( name )
|
23
|
+
@name = name
|
24
|
+
reset
|
25
|
+
end
|
26
|
+
|
27
|
+
# Resets the internal counters so you can start sampling again.
|
28
|
+
#
|
29
|
+
def reset
|
30
|
+
@sum = 0.0
|
31
|
+
@sumsq = 0.0
|
32
|
+
@num = 0
|
33
|
+
@min = 0.0
|
34
|
+
@max = 0.0
|
35
|
+
@last = nil
|
36
|
+
@last_time = Time.now.to_f
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Coalesce the statistics from the _other_ sampler into this one. The
|
41
|
+
# _other_ sampler is not modified by this method.
|
42
|
+
#
|
43
|
+
# Coalescing the same two samplers mutliple times should only be done if
|
44
|
+
# one of the samplers is reset between calls to this method. Otherwise
|
45
|
+
# statistics will be counted multiple times.
|
46
|
+
#
|
47
|
+
def coalesce( other )
|
48
|
+
@sum += other.sum
|
49
|
+
@sumsq += other.sumsq
|
50
|
+
if other.num > 0
|
51
|
+
@min = other.min if @min > other.min
|
52
|
+
@max = other.max if @max < other.max
|
53
|
+
@last = other.last
|
54
|
+
end
|
55
|
+
@num += other.num
|
56
|
+
end
|
57
|
+
|
58
|
+
# Adds a sampling to the calculations.
|
59
|
+
#
|
60
|
+
def sample( s )
|
61
|
+
@sum += s
|
62
|
+
@sumsq += s * s
|
63
|
+
if @num == 0
|
64
|
+
@min = @max = s
|
65
|
+
else
|
66
|
+
@min = s if @min > s
|
67
|
+
@max = s if @max < s
|
68
|
+
end
|
69
|
+
@num += 1
|
70
|
+
@last = s
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns statistics in a common format.
|
74
|
+
#
|
75
|
+
def to_s
|
76
|
+
"[%s]: SUM=%0.6f, SUMSQ=%0.6f, NUM=%d, MEAN=%0.6f, SD=%0.6f, MIN=%0.6f, MAX=%0.6f" % to_a
|
77
|
+
end
|
78
|
+
|
79
|
+
# An array of the values: [name,sum,sumsq,num,mean,sd,min,max]
|
80
|
+
#
|
81
|
+
def to_a
|
82
|
+
[name, sum, sumsq, num, mean, sd, min, max]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Class method that returns the headers that a CSV file would have for the
|
86
|
+
# values that this stats object is using.
|
87
|
+
#
|
88
|
+
def self.keys
|
89
|
+
%w[name sum sumsq num mean sd min max]
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_hash
|
93
|
+
{:name => name, :sum => sum, :sumsq => sumsq, :num => num,
|
94
|
+
:mean => mean, :sd => sd, :min => min, :max => max}
|
95
|
+
end
|
96
|
+
|
97
|
+
# Calculates and returns the mean for the data passed so far.
|
98
|
+
#
|
99
|
+
def mean
|
100
|
+
return 0.0 if num < 1
|
101
|
+
sum / num
|
102
|
+
end
|
103
|
+
|
104
|
+
# Calculates the standard deviation of the data so far.
|
105
|
+
#
|
106
|
+
def sd
|
107
|
+
return 0.0 if num < 2
|
108
|
+
|
109
|
+
# (sqrt( ((s).sumsq - ( (s).sum * (s).sum / (s).num)) / ((s).num-1) ))
|
110
|
+
begin
|
111
|
+
return Math.sqrt( (sumsq - ( sum * sum / num)) / (num-1) )
|
112
|
+
rescue Errno::EDOM
|
113
|
+
return 0.0
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# You can just call tick repeatedly if you need the delta times
|
118
|
+
# between a set of sample periods, but many times you actually want
|
119
|
+
# to sample how long something takes between a start/end period.
|
120
|
+
# Call mark at the beginning and then tick at the end you'll get this
|
121
|
+
# kind of measurement. Don't mix mark/tick and tick sampling together
|
122
|
+
# or the measurement will be meaningless.
|
123
|
+
#
|
124
|
+
def mark
|
125
|
+
@last_time = Time.now.to_f
|
126
|
+
end
|
127
|
+
|
128
|
+
# Adds a time delta between now and the last time you called this. This
|
129
|
+
# will give you the average time between two activities.
|
130
|
+
#
|
131
|
+
# An example is:
|
132
|
+
#
|
133
|
+
# t = Sampler.new("do_stuff")
|
134
|
+
# 10000.times { do_stuff(); t.tick }
|
135
|
+
# t.dump("time")
|
136
|
+
#
|
137
|
+
def tick
|
138
|
+
now = Time.now.to_f
|
139
|
+
sample(now - @last_time)
|
140
|
+
@last_time = now
|
141
|
+
end
|
142
|
+
end # class Sampler
|
143
|
+
|
144
|
+
# The Tracker class provides synchronized access to a collection of
|
145
|
+
# related samplers.
|
146
|
+
#
|
147
|
+
class Tracker
|
148
|
+
|
149
|
+
attr_reader :stats
|
150
|
+
|
151
|
+
# Create a new Tracker instance. An optional boolean can be bassed in to
|
152
|
+
# change the "threadsafe" value of the tracker. By default all trackers
|
153
|
+
# are created to be threadsafe.
|
154
|
+
#
|
155
|
+
def initialize( threadsafe = true )
|
156
|
+
@stats = Hash.new do |h,name|
|
157
|
+
h[name] = ::Logging::Stats::Sampler.new(name)
|
158
|
+
end
|
159
|
+
@mutex = threadsafe ? ReentrantMutex.new : nil
|
160
|
+
@runner = nil
|
161
|
+
end
|
162
|
+
|
163
|
+
# Coalesce the samplers from the _other_ tracker into this one. The
|
164
|
+
# _other_ tracker is not modified by this method.
|
165
|
+
#
|
166
|
+
# Coalescing the same two trackers mutliple times should only be done if
|
167
|
+
# one of the trackers is reset between calls to this method. Otherwise
|
168
|
+
# statistics will be counted multiple times.
|
169
|
+
#
|
170
|
+
# Only this tracker is locked when the coalescing is happening. It is
|
171
|
+
# left to the user to lock the other tracker if that is the desired
|
172
|
+
# behavior. This is a deliberate choice in order to prevent deadlock
|
173
|
+
# situations where two threads are contending on the same mutex.
|
174
|
+
#
|
175
|
+
def coalesce( other )
|
176
|
+
sync {
|
177
|
+
other.stats.each do |name,sampler|
|
178
|
+
stats[name].coalesce(sampler)
|
179
|
+
end
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
# Add the given _value_ to the named _event_ sampler. The sampler will
|
184
|
+
# be created if it does not exist.
|
185
|
+
#
|
186
|
+
def sample( event, value )
|
187
|
+
sync {stats[event].sample(value)}
|
188
|
+
end
|
189
|
+
|
190
|
+
# Mark the named _event_ sampler. The sampler will be created if it does
|
191
|
+
# not exist.
|
192
|
+
#
|
193
|
+
def mark( event )
|
194
|
+
sync {stats[event].mark}
|
195
|
+
end
|
196
|
+
|
197
|
+
# Tick the named _event_ sampler. The sampler will be created if it does
|
198
|
+
# not exist.
|
199
|
+
#
|
200
|
+
def tick( event )
|
201
|
+
sync {stats[event].tick}
|
202
|
+
end
|
203
|
+
|
204
|
+
# Time the execution of the given block and store the results in the
|
205
|
+
# named _event_ sampler. The sampler will be created if it does not
|
206
|
+
# exist.
|
207
|
+
#
|
208
|
+
def time( event )
|
209
|
+
sync {stats[event].mark}
|
210
|
+
yield
|
211
|
+
ensure
|
212
|
+
sync {stats[event].tick}
|
213
|
+
end
|
214
|
+
|
215
|
+
# Reset all the samplers managed by this tracker.
|
216
|
+
#
|
217
|
+
def reset
|
218
|
+
sync {stats.each_value {|sampler| sampler.reset}}
|
219
|
+
self
|
220
|
+
end
|
221
|
+
|
222
|
+
# Periodically execute the given _block_ at the given _period_. The
|
223
|
+
# tracker will be locked while the block is executing.
|
224
|
+
#
|
225
|
+
# This method is useful for logging statistics at given interval.
|
226
|
+
#
|
227
|
+
# Example
|
228
|
+
#
|
229
|
+
# periodically_run( 300 ) {
|
230
|
+
# logger = Logging::Logger['stats']
|
231
|
+
# tracker.each {|sampler| logger << sampler.to_s}
|
232
|
+
# tracker.reset
|
233
|
+
# }
|
234
|
+
#
|
235
|
+
def periodically_run( period, &block )
|
236
|
+
raise ArgumentError, 'a runner already exists' unless @runner.nil?
|
237
|
+
|
238
|
+
@runner = Thread.new do
|
239
|
+
start = stop = Time.now.to_f
|
240
|
+
loop do
|
241
|
+
seconds = period - (stop-start)
|
242
|
+
seconds = period if seconds <= 0
|
243
|
+
sleep seconds
|
244
|
+
|
245
|
+
start = Time.now.to_f
|
246
|
+
break if Thread.current[:stop] == true
|
247
|
+
if @mutex then @mutex.synchronize(&block)
|
248
|
+
else block.call end
|
249
|
+
stop = Time.now.to_f
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Stop the current periodic runner if present.
|
255
|
+
#
|
256
|
+
def stop
|
257
|
+
return if @runner.nil?
|
258
|
+
@runner[:stop] = true
|
259
|
+
@runner.wakeup if @runner.status
|
260
|
+
@runner = nil
|
261
|
+
end
|
262
|
+
|
263
|
+
# call-seq:
|
264
|
+
# sync { block }
|
265
|
+
#
|
266
|
+
# Obtains an exclusive lock, runs the block, and releases the lock when
|
267
|
+
# the block completes. This method is re-entrant so that a single thread
|
268
|
+
# can call +sync+ multiple times without hanging the thread.
|
269
|
+
#
|
270
|
+
def sync
|
271
|
+
return yield if @mutex.nil?
|
272
|
+
@mutex.synchronize {yield}
|
273
|
+
end
|
274
|
+
end # class Tracker
|
275
|
+
|
276
|
+
end # module Logging::Stats
|
277
|
+
|
278
|
+
# EOF
|
data/lib/logging/utils.rb
CHANGED
@@ -102,4 +102,29 @@ class Module
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
class ReentrantMutex < Mutex
|
106
|
+
|
107
|
+
def initialize
|
108
|
+
super
|
109
|
+
@locker = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
alias :original_synchronize :synchronize
|
113
|
+
|
114
|
+
def synchronize
|
115
|
+
if @locker == Thread.current
|
116
|
+
yield
|
117
|
+
else
|
118
|
+
original_synchronize {
|
119
|
+
begin
|
120
|
+
@locker = Thread.current
|
121
|
+
yield
|
122
|
+
ensure
|
123
|
+
@locker = nil
|
124
|
+
end
|
125
|
+
}
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end # class ReentrantMutex
|
129
|
+
|
105
130
|
# EOF
|
data/test/test_stats.rb
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), %w[setup])
|
3
|
+
|
4
|
+
module TestLogging
|
5
|
+
module TestStats
|
6
|
+
|
7
|
+
class TestSampler < Test::Unit::TestCase
|
8
|
+
include LoggingTestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
@sampler = ::Logging::Stats::Sampler.new('test')
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_reset
|
16
|
+
(1..10).each {|n| @sampler.sample n}
|
17
|
+
|
18
|
+
assert_equal 55, @sampler.sum
|
19
|
+
assert_equal 385, @sampler.sumsq
|
20
|
+
assert_equal 10, @sampler.num
|
21
|
+
assert_equal 1, @sampler.min
|
22
|
+
assert_equal 10, @sampler.max
|
23
|
+
assert_equal 10, @sampler.last
|
24
|
+
|
25
|
+
@sampler.reset
|
26
|
+
|
27
|
+
assert_equal 0, @sampler.sum
|
28
|
+
assert_equal 0, @sampler.sumsq
|
29
|
+
assert_equal 0, @sampler.num
|
30
|
+
assert_equal 0, @sampler.min
|
31
|
+
assert_equal 0, @sampler.max
|
32
|
+
assert_nil @sampler.last
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_coalesce
|
36
|
+
other = ::Logging::Stats::Sampler.new('other')
|
37
|
+
(1..5).each {|n| other.sample n}
|
38
|
+
(6..10).each {|n| @sampler.sample n}
|
39
|
+
|
40
|
+
assert_equal 5, @sampler.num
|
41
|
+
|
42
|
+
@sampler.coalesce other
|
43
|
+
|
44
|
+
assert_equal 55, @sampler.sum
|
45
|
+
assert_equal 385, @sampler.sumsq
|
46
|
+
assert_equal 10, @sampler.num
|
47
|
+
assert_equal 1, @sampler.min
|
48
|
+
assert_equal 10, @sampler.max
|
49
|
+
assert_equal 5, @sampler.last
|
50
|
+
|
51
|
+
@sampler.coalesce ::Logging::Stats::Sampler.new('tmp')
|
52
|
+
|
53
|
+
assert_equal 55, @sampler.sum
|
54
|
+
assert_equal 385, @sampler.sumsq
|
55
|
+
assert_equal 10, @sampler.num
|
56
|
+
assert_equal 1, @sampler.min
|
57
|
+
assert_equal 10, @sampler.max
|
58
|
+
assert_equal 5, @sampler.last
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_sample
|
62
|
+
@sampler.sample 1
|
63
|
+
|
64
|
+
assert_equal 1, @sampler.sum
|
65
|
+
assert_equal 1, @sampler.sumsq
|
66
|
+
assert_equal 1, @sampler.num
|
67
|
+
assert_equal 1, @sampler.min
|
68
|
+
assert_equal 1, @sampler.max
|
69
|
+
assert_equal 1, @sampler.last
|
70
|
+
|
71
|
+
@sampler.sample 2
|
72
|
+
|
73
|
+
assert_equal 3, @sampler.sum
|
74
|
+
assert_equal 5, @sampler.sumsq
|
75
|
+
assert_equal 2, @sampler.num
|
76
|
+
assert_equal 1, @sampler.min
|
77
|
+
assert_equal 2, @sampler.max
|
78
|
+
assert_equal 2, @sampler.last
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_to_s
|
82
|
+
(1..10).each {|n| @sampler.sample n}
|
83
|
+
assert_equal(
|
84
|
+
"[test]: SUM=55.000000, SUMSQ=385.000000, NUM=10, MEAN=5.500000, SD=3.027650, MIN=1.000000, MAX=10.000000",
|
85
|
+
@sampler.to_s
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_to_a
|
90
|
+
(1..10).each {|n| @sampler.sample n}
|
91
|
+
assert_equal(
|
92
|
+
['test', 55, 385, 10, 5.5, @sampler.sd, 1, 10],
|
93
|
+
@sampler.to_a
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_to_hash
|
98
|
+
(1..10).each {|n| @sampler.sample n}
|
99
|
+
assert_equal(
|
100
|
+
{:name => 'test', :sum => 55, :sumsq => 385, :num => 10, :mean => 5.5, :sd => @sampler.sd, :min => 1, :max => 10},
|
101
|
+
@sampler.to_hash
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_mean
|
106
|
+
assert_equal 0, @sampler.mean
|
107
|
+
|
108
|
+
@sampler.sample 10
|
109
|
+
assert_equal 10, @sampler.mean
|
110
|
+
|
111
|
+
@sampler.sample 20
|
112
|
+
assert_equal 15, @sampler.mean
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_sd
|
116
|
+
assert_equal 0, @sampler.sd
|
117
|
+
|
118
|
+
@sampler.sample 1
|
119
|
+
assert_equal 0, @sampler.sd
|
120
|
+
|
121
|
+
@sampler.sample 2
|
122
|
+
assert_in_delta 0.707106781186548, @sampler.sd, 1e-10
|
123
|
+
|
124
|
+
@sampler.sample 3
|
125
|
+
assert_in_delta 1.0, @sampler.sd, 1e-10
|
126
|
+
|
127
|
+
@sampler.sample 4
|
128
|
+
assert_in_delta 1.29099444873581, @sampler.sd, 1e-10
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_mark_and_tick
|
132
|
+
10.times do
|
133
|
+
@sampler.mark
|
134
|
+
sleep 0.01
|
135
|
+
@sampler.tick
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_equal 10, @sampler.num
|
139
|
+
assert_in_delta 0.01, @sampler.mean, 1e-3
|
140
|
+
end
|
141
|
+
end # class TestSampler
|
142
|
+
|
143
|
+
class TestTracker < Test::Unit::TestCase
|
144
|
+
include LoggingTestCase
|
145
|
+
|
146
|
+
def setup
|
147
|
+
super
|
148
|
+
@tracker = ::Logging::Stats::Tracker.new
|
149
|
+
@stats = @tracker.stats
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_coalesce
|
153
|
+
1.times {|n| @tracker.sample('foo', n)}
|
154
|
+
2.times {|n| @tracker.sample('bar', n)}
|
155
|
+
3.times {|n| @tracker.sample('baz', n)}
|
156
|
+
|
157
|
+
assert_equal %w[bar baz foo], @stats.keys.sort
|
158
|
+
assert_equal 1, @stats['foo'].num
|
159
|
+
assert_equal 2, @stats['bar'].num
|
160
|
+
assert_equal 3, @stats['baz'].num
|
161
|
+
|
162
|
+
# when other is empty, nothing should change in our tracker
|
163
|
+
other = ::Logging::Stats::Tracker.new
|
164
|
+
@tracker.coalesce other
|
165
|
+
|
166
|
+
assert_equal %w[bar baz foo], @stats.keys.sort
|
167
|
+
assert_equal 1, @stats['foo'].num
|
168
|
+
assert_equal 2, @stats['bar'].num
|
169
|
+
assert_equal 3, @stats['baz'].num
|
170
|
+
|
171
|
+
# now add some samples to other
|
172
|
+
4.times {|n| other.sample('buz', n)}
|
173
|
+
5.times {|n| other.sample('bar', n)}
|
174
|
+
@tracker.coalesce other
|
175
|
+
|
176
|
+
assert_equal %w[bar baz buz foo], @stats.keys.sort
|
177
|
+
assert_equal 1, @stats['foo'].num
|
178
|
+
assert_equal 7, @stats['bar'].num
|
179
|
+
assert_equal 3, @stats['baz'].num
|
180
|
+
assert_equal 4, @stats['buz'].num
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_mark
|
184
|
+
assert @stats.empty?
|
185
|
+
@tracker.mark 'foo'
|
186
|
+
assert !@stats.empty?
|
187
|
+
|
188
|
+
sampler = @stats['foo']
|
189
|
+
assert_equal 0, sampler.num
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_tick
|
193
|
+
assert @stats.empty?
|
194
|
+
@tracker.tick 'foo'
|
195
|
+
assert !@stats.empty?
|
196
|
+
|
197
|
+
sampler = @stats['foo']
|
198
|
+
assert_equal 1, sampler.num
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_sample
|
202
|
+
assert @stats.empty?
|
203
|
+
@tracker.sample 'foo', 1
|
204
|
+
assert !@stats.empty?
|
205
|
+
|
206
|
+
sampler = @stats['foo']
|
207
|
+
assert_equal 1, sampler.num
|
208
|
+
assert_equal 1, sampler.last
|
209
|
+
|
210
|
+
@tracker.sample 'foo', 2
|
211
|
+
assert_equal 2, sampler.num
|
212
|
+
assert_equal 2, sampler.last
|
213
|
+
assert_equal 3, sampler.sum
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_time
|
217
|
+
assert @stats.empty?
|
218
|
+
@tracker.time('foo') {sleep 0.05}
|
219
|
+
assert !@stats.empty?
|
220
|
+
|
221
|
+
sampler = @stats['foo']
|
222
|
+
assert_equal 1, sampler.num
|
223
|
+
assert_in_delta 0.05, sampler.sum, 1e-3
|
224
|
+
|
225
|
+
@tracker.time('foo') {sleep 0.05}
|
226
|
+
assert_equal 2, sampler.num
|
227
|
+
assert_in_delta 0.10, sampler.sum, 1e-2
|
228
|
+
|
229
|
+
assert_raise(RuntimeError) do
|
230
|
+
@tracker.time('foo') {raise 'Uh Oh!'}
|
231
|
+
end
|
232
|
+
assert_equal 3, sampler.num
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_reset
|
236
|
+
1.times {|n| @tracker.sample('foo', n)}
|
237
|
+
2.times {|n| @tracker.sample('bar', n)}
|
238
|
+
3.times {|n| @tracker.sample('baz', n)}
|
239
|
+
|
240
|
+
assert_equal 1, @stats['foo'].num
|
241
|
+
assert_equal 2, @stats['bar'].num
|
242
|
+
assert_equal 3, @stats['baz'].num
|
243
|
+
|
244
|
+
@tracker.reset
|
245
|
+
|
246
|
+
assert_equal 0, @stats['foo'].num
|
247
|
+
assert_equal 0, @stats['bar'].num
|
248
|
+
assert_equal 0, @stats['baz'].num
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_reentrant_synchronization
|
252
|
+
assert_nothing_raised do
|
253
|
+
@tracker.sync {
|
254
|
+
@tracker.sample('foo', Math::PI)
|
255
|
+
@tracker.reset
|
256
|
+
}
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_periodically_run
|
261
|
+
@tracker.periodically_run(0.1) {
|
262
|
+
@tracker.tick 'foo'
|
263
|
+
}
|
264
|
+
sleep 0.5
|
265
|
+
@tracker.stop
|
266
|
+
|
267
|
+
assert(@stats['foo'].num > 1)
|
268
|
+
end
|
269
|
+
end # class TestTracker
|
270
|
+
|
271
|
+
end # module TestStats
|
272
|
+
end # module TestLogging
|
273
|
+
|
274
|
+
# EOF
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Pease
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-09-
|
12
|
+
date: 2008-09-13 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/logging/logger.rb
|
70
70
|
- lib/logging/repository.rb
|
71
71
|
- lib/logging/root_logger.rb
|
72
|
+
- lib/logging/stats.rb
|
72
73
|
- lib/logging/utils.rb
|
73
74
|
- tasks/ann.rake
|
74
75
|
- tasks/bones.rake
|
@@ -101,6 +102,7 @@ files:
|
|
101
102
|
- test/test_logging.rb
|
102
103
|
- test/test_repository.rb
|
103
104
|
- test/test_root_logger.rb
|
105
|
+
- test/test_stats.rb
|
104
106
|
- test/test_utils.rb
|
105
107
|
has_rdoc: true
|
106
108
|
homepage: http://logging.rubyforge.org/
|
@@ -148,4 +150,5 @@ test_files:
|
|
148
150
|
- test/test_logging.rb
|
149
151
|
- test/test_repository.rb
|
150
152
|
- test/test_root_logger.rb
|
153
|
+
- test/test_stats.rb
|
151
154
|
- test/test_utils.rb
|