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,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