class Systemd::Journal
Class to allow interacting with the systemd journal. To read from the journal, instantiate a new {Systemd::Journal}; to write to the journal, use {Systemd::Journal::Writable::ClassMethods#message Journal.message} or {Systemd::Journal::Writable::ClassMethods#print Journal.print}.
Constants
- KERNEL_FIELDS
Fields used in messages originating from the kernel.
- TRUSTED_FIELDS
Fields generated by the journal and added to each event.
- USER_FIELDS
Fields directly passed by client programs and stored in the journal.
- VERSION
The version of the systemd-journal gem.
Public Class Methods
# File lib/systemd/journal.rb, line 137 def self.catalog_for(message_id) out_ptr = FFI::MemoryPointer.new(:pointer, 1) rc = Native.sd_journal_get_catalog_for_message_id( Systemd::Id128::Native::Id128.from_s(message_id), out_ptr ) raise JournalError, rc if rc < 0 read_and_free_outstr(out_ptr.read_pointer) end
Returns a new instance of a Journal
, opened with the provided options. @param [Hash] opts optional initialization parameters. @option opts [Integer] :flags a set of bitwise OR-ed
{Systemd::Journal::Flags} which control what journal files are opened. Defaults to `0`, meaning all journals avaiable to the current user.
@option opts [String] :path if provided, open the journal files living
in the provided directory only. Any provided flags will be ignored since sd_journal_open_directory does not currently accept any flags.
@option opts [Array] :files if provided, open the provided journal files
only. Any provided flags will be ignored since sd_journal_open_files does not currently accept any flags.
@option opts [String] :container if provided, open the journal files from
the container with the provided machine name only.
@example Read only system journal entries
j = Systemd::Journal.new(flags: Systemd::Journal::Flags::SYSTEM_ONLY)
@example Directly open a journal directory
j = Systemd::Journal.new( path: '/var/log/journal/5f5777e46c5f4131bd9b71cbed6b9abf' )
# File lib/systemd/journal.rb, line 47 def initialize(opts = {}) open_type, flags = validate_options!(opts) ptr = FFI::MemoryPointer.new(:pointer, 1) @finalize = (opts.key?(:finalize) ? opts.delete(:finalize) : true) rc = open_journal(open_type, ptr, opts, flags) raise JournalError, rc if rc < 0 @ptr = ptr.read_pointer file_descriptor ObjectSpace.define_finalizer(self, self.class.finalize(@ptr)) if @finalize end
# File lib/systemd/journal.rb, line 60 def self.open(opts = {}) j = new(opts.merge(finalize: false)) yield j ensure j.close if j end
Private Class Methods
# File lib/systemd/journal.rb, line 300 def self.finalize(ptr) proc { Native.sd_journal_close(ptr) unless ptr.nil? } end
some sd_journal_* functions return strings that we're expected to free ourselves. This function copies the string from a char* to a ruby string, frees the char*, and returns the ruby string.
# File lib/systemd/journal.rb, line 323 def self.read_and_free_outstr(ptr) str = ptr.read_string LibC.free(ptr) str end
Public Instance Methods
Explicitly close the underlying Journal
file. Once this is done, any operations on the instance will fail and raise an exception.
# File lib/systemd/journal.rb, line 206 def close return if @ptr.nil? ObjectSpace.undefine_finalizer(self) if @finalize Native.sd_journal_close(@ptr) @ptr = nil end
# File lib/systemd/journal.rb, line 215 def closed? @ptr.nil? end
# File lib/systemd/journal.rb, line 128 def current_catalog out_ptr = FFI::MemoryPointer.new(:pointer, 1) rc = Native.sd_journal_get_catalog(@ptr, out_ptr) raise JournalError, rc if rc < 0 Journal.read_and_free_outstr(out_ptr.read_pointer) end
Read the contents of all fields from the current journal entry. If given a block, it will yield each field in the form of `(fieldname, value)`.
{#move_next} or {#move_previous} must be called at least once after initialization or seeking prior to calling {#current_entry}
@return [Hash] the contents of the current journal entry. @example Print all items in the current entry
j = Systemd::Journal.new j.move_next j.current_entry{ |field, value| puts "#{field}: #{value}" }
# File lib/systemd/journal.rb, line 111 def current_entry Native.sd_journal_restart_data(@ptr) results = {} while (kvpair = enumerate_helper(:sd_journal_enumerate_data)) key, value = kvpair results[key] = value yield(key, value) if block_given? end JournalEntry.new( results, realtime_ts: read_realtime, monotonic_ts: read_monotonic ) end
Get the maximum length of a data field that will be returned. Fields longer than this will be truncated. Default is 64K. @return [Integer] size in bytes.
# File lib/systemd/journal.rb, line 186 def data_threshold size_ptr = FFI::MemoryPointer.new(:size_t, 1) if (rc = Native.sd_journal_get_data_threshold(@ptr, size_ptr)) < 0 raise JournalError, rc end size_ptr.read_size_t end
Set the maximum length of a data field that will be returned. Fields longer than this will be truncated.
# File lib/systemd/journal.rb, line 197 def data_threshold=(threshold) if (rc = Native.sd_journal_set_data_threshold(@ptr, threshold)) < 0 raise JournalError, rc end end
Get the number of bytes the Journal
is currently using on disk. If {Systemd::Journal::Flags::LOCAL_ONLY} was passed when opening the journal, this value will only reflect the size of journal files of the local host, otherwise of all hosts. @return [Integer] size in bytes
# File lib/systemd/journal.rb, line 175 def disk_usage size_ptr = FFI::MemoryPointer.new(:uint64) rc = Native.sd_journal_get_usage(@ptr, size_ptr) raise JournalError, rc if rc < 0 size_ptr.read_uint64 end
Iterate over each entry in the journal, respecting the applied conjunctions/disjunctions. If a block is given, it is called with each entry until no more entries remain. Otherwise, returns an enumerator which can be chained.
# File lib/systemd/journal.rb, line 71 def each return to_enum(:each) unless block_given? seek(:head) yield current_entry while move_next end
@private
# File lib/systemd/journal.rb, line 220 def inspect format( '#<%s:0x%016x target: "%s", flags: 0x%08x>', self.class.name, object_id, @open_target, @open_flags ) end
Get the list of unique values stored in the journal for the given field. If passed a block, each possible value will be yielded. @return [Array] the list of possible values. @example Fetch all possible boot ids from the journal
j = Systemd::Journal.new j.query_unique('_BOOT_ID')
# File lib/systemd/journal.rb, line 155 def query_unique(field) results = [] Native.sd_journal_restart_unique(@ptr) rc = Native.sd_journal_query_unique(@ptr, field.to_s.upcase) raise JournalError, rc if rc < 0 while (kvpair = enumerate_helper(:sd_journal_enumerate_unique)) results << kvpair.last end results end
Read the contents of the provided field from the current journal entry.
{#move_next} or {#move_previous} must be called at least once after initialization or seeking prior to attempting to read data.
@param [String] field the name of the field to read. @return [String] the value of the requested field. @example Read the `MESSAGE` field from the current entry
j = Systemd::Journal.new j.move_next puts j.read_field('MESSAGE')
# File lib/systemd/journal.rb, line 87 def read_field(field) len_ptr = FFI::MemoryPointer.new(:size_t, 1) out_ptr = FFI::MemoryPointer.new(:pointer, 1) field = field.to_s.upcase rc = Native.sd_journal_get_data(@ptr, field, out_ptr, len_ptr) raise JournalError, rc if rc < 0 len = len_ptr.read_size_t string_from_out_ptr(out_ptr, len).split('=', 2).last end
Private Instance Methods
# File lib/systemd/journal.rb, line 272 def array_to_ptrs(strings) ptr = FFI::MemoryPointer.new(:pointer, strings.length + 1) strings.each_with_index do |s, i| ptr[i].put_pointer(0, FFI::MemoryPointer.from_string(s)) end ptr[strings.length].put_pointer(0, nil) ptr end
# File lib/systemd/journal.rb, line 304 def enumerate_helper(enum_function) len_ptr = FFI::MemoryPointer.new(:size_t, 1) out_ptr = FFI::MemoryPointer.new(:pointer, 1) rc = Native.send(enum_function, @ptr, out_ptr, len_ptr) raise JournalError, rc if rc < 0 return nil if rc == 0 len = len_ptr.read_size_t string_from_out_ptr(out_ptr, len).split('=', 2) end
# File lib/systemd/journal.rb, line 232 def open_journal(type, ptr, opts, flags) @open_flags = flags case type when :path @open_target = "path:#{opts[:path]}" Native.sd_journal_open_directory(ptr, opts[:path], 0) when :files, :file files = Array(opts[type]) @open_target = "file#{files.one? ? '' : 's'}:#{files.join(',')}" Native.sd_journal_open_files(ptr, array_to_ptrs(files), 0) when :container @open_target = "container:#{opts[:container]}" Native.sd_journal_open_container(ptr, opts[:container], flags) when :local @open_target = 'journal:local' Native.sd_journal_open(ptr, flags) else raise ArgumentError, "Unknown open type: #{type}" end end
# File lib/systemd/journal.rb, line 262 def read_monotonic out = FFI::MemoryPointer.new(:uint64, 1) boot = FFI::MemoryPointer.new(Systemd::Id128::Native::Id128, 1) rc = Native.sd_journal_get_monotonic_usec(@ptr, out, boot) raise JournalError, rc if rc < 0 [out.read_uint64, Systemd::Id128::Native::Id128.new(boot).to_s] end
# File lib/systemd/journal.rb, line 254 def read_realtime out = FFI::MemoryPointer.new(:uint64, 1) rc = Native.sd_journal_get_realtime_usec(@ptr, out) raise JournalError, rc if rc < 0 out.read_uint64 end
# File lib/systemd/journal.rb, line 316 def string_from_out_ptr(p, len) p.read_pointer.read_string(len) end
# File lib/systemd/journal.rb, line 281 def validate_options!(opts) exclusive = [:path, :files, :container, :file] given = (opts.keys & exclusive) raise ArgumentError, "conflicting options: #{given}" if given.length > 1 type = given.first || :local if type == :container && !Native.open_container? raise ArgumentError, 'This native library version does not support opening containers' end flags = opts[:flags] if [:local, :container].include?(type) flags ||= 0 [type, flags] end