sawmill 0.0.1

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 (38) hide show
  1. data/History.rdoc +3 -0
  2. data/README.rdoc +81 -0
  3. data/Rakefile +147 -0
  4. data/lib/sawmill/entry.rb +307 -0
  5. data/lib/sawmill/entry_classifier.rb +75 -0
  6. data/lib/sawmill/entry_processor/build_records.rb +157 -0
  7. data/lib/sawmill/entry_processor/conditionals.rb +444 -0
  8. data/lib/sawmill/entry_processor/filter_basic_fields.rb +145 -0
  9. data/lib/sawmill/entry_processor/format.rb +228 -0
  10. data/lib/sawmill/entry_processor/simple_queue.rb +116 -0
  11. data/lib/sawmill/entry_processor.rb +158 -0
  12. data/lib/sawmill/errors.rb +66 -0
  13. data/lib/sawmill/level.rb +264 -0
  14. data/lib/sawmill/log_record_middleware.rb +93 -0
  15. data/lib/sawmill/logger.rb +373 -0
  16. data/lib/sawmill/parser.rb +181 -0
  17. data/lib/sawmill/record.rb +255 -0
  18. data/lib/sawmill/record_processor/conditionals.rb +330 -0
  19. data/lib/sawmill/record_processor/decompose.rb +75 -0
  20. data/lib/sawmill/record_processor/filter_by_attributes.rb +87 -0
  21. data/lib/sawmill/record_processor/filter_by_record_id.rb +77 -0
  22. data/lib/sawmill/record_processor/format.rb +88 -0
  23. data/lib/sawmill/record_processor/simple_queue.rb +117 -0
  24. data/lib/sawmill/record_processor.rb +137 -0
  25. data/lib/sawmill/rotater/base.rb +90 -0
  26. data/lib/sawmill/rotater/date_based_log_file.rb +145 -0
  27. data/lib/sawmill/rotater/shifting_log_file.rb +166 -0
  28. data/lib/sawmill/rotater.rb +236 -0
  29. data/lib/sawmill/util/queue.rb +138 -0
  30. data/lib/sawmill/version.rb +47 -0
  31. data/lib/sawmill.rb +78 -0
  32. data/tests/tc_entry_processors.rb +138 -0
  33. data/tests/tc_formatter_parser.rb +144 -0
  34. data/tests/tc_levels.rb +118 -0
  35. data/tests/tc_logger.rb +315 -0
  36. data/tests/tc_record_processors.rb +117 -0
  37. data/tests/tc_records.rb +206 -0
  38. metadata +116 -0
