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.
- data/History.rdoc +3 -0
- data/README.rdoc +81 -0
- data/Rakefile +147 -0
- data/lib/sawmill/entry.rb +307 -0
- data/lib/sawmill/entry_classifier.rb +75 -0
- data/lib/sawmill/entry_processor/build_records.rb +157 -0
- data/lib/sawmill/entry_processor/conditionals.rb +444 -0
- data/lib/sawmill/entry_processor/filter_basic_fields.rb +145 -0
- data/lib/sawmill/entry_processor/format.rb +228 -0
- data/lib/sawmill/entry_processor/simple_queue.rb +116 -0
- data/lib/sawmill/entry_processor.rb +158 -0
- data/lib/sawmill/errors.rb +66 -0
- data/lib/sawmill/level.rb +264 -0
- data/lib/sawmill/log_record_middleware.rb +93 -0
- data/lib/sawmill/logger.rb +373 -0
- data/lib/sawmill/parser.rb +181 -0
- data/lib/sawmill/record.rb +255 -0
- data/lib/sawmill/record_processor/conditionals.rb +330 -0
- data/lib/sawmill/record_processor/decompose.rb +75 -0
- data/lib/sawmill/record_processor/filter_by_attributes.rb +87 -0
- data/lib/sawmill/record_processor/filter_by_record_id.rb +77 -0
- data/lib/sawmill/record_processor/format.rb +88 -0
- data/lib/sawmill/record_processor/simple_queue.rb +117 -0
- data/lib/sawmill/record_processor.rb +137 -0
- data/lib/sawmill/rotater/base.rb +90 -0
- data/lib/sawmill/rotater/date_based_log_file.rb +145 -0
- data/lib/sawmill/rotater/shifting_log_file.rb +166 -0
- data/lib/sawmill/rotater.rb +236 -0
- data/lib/sawmill/util/queue.rb +138 -0
- data/lib/sawmill/version.rb +47 -0
- data/lib/sawmill.rb +78 -0
- data/tests/tc_entry_processors.rb +138 -0
- data/tests/tc_formatter_parser.rb +144 -0
- data/tests/tc_levels.rb +118 -0
- data/tests/tc_logger.rb +315 -0
- data/tests/tc_record_processors.rb +117 -0
- data/tests/tc_records.rb +206 -0
- metadata +116 -0
@@ -0,0 +1,255 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill log record class
|
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
|
+
|
40
|
+
# A log record.
|
41
|
+
#
|
42
|
+
# Log records are sequences of related log entries with a common record
|
43
|
+
# ID, beginning with a begin_record and ending with an end_record. Log
|
44
|
+
# records therefore can be analyzed as a group, for example to measure the
|
45
|
+
# time taken by an aggregate action such as a website request.
|
46
|
+
#
|
47
|
+
# Log records must follow a particular protocol:
|
48
|
+
#
|
49
|
+
# * The first entry must be begin_record.
|
50
|
+
# * The last entry must be end_record.
|
51
|
+
# * No other begin_record or end_record entries may be present.
|
52
|
+
# * All entries must have the same non-nil record_id.
|
53
|
+
# * Entries must be in nondecreasing order of timestamp.
|
54
|
+
#
|
55
|
+
# The only exception to these rules are incomplete log records, in which
|
56
|
+
# the final end_record is not present.
|
57
|
+
|
58
|
+
class Record
|
59
|
+
|
60
|
+
|
61
|
+
# Create a log record.
|
62
|
+
#
|
63
|
+
# You may optionally pass in an array of log entries to populate the
|
64
|
+
# record either partially or completely. If you do, note that
|
65
|
+
# Errors::IllegalRecordError may be raised if the log entries do not
|
66
|
+
# follow the log record protocol-- e.g. if the first entry is not a
|
67
|
+
# begin_record, etc.
|
68
|
+
|
69
|
+
def initialize(entries_=nil)
|
70
|
+
@started = false
|
71
|
+
@complete = false
|
72
|
+
@message_count = 0
|
73
|
+
@entries = []
|
74
|
+
@attributes = {}
|
75
|
+
if entries_ && entries_.size > 0
|
76
|
+
entries_.each do |entry_|
|
77
|
+
add_entry(entry_)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def to_s # :nodoc:
|
84
|
+
"#{type}: #{@line}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def inspect # :nodoc:
|
88
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} record_id=#{record_id.inspect}>"
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Append a log entry to this record.
|
93
|
+
#
|
94
|
+
# Entries must be added in order. Raises Errors::IllegalRecordError if
|
95
|
+
# the log record protocol is violated
|
96
|
+
|
97
|
+
def add_entry(entry_)
|
98
|
+
empty_ = @entries.size == 0
|
99
|
+
if entry_.type == :unknown_data
|
100
|
+
raise Errors::IllegalRecordError, "You cannot add an unknown_data entry to a record"
|
101
|
+
end
|
102
|
+
if empty_ && entry_.type != :begin_record
|
103
|
+
raise Errors::IllegalRecordError, "First entry in a record must be a begin_record"
|
104
|
+
elsif !empty_ && entry_.type == :begin_record
|
105
|
+
raise Errors::IllegalRecordError, "Extra begin_record found"
|
106
|
+
end
|
107
|
+
if @complete
|
108
|
+
raise Errors::IllegalRecordError, "Cannot have entries after end_record"
|
109
|
+
end
|
110
|
+
if empty_
|
111
|
+
if entry_.record_id.nil?
|
112
|
+
raise Errors::IllegalRecordError, "Entry has no record_id"
|
113
|
+
end
|
114
|
+
else
|
115
|
+
last_ = @entries.last
|
116
|
+
if last_.record_id != entry_.record_id
|
117
|
+
raise Errors::IllegalRecordError, "Entry has a mismatching record_id"
|
118
|
+
end
|
119
|
+
if last_.timestamp > entry_.timestamp
|
120
|
+
raise Errors::IllegalRecordError, "Entry's timestamp is earlier than the previous entry"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
case entry_.type
|
124
|
+
when :begin_record
|
125
|
+
@started = true
|
126
|
+
when :end_record
|
127
|
+
@complete = true
|
128
|
+
when :attribute
|
129
|
+
case entry_.operation
|
130
|
+
when :set
|
131
|
+
@attributes[entry_.key] = entry_.value
|
132
|
+
when :append
|
133
|
+
val_ = @attributes[entry_.key]
|
134
|
+
case val_
|
135
|
+
when Array
|
136
|
+
val_ << entry_.value
|
137
|
+
when String
|
138
|
+
@attributes[entry_.key] = [val_, entry_.value]
|
139
|
+
when nil
|
140
|
+
@attributes[entry_.key] = [entry_.value]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
when :message
|
144
|
+
@message_count += 1
|
145
|
+
end
|
146
|
+
@entries << entry_
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# Returns true if the initial begin_record has been added.
|
151
|
+
|
152
|
+
def started?
|
153
|
+
@started
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
# Returns true if the final end_record has been added.
|
158
|
+
|
159
|
+
def complete?
|
160
|
+
@complete
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# Returns the record ID as a string.
|
165
|
+
|
166
|
+
def record_id
|
167
|
+
@entries.size > 0 ? @entries.first.record_id : nil
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
# Returns the beginning timestamp as a Time object, if the log record
|
172
|
+
# has been started, or nil otherwise.
|
173
|
+
|
174
|
+
def begin_timestamp
|
175
|
+
@started ? @entries.first.timestamp : nil
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
# Returns the ending timestamp as a Time object, if the log record
|
180
|
+
# has been completed, or nil otherwise.
|
181
|
+
|
182
|
+
def end_timestamp
|
183
|
+
@complete ? @entries.last.timestamp : nil
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# Returns the number of log entries currently in this record.
|
188
|
+
|
189
|
+
def entry_count
|
190
|
+
@entries.size
|
191
|
+
end
|
192
|
+
|
193
|
+
alias_method :size, :entry_count
|
194
|
+
|
195
|
+
|
196
|
+
# Returns the number of message entries currently in this record.
|
197
|
+
|
198
|
+
def message_count
|
199
|
+
@message_count
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
# Iterate over all log entries, passing each to the given block.
|
204
|
+
|
205
|
+
def each_entry(&block_)
|
206
|
+
@entries.each(&block_)
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# Iterate over all log message entries, passing each to the given block.
|
211
|
+
|
212
|
+
def each_message
|
213
|
+
@entries.each do |entry_|
|
214
|
+
if entry_.type == :message
|
215
|
+
yield entry_
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
# Returns an array of all log entries.
|
222
|
+
|
223
|
+
def all_entries
|
224
|
+
@entries.dup
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# Returns an array of all log message entries.
|
229
|
+
|
230
|
+
def all_messages
|
231
|
+
@entries.find_all{ |entry_| entry_.type == :message }
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
# Get the value of the given attribute.
|
236
|
+
# Returns a string if the attribute has a single value.
|
237
|
+
# Returns an array of strings if the attribute has multiple values.
|
238
|
+
# Returns nil if the attribute is not set.
|
239
|
+
|
240
|
+
def attribute(key_)
|
241
|
+
@attributes[key_.to_s]
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
# Get an array of attribute keys present in this log record.
|
246
|
+
|
247
|
+
def attribute_keys
|
248
|
+
@attributes.keys
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
end
|
@@ -0,0 +1,330 @@
|
|
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 RecordProcessor
|
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 records
|
48
|
+
# to STDOUT only if they have a "user" attribute of "daniel":
|
49
|
+
#
|
50
|
+
# processor = Sawmill::RecordProcessor.build do
|
51
|
+
# If(FilterByAttributes('user' => 'daniel'), 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 record should be accepted.
|
61
|
+
# The second parameter is a processor to run on accepted records.
|
62
|
+
# The optional third parameter is an "else" processor to run on
|
63
|
+
# rejected records.
|
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 record(record_)
|
72
|
+
if @condition.record(record_)
|
73
|
+
@on_true.record(record_)
|
74
|
+
elsif @on_false
|
75
|
+
@on_false.record(record_)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def extra_entry(entry_)
|
80
|
+
if @condition.extra_entry(entry_)
|
81
|
+
@on_true.extra_entry(entry_)
|
82
|
+
elsif @on_false
|
83
|
+
@on_false.extra_entry(entry_)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def close
|
88
|
+
@on_true.close
|
89
|
+
@on_false.close if @on_false
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# A boolean processor that returns the boolean negation of a given
|
97
|
+
# processor.
|
98
|
+
#
|
99
|
+
# For example, this builds a processor that sends formatted log records
|
100
|
+
# to STDOUT only if they do NOT have a "user" attribute of "daniel":
|
101
|
+
#
|
102
|
+
# processor = Sawmill::RecordProcessor.build do
|
103
|
+
# If(Not(FilterByAttributes('user' => 'daniel')), Format(STDOUT))
|
104
|
+
# end
|
105
|
+
|
106
|
+
class Not < Base
|
107
|
+
|
108
|
+
|
109
|
+
# Create a "not" boolean.
|
110
|
+
# The parameter is a boolean processor to run. This processor returns
|
111
|
+
# the boolean negation of its output.
|
112
|
+
|
113
|
+
def initialize(child_)
|
114
|
+
@child = _interpret_processor(child_)
|
115
|
+
end
|
116
|
+
|
117
|
+
def record(record_)
|
118
|
+
!@child.record(record_)
|
119
|
+
end
|
120
|
+
|
121
|
+
def extra_entry(entry_)
|
122
|
+
!@child.extra_entry(record_)
|
123
|
+
end
|
124
|
+
|
125
|
+
def close
|
126
|
+
@child.close
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
# A boolean processor that returns true if and only if all its child
|
134
|
+
# processors return true. This version short-circuits the processing,
|
135
|
+
# so once one child returns false, subsequent children are not called
|
136
|
+
# at all.
|
137
|
+
#
|
138
|
+
# For example, this builds a processor that sends formatted log records
|
139
|
+
# to STDOUT only if they have a "user" attribute of "daniel" AND their
|
140
|
+
# record ID is '12345678':
|
141
|
+
#
|
142
|
+
# processor = Sawmill::RecordProcessor.build do
|
143
|
+
# If(And(FilterByAttributes('user' => 'daniel'),
|
144
|
+
# FilterByRecordID('12345678')),
|
145
|
+
# Format(STDOUT))
|
146
|
+
# end
|
147
|
+
|
148
|
+
class And < Base
|
149
|
+
|
150
|
+
|
151
|
+
# Create an "and" boolean.
|
152
|
+
# The parameters are child processors whose return values should be
|
153
|
+
# combined with an AND operation.
|
154
|
+
|
155
|
+
def initialize(*children_)
|
156
|
+
@children = _interpret_processor_array(children_)
|
157
|
+
end
|
158
|
+
|
159
|
+
def record(record_)
|
160
|
+
@children.each do |child_|
|
161
|
+
return false unless child_.record(record_)
|
162
|
+
end
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
166
|
+
def extra_entry(entry_)
|
167
|
+
@children.each do |child_|
|
168
|
+
return false unless child_.extra_entry(entry_)
|
169
|
+
end
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
def close
|
174
|
+
@children.each{ |child_| child_.close }
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# A boolean processor that returns true if and only if any of its child
|
182
|
+
# processors returns true. This version short-circuits the processing,
|
183
|
+
# so once one child returns true, subsequent children are not called
|
184
|
+
# at all.
|
185
|
+
#
|
186
|
+
# For example, this builds a processor that sends formatted log records
|
187
|
+
# to STDOUT only if they have a "user" attribute of "daniel" OR their
|
188
|
+
# record ID is '12345678':
|
189
|
+
#
|
190
|
+
# processor = Sawmill::RecordProcessor.build do
|
191
|
+
# If(Or(FilterByAttributes('user' => 'daniel'),
|
192
|
+
# FilterByRecordID('12345678')),
|
193
|
+
# Format(STDOUT))
|
194
|
+
# end
|
195
|
+
|
196
|
+
class Or < Base
|
197
|
+
|
198
|
+
|
199
|
+
# Create an "or" boolean.
|
200
|
+
# The parameters are child processors whose return values should be
|
201
|
+
# combined with an OR operation.
|
202
|
+
|
203
|
+
def initialize(*children_)
|
204
|
+
@children = _interpret_processor_array(children_)
|
205
|
+
end
|
206
|
+
|
207
|
+
def record(record_)
|
208
|
+
@children.each do |child_|
|
209
|
+
return true if child_.record(record_)
|
210
|
+
end
|
211
|
+
false
|
212
|
+
end
|
213
|
+
|
214
|
+
def extra_entry(entry_)
|
215
|
+
@children.each do |child_|
|
216
|
+
return true if child_.extra_entry(entry_)
|
217
|
+
end
|
218
|
+
false
|
219
|
+
end
|
220
|
+
|
221
|
+
def close
|
222
|
+
@children.each{ |child_| child_.close }
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
# A boolean processor that returns true if and only if all its child
|
230
|
+
# processors return true. This version does not short-circuit the
|
231
|
+
# processing, so all children are always called even if an early one
|
232
|
+
# returns false. Thus, this processor is also a good one to use as a
|
233
|
+
# multiplexor to simply run a bunch of processors.
|
234
|
+
#
|
235
|
+
# For example, this builds a processor that sends formatted log records
|
236
|
+
# to STDOUT only if they have a "user" attribute of "daniel" AND their
|
237
|
+
# record ID is '12345678':
|
238
|
+
#
|
239
|
+
# processor = Sawmill::RecordProcessor.build do
|
240
|
+
# If(All(FilterByAttributes('user' => 'daniel'),
|
241
|
+
# FilterByRecordID('12345678')),
|
242
|
+
# Format(STDOUT))
|
243
|
+
# end
|
244
|
+
#
|
245
|
+
# This processor just formats both to STDOUT and STDERR:
|
246
|
+
#
|
247
|
+
# processor = Sawmill::RecordProcessor.build do
|
248
|
+
# All(Format(STDOUT), Format(STDERR))
|
249
|
+
# end
|
250
|
+
|
251
|
+
class All < Base
|
252
|
+
|
253
|
+
|
254
|
+
# Create an "all" boolean.
|
255
|
+
# The parameters are child processors whose return values should be
|
256
|
+
# combined with an AND operation.
|
257
|
+
|
258
|
+
def initialize(*children_)
|
259
|
+
@children = _interpret_processor_array(children_)
|
260
|
+
end
|
261
|
+
|
262
|
+
def record(record_)
|
263
|
+
@children.inject(true) do |result_, child_|
|
264
|
+
child_.record(record_) && result_
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def extra_entry(entry_)
|
269
|
+
@children.inject(true) do |result_, child_|
|
270
|
+
child_.extra_entry(entry_) && result_
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def close
|
275
|
+
@children.each{ |child_| child_.close }
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
# A boolean processor that returns true if and only if any of its child
|
283
|
+
# processors returns true. This version does not short-circuit the
|
284
|
+
# processing, so all children are always called even if an early one
|
285
|
+
# returns true.
|
286
|
+
#
|
287
|
+
# For example, this builds a processor that sends formatted log records
|
288
|
+
# to STDOUT only if they have a "user" attribute of "daniel" OR their
|
289
|
+
# record ID is '12345678':
|
290
|
+
#
|
291
|
+
# processor = Sawmill::RecordProcessor.build do
|
292
|
+
# If(Any(FilterByAttributes('user' => 'daniel'),
|
293
|
+
# FilterByRecordID('12345678')),
|
294
|
+
# Format(STDOUT))
|
295
|
+
# end
|
296
|
+
|
297
|
+
class Any < Base
|
298
|
+
|
299
|
+
|
300
|
+
# Create an "any" boolean.
|
301
|
+
# The parameters are child processors whose return values should be
|
302
|
+
# combined with an OR operation.
|
303
|
+
|
304
|
+
def initialize(*children_)
|
305
|
+
@children = _interpret_processor_array(children_)
|
306
|
+
end
|
307
|
+
|
308
|
+
def record(record_)
|
309
|
+
@children.inject(false) do |result_, child_|
|
310
|
+
child_.record(record_) || result_
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def extra_entry(entry_)
|
315
|
+
@children.inject(false) do |result_, child_|
|
316
|
+
child_.extra_entry(entry_) || result_
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def close
|
321
|
+
@children.each{ |child_| child_.close }
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Sawmill record processor that decomposes a record into entries
|
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 RecordProcessor
|
40
|
+
|
41
|
+
|
42
|
+
# A processor that decomposes records into constituent entries and
|
43
|
+
# passes those entries to an entry processor.
|
44
|
+
|
45
|
+
class Decompose < Base
|
46
|
+
|
47
|
+
|
48
|
+
# Create a new decomposer that emits to the given entry processor.
|
49
|
+
|
50
|
+
def initialize(processor_, opts_={})
|
51
|
+
@processor = processor_
|
52
|
+
@classifier = ::Sawmill::EntryClassifier.new(processor_)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def record(record_)
|
57
|
+
record_.each_entry{ |entry_| @classifier.entry(entry_) }
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def extra_entry(entry_)
|
62
|
+
@classifier.entry(entry_)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def close
|
67
|
+
@processor.close
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|