class AIX::Errlog::Errlog

The core errlog class. Used to open an errlog file.

The main method that should be used here is ::open (more likely AIX::Errlog.open for convenience), and the block form is strongly recommended wherever possible. If you do not use the block form, make sure you call close when you are done with it (enforce it with an ensure block if possible). The garbage collector will not do this automatically, and you can leak.

forward_each and reverse_each should do everything you need to do. If you use the enumerator form, or even the block form of these, make sure you consume the entire enumerator before opening a new one. If you need to enumerate the errlog in a nested loop or something like that, you'll need multuple instances of this open, otherwise it will fail (the handle operates as a cursor, so if you try to re-invoke one before it is finished, the cursor will get reset, and you'll get jumbled results). While one of these enumerators as active, trying to re-invoke one will raise an Errors::EnumeratorError.

If you need to do complex matching, use match or ::match to build a match expression using a DSL.

A simple example, showing how to get a list of all labels of all log entries in forward order which contain the string 'KILL' in their label and happen in the month of January 2017, and which have a sequence ID above 1000, or that happen in December 2016 and have a sequence ID below 500, might look something like this:

require 'date'

require 'aix/errlog'

AIX::Errlog.open do |log|
  log.forward_each(log.match {|m|
    m.label.include?('KILL') & (
      (
        (m.sequence > 1000) &
        (m.timestamp >= DateTime.new(2017, 1, 1)) &
        (m.timestamp < DateTime.new(2017, 2, 1))
      ) | (
        (m.sequence < 500) &
        (m.timestamp >= DateTime.new(2016, 12, 1)) &
        (m.timestamp < DateTime.new(2017, 1, 1))
      )
    )
  }).map(&:label)
end

Certainly, that looks a little complex, but it is a bit more efficient than iterating all log entries as a whole and then filtering after the fact, and it's a lot more pleasant than building the Match tree from scratch in C.

Public Class Methods

match() { |Match| ... } click to toggle source

Used to build a match expression. This simply yields the Match class.

A previous version of this used instance_eval to get a modified self, but that caused some unsavory issues, especially in respect to instance variables:

[1] pry(main)> @x = 7
=> 7
[2] pry(main)> Object.new.instance_eval {@x}

Essentially, you could not use instance variables in the DSL, so you'd have to reassign everything to locals so they'd be available in the block.

# File lib/aix/errlog/errlog.rb, line 217
def self.match(&block)
  yield Match
end
new(path='/var/adm/ras/errlog'.freeze, mode='r'.freeze) click to toggle source

path is the string path to the file. mode matches as closely as possible to the semantics of the fopen mode.

# File lib/aix/errlog/errlog.rb, line 72
def initialize(path='/var/adm/ras/errlog'.freeze, mode='r'.freeze)
  mode_r = mode.include? 'r'
  mode_w = mode.include? 'w'
  mode_a = mode.include? 'a'
  mode_x = mode.include? 'x'
  mode_p = mode.include? '+'

  mode_flags =
    if mode_p
      Constants::O_RDRW
    elsif mode_r
      Constants::O_RDONLY
    else
      Constants::O_WRONLY
    end
  mode_flags |= Constants::O_CREAT unless mode_r
  mode_flags |= Constants::O_TRUNC if mode_w
  mode_flags |= Constants::O_APPEND if mode_a
  mode_flags |= Constants::O_EXCL if mode_x

  handle_p = FFI::MemoryPointer.new(:pointer)

  status = Lib.errlog_open(
    path,
    mode_flags,
    Constants::LE_MAGIC,
    handle_p,
  )

  Errors.throw(status, "path: #{path}, mode: #{mode}") unless status == :ok

  @enum_active = false

  # Just hold the handle directly
  @handle = handle_p.get_pointer
end
open(*args) { |errlog| ... } click to toggle source

Opens the given error log. Arguments are passed directly into ::new

If a block is given, close will be automatically called when the block exits, and the return value of the block will be the return value of this.

# File lib/aix/errlog/errlog.rb, line 115
def self.open(*args)
  errlog = new(*args)
  if block_given?
    begin
      return yield errlog
    ensure
      errlog.close
    end
  else
    log
  end
end

Public Instance Methods

close() click to toggle source

Closes the handle. This must be called, either directly or indirectly through ::open with a block. This may be called multiple times, but after this is called, no other functions that try to use the errlog handle may be called.

# File lib/aix/errlog/errlog.rb, line 133
def close
  unless @handle.nil?
    status = Lib.errlog_close(@handle)
    Errors.throw(status, "handle: #{@handle}") unless status == :ok
    @handle = nil
  end
end
forward_each(matcher=nil, &block) click to toggle source

Enumerate log Entry objects in forward order (default is reverse). If no block is given, returns an enumerator.

The single option is a matcher. It specifies a match if the passed in value is a Match, or a sequence ID otherwise.

sequence specifies the sequence ID to start with. It will be included in the results if specifed. The match is a more complex matcher.

