class Puppet::Util::Log

Pass feedback to the user. Log levels are modeled after syslog’s, and it is expected that that will be the most common log destination. Supports multiple destinations, one of which is a remote server.

Public Instance Methods

initialize_from_hash(data) click to toggle source
# File lib/puppet/util/log.rb, line 270
def initialize_from_hash(data)
  @level = data['level'].intern
  @message = data['message']
  @source = data['source']
  @tags = Puppet::Util::TagSet.new(data['tags'])
  @time = data['time']
  if @time.is_a? String
    @time = Time.parse(@time)
  end
  # Don't add these unless defined (preserve 3.x API as much as possible)
  %w(file line pos issue_code environment node backtrace).each do |name|
    next unless value = data[name]
    send(name + '=', value)
  end
end
level=(level) click to toggle source
# File lib/puppet/util/log.rb, line 326
def level=(level)
  raise ArgumentError, "Puppet::Util::Log requires a log level" unless level
  raise ArgumentError, "Puppet::Util::Log requires a symbol or string" unless level.respond_to? "to_sym"
  @level = level.to_sym
  raise ArgumentError, "Invalid log level #{@level}" unless self.class.validlevel?(@level)

  # Tag myself with my log level
  tag(level)
end
message=(msg) click to toggle source
# File lib/puppet/util/log.rb, line 321
def message=(msg)
  raise ArgumentError, "Puppet::Util::Log requires a message" unless msg
  @message = msg.to_s
end
source=(source) click to toggle source

If they pass a source in to us, we make sure it is a string, and we retrieve any tags we can.

# File lib/puppet/util/log.rb, line 338
def source=(source)
  if defined?(Puppet::Type) && source.is_a?(Puppet::Type)
    @source = source.path
    source.tags.each { |t| tag(t) }
    self.file = source.file
    self.line = source.line
  else
    @source = source.to_s
  end
end
to_data_hash() click to toggle source
# File lib/puppet/util/log.rb, line 290
def to_data_hash
  {
    'level' => @level,
    'message' => to_s,
    'source' => @source,
    'tags' => @tags.to_a,
    'time' => @time.iso8601(9),
    'file' => @file,
    'line' => @line,
  }
end
to_hash() click to toggle source
# File lib/puppet/util/log.rb, line 286
def to_hash
  self.to_data_hash
end
to_pson(*args) click to toggle source
# File lib/puppet/util/log.rb, line 317
def to_pson(*args)
  to_data_hash.to_pson(*args)
end
to_report() click to toggle source
# File lib/puppet/util/log.rb, line 349
def to_report
  "#{time} #{source} (#{level}): #{to_s}"
end
to_s() click to toggle source
# File lib/puppet/util/log.rb, line 353
def to_s
  msg = message

  # Issue based messages do not have details in the message. It
  # must be appended here
  unless issue_code.nil?
    msg = "Could not parse for environment #{environment}: #{msg}" unless environment.nil?
    if file && line && pos
      msg = "#{msg} at #{file}:#{line}:#{pos}"
    elsif file and line
      msg = "#{msg}  at #{file}:#{line}"
    elsif line && pos
      msg = "#{msg}  at line #{line}:#{pos}"
    elsif line
      msg = "#{msg}  at line #{line}"
    elsif file
      msg = "#{msg}  in #{file}"
    end
    msg = "#{msg} on node #{node}" unless node.nil?
    if @backtrace.is_a?(Array)
      msg += "\n"
      msg += @backtrace.join("\n")
    end
  end
  msg
end
to_structured_hash() click to toggle source
# File lib/puppet/util/log.rb, line 302
def to_structured_hash
  hash = {
    'level' => @level,
    'message' => @message,
    'source' => @source,
    'tags' => @tags.to_a,
    'time' => @time.iso8601(9),
  }
  %w(file line pos issue_code environment node backtrace).each do |name|
    attr_name = "@#{name}"
    hash[name] = instance_variable_get(attr_name) if instance_variable_defined?(attr_name)
  end
  hash
