logging 1.8.2 → 2.0.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/History.txt +20 -0
  4. data/README.md +159 -0
  5. data/Rakefile +9 -5
  6. data/examples/appenders.rb +0 -4
  7. data/examples/layouts.rb +1 -8
  8. data/examples/names.rb +4 -4
  9. data/lib/logging.rb +24 -76
  10. data/lib/logging/appender.rb +71 -16
  11. data/lib/logging/appenders.rb +0 -2
  12. data/lib/logging/appenders/buffering.rb +32 -16
  13. data/lib/logging/appenders/file.rb +2 -2
  14. data/lib/logging/appenders/io.rb +1 -1
  15. data/lib/logging/appenders/rolling_file.rb +228 -165
  16. data/lib/logging/appenders/string_io.rb +1 -1
  17. data/lib/logging/appenders/syslog.rb +4 -4
  18. data/lib/logging/color_scheme.rb +20 -3
  19. data/lib/logging/diagnostic_context.rb +142 -17
  20. data/lib/logging/filter.rb +18 -0
  21. data/lib/logging/filters.rb +4 -0
  22. data/lib/logging/filters/level.rb +29 -0
  23. data/lib/logging/layout.rb +2 -2
  24. data/lib/logging/layouts/parseable.rb +5 -2
  25. data/lib/logging/layouts/pattern.rb +309 -168
  26. data/lib/logging/log_event.rb +5 -5
  27. data/lib/logging/logger.rb +55 -68
  28. data/lib/logging/repository.rb +24 -39
  29. data/lib/logging/root_logger.rb +1 -1
  30. data/lib/logging/utils.rb +4 -65
  31. data/lib/logging/version.rb +8 -0
  32. data/lib/rspec/logging_helper.rb +3 -3
  33. data/logging.gemspec +46 -0
  34. data/test/appenders/test_buffered_io.rb +29 -0
  35. data/test/appenders/test_file.rb +2 -2
  36. data/test/appenders/test_rolling_file.rb +62 -1
  37. data/test/layouts/test_color_pattern.rb +1 -1
  38. data/test/layouts/test_json.rb +3 -0
  39. data/test/layouts/test_pattern.rb +6 -2
  40. data/test/layouts/test_yaml.rb +4 -1
  41. data/test/test_appender.rb +56 -0
  42. data/test/test_filter.rb +33 -0
  43. data/test/test_layout.rb +4 -8
  44. data/test/test_log_event.rb +3 -3
  45. data/test/test_logger.rb +81 -57
  46. data/test/test_logging.rb +0 -59
  47. data/test/test_mapped_diagnostic_context.rb +49 -1
  48. data/test/test_nested_diagnostic_context.rb +16 -1
  49. data/test/test_repository.rb +24 -32
  50. data/test/test_utils.rb +14 -50
  51. metadata +35 -53
  52. data/README.rdoc +0 -143
  53. data/data/bad_logging_1.rb +0 -13
  54. data/data/bad_logging_2.rb +0 -21
  55. data/data/logging.rb +0 -42
  56. data/data/logging.yaml +0 -63
  57. data/data/simple_logging.rb +0 -13
  58. data/examples/consolidation.rb +0 -83
  59. data/lib/logging/appenders/email.rb +0 -178
  60. data/lib/logging/appenders/growl.rb +0 -200
  61. data/lib/logging/config/configurator.rb +0 -187
  62. data/lib/logging/config/yaml_configurator.rb +0 -190
  63. data/lib/logging/stats.rb +0 -277
  64. data/test/appenders/test_email.rb +0 -170
  65. data/test/appenders/test_growl.rb +0 -138
  66. data/test/config/test_configurator.rb +0 -69
  67. data/test/config/test_yaml_configurator.rb +0 -39
  68. data/test/test_consolidate.rb +0 -45
  69. data/test/test_stats.rb +0 -273
  70. data/version.txt +0 -1
