class 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.
Attributes
Public Class Methods
# File lib/puppet/util/log.rb 77 def Log.autoflush=(v) 78 @destinations.each do |type, dest| 79 dest.autoflush = v if dest.respond_to?(:autoflush=) 80 end 81 end
Reset log to basics. Basically just flushes and closes files and undefs other objects.
# File lib/puppet/util/log.rb 54 def Log.close(destination) 55 if @destinations.include?(destination) 56 @destinations[destination].flush if @destinations[destination].respond_to?(:flush) 57 @destinations[destination].close if @destinations[destination].respond_to?(:close) 58 @destinations.delete(destination) 59 end 60 end
# File lib/puppet/util/log.rb 62 def self.close_all 63 @destinations.keys.each { |dest| 64 close(dest) 65 } 66 #TRANSLATORS "Log.close_all" is a method name and should not be translated 67 raise Puppet::DevError.new(_("Log.close_all failed to close %{destinations}") % { destinations: @destinations.keys.inspect }) if !@destinations.empty? 68 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 85 def Log.create(hash) 86 raise Puppet::DevError, _("Logs require a level") unless hash.include?(:level) 87 raise Puppet::DevError, _("Invalid log level %{level}") % { level: hash[:level] } unless @levels.index(hash[:level]) 88 @levels.index(hash[:level]) >= @loglevel ? Puppet::Util::Log.new(hash) : nil 89 end
# File lib/puppet/util/log.rb 91 def Log.destinations 92 @destinations 93 end
Yield each valid level in turn
# File lib/puppet/util/log.rb 96 def Log.eachlevel 97 @levels.each { |level| yield level } 98 end
Flush any log destinations that support such operations.
# File lib/puppet/util/log.rb 71 def Log.flush 72 @destinations.each { |type, dest| 73 dest.flush if dest.respond_to?(:flush) 74 } 75 end
# File lib/puppet/util/log.rb 209 def Log.flushqueue 210 return unless @destinations.size >= 1 211 @queued.each do |msg| 212 Log.newmessage(msg) 213 end 214 @queued.clear 215 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 224 def Log.force_flushqueue() 225 if (@destinations.empty? and !(@queued.empty?)) 226 newdestination(:console) 227 end 228 flushqueue 229 end
# File lib/puppet/util/log.rb 267 def self.from_data_hash(data) 268 obj = allocate 269 obj.initialize_from_hash(data) 270 obj 271 end
Return the current log level.
# File lib/puppet/util/log.rb 101 def Log.level 102 @levels[@loglevel] 103 end
Set the current log level.
# File lib/puppet/util/log.rb 106 def Log.level=(level) 107 level = level.intern unless level.is_a?(Symbol) 108 109 # loglevel is a 0-based index 110 loglevel = @levels.index(level) 111 raise Puppet::DevError, _("Invalid loglevel %{level}") % { level: level } unless loglevel 112 113 return if @loglevel == loglevel 114 115 # loglevel changed 116 @loglevel = loglevel 117 118 # Enable or disable Facter debugging 119 Puppet.runtime[:facter].debugging(level == :debug) 120 end
# File lib/puppet/util/log.rb 122 def Log.levels 123 @levels.dup 124 end
Log output using scope and level
@param [Puppet::Parser::Scope] scope @param [Symbol] level log level @param [Array<Object>] vals the values to log (will be converted to string and joined with space)
# File lib/puppet/util/log.rb 279 def self.log_func(scope, level, vals) 280 # NOTE: 3x, does this: vals.join(" ") 281 # New implementation uses the evaluator to get proper formatting per type 282 vals = vals.map { |v| Puppet::Pops::Evaluator::EvaluatorImpl.new.string(v, scope) } 283 284 # Bypass Puppet.<level> call since it picks up source from "self" which is not applicable in the 4x 285 # Function API. 286 # TODO: When a function can obtain the file, line, pos of the call merge those in (3x supports 287 # options :file, :line. (These were never output when calling the 3x logging functions since 288 # 3x scope does not know about the calling location at that detailed level, nor do they 289 # appear in a report to stdout/error when included). Now, the output simply uses scope (like 3x) 290 # as this is good enough, but does not reflect the true call-stack, but is a rough estimate 291 # of where the logging call originates from). 292 # 293 Puppet::Util::Log.create({:level => level, :source => scope, :message => vals.join(" ")}) 294 nil 295 end
# File lib/puppet/util/log.rb 301 def initialize(args) 302 self.level = args[:level] 303 self.message = args[:message] 304 self.source = args[:source] || "Puppet" 305 306 @time = Time.now 307 308 tags = args[:tags] 309 if tags 310 tags.each { |t| self.tag(t) } 311 end 312 313 # Don't add these unless defined (preserve 3.x API as much as possible) 314 [:file, :line, :pos, :issue_code, :environment, :node, :backtrace].each do |attr| 315 value = args[attr] 316 next unless value 317 send(attr.to_s + '=', value) 318 end 319 320 Log.newmessage(self) 321 end
Create a new log destination.
# File lib/puppet/util/log.rb 127 def Log.newdestination(dest) 128 # Each destination can only occur once. 129 if @destinations.find { |name, obj| obj.name == dest } 130 return 131 end 132 133 _, type = @desttypes.find do |name, klass| 134 klass.match?(dest) 135 end 136 137 if type.respond_to?(:suitable?) and not type.suitable?(dest) 138 return 139 end 140 141 raise Puppet::DevError, _("Unknown destination type %{dest}") % { dest: dest} unless type 142 143 begin 144 if type.instance_method(:initialize).arity == 1 145 @destinations[dest] = type.new(dest) 146 else 147 @destinations[dest] = type.new 148 end 149 flushqueue 150 @destinations[dest] 151 rescue => detail 152 Puppet.log_exception(detail) 153 154 # If this was our only destination, then add the console back in. 155 if destinations.empty? && dest.intern != :console 156 newdestination(:console) 157 end 158 159 # Re-raise (end exit Puppet) because we could not set up logging correctly. 160 raise detail 161 end 162 end
Create a new destination type.
# File lib/puppet/util/log.rb 23 def self.newdesttype(name, options = {}, &block) 24 25 dest = genclass( 26 name, 27 :parent => Puppet::Util::Log::Destination, 28 :prefix => "Dest", 29 :block => block, 30 :hash => @desttypes, 31 :attributes => options 32 ) 33 dest.match(dest.name) 34 35 dest 36 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 192 def Log.newmessage(msg) 193 return if @levels.index(msg.level) < @loglevel 194 195 msg.message = coerce_string(msg.message) 196 msg.source = coerce_string(msg.source) 197 198 queuemessage(msg) if @destinations.length == 0 199 200 @destinations.each do |name, dest| 201 dest.handle(msg) 202 end 203 end
# File lib/puppet/util/log.rb 205 def Log.queuemessage(msg) 206 @queued.push(msg) 207 end
Reopen all of our logs.
# File lib/puppet/util/log.rb 236 def Log.reopen 237 Puppet.notice _("Reopening log files") 238 types = @destinations.keys 239 @destinations.each { |type, dest| 240 dest.close if dest.respond_to?(:close) 241 } 242 @destinations.clear 243 # We need to make sure we always end up with some kind of destination 244 begin 245 types.each { |type| 246 Log.newdestination(type) 247 } 248 rescue => detail 249 if @destinations.empty? 250 Log.setup_default 251 Puppet.err detail.to_s 252 end 253 end 254 end
# File lib/puppet/util/log.rb 231 def Log.sendlevel?(level) 232 @levels.index(level) >= @loglevel 233 end
# File lib/puppet/util/log.rb 256 def self.setup_default 257 Log.newdestination( 258 (Puppet.features.syslog? ? :syslog : 259 (Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog]))) 260 end
Is the passed level a valid log level?
# File lib/puppet/util/log.rb 263 def self.validlevel?(level) 264 @levels.include?(level) 265 end
# File lib/puppet/util/log.rb 164 def Log.with_destination(destination, &block) 165 if @destinations.include?(destination) 166 yield 167 else 168 newdestination(destination) 169 begin 170 yield 171 ensure 172 close(destination) 173 end 174 end 175 end
Private Class Methods
# File lib/puppet/util/log.rb 177 def Log.coerce_string(str) 178 return Puppet::Util::CharacterEncoding.convert_to_utf_8(str) if str.valid_encoding? 179 180 # We only select the last 10 callers in the stack to avoid being spammy 181 message = _("Received a Log attribute with invalid encoding:%{log_message}") % 182 { log_message: Puppet::Util::CharacterEncoding.convert_to_utf_8(str.dump)} 183 message += '\n' + _("Backtrace:\n%{backtrace}") % { backtrace: caller(1, 10).join("\n") } 184 message 185 end
Public Instance Methods
# File lib/puppet/util/log.rb 323 def initialize_from_hash(data) 324 @level = data['level'].intern 325 @message = data['message'] 326 @source = data['source'] 327 @tags = Puppet::Util::TagSet.new(data['tags']) 328 @time = data['time'] 329 if @time.is_a? String 330 @time = Time.parse(@time) 331 end 332 # Don't add these unless defined (preserve 3.x API as much as possible) 333 %w(file line pos issue_code environment node backtrace).each do |name| 334 value = data[name] 335 next unless value 336 send(name + '=', value) 337 end 338 end
# File lib/puppet/util/log.rb 377 def level=(level) 378 #TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class 379 raise ArgumentError, _("Puppet::Util::Log requires a log level") unless level 380 #TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class 381 raise ArgumentError, _("Puppet::Util::Log requires a symbol or string") unless level.respond_to? "to_sym" 382 @level = level.to_sym 383 raise ArgumentError, _("Invalid log level %{level}") % { level: @level } unless self.class.validlevel?(@level) 384 385 # Tag myself with my log level 386 tag(level) 387 end
# File lib/puppet/util/log.rb 371 def message=(msg) 372 #TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class 373 raise ArgumentError, _("Puppet::Util::Log requires a message") unless msg 374 @message = msg.to_s 375 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 391 def source=(source) 392 if defined?(Puppet::Type) && source.is_a?(Puppet::Type) 393 @source = source.path 394 merge_tags_from(source) 395 self.file = source.file 396 self.line = source.line 397 else 398 @source = source.to_s 399 end 400 end
# File lib/puppet/util/log.rb 344 def to_data_hash 345 { 346 'level' => @level.to_s, 347 'message' => to_s, 348 'source' => @source, 349 'tags' => @tags.to_a, 350 'time' => @time.iso8601(9), 351 'file' => @file, 352 'line' => @line, 353 } 354 end
# File lib/puppet/util/log.rb 340 def to_hash 341 self.to_data_hash 342 end
# File lib/puppet/util/log.rb 402 def to_report 403 "#{time} #{source} (#{level}): #{self}" 404 end
# File lib/puppet/util/log.rb 406 def to_s 407 msg = message 408 409 # Issue based messages do not have details in the message. It 410 # must be appended here 411 unless issue_code.nil? 412 msg = _("Could not parse for environment %{environment}: %{msg}") % { environment: environment, msg: msg } unless environment.nil? 413 msg += Puppet::Util::Errors.error_location_with_space(file, line, pos) 414 msg = _("%{msg} on node %{node}") % { msg: msg, node: node } unless node.nil? 415 if @backtrace.is_a?(Array) 416 msg += "\n" 417 msg += @backtrace.join("\n") 418 end 419 end 420 msg 421 end
# File lib/puppet/util/log.rb 356 def to_structured_hash 357 hash = { 358 'level' => @level, 359 'message' => @message, 360 'source' => @source, 361 'tags' => @tags.to_a, 362 'time' => @time.iso8601(9), 363 } 364 %w(file line pos issue_code environment node backtrace).each do |name| 365 attr_name = "@#{name}" 366 hash[name] = instance_variable_get(attr_name) if instance_variable_defined?(attr_name) 367 end 368 hash 369 end