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.
# 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
# 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
# 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
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
# 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
# File lib/puppet/util/log.rb, line 286 def to_hash self.to_data_hash end
# File lib/puppet/util/log.rb, line 317 def to_pson(*args) to_data_hash.to_pson(*args) end
# File lib/puppet/util/log.rb, line 349 def to_report "#{time} #{source} (#{level}): #{to_s}" end
# 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
# 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
# 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
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
# 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 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
# File lib/puppet/util/log.rb, line 87 def Log.destinations @destinations end
Yield each valid level in turn
# File lib/puppet/util/log.rb, line 92 def Log.eachlevel @levels.each { |level| yield level } end
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
# 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
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
# File lib/puppet/util/log.rb, line 236 def self.from_data_hash(data) obj = allocate obj.initialize_from_hash(data) obj end
# 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
Return the current log level.
# File lib/puppet/util/log.rb, line 97 def Log.level @levels[@loglevel] end
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
# File lib/puppet/util/log.rb, line 110 def Log.levels @levels.dup end
# 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
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
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
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
# File lib/puppet/util/log.rb, line 174 def Log.queuemessage(msg) @queued.push(msg) end
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
# File lib/puppet/util/log.rb, line 200 def Log.sendlevel?(level) @levels.index(level) >= @loglevel end
# 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
Is the passed level a valid log level?
# File lib/puppet/util/log.rb, line 232 def self.validlevel?(level) @levels.include?(level) end
# 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