@@ -0,0 +1,157 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill entry processor that builds log records
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2009 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module Sawmill
38
+
39
+ module EntryProcessor
40
+
41
+
42
+ # An entry processor that builds log records from a stream of entries,
43
+ # and passes those log records to the given record processor.
44
+
45
+ class BuildRecords < Base
46
+
47
+
48
+ # Create record builder emitting to the given record processor.
49
+ #
50
+ # Recognized options include:
51
+ #
52
+ # <tt>:emit_incomplete_records_on_close</tt>::
53
+ # When the processor is closed, any records that are still not
54
+ # complete will be emitted to the record processor anyway, even
55
+ # in their incomplete state.
56
+
57
+ def initialize(processor_, opts_={})
58
+ @processor = processor_
59
+ @records = {}
60
+ @emit_incomplete_records_on_close = opts_[:emit_incomplete_records_on_close]
61
+ end
62
+
63
+
64
+ # Emit all currently incomplete records immediately in their
65
+ # incomplete state. This clears those incomplete records, so note that
66
+ # if they do get completed later, they will not be re-emitted.
67
+
68
+ def emit_incomplete_records
69
+ if @records
70
+ @records.values.each do |record_|
71
+ @processor.record(record_)
72
+ end
73
+ @records.clear
74
+ end
75
+ end
76
+
77
+
78
+ def begin_record(entry_)
79
+ return unless @records
80
+ record_id_ = entry_.record_id
81
+ if @records.include?(record_id_)
82
+ @processor.extra_entry(entry_)
83
+ false
84
+ else
85
+ @records[record_id_] = Record.new([entry_])
86
+ true
87
+ end
88
+ end
89
+
90
+
91
+ def end_record(entry_)
92
+ return unless @records
93
+ record_ = @records.delete(entry_.record_id)
94
+ if record_
95
+ record_.add_entry(entry_)
96
+ @processor.record(record_)
97
+ true
98
+ else
99
+ @processor.extra_entry(entry_)
100
+ false
101
+ end
102
+ end
103
+
104
+
105
+ def message(entry_)
106
+ return unless @records
107
+ record_ = @records[entry_.record_id]
108
+ if record_
109
+ record_.add_entry(entry_)
110
+ true
111
+ else
112
+ @processor.extra_entry(entry_)
113
+ false
114
+ end
115
+ end
116
+
117
+
118
+ def attribute(entry_)
119
+ return unless @records
120
+ record_ = @records[entry_.record_id]
121
+ if record_
122
+ record_.add_entry(entry_)
123
+ true
124
+ else
125
+ @processor.extra_entry(entry_)
126
+ false
127
+ end
128
+ end
129
+
130
+
131
+ def unknown_data(entry_)
132
+ return unless @records
133
+ @processor.extra_entry(entry_)
134
+ false
135
+ end
136
+
137
+
138
+ def close
139
+ if @records && @emit_incomplete_records_on_close
140
+ emit_incomplete_records
141
+ end
142
+ @records = nil
143
+ end
144
+
145
+
146
+ end
147
+
148
+
149
+ end
150
+
151
+
152
+ # Sawmill::RecordBuilder is an alternate name for
153
+ # Sawmill::EntryProcessor::BuildRecords
154
+ RecordBuilder = EntryProcessor::BuildRecords
155
+
156
+
157
+ end
@@ -0,0 +1,444 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill basic entry processors that implement conditionals
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2009 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module Sawmill
38
+
39
+ module EntryProcessor
40
+
41
+
42
+ # An "if" conditional.
43
+ #
44
+ # Takes a boolean condition processor and executes a processor on true
45
+ # (and optionally another one on false).
46
+ #
47
+ # For example, this builds a processor that sends formatted log entries
48
+ # to STDOUT only if their level is at least INFO:
49
+ #
50
+ # processor = Sawmill::EntryProcessor.build do
51
+ # If(FilterBasicFields(:level => :INFO), Format(STDOUT))
52
+ # end
53
+
54
+ class If < Base
55
+
56
+
57
+ # Create an "if" conditional.
58
+ #
59
+ # The first parameter must be a processor whose methods return a
60
+ # boolean value indicating whether the entry should be accepted.
61
+ # The second parameter is a processor to run on accepted entries.
62
+ # The optional third parameter is an "else" processor to run on
63
+ # rejected entries.
64
+
65
+ def initialize(condition_, on_true_, on_false_=nil)
66
+ @condition = condition_
67
+ @on_true = on_true_
68
+ @on_false = on_false_
69
+ end
70
+
71
+ def begin_record(entry_)
72
+ if @condition.begin_record(entry_)
73
+ @on_true.begin_record(entry_)
74
+ elsif @on_false
75
+ @on_false.begin_record(entry_)
76
+ end
77
+ end
78
+
79
+ def end_record(entry_)
80
+ if @condition.end_record(entry_)
81
+ @on_true.end_record(entry_)
82
+ elsif @on_false
83
+ @on_false.end_record(entry_)
84
+ end
85
+ end
86
+
87
+ def message(entry_)
88
+ if @condition.message(entry_)
89
+ @on_true.message(entry_)
90
+ elsif @on_false
91
+ @on_false.message(entry_)
92
+ end
93
+ end
94
+
95
+ def attribute(entry_)
96
+ if @condition.attribute(entry_)
97
+ @on_true.attribute(entry_)
98
+ elsif @on_false
99
+ @on_false.attribute(entry_)
100
+ end
101
+ end
102
+
103
+ def unknown_data(entry_)
104
+ if @condition.unknown_data(entry_)
105
+ @on_true.unknown_data(entry_)
106
+ elsif @on_false
107
+ @on_false.unknown_data(entry_)
108
+ end
109
+ end
110
+
111
+ def close
112
+ @on_true.close
113
+ @on_false.close if @on_false
114
+ end
115
+
116
+
117
+ end
118
+
119
+
120
+ # A boolean processor that returns the boolean negation of a given
121
+ # processor.
122
+ #
123
+ # For example, this builds a processor that sends formatted log entries
124
+ # to STDOUT only if their level is NOT at least INFO:
125
+ #
126
+ # processor = Sawmill::EntryProcessor.build do
127
+ # If(Not(FilterBasicFields(:level => :INFO)), Format(STDOUT))
128
+ # end
129
+
130
+ class Not < Base
131
+
132
+
133
+ # Create a "not" boolean.
134
+ # The parameter is a boolean processor to run. This processor returns
135
+ # the boolean negation of its output.
136
+
137
+ def initialize(child_)
138
+ @child = _interpret_processor(child_)
139
+ end
140
+
141
+ def begin_record(entry_)
142
+ !@child.begin_record(entry_)
143
+ end
144
+
145
+ def end_record(entry_)
146
+ !@child.end_record(entry_)
147
+ end
148
+
149
+ def message(entry_)
150
+ !@child.message(entry_)
151
+ end
152
+
153
+ def attribute(entry_)
154
+ !@child.attribute(entry_)
155
+ end
156
+
157
+ def unknown_data(entry_)
158
+ !@child.unknown_data(entry_)
159
+ end
160
+
161
+ def close
162
+ @child.close
163
+ end
164
+
165
+
166
+ end
167
+
168
+
169
+ # A boolean processor that returns true if and only if all its child
170
+ # processors return true. This version short-circuits the processing,
171
+ # so once one child returns false, subsequent children are not called
172
+ # at all.
173
+ #
174
+ # For example, this builds a processor that sends formatted log entries
175
+ # to STDOUT only if their level is at least INFO AND the progname is
176
+ # is "rails":
177
+ #
178
+ # processor = Sawmill::EntryProcessor.build do
179
+ # If(And(FilterBasicFields(:level => :INFO),
180
+ # FilterBasicFields(:progname => 'rails')),
181
+ # Format(STDOUT))
182
+ # end
183
+
184
+ class And < Base
185
+
186
+
187
+ # Create an "and" boolean.
188
+ # The parameters are child processors whose return values should be
189
+ # combined with an AND operation.
190
+
191
+ def initialize(*children_)
192
+ @children = _interpret_processor_array(children_)
193
+ end
194
+
195
+ def begin_record(entry_)
196
+ @children.each do |child_|
197
+ return false unless child_.begin_record(entry_)
198
+ end
199
+ true
200
+ end
201
+
202
+ def end_record(entry_)
203
+ @children.each do |child_|
204
+ return false unless child_.end_record(entry_)
205
+ end
206
+ true
207
+ end
208
+
209
+ def message(entry_)
210
+ @children.each do |child_|
211
+ return false unless child_.message(entry_)
212
+ end
213
+ true
214
+ end
215
+
216
+ def attribute(entry_)
217
+ @children.each do |child_|
218
+ return false unless child_.attribute(entry_)
219
+ end
220
+ true
221
+ end
222
+
223
+ def unknown_data(entry_)
224
+ @children.each do |child_|
225
+ return false unless child_.unknown_data(entry_)
226
+ end
227
+ true
228
+ end
229
+
230
+ def close
231
+ @children.each{ |forward_| forward_.close }
232
+ end
233
+
234
+
235
+ end
236
+
237
+
238
+ # A boolean processor that returns true if and only if any of its child
239
+ # processors returns true. This version short-circuits the processing,
240
+ # so once one child returns true, subsequent children are not called
241
+ # at all.
242
+ #
243
+ # For example, this builds a processor that sends formatted log entries
244
+ # to STDOUT only if their level is at least INFO OR the progname is
245
+ # "rails":
246
+ #
247
+ # processor = Sawmill::EntryProcessor.build do
248
+ # If(Or(FilterBasicFields(:level => :INFO),
249
+ # FilterBasicFields(:progname => 'rails')),
250
+ # Format(STDOUT))
251
+ # end
252
+
253
+ class Or < Base
254
+
255
+
256
+ # Create an "or" boolean.
257
+ # The parameters are child processors whose return values should be
258
+ # combined with an OR operation.
259
+
260
+ def initialize(*children_)
261
+ @children = _interpret_processor_array(children_)
262
+ end
263
+
264
+ def begin_record(entry_)
265
+ @children.each do |child_|
266
+ return true if child_.begin_record(entry_)
267
+ end
268
+ false
269
+ end
270
+
271
+ def end_record(entry_)
272
+ @children.each do |child_|
273
+ return true if child_.end_record(entry_)
274
+ end
275
+ false
276
+ end
277
+
278
+ def message(entry_)
279
+ @children.each do |child_|
280
+ return true if child_.message(entry_)
281
+ end
282
+ false
283
+ end
284
+
285
+ def attribute(entry_)
286
+ @children.each do |child_|
287
+ return true if child_.attribute(entry_)
288
+ end
289
+ false
290
+ end
291
+
292
+ def unknown_data(entry_)
293
+ @children.each do |child_|
294
+ return true if child_.unknown_data(entry_)
295
+ end
296
+ false
297
+ end
298
+
299
+ def close
300
+ @children.each{ |forward_| forward_.close }
301
+ end
302
+
303
+
304
+ end
305
+
306
+
307
+ # A boolean processor that returns true if and only if all its child
308
+ # processors return true. This version does not short-circuit the
309
+ # processing, so all children are always called even if an early one
310
+ # returns false. Thus, this processor is also a good one to use as a
311
+ # multiplexor to simply run a bunch of processors.
312
+ #
313
+ # For example, this builds a processor that sends formatted log entries
314
+ # to STDOUT only if their level is at least INFO AND the progname is
315
+ # "rails":
316
+ #
317
+ # processor = Sawmill::EntryProcessor.build do
318
+ # If(All(FilterBasicFields(:level => :INFO),
319
+ # FilterBasicFields(:progname => 'rails')),
320
+ # Format(STDOUT))
321
+ # end
322
+ #
323
+ # This processor just formats both to STDOUT and STDERR:
324
+ #
325
+ # processor = Sawmill::EntryProcessor.build do
326
+ # All(Format(STDOUT), Format(STDERR))
327
+ # end
328
+
329
+ class All < Base
330
+
331
+
332
+ # Create an "all" boolean.
333
+ # The parameters are child processors whose return values should be
334
+ # combined with an AND operation.
335
+
336
+ def initialize(*children_)
337
+ @children = _interpret_processor_array(children_)
338
+ end
339
+
340
+ def begin_record(entry_)
341
+ @children.inject(true) do |result_, child_|
342
+ child_.begin_record(entry_) && result_
343
+ end
344
+ end
345
+
346
+ def end_record(entry_)
347
+ @children.inject(true) do |result_, child_|
348
+ child_.end_record(entry_) && result_
349
+ end
350
+ end
351
+
352
+ def message(entry_)
353
+ @children.inject(true) do |result_, child_|
354
+ child_.message(entry_) && result_
355
+ end
356
+ end
357
+
358
+ def attribute(entry_)
359
+ @children.inject(true) do |result_, child_|
360
+ child_.attribute(entry_) && result_
361
+ end
362
+ end
363
+
364
+ def unknown_data(entry_)
365
+ @children.inject(true) do |result_, child_|
366
+ child_.unknown_data(entry_) && result_
367
+ end
368
+ end
369
+
370
+ def close
371
+ @children.each{ |forward_| forward_.close }
372
+ end
373
+
374
+
375
+ end
376
+
377
+
378
+ # A boolean processor that returns true if and only if any of its child
379
+ # processors returns true. This version does not short-circuit the
380
+ # processing, so all children are always called even if an early one
381
+ # returns true.
382
+ #
383
+ # For example, this builds a processor that sends formatted log entries
384
+ # to STDOUT only if their level is at least INFO OR the progname is
385
+ # "rails":
386
+ #
387
+ # processor = Sawmill::EntryProcessor.build do
388
+ # If(Any(FilterBasicFields(:level => :INFO),
389
+ # FilterBasicFields(:progname => 'rails')),
390
+ # Format(STDOUT))
391
+ # end
392
+
393
+ class Any < Base
394
+
395
+
396
+ # Create an "any" boolean.
397
+ # The parameters are child processors whose return values should be
398
+ # combined with an OR operation.
399
+
400
+ def initialize(*children_)
401
+ @children = _interpret_processor_array(children_)
402
+ end
403
+
404
+ def begin_record(entry_)
405
+ @children.inject(false) do |result_, child_|
406
+ child_.begin_record(entry_) || result_
407
+ end
408
+ end
409
+
410
+ def end_record(entry_)
411
+ @children.inject(false) do |result_, child_|
412
+ child_.end_record(entry_) || result_
413
+ end
414
+ end
415
+
416
+ def message(entry_)
417
+ @children.inject(false) do |result_, child_|
418
+ child_.message(entry_) || result_
419
+ end
420
+ end
421
+
422
+ def attribute(entry_)
423
+ @children.inject(false) do |result_, child_|
424
+ child_.attribute(entry_) || result_
425
+ end
426
+ end
427
+
428
+ def unknown_data(entry_)
429
+ @children.inject(false) do |result_, child_|
430
+ child_.unknown_data(entry_) || result_
431
+ end
432
+ end
433
+
434
+ def close
435
+ @children.each{ |forward_| forward_.close }
436
+ end
437
+
438
+
439
+ end
440
+
441
+
442
+ end
443
+
444
+ end