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.
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 204 def Log.flushqueue 205 return unless @destinations.size >= 1 206 @queued.each do |msg| 207 Log.newmessage(msg) 208 end 209 @queued.clear 210 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 219 def Log.force_flushqueue() 220 if (@destinations.empty? and !(@queued.empty?)) 221 newdestination(:console) 222 end 223 flushqueue 224 end
# File lib/puppet/util/log.rb 262 def self.from_data_hash(data) 263 obj = allocate 264 obj.initialize_from_hash(data) 265 obj 266 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 raise Puppet::DevError, _("Invalid loglevel %{level}") % { level: level } unless @levels.include?(level) 110 111 @loglevel = @levels.index(level) 112 113 # Enable or disable Facter debugging 114 Facter.debugging(level == :debug) if Facter.respond_to? :debugging 115 end
# File lib/puppet/util/log.rb 117 def Log.levels 118 @levels.dup 119 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 274 def self.log_func(scope, level, vals) 275 # NOTE: 3x, does this: vals.join(" ") 276 # New implementation uses the evaluator to get proper formatting per type 277 vals = vals.map { |v| Puppet::Pops::Evaluator::EvaluatorImpl.new.string(v, scope) } 278 279 # Bypass Puppet.<level> call since it picks up source from "self" which is not applicable in the 4x 280 # Function API. 281 # TODO: When a function can obtain the file, line, pos of the call merge those in (3x supports 282 # options :file, :line. (These were never output when calling the 3x logging functions since 283 # 3x scope does not know about the calling location at that detailed level, nor do they 284 # appear in a report to stdout/error when included). Now, the output simply uses scope (like 3x) 285 # as this is good enough, but does not reflect the true call-stack, but is a rough estimate 286 # of where the logging call originates from). 287 # 288 Puppet::Util::Log.create({:level => level, :source => scope, :message => vals.join(" ")}) 289 nil 290 end
# File lib/puppet/util/log.rb 296 def initialize(args) 297 self.level = args[:level] 298 self.message = args[:message] 299 self.source = args[:source] || "Puppet" 300 301 @time = Time.now 302 303 tags = args[:tags] 304 if tags 305 tags.each { |t| self.tag(t) } 306 end 307 308 # Don't add these unless defined (preserve 3.x API as much as possible) 309 [:file, :line, :pos, :issue_code, :environment, :node, :backtrace].each do |attr| 310 value = args[attr] 311 next unless value 312 send(attr.to_s + '=', value) 313 end 314 315 Log.newmessage(self) 316 end
Create a new log destination.
# File lib/puppet/util/log.rb 122 def Log.newdestination(dest) 123 # Each destination can only occur once. 124 if @destinations.find { |name, obj| obj.name == dest } 125 return 126 end 127 128 _, type = @desttypes.find do |name, klass| 129 klass.match?(dest) 130 end 131 132 if type.respond_to?(:suitable?) and not type.suitable?(dest) 133 return 134 end 135 136 raise Puppet::DevError, _("Unknown destination type %{dest}") % { dest: dest} unless type 137 138 begin 139 if type.instance_method(:initialize).arity == 1 140 @destinations[dest] = type.new(dest) 141 else 142 @destinations[dest] = type.new 143 end 144 flushqueue 145 @destinations[dest] 146 rescue => detail 147 Puppet.log_exception(detail) 148 149 # If this was our only destination, then add the console back in. 150 if destinations.empty? && dest.intern != :console 151 newdestination(:console) 152 end 153 154 # Re-raise (end exit Puppet) because we could not set up logging correctly. 155 raise detail 156 end 157 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 187 def Log.newmessage(msg) 188 return if @levels.index(msg.level) < @loglevel 189 190 msg.message = coerce_string(msg.message) 191 msg.source = coerce_string(msg.source) 192 193 queuemessage(msg) if @destinations.length == 0 194 195 @destinations.each do |name, dest| 196 dest.handle(msg) 197 end 198 end
# File lib/puppet/util/log.rb 200 def Log.queuemessage(msg) 201 @queued.push(msg) 202 end
Reopen all of our logs.
# File lib/puppet/util/log.rb 231 def Log.reopen 232 Puppet.notice _("Reopening log files") 233 types = @destinations.keys 234 @destinations.each { |type, dest| 235 dest.close if dest.respond_to?(:close) 236 } 237 @destinations.clear 238 # We need to make sure we always end up with some kind of destination 239 begin 240 types.each { |type| 241 Log.newdestination(type) 242 } 243 rescue => detail 244 if @destinations.empty? 245 Log.setup_default 246 Puppet.err detail.to_s 247 end 248 end 249 end
# File lib/puppet/util/log.rb 226 def Log.sendlevel?(level) 227 @levels.index(level) >= @loglevel 228 end
# File lib/puppet/util/log.rb 251 def self.setup_default 252 Log.newdestination( 253 (Puppet.features.syslog? ? :syslog : 254 (Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog]))) 255 end
Is the passed level a valid log level?
# File lib/puppet/util/log.rb 258 def self.validlevel?(level) 259 @levels.include?(level) 260 end
# File lib/puppet/util/log.rb 159 def Log.with_destination(destination, &block) 160 if @destinations.include?(destination) 161 yield 162 else 163 newdestination(destination) 164 begin 165 yield 166 ensure 167 close(destination) 168 end 169 end 170 end
Private Class Methods
# File lib/puppet/util/log.rb 172 def Log.coerce_string(str) 173 return Puppet::Util::CharacterEncoding.convert_to_utf_8(str) if str.valid_encoding? 174 175 # We only select the last 10 callers in the stack to avoid being spammy 176 message = _("Received a Log attribute with invalid encoding:%{log_message}") % 177 { log_message: Puppet::Util::CharacterEncoding.convert_to_utf_8(str.dump)} 178 message += '\n' + _("Backtrace:\n%{backtrace}") % { backtrace: caller(1, 10).join("\n") } 179 message 180 end
Public Instance Methods
# File lib/puppet/util/log.rb 318 def initialize_from_hash(data) 319 @level = data['level'].intern 320 @message = data['message'] 321 @source = data['source'] 322 @tags = Puppet::Util::TagSet.new(data['tags']) 323 @time = data['time'] 324 if @time.is_a? String 325 @time = Time.parse(@time) 326 end 327 # Don't add these unless defined (preserve 3.x API as much as possible) 328 %w(file line pos issue_code environment node backtrace).each do |name| 329 value = data[name] 330 next unless value 331 send(name + '=', value) 332 end 333 end
# File lib/puppet/util/log.rb 372 def level=(level) 373 #TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class 374 raise ArgumentError, _("Puppet::Util::Log requires a log level") unless level 375 #TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class 376 raise ArgumentError, _("Puppet::Util::Log requires a symbol or string") unless level.respond_to? "to_sym" 377 @level = level.to_sym 378 raise ArgumentError, _("Invalid log level %{level}") % { level: @level } unless self.class.validlevel?(@level) 379 380 # Tag myself with my log level 381 tag(level) 382 end
# File lib/puppet/util/log.rb 366 def message=(msg) 367 #TRANSLATORS 'Puppet::Util::Log' refers to a Puppet source code class 368 raise ArgumentError, _("Puppet::Util::Log requires a message") unless msg 369 @message = msg.to_s 370 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 386 def source=(source) 387 if defined?(Puppet::Type) && source.is_a?(Puppet::Type) 388 @source = source.path 389 merge_tags_from(source) 390 self.file = source.file 391 self.line = source.line 392 else 393 @source = source.to_s 394 end 395 end
# File lib/puppet/util/log.rb 339 def to_data_hash 340 { 341 'level' => @level.to_s, 342 'message' => to_s, 343 'source' => @source, 344 'tags' => @tags.to_a, 345 'time' => @time.iso8601(9), 346 'file' => @file, 347 'line' => @line, 348 } 349 end
# File lib/puppet/util/log.rb 335 def to_hash 336 self.to_data_hash 337 end
# File lib/puppet/util/log.rb 397 def to_report 398 "#{time} #{source} (#{level}): #{self}" 399 end
# File lib/puppet/util/log.rb 401 def to_s 402 msg = message 403 404 # Issue based messages do not have details in the message. It 405 # must be appended here 406 unless issue_code.nil? 407 msg = _("Could not parse for environment %{environment}: %{msg}") % { environment: environment, msg: msg } unless environment.nil? 408 msg += Puppet::Util::Errors.error_location_with_space(file, line, pos) 409 msg = _("%{msg} on node %{node}") % { msg: msg, node: node } unless node.nil? 410 if @backtrace.is_a?(Array) 411 msg += "\n" 412 msg += @backtrace.join("\n") 413 end 414 end 415 msg 416 end
# File lib/puppet/util/log.rb 351 def to_structured_hash 352 hash = { 353 'level' => @level, 354 'message' => @message, 355 'source' => @source, 356 'tags' => @tags.to_a, 357 'time' => @time.iso8601(9), 358 } 359 %w(file line pos issue_code environment node backtrace).each do |name| 360 attr_name = "@#{name}" 361 hash[name] = instance_variable_get(attr_name) if instance_variable_defined?(attr_name) 362 end 363 hash 364 end