class Fluent::Plugin::SystemdEntryMutator

A simple stand-alone configurable mutator for systemd journal entries.

Note regarding field mapping: The input `field_map` option is meant to have a structure that is intuative or logical for humans when declaring a field map. {

"<source_field1>" => "<new_field1>",
"<source_field2>" => ["<new_field1>", "<new_field2>"]

} Internally the inverse of the human-friendly field_map is computed (and cached) upon object creation and used as a “mapped model” {

"<new_field1>" => ["<source_field1>", "<source_field2>"],
"<new_field2>" => ["<source_field2>"]

}

Constants

Options

Public Class Methods

default_opts() click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 44
def self.default_opts
  Options.new({}, false, false, false)
end
new(**options) click to toggle source

Constructor keyword options (all other kwargs are ignored): field_map - hash describing the desired field mapping in the form:

{"<source_field>" => "<new_field>", ...}
where `new_field` is a string or array of strings

field_map_strict - boolean if true will only include new fields

defined in `field_map`

fields_strip_underscores - boolean if true will strip all leading

underscores from non-mapped fields

fields_lowercase - boolean if true lowercase all non-mapped fields

raises `Fluent::ConfigError` for invalid options

# File lib/fluent/plugin/systemd/entry_mutator.rb, line 59
def initialize(**options)
  @opts = options_from_hash(options)
  validate_options(@opts)
  @map = invert_field_map(@opts.field_map)
  @map_src_fields = @opts.field_map.keys
  @no_transform = @opts == self.class.default_opts
end

Public Instance Methods

format_fields(entry, mapped = nil) click to toggle source

Run field formatting (mutations applied to all non-mapped fields) against a single journal entry. Returns the mutated entry hash. entry - hash or `Systemd::Journal:Entry` mapped - Optional hash that represents a previously mapped entry to

which the formatted fields will be added
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 102
def format_fields(entry, mapped = nil)
  entry.each_with_object(mapped || {}) do |(fld, val), formatted_entry|
    # don't mess with explicitly mapped fields
    next if @map_src_fields.include?(fld)
    fld = format_field_name(fld)
    # account for mapping (appending) to an existing systemd field
    formatted_entry[fld] = join_if_needed([val, mapped[fld]])
  end
end
map_fields(entry) click to toggle source

Run field mapping against a single journal entry. Returns the mutated entry hash. entry - hash or `Systemd::Journal:Entry`

# File lib/fluent/plugin/systemd/entry_mutator.rb, line 89
def map_fields(entry)
  @map.each_with_object({}) do |(cstm, sysds), mapped|
    vals = sysds.collect { |fld| entry[fld] }.compact
    next if vals.empty? # systemd field does not exist in source entry
    mapped[cstm] = join_if_needed(vals)
  end
end
method_missing(sym, *args) click to toggle source

Expose config state as read-only instance properties of the mutator.

Calls superclass method
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 68
def method_missing(sym, *args)
  return @opts[sym] if @opts.members.include?(sym)
  super
end
respond_to_missing?(sym, include_private = false) click to toggle source
Calls superclass method
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 73
def respond_to_missing?(sym, include_private = false)
  @opts.members.include?(sym) || super
end
run(entry) click to toggle source

The main run method that performs all configured mutations, if any, against a single journal entry. Returns the mutated entry hash. entry - hash or `Systemd::Journal:Entry`

# File lib/fluent/plugin/systemd/entry_mutator.rb, line 80
def run(entry)
  return entry.to_h if @no_transform
  return map_fields(entry) if @opts.field_map_strict
  format_fields(entry, map_fields(entry))
end
warnings() click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 112
def warnings
  return [] unless field_map_strict && field_map.empty?
  '`field_map_strict` set to true with empty `field_map`, expect no fields'
end

Private Instance Methods

format_field_name(name) click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 125
def format_field_name(name)
  name = name.gsub(/\A_+/, '') if @opts.fields_strip_underscores
  name = name.downcase if @opts.fields_lowercase
  name
end
invert_field_map(field_map) click to toggle source

Compute the inverse of a human friendly field map `field_map` which is what the mutator uses for the actual mapping. The resulting structure for the inverse field map hash is: {“<new_field_name>” => [“<source_field_name>”, …], …}

# File lib/fluent/plugin/systemd/entry_mutator.rb, line 164
def invert_field_map(field_map)
  invs = {}
  field_map.values.flatten.uniq.each do |cstm|
    sysds = field_map.select { |_, v| (v == cstm || v.include?(cstm)) }
    invs[cstm] = sysds.keys
  end
  invs
end
join_if_needed(values) click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 119
def join_if_needed(values)
  values.compact!
  return values.first if values.length == 1
  values.join(' ')
end
options_from_hash(opts) click to toggle source

Returns a `SystemdEntryMutator::Options` struct derived from the elements in the supplied hash merged with the option defaults

# File lib/fluent/plugin/systemd/entry_mutator.rb, line 133
def options_from_hash(opts)
  merged = self.class.default_opts
  merged.each_pair do |k, _|
    merged[k] = opts[k] if opts.key?(k)
  end
  merged
end
validate_all_strings(arr, message, allow_nesting = false) click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 149
def validate_all_strings(arr, message, allow_nesting = false)
  valid = arr.all? do |value|
    value.is_a?(String) || allow_nesting && value.is_a?(Array) && value.all? { |key| key.is_a?(String) }
  end
  raise Fluent::ConfigError, message unless valid
end
validate_boolean(value, name) click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 156
def validate_boolean(value, name)
  raise Fluent::ConfigError, "`#{name}` must be boolean" unless [true, false].include?(value)
end
validate_options(opts) click to toggle source
# File lib/fluent/plugin/systemd/entry_mutator.rb, line 141
def validate_options(opts)
  validate_all_strings opts[:field_map].keys, '`field_map` keys must be strings'
  validate_all_strings opts[:field_map].values, '`field_map` values must be strings or an array of strings', true
  %i[field_map_strict fields_strip_underscores fields_lowercase].each do |opt|
    validate_boolean opts[opt], opt
  end
end