end

Public Class Methods

autoflush=(v) click to toggle source
# File lib/puppet/util/log.rb, line 73
def Log.autoflush=(v)
  @destinations.each do |type, dest|
    dest.autoflush = v if dest.respond_to?(:autoflush=)
  end
end
close(destination) click to toggle source

Reset log to basics. Basically just flushes and closes files and undefs other objects.

# File lib/puppet/util/log.rb, line 51
def Log.close(destination)
  if @destinations.include?(destination)
    @destinations[destination].flush if @destinations[destination].respond_to?(:flush)
    @destinations[destination].close if @destinations[destination].respond_to?(:close)
    @destinations.delete(destination)
  end
end
close_all() click to toggle source
# File lib/puppet/util/log.rb, line 59
def self.close_all
  destinations.keys.each { |dest|
    close(dest)
  }
  raise Puppet::DevError.new("Log.close_all failed to close #{@destinations.keys.inspect}") if !@destinations.empty?
end
create(hash) click to toggle source

Create a new log message. The primary role of this method is to avoid creating log messages below the loglevel.

# File lib/puppet/util/log.rb, line 81
def Log.create(hash)
  raise Puppet::DevError, "Logs require a level" unless hash.include?(:level)
  raise Puppet::DevError, "Invalid log level #{hash[:level]}" unless @levels.index(hash[:level])
  @levels.index(hash[:level]) >= @loglevel ? Puppet::Util::Log.new(hash) : nil
end
destinations() click to toggle source
# File lib/puppet/util/log.rb, line 87
def Log.destinations
  @destinations
end
eachlevel() { |level| ... } click to toggle source

Yield each valid level in turn

# File lib/puppet/util/log.rb, line 92
def Log.eachlevel
  @levels.each { |level| yield level }
end
flush() click to toggle source

Flush any log destinations that support such operations.

# File lib/puppet/util/log.rb, line 67
def Log.flush
  @destinations.each { |type, dest|
    dest.flush if dest.respond_to?(:flush)
  }
end
flushqueue() click to toggle source
# File lib/puppet/util/log.rb, line 178
def Log.flushqueue
  return unless @destinations.size >= 1
  @queued.each do |msg|
    Log.newmessage(msg)
  end
  @queued.clear
end
force_flushqueue() click to toggle source

Flush the logging queue. If there are no destinations available,

adds in a console logger before flushing the queue.

This is mainly intended to be used as a last-resort attempt

to ensure that logging messages are not thrown away before
the program is about to exit--most likely in a horrific
error scenario.

@return nil

# File lib/puppet/util/log.rb, line 193
def Log.force_flushqueue()
  if (@destinations.empty? and !(@queued.empty?))
    newdestination(:console)
  end
  flushqueue
end
from_data_hash(data) click to toggle source
# File lib/puppet/util/log.rb, line 236
def self.from_data_hash(data)
  obj = allocate
  obj.initialize_from_hash(data)
  obj
end
from_pson(data) click to toggle source
# File lib/puppet/util/log.rb, line 242
def self.from_pson(data)
  Puppet.deprecation_warning("from_pson is being removed in favour of from_data_hash.")
  self.from_data_hash(data)
end
level() click to toggle source

Return the current log level.

# File lib/puppet/util/log.rb, line 97
def Log.level
  @levels[@loglevel]
end
level=(level) click to toggle source

Set the current log level.

# File lib/puppet/util/log.rb, line 102
def Log.level=(level)
  level = level.intern unless level.is_a?(Symbol)

  raise Puppet::DevError, "Invalid loglevel #{level}" unless @levels.include?(level)

  @loglevel = @levels.index(level)
end
levels() click to toggle source
# File lib/puppet/util/log.rb, line 110
def Log.levels
  @levels.dup