@@ -1,277 +0,0 @@
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 multiple 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 passed 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 multiple 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
-
@@ -1,170 +0,0 @@
1
-
2
- require File.expand_path('../setup', File.dirname(__FILE__))
3
- require 'flexmock'
4
-
5
- module TestLogging
6
- module TestAppenders
7
-
8
- class TestEmail < Test::Unit::TestCase
9
- include FlexMock::TestCase
10
- include LoggingTestCase
11
-
12
- def setup
13
- super
14
-
15
- flexmock(Net::SMTP).new_instances do |m|
16
- m.should_receive(:start).at_least.once.with(
17
- 'test.logging', 'test', 'test', :plain, Proc).and_yield(m)
18
- m.should_receive(:sendmail).at_least.once.with(String, 'me', ['you'])
19
- end
20
-
21
- @appender = Logging.appenders.email('email',
22
- 'from' => 'me', 'to' => 'you',
23
- :buffer_size => '3', :immediate_at => 'error, fatal',
24
- :domain => 'test.logging', :user_name => 'test', :password => 'test'
25
- )
26
- @levels = Logging::LEVELS
27
- end
28
-
29
- def test_initialize
30
- assert_raise(ArgumentError, 'Must specify from address') {
31
- Logging.appenders.email('email')
32
- }
33
- assert_raise(ArgumentError, 'Must specify to address') {
34
- Logging.appenders.email('email', :from => 'me')
35
- }
36
- assert_nothing_raised {
37
- Logging.appenders.email('email', :from => 'me', :to => 'you')
38
- }
39
-
40
- appender = Logging.appenders.email('email',
41
- 'from' => 'me', 'to' => 'you'
42
- )
43
-
44
- assert_equal(100, appender.auto_flushing)
45
- assert_equal([], appender.instance_variable_get(:@immediate))
46
- assert_equal('localhost', appender.address)
47
- assert_equal(25, appender.port)
48
-
49
- domain = ENV['HOSTNAME'] || 'localhost.localdomain'
50
- assert_equal(domain, appender.domain)
51
- assert_equal(nil, appender.user_name)
52
- assert_equal(:plain, appender.authentication)
53
- assert_equal("Message from #{$0}", appender.subject)
54
-
55
- appender = Logging.appenders.email('email',
56
- 'from' => 'lbrinn@gmail.com', 'to' => 'everyone',
57
- :buffsize => '1000', :immediate_at => 'error, fatal',
58
- :address => 'smtp.google.com', :port => '443',
59
- :domain => 'google.com', :user_name => 'lbrinn',
60
- :password => '1234', :authentication => 'plain', :enable_starttls_auto => true,
61
- :subject => "I'm rich and you're not"
62
- )
63
-
64
- assert_equal('lbrinn@gmail.com', appender.instance_variable_get(:@from))
65
- assert_equal(['everyone'], appender.instance_variable_get(:@to))
66
- assert_equal(1000, appender.auto_flushing)
67
- assert_equal('1234', appender.password)
68
- assert_equal([nil, nil, nil, true, true],
69
- appender.instance_variable_get(:@immediate))
70
- assert_equal('smtp.google.com', appender.address)
71
- assert_equal(443, appender.port)
72
- assert_equal('google.com', appender.domain)
73
- assert_equal('lbrinn', appender.user_name)
74
- assert_equal(:plain, appender.authentication)
75
- assert(appender.enable_starttls_auto)
76
- assert_equal("I'm rich and you're not", appender.subject)
77
-
78
- appender = Logging.appenders.email('email',
79
- 'from' => 'me', 'to' => 'you', :auto_flushing => 42
80
- )
81
- assert_equal(42, appender.auto_flushing)
82
- end
83
-
84
- def test_append
85
- # with auto_flushing enabled, mail will be sent each time a log event
86
- # occurs
87
- @appender.auto_flushing = true
88
- event = Logging::LogEvent.new('TestLogger', @levels['warn'],
89
- [1, 2, 3, 4], false)
90
- @appender.append event
91
- assert_not_equal(@levels.length, @appender.level)
92
- assert_equal(0, @appender.buffer.length)
93
-
94
- # increase the buffer size and log a few events
95
- @appender.auto_flushing = 3
96
- @appender.append event
97
- @appender.append event
98
- assert_equal(2, @appender.buffer.length)
99
-
100
- @appender.append event
101
- assert_not_equal(@levels.length, @appender.level)
102
- assert_equal(0, @appender.buffer.length)
103
-
104
- # error and fatal messages should be send immediately (no buffering)
105
- error = Logging::LogEvent.new('ErrLogger', @levels['error'],
106
- 'error message', false)
107
- fatal = Logging::LogEvent.new('FatalLogger', @levels['fatal'],
108
- 'fatal message', false)
109
-
110
- @appender.append event
111
- @appender.append fatal
112
- assert_not_equal(@levels.length, @appender.level)
113
- assert_equal(0, @appender.buffer.length)
114
-
115
- @appender.append error
116
- assert_not_equal(@levels.length, @appender.level)
117
- assert_equal(0, @appender.buffer.length)
118
-
119
- @appender.append event
120
- assert_equal(1, @appender.buffer.length)
121
- end
122
-
123
- def test_concat
124
- # with auto_flushing enabled, mail will be sent each time a log event
125
- # occurs
126
- @appender.auto_flushing = true
127
- @appender << 'test message'
128
- assert_not_equal(@levels.length, @appender.level)
129
- assert_equal(0, @appender.buffer.length)
130
-
131
- # increase the buffer size and log a few events
132
- @appender.auto_flushing = 3
133
- @appender << 'another test message'
134
- @appender << 'a second test message'
135
- assert_equal(2, @appender.buffer.length)
136
-
137
- @appender << 'a third test message'
138
- assert_not_equal(@levels.length, @appender.level)
139
- assert_equal(0, @appender.buffer.length)
140
- end
141
-
142
- def test_flush
143
- event = Logging::LogEvent.new('TestLogger', @levels['info'],
144
- [1, 2, 3, 4], false)
145
- @appender.append event
146
- @appender << 'test message'
147
- assert_equal(2, @appender.buffer.length)
148
-
149
- @appender.flush
150
- assert_not_equal(@levels.length, @appender.level)
151
- assert_equal(0, @appender.buffer.length)
152
- end
153
-
154
- def test_close
155
- event = Logging::LogEvent.new('TestLogger', @levels['info'],
156
- [1, 2, 3, 4], false)
157
- @appender.append event
158
- @appender << 'test message'
159
- assert_equal(2, @appender.buffer.length)
160
-
161
- @appender.close
162
- assert_not_equal(@levels.length, @appender.level)
163
- assert_equal(0, @appender.buffer.length)
164
- assert(@appender.closed?)
165
- end
166
-
167
- end # class TestEmail
168
- end # module TestLogging
169
- end # module TestAppenders
170
-
@@ -1,138 +0,0 @@
1
-
2
- require File.expand_path('../setup', File.dirname(__FILE__))
3
- require 'flexmock'
4
-
5
- module TestLogging
6
- module TestAppenders
7
-
8
- class TestGrowl < Test::Unit::TestCase
9
- include FlexMock::TestCase
10
- include LoggingTestCase
11
-
12
- def setup
13
- super
14
-
15
- @appender = Logging.appenders.growl('growl',
16
- :coalesce => true, :separator => "\000",
17
- :layout => Logging.layouts.pattern(:pattern => "%5l - Test\000%m")
18
- )
19
- @appender.level = :all
20
- @growl = @appender.instance_variable_get(:@growl).dup
21
- @levels = Logging::LEVELS
22
- end
23
-
24
- def test_initialize
25
- assert_equal('growlnotify -w -n "growl" -t "%s" -m "%s" -p %d &', @growl)
26
- assert_equal(true, @appender.instance_variable_get(:@coalesce))
27
- assert_equal("\000", @appender.instance_variable_get(:@title_sep))
28
- end
29
-
30
- def test_append
31
- info = Logging::LogEvent.new('TestLogger', @levels['info'],
32
- 'info message', false)
33
- warn = Logging::LogEvent.new('TestLogger', @levels['warn'],
34
- 'warning message', false)
35
-
36
- flexmock(@appender).should_receive(:system => true).once.with(
37
- @growl % ['WARN - Test', "warning message\nwarning message\nwarning message", 0])
38
-
39
- flexmock(@appender).should_receive(:system => true).once.with(
40
- @growl % ['INFO - Test', "info message\ninfo message", -1])
41
-
42
- flexmock(@appender).should_receive(:system => true).once.with(
43
- @growl % ['WARN - Test', "warning message", 0])
44
-
45
- @appender.append warn
46
- @appender.append warn
47
- @appender.append warn
48
- @appender.append info
49
- @appender.append info
50
- @appender.append warn
51
- ensure_queue_is_empty
52
- end
53
-
54
- def test_append_without_coalescing
55
- @appender.instance_variable_set(:@coalesce, false)
56
- event = Logging::LogEvent.new('TestLogger', @levels['warn'],
57
- 'warning message', false)
58
-
59
- flexmock(@appender).should_receive(:system => true).twice.with(
60
- @growl % ['WARN - Test', 'warning message', 0])
61
-
62
- @appender.append event
63
- @appender.append event
64
- end
65
-
66
- def test_concat
67
- flexmock(@appender).should_receive(:system => true).once.with(
68
- @growl % ['', "first message\nsecond message\nthird message", 0])
69
-
70
- @appender << 'first message'
71
- @appender << 'second message'
72
- @appender << 'third message'
73
- ensure_queue_is_empty
74
- end
75
-
76
- def test_concat_without_coalescing
77
- @appender.instance_variable_set(:@coalesce, false)
78
-
79
- flexmock(@appender).should_receive(:system => true).twice.with(
80
- @growl % ['', 'concat message', 0])
81
-
82
- @appender << 'concat message'
83
- @appender << 'concat message'
84
- end
85
-
86
- def test_map_eq
87
- get_map = lambda {@appender.instance_variable_get(:@map)}
88
- assert_equal([-2,-1,0,1,2], get_map.call)
89
-
90
- @appender.map = {
91
- 'fatal' => '0',
92
- :error => -2,
93
- :warn => '2',
94
- 'INFO' => 1,
95
- 'Debug' => -1
96
- }
97
- assert_equal([-1,1,2,-2,0], get_map.call)
98
-
99
- assert_raise(ArgumentError) do
100
- @appender.map = {:fatal => 'not a number', :error => 2}
101
- end
102
-
103
- assert_raise(ArgumentError) do
104
- @appender.map = {:fatal => -3, :error => 3}
105
- end
106
- end
107
-
108
- def test_disabling
109
- @appender.instance_variable_set(:@coalesce, false)
110
- event = Logging::LogEvent.new('TestLogger', @levels['warn'],
111
- 'warning message', false)
112
-
113
- flexmock(@appender).should_receive(:system => false).once.with(
114
- @growl % ['WARN - Test', 'warning message', 0])
115
-
116
- assert_equal 0, @appender.level
117
- @appender.append event
118
- assert_equal 5, @appender.level
119
- @appender.append event
120
- @appender.append event
121
- end
122
-
123
- private
124
-
125
- def ensure_queue_is_empty
126
- start = Time.now
127
-
128
- queue = @appender.instance_variable_get :@c_queue
129
- sleep 0.2 until queue.empty? or (Time.now - start > 10)
130
-
131
- thread = @appender.instance_variable_get :@c_thread
132
- sleep 0.2 until thread.status == 'sleep' or (Time.now - start > 10)
133
- end
134
-
135
- end # class TestGrowl
136
- end # module TestLogging
137
- end # module TestAppenders
138
-