sawmill 0.0.1

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