end
new(args) click to toggle source
# File lib/puppet/util/log.rb, line 250
def initialize(args)
  self.level = args[:level]
  self.message = args[:message]
  self.source = args[:source] || "Puppet"

  @time = Time.now

  if tags = args[:tags]
    tags.each { |t| self.tag(t) }
  end

  # Don't add these unless defined (preserve 3.x API as much as possible)
  [:file, :line, :pos, :issue_code, :environment, :node, :backtrace].each do |attr|
    next unless value = args[attr]
    send(attr.to_s + '=', value)
  end

  Log.newmessage(self)
end
newdestination(dest) click to toggle source

Create a new log destination.

# File lib/puppet/util/log.rb, line 115
def Log.newdestination(dest)
  # Each destination can only occur once.
  if @destinations.find { |name, obj| obj.name == dest }
    return
  end

  name, type = @desttypes.find do |name, klass|
    klass.match?(dest)
  end

  if type.respond_to?(:suitable?) and not type.suitable?(dest)
    return
  end

  raise Puppet::DevError, "Unknown destination type #{dest}" unless type

  begin
    if type.instance_method(:initialize).arity == 1
      @destinations[dest] = type.new(dest)
    else
      @destinations[dest] = type.new
    end
    flushqueue
    @destinations[dest]
  rescue => detail
    Puppet.log_exception(detail)

    # If this was our only destination, then add the console back in.
    newdestination(:console) if @destinations.empty? and (dest != :console and dest != "console")
  end
end
newdesttype(name, options = {}, &block) click to toggle source

Create a new destination type.

# File lib/puppet/util/log.rb, line 20
def self.newdesttype(name, options = {}, &block)

  dest = genclass(
    name,
    :parent     => Puppet::Util::Log::Destination,
    :prefix     => "Dest",
    :block      => block,
    :hash       => @desttypes,
    :attributes => options
  )
  dest.match(dest.name)

  dest
end
newmessage(msg) click to toggle source

Route the actual message. FIXME There are lots of things this method should do, like caching and a bit more. It’s worth noting that there’s a potential for a loop here, if the machine somehow gets the destination set as itself.

# File lib/puppet/util/log.rb, line 164
def Log.newmessage(msg)
  return if @levels.index(msg.level) < @loglevel

  queuemessage(msg) if @destinations.length == 0

  @destinations.each do |name, dest|
    dest.handle(msg)
  end
end
queuemessage(msg) click to toggle source
# File lib/puppet/util/log.rb, line 174
def Log.queuemessage(msg)
  @queued.push(msg)
end
reopen() click to toggle source

Reopen all of our logs.

# File lib/puppet/util/log.rb, line 205
def Log.reopen
  Puppet.notice "Reopening log files"
  types = @destinations.keys
  @destinations.each { |type, dest|
    dest.close if dest.respond_to?(:close)
  }
  @destinations.clear
  # We need to make sure we always end up with some kind of destination
  begin
    types.each { |type|
      Log.newdestination(type)
    }
  rescue => detail
    if @destinations.empty?
      Log.setup_default
      Puppet.err detail.to_s
    end
  end
end
sendlevel?(level) click to toggle source
# File lib/puppet/util/log.rb, line 200
def Log.sendlevel?(level)
  @levels.index(level) >= @loglevel
end
setup_default() click to toggle source
# File lib/puppet/util/log.rb, line 225
def self.setup_default
  Log.newdestination(
    (Puppet.features.syslog?   ? :syslog   :
    (Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog])))
end
validlevel?(level) click to toggle source

Is the passed level a valid log level?

# File lib/puppet/util/log.rb, line 232
def self.validlevel?(level)
  @levels.include?(level)
end
with_destination(destination) { || ... } click to toggle source
# File lib/puppet/util/log.rb, line 147
def Log.with_destination(destination, &block)
  if @destinations.include?(destination)
    yield
  else
    newdestination(destination)
    begin
      yield
    ensure
      close(destination)
    end
  end
end