An active enumerator can not be nested within another active enumerator, including the block form of this. If you invoke any of the each_ methods while another has not finished and exited, you'll raise an Errors::EnumeratorError. You can create an enumerator of one within the other, as long as you don't activate it until the first one has exited.

Warning: if the sequence does not exist (which is common when error logs are cleaned), no entries will be returned, even if they follow the sequence ID. If you want all entries based on their sequence number, use a Match instead. You're usually better off using the timestamp instead of sequence number, because the sequence number is 32 bits and might wrap.

# File lib/aix/errlog/errlog.rb, line 163
def forward_each(matcher=nil, &block)
  return to_enum(:forward_each, matcher) unless block_given?
  set_direction :forward
  each(matcher, &block)
end
match(&block) click to toggle source

Calls ::match

# File lib/aix/errlog/errlog.rb, line 199
def match(&block)
  Errlog.match(&block)
end
reverse_each(matcher=nil, &block) click to toggle source

Enumerate log Entry objects in reverse order (default is reverse). If no block is given, returns an enumerator.

The single option is a matcher. It specifies a match if the passed in value is a Match, or a sequence ID otherwise.

sequence specifies the sequence ID to start with. It will be included in the results if specifed. The match is a more complex matcher.

An active enumerator can not be nested within another active enumerator, including the block form of this. If you invoke any of the each_ methods while another has not finished and exited, you'll raise an Errors::EnumeratorError. You can create an enumerator of one within the other, as long as you don't activate it until the first one has exited.

Warning: if the sequence does not exist (which is common when error logs are cleaned), no entries will be returned, even if they follow the sequence ID. If you want all entries based on their sequence number, use a Match instead. You're usually better off using the timestamp instead of sequence number, because the sequence number is 32 bits and might wrap.

# File lib/aix/errlog/errlog.rb, line 191
def reverse_each(matcher=nil, &block)
  return to_enum(:reverse_each, matcher) unless block_given?
  set_direction :reverse
  each(matcher, &block)
end

Private Instance Methods

each(matcher=nil) { |entry| ... } click to toggle source

Enumerate log entries in the order set in set_direction (default is reverse).

The single option is a matcher. It specifies a match if the passed in value is a Match, or a sequence ID otherwise.

sequence specifies the sequence ID to start with. It will be included in the results if specifed. The match is a more complex matcher.

An active enumerator can not be nested within another active enumerator, including the block form of this. If you invoke any of the each_ methods while another has not finished and exited, you'll raise an Errors::EnumeratorError. You can create an enumerator of one within the other, as long as you don't activate it until the first one has exited.

Warning: if the sequence does not exist (which is common when error logs are cleaned), no entries will be returned, even if they follow the sequence ID. If you want all entries based on their sequence number, use a Match instead. You're usually better off using the timestamp instead of sequence number, because the sequence number is 32 bits and might wrap.

The user-facing entry points to this are forward_each and reverse_each

# File lib/aix/errlog/errlog.rb, line 288
def each(matcher=nil)
  # Does not return an enumerator, because this will always be called with
  # an active block

  begin
    raise Errors::EnumeratorError if @enum_active
    @enum_active = true

    if matcher
      entry = 
        if matcher.is_a? Match
          find_first(matcher)
        else
          find_sequence(matcher)
        end
      return if entry.nil?
      yield entry
    end

    while entry = find_next
      yield entry
    end
  ensure
    @enum_active = false
  end
end
find_first(match) click to toggle source

Get an entry matching the passed-in Match, if there is any, and nil otherwise.

# File lib/aix/errlog/errlog.rb, line 238
def find_first(match)
  entry = Lib::ErrlogEntry.new
  status = Lib.errlog_find_first(@handle, match.to_struct, entry)
  return if status == :done
  Errors.throw(status, "handle: #{@handle}, match: #{match}") unless status == :ok

  Entry.new(entry)
end
find_next() click to toggle source

Get the next entry.

# File lib/aix/errlog/errlog.rb, line 249
def find_next
  entry = Lib::ErrlogEntry.new
  status = Lib.errlog_find_next(@handle, entry)
  return if status == :done
  Errors.throw(status, "handle: #{@handle}") unless status == :ok
  Entry.new(entry)
end
find_sequence(id) click to toggle source

Get an entry for the specified sequence, if there is any, and nil otherwise.

# File lib/aix/errlog/errlog.rb, line 226
def find_sequence(id)
  entry = Lib::ErrlogEntry.new
  status = Lib.errlog_find_sequence(@handle, id, entry)
  return if status == :done
  Errors.throw(status, "handle: #{@handle}, sequence: #{id}") unless status == :ok

  Entry.new(entry)
end
set_direction(direction) click to toggle source

Sets the search direction for iteration.

# File lib/aix/errlog/errlog.rb, line 259
def set_direction(direction)
  status = Lib.errlog_set_direction(@handle, direction)
  Errors.throw(status, "handle: #{@handle}, direction: #{direction}") unless status == :ok
end