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

desttypes[R]
backtrace[RW]
environment[RW]
file[RW]
issue_code[RW]
level[R]
line[RW]
message[R]
node[RW]
pos[RW]
remote[RW]
source[R]
time[RW]

Public Class Methods

autoflush=(v) click to toggle source
   # 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
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
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
close_all() click to toggle source
   # 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(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
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
destinations() click to toggle source
   # File lib/puppet/util/log.rb
91 def Log.destinations
92   @destinations
93 end
eachlevel() { |level| ... } click to toggle source

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() click to toggle source

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
flushqueue() click to toggle source
    # 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
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
224 def Log.force_flushqueue()
225   if (@destinations.empty? and !(@queued.empty?))
226     newdestination(:console)
227   end
228   flushqueue
229 end
from_data_hash(data) click to toggle source
    # 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
level() click to toggle source

Return the current log level.

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

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
levels() click to toggle source
    # File lib/puppet/util/log.rb
122 def Log.levels
123   @levels.dup
124 end
log_func(scope, level, vals) click to toggle source

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
new(args) click to toggle source
    # 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
newdestination(dest) click to toggle source

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
newdesttype(name, options = {}, &block) click to toggle source

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
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
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
queuemessage(msg) click to toggle source
    # File lib/puppet/util/log.rb
205 def Log.queuemessage(msg)
206   @queued.push(msg)
207 end
reopen() click to toggle source

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
sendlevel?(level) click to toggle source
    # File lib/puppet/util/log.rb
231 def Log.sendlevel?(level)
232   @levels.index(level) >= @loglevel
233 end
setup_default() click to toggle source
    # 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
validlevel?(level) click to toggle source

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
with_destination(destination) { || ... } click to toggle source
    # 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

coerce_string(str) click to toggle source
    # 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

initialize_from_hash(data) click to toggle source
    # 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
level=(level) click to toggle source
    # 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
message=(msg) click to toggle source
    # 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
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
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
to_data_hash() click to toggle source
    # 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
to_hash() click to toggle source
    # File lib/puppet/util/log.rb
340 def to_hash
341   self.to_data_hash
342 end
to_report() click to toggle source
    # File lib/puppet/util/log.rb
402 def to_report
403   "#{time} #{source} (#{level}): #{self}"
404 end
to_s() click to toggle source
    # 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
to_structured_hash() click to toggle source
    # 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