A logfile parser that parses log entries from a logfile and sends them to an entry processor.
Create a new parser that reads from the given stream.
You should provide a processor to receive the data from the logfile. The processor may be either an entry processor or a record processor. You may also pass nil for the processor. In this case, the generated log entries will not be sent to a processor but will still be returned by the #parse_one_entry method.
Recognized options include:
:levels
Sawmill::LevelGroup to use to parse log levels. If not specified, Sawmill::STANDARD_LEVELS is used by default.
:emit_incomplete_records_at_eof
If set to true, causes any incomplete log records to be emitted in their incomplete state when EOF is reached.
:encoding
Overrides the IO encoding. (Ruby 1.9 only). If specified, lines read from the stream are assumed to be in this encoding. If not specified, the IO’s default encoding is honored. Note that the encoding may also be modified by the stream itself, if an appropriate parser directive is encountered.
:internal_encoding
Transcodes strings as they are read. (Ruby 1.9 only). If specified, lines are transcoded into this encoding after they are read from the stream. If not specified, no post-transcoding is done.
# File lib/sawmill/parser.rb, line 81 def initialize(io_, processor_, opts_={}) @io = io_ @processor = nil if processor_.respond_to?(:record) && processor_.respond_to?(:extra_entry) @processor = RecordBuilder.new(processor_) elsif processor_.respond_to?(:begin_record) && processor_.respond_to?(:end_record) @processor = processor_ end @levels = opts_[:levels] || STANDARD_LEVELS @emit_incomplete_records_at_eof = opts_[:emit_incomplete_records_at_eof] @current_record_id = nil if SUPPORTS_ENCODING @encoding = opts_[:encoding] @encoding = ::Encoding.find(@encoding) if @encoding && !@encoding.kind_of?(::Encoding) @internal_encoding = opts_[:internal_encoding] @internal_encoding = ::Encoding.find(@internal_encoding) if @internal_encoding && !@internal_encoding.kind_of?(::Encoding) end end
Parse the rest of the stream until EOF is reached, and emit the log entries to the processor.
# File lib/sawmill/parser.rb, line 189 def parse_all while parse_one_entry; end end
Parse one log entry from the stream and emit it to the processor. Also returns the log entry. Returns nil if EOF has been reached.
# File lib/sawmill/parser.rb, line 105 def parse_one_entry str_ = _get_next_line entry_ = nil if str_ match_ = LINE_REGEXP.match(str_) if match_ level_ = @levels.get(match_[1]) timestamp_ = ::Time.utc(match_[2].to_i, match_[3].to_i, match_[4].to_i, match_[6].to_i, match_[7].to_i, match_[8].to_i, match_[10].to_s.ljust(6, '0').to_i) offset_ = match_[11].to_i if offset_ != 0 neg_ = offset_ < 0 offset_ = -offset_ if neg_ secs_ = offset_ / 100 * 3600 + offset_ % 100 * 60 if neg_ timestamp_ += secs_ else timestamp_ -= secs_ end end progname_ = match_[12] record_id_ = match_[14] || @current_record_id type_code_ = match_[15] str_ = match_[16] if str_ =~ %r(\\+)$/ count_ = $1.length str_ = $` + "\\"*(count_/2) while count_ % 2 == 1 str2_ = _get_next_line if str2_ && str2_ =~ %r(\\*)\n?$/ count_ = $1.length str_ << "\n" << $` << "\\"*(count_/2) else break end end end case type_code_ when '^' if str_ =~ %r^BEGIN\s/ @current_record_id = $' entry_ = Entry::BeginRecord.new(level_, timestamp_, progname_, @current_record_id) @processor.begin_record(entry_) if @processor end when '$' if str_ =~ %r^END\s/ @current_record_id = $' entry_ = Entry::EndRecord.new(level_, timestamp_, progname_, @current_record_id) @current_record_id = nil @processor.end_record(entry_) if @processor end when '=' if str_ =~ ATTRIBUTE_REGEXP key_ = $1 opcode_ = $2 value_ = $' operation_ = opcode_ == '+' ? :append : :set entry_ = Entry::Attribute.new(level_, timestamp_, progname_, record_id_, key_, value_, operation_) @processor.attribute(entry_) if @processor end end unless entry_ entry_ = Entry::Message.new(level_, timestamp_, progname_, record_id_, str_) @processor.message(entry_) if @processor end else if str_ =~ DIRECTIVE_REGEXP _set_parser_directive($1, $2) end entry_ = Entry::UnknownData.new(str_.chomp) @processor.unknown_data(entry_) if @processor.respond_to?(:unknown_data) end else if @emit_incomplete_records_at_eof && @processor.respond_to?(:emit_incomplete_records) @processor.emit_incomplete_records end end entry_ end