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,373 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill logger 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
+ if RUBY_VERSION >= '1.9'
38
+ require 'securerandom'
39
+ end
40
+
41
+
42
+ module Sawmill
43
+
44
+
45
+ # This is the Sawmill logger.
46
+ # It duck-types most of the API of the logger class from the ruby
47
+ # standard library, and adds capabilities specific to Sawmill.
48
+
49
+ class Logger
50
+
51
+
52
+ # Create a new logger.
53
+ #
54
+ # Supported options include:
55
+ #
56
+ # <tt>:levels</tt>::
57
+ # Use a custom Sawmill::LevelGroup. Normally, you should leave this
58
+ # set to the default, which is Sawmill::STANDARD_LEVELS.
59
+ # <tt>:level</tt>::
60
+ # Default level to use for log messages when no level is explicitly
61
+ # provided. By default, this is set to the level group's default,
62
+ # which in the case of the standard levels is :INFO.
63
+ # <tt>:attribute_level</tt>::
64
+ # Default level to use for attributes when no level is explicitly
65
+ # provided. By default, this is set to the level group's highest,
66
+ # level, which in the case of the standard levels is :ANY.
67
+ # <tt>:progname</tt>::
68
+ # Progname to use in log messages. Default is "sawmill".
69
+ # <tt>:record_progname</tt>::
70
+ # Progname to use in special log entries dealing with log records
71
+ # (i.e. record delimiters and attribute messages). Default is the
72
+ # same as the normal progname setting.
73
+ # <tt>:record_id_generator</tt>::
74
+ # A proc that generates and returns a new record ID if one is not
75
+ # explicitly passed into begin_record. If you do not provide a
76
+ # generator, the default one is used, which generates an ID using the
77
+ # variant 4 (random) UUID standard.
78
+ # <tt>:processor</tt>::
79
+ # A processor for log entries generated by this logger.
80
+ # If not specified, log entries are written out to STDOUT.
81
+
82
+ def initialize(opts_={})
83
+ @levels = opts_[:levels] || ::Sawmill::STANDARD_LEVELS
84
+ @level = @levels.get(opts_[:level])
85
+ if opts_.include?(:attribute_level)
86
+ @attribute_level = @levels.get(opts_[:attribute_level])
87
+ else
88
+ @attribute_level = @levels.highest
89
+ end
90
+ @progname = opts_[:progname] || 'sawmill'
91
+ @record_progname = opts_[:record_progname] || @progname
92
+ @record_id_generator = opts_[:record_id_generator] || _get_default_record_id_generator
93
+ @processor = opts_[:processor] || ::Sawmill::Formatter.new(STDOUT)
94
+ @current_record_id = nil
95
+ end
96
+
97
+
98
+ # Emit a log message. This method has the same behavior as the
99
+ # corresponding method in ruby's logger class.
100
+
101
+ def add(level_, message_=nil, progname_=nil, &block_)
102
+ level_obj_ = @levels.get(level_)
103
+ if level_obj_.nil?
104
+ raise Errors::UnknownLevelError, level_
105
+ end
106
+ return true if level_obj_ < @level
107
+ progname_ ||= @progname
108
+ if message_.nil?
109
+ if block_given?
110
+ message_ = yield
111
+ else
112
+ message_ = progname_
113
+ progname_ = @progname
114
+ end
115
+ end
116
+ case message_
117
+ when ::String
118
+ # Do nothing
119
+ when ::Exception
120
+ message_ = "#{message_.message} (#{message_.class})\n" +
121
+ (message_.backtrace || []).join("\n")
122
+ else
123
+ message_ = message_.inspect
124
+ end
125
+ @processor.message(Entry::Message.new(level_obj_, Time.now, progname_, @current_record_id, message_))
126
+ true
127
+ end
128
+ alias_method :log, :add
129
+
130
+
131
+ def to_s # :nodoc:
132
+ inspect
133
+ end
134
+
135
+ def inspect # :nodoc:
136
+ "#<#{self.class}:0x#{object_id.to_s(16)} progname=#{@progname.inspect} level=#{@level.name}>"
137
+ end
138
+
139
+
140
+ # Emits an "unknown" log entry. This is equivalent to the corresponding
141
+ # method in ruby's logger class, which dumps the given string to the log
142
+ # device without any formatting. Normally, you would not use this method
143
+ # because it bypasses the log formatting and parsing capability.
144
+
145
+ def <<(message_)
146
+ add(@levels.default, message_)
147
+ end
148
+
149
+
150
+ # Emits a begin_record log entry. This begins a new log record.
151
+ #
152
+ # If you pass a string ID, that ID is used as the record ID for the new
153
+ # log record. If you leave it as nil, an ID is generated for you, using
154
+ # the record id generator for this logger. In either case, the record ID
155
+ # for the new record is returned.
156
+ #
157
+ # If you call this when a record is already open, the current record is
158
+ # automatically closed before the new record is opened. That is, an
159
+ # end_record is implicitly called in this case.
160
+
161
+ def begin_record(id_=nil)
162
+ end_record if @current_record_id
163
+ @current_record_id = (id_ || @record_id_generator.call).to_s
164
+ @processor.begin_record(Entry::BeginRecord.new(@levels.highest, Time.now, @record_progname, @current_record_id))
165
+ @current_record_id
166
+ end
167
+
168
+
169
+ # Returns the record ID for the currently open log record, or nil if
170
+ # there is not a log record currently open.
171
+
172
+ def current_record_id
173
+ @current_record_id
174
+ end
175
+
176
+
177
+ # Ends the current log record by emitting an end_record log entry, if
178
+ # a record is currently open. Returns the record ID of the ended log
179
+ # record if one was open, or nil if no log record was open.
180
+
181
+ def end_record
182
+ if @current_record_id
183
+ @processor.end_record(Entry::EndRecord.new(@levels.highest, Time.now, @record_progname, @current_record_id))
184
+ id_ = @current_record_id
185
+ @current_record_id = nil
186
+ id_
187
+ else
188
+ nil
189
+ end
190
+ end
191
+
192
+
193
+ # Emits an attribute log entry in the current record.
194
+ # You must specify a key and a value as strings, and an operation.
195
+ # The operation defaults to <tt>:set</tt> if not specified.
196
+ #
197
+ # If you specify a level, it will be used; otherwise the logger's
198
+ # default attribute level is used.
199
+ # Raises Errors::UnknownLevelError if you specify a level that doesn't
200
+ # exist.
201
+
202
+ def attribute(key_, value_, operation_=nil, level_=true, progname_=nil)
203
+ if level_ == true
204
+ level_obj_ = @attribute_level
205
+ else
206
+ level_obj_ = @levels.get(level_)
207
+ if level_obj_.nil?
208
+ raise Errors::UnknownLevelError, level_
209
+ end
210
+ end
211
+ return true if level_obj_ < @level
212
+ @processor.attribute(Entry::Attribute.new(level_obj_, Time.now, progname_ || @record_progname, @current_record_id, key_, value_, operation_))
213
+ true
214
+ end
215
+
216
+
217
+ # Emits a set-attribute log entry in the current record.
218
+ # You must specify a key and a value as strings.
219
+
220
+
221
+ def set_attribute(key_, value_)
222
+ attribute(key_, value_, :set)
223
+ end
224
+
225
+
226
+ # Emits an append-attribute log entry in the current record.
227
+ # You must specify a key and a value as strings.
228
+
229
+
230
+ def append_attribute(key_, value_)
231
+ attribute(key_, value_, :append)
232
+ end
233
+
234
+
235
+ # Close the logger by closing the log entry processor to which it is
236
+ # emitting log entries.
237
+
238
+ def close
239
+ @processor.close
240
+ end
241
+
242
+
243
+ # Get the current progname setting for this logger
244
+
245
+ def progname
246
+ @progname
247
+ end
248
+
249
+
250
+ # Set the current progname setting for this logger
251
+
252
+ def progname=(value_)
253
+ @progname = value_.to_s.gsub(/\s+/, '')
254
+ end
255
+
256
+
257
+ # Get the current level setting for this logger as a Sawmill::Level.
258
+
259
+ def level
260
+ @level
261
+ end
262
+
263
+
264
+ # Set the current level setting for this logger.
265
+ # You may specify the level as a string, a symbol, an integer, or a
266
+ # Sawmill::Level. Ruby's logger constants such as ::Logger::INFO
267
+ # will also work.
268
+
269
+ def level=(value_)
270
+ if value_.kind_of?(Level)
271
+ @level = value_
272
+ else
273
+ level_obj_ = @levels.get(value_)
274
+ if level_obj_.nil?
275
+ raise Errors::UnknownLevelError, value_
276
+ end
277
+ @level = level_obj_
278
+ end
279
+ end
280
+
281
+ alias_method :sev_threshold=, :level=
282
+ alias_method :sev_threshold, :level
283
+
284
+
285
+ # Provide a block that generates and returns a unique record ID string.
286
+ # This block will be called when begin_record is called without an
287
+ # explicit ID provided. If you do not provide a block, Sawmill will use
288
+ # a default generator which uses the variant 4 (random) UUID standard.
289
+
290
+ def to_generate_record_id(&block_)
291
+ @record_id_generator = block_ || _get_default_record_id_generator
292
+ end
293
+
294
+
295
+ # You may call additional methods on the logger as shortcuts to log
296
+ # messages at specific levels, or to query whether the logger is logging
297
+ # to a given level. These methods match the corresponding methods in the
298
+ # classic ruby logger object, except that they are configurable for
299
+ # custom level schemes.
300
+ #
301
+ # For example, in the standard level scheme, the method "info" is
302
+ # defined, so you may call:
303
+ #
304
+ # logger.info("MainApp") { "Received connection from #{ip}" }
305
+ # # ...
306
+ # logger.info "Waiting for input from user"
307
+ # # ...
308
+ # logger.info { "User typed #{input}" }
309
+ #
310
+ # You may also call:
311
+ #
312
+ # logger.info? # Returns true if INFO messages are accepted
313
+ #
314
+ # Methods available in the standard level scheme are as follows:
315
+ #
316
+ # * <tt>debug</tt>
317
+ # * <tt>info</tt>
318
+ # * <tt>warn</tt>
319
+ # * <tt>error</tt>
320
+ # * <tt>fatal</tt>
321
+ # * <tt>unknown</tt>
322
+ # * <tt>any</tt>
323
+ #
324
+ # The "unknown" and "any" methods both correspond to the +ANY+ level.
325
+ # The latter is the preferred name under Sawmill. The former is for
326
+ # backward compatibility with ruby's classic logger.
327
+
328
+ def method_missing(method_, *args_, &block_)
329
+ method_name_ = method_.to_s
330
+ question_ = method_name_[-1..-1] == '?'
331
+ method_name_ = method_name_[0..-2] if question_
332
+ level_ = @levels.lookup_method(method_name_)
333
+ return super(method_, *args_, &block_) unless level_
334
+ if question_
335
+ level_ >= @level
336
+ else
337
+ add(level_, nil, args_[0], &block_)
338
+ end
339
+ end
340
+
341
+
342
+ def _get_default_record_id_generator # :nodoc:
343
+ if RUBY_VERSION >= '1.9'
344
+ lambda do
345
+ uuid_ = SecureRandom.hex(32)
346
+ uuid_[12] = '4'
347
+ uuid_[16] = (uuid_[16,1].to_i(16)&3|8).to_s(16)
348
+ uuid_.insert(8, '-')
349
+ uuid_.insert(13, '-')
350
+ uuid_.insert(18, '-')
351
+ uuid_.insert(23, '-')
352
+ uuid_
353
+ end
354
+ else
355
+ lambda do
356
+ uuid_ = Kernel.rand(0x100000000000000000000000000000000).to_s(16).rjust(32, '0')
357
+ uuid_[12] = '4'
358
+ uuid_[16] = (uuid_[16,1].to_i(16)&3|8).to_s(16)
359
+ uuid_.insert(8, '-')
360
+ uuid_.insert(13, '-')
361
+ uuid_.insert(18, '-')
362
+ uuid_.insert(23, '-')
363
+ uuid_
364
+ end
365
+ end
366
+ end
367
+ private :_get_default_record_id_generator
368
+
369
+
370
+ end
371
+
372
+
373
+ end
@@ -0,0 +1,181 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Sawmill stream parser utility
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 logfile parser that parses log entries from a logfile and sends them
41
+ # to an entry processor.
42
+
43
+ class Parser
44
+
45
+ # :stopdoc:
46
+ LINE_REGEXP = /^\[\s*([[:graph:]]+)\s+(\d{4})-(\d{2})-(\d{2})(T|\s)(\d{2}):(\d{2}):(\d{2})(.(\d{1,6}))?Z?\s?([+-]\d{4})?\s+([[:graph:]]+)(\s+([[:graph:]]+))?\s+([\^$.=])\]\s(.*)$/
47
+ DIRECTIVE_REGEXP = /^#\s+sawmill_format:\s+(\w+)=(.*)$/
48
+ ATTRIBUTE_REGEXP = /^([[:graph:]]+)\s([=+\/-])\s/
49
+ # :startdoc:
50
+
51
+
52
+ # Create a new parser that reads from the given stream.
53
+ #
54
+ # You should provide a processor to receive the data from the logfile.
55
+ # The processor may be either an entry processor or a record processor.
56
+ # You may also pass nil for the processor. In this case, the generated
57
+ # log entries will not be sent to a processor but will still be returned
58
+ # by the parse_one_entry method.
59
+ #
60
+ # Recognized options include:
61
+ #
62
+ # <tt>:levels</tt>
63
+ # Sawmill::LevelGroup to use to parse log levels.
64
+ # If not specified, Sawmill::STANDARD_LEVELS is used by default.
65
+ # <tt>:emit_incomplete_records_on_complete</tt>
66
+ # If set to true, causes any incomplete log records to be emitted
67
+ # in their incomplete state when EOF is reached.
68
+
69
+ def initialize(io_, processor_, opts_={})
70
+ @io = io_
71
+ @record_processor = nil
72
+ @processor = nil
73
+ if processor_.respond_to?(:record) && processor_.respond_to?(:extra_entry)
74
+ @processor = RecordBuilder.new(processor_)
75
+ elsif processor_.respond_to?(:begin_record) && processor_.respond_to?(:end_record)
76
+ @processor = processor_
77
+ end
78
+ @levels = opts_[:levels] || ::Sawmill::STANDARD_LEVELS
79
+ @emit_incomplete_records_on_complete = opts_[:emit_incomplete_records_on_complete]
80
+ @current_record_id = nil
81
+ @parser_directives = {}
82
+ end
83
+
84
+
85
+ # Parse one log entry from the stream and emit it to the processor.
86
+ # Also returns the log entry.
87
+ # Returns nil if EOF has been reached.
88
+
89
+ def parse_one_entry
90
+ str_ = @io.gets
91
+ entry_ = nil
92
+ if str_
93
+ match_ = LINE_REGEXP.match(str_)
94
+ if match_
95
+ level_ = @levels.get(match_[1])
96
+ timestamp_ = ::Time.utc(match_[2].to_i, match_[3].to_i, match_[4].to_i,
97
+ match_[6].to_i, match_[7].to_i, match_[8].to_i, match_[10].to_s.ljust(6, '0').to_i)
98
+ offset_ = match_[11].to_i
99
+ if offset_ != 0
100
+ neg_ = offset_ < 0
101
+ offset_ = -offset_ if neg_
102
+ secs_ = offset_ / 100 * 3600 + offset_ % 100 * 60
103
+ if neg_
104
+ timestamp_ += secs_
105
+ else
106
+ timestamp_ -= secs_
107
+ end
108
+ end
109
+ progname_ = match_[12]
110
+ record_id_ = match_[14] || @current_record_id
111
+ type_code_ = match_[15]
112
+ str_ = match_[16]
113
+ if str_ =~ /(\\+)$/
114
+ count_ = $1.length
115
+ str_ = $` + "\\"*(count_/2)
116
+ while count_ % 2 == 1
117
+ str2_ = @io.gets
118
+ if str2_ && str2_ =~ /(\\*)\n?$/
119
+ count_ = $1.length
120
+ str_ << "\n" << $` << "\\"*(count_/2)
121
+ else
122
+ break
123
+ end
124
+ end
125
+ end
126
+ case type_code_
127
+ when '^'
128
+ if str_ =~ /^BEGIN\s/
129
+ @current_record_id = $'
130
+ entry_ = Entry::BeginRecord.new(level_, timestamp_, progname_, @current_record_id)
131
+ @processor.begin_record(entry_) if @processor
132
+ end
133
+ when '$'
134
+ if str_ =~ /^END\s/
135
+ @current_record_id = $'
136
+ entry_ = Entry::EndRecord.new(level_, timestamp_, progname_, @current_record_id)
137
+ @current_record_id = nil
138
+ @processor.end_record(entry_) if @processor
139
+ end
140
+ when '='
141
+ if str_ =~ ATTRIBUTE_REGEXP
142
+ key_ = $1
143
+ opcode_ = $2
144
+ value_ = $'
145
+ operation_ = opcode_ == '+' ? :append : :set
146
+ entry_ = Entry::Attribute.new(level_, timestamp_, progname_, record_id_, key_, value_, operation_)
147
+ @processor.attribute(entry_) if @processor
148
+ end
149
+ end
150
+ unless entry_
151
+ entry_ = Entry::Message.new(level_, timestamp_, progname_, record_id_, str_)
152
+ @processor.message(entry_) if @processor
153
+ end
154
+ else
155
+ if str_ =~ DIRECTIVE_REGEXP
156
+ @parser_directives[$1] = $2
157
+ end
158
+ entry_ = Entry::UnknownData.new(str_)
159
+ @processor.unknown_data(entry_) if @processor.respond_to?(:unknown_data)
160
+ end
161
+ else
162
+ if @emit_incomplete_records_on_complete && @processor.respond_to?(:emit_incomplete_records)
163
+ @processor.emit_incomplete_records
164
+ end
165
+ end
166
+ entry_
167
+ end
168
+
169
+
170
+ # Parse the rest of the stream until EOF is reached, and emit the log
171
+ # entries to the processor.
172
+
173
+ def parse_all
174
+ while process_one_entry; end
175
+ end
176
+
177
+
178
+ end
179
+
180
+
181
+ end