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

desttypes[R]
backtrace[RW]
environment[RW]
file[RW]
issue_code[RW]
level[R]
line[RW]
message[R]
node[RW]
pos[RW]
remote[RW]
source[RW]
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
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
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
219 def Log.force_flushqueue()
220   if (@destinations.empty? and !(@queued.empty?))
221     newdestination(:console)
222   end
223   flushqueue
224 end
from_data_hash(data) click to toggle source
    # 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
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   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
levels() click to toggle source
    # File lib/puppet/util/log.rb
117 def Log.levels
118   @levels.dup
119 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
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
new(args) click to toggle source
    # 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
newdestination(dest) click to toggle source

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

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

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

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

initialize_from_hash(data) click to toggle source
    # 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
level=(level) click to toggle source
    # 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
message=(msg) click to toggle source
    # 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
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
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
to_data_hash() click to toggle source
    # 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
to_hash() click to toggle source
    # File lib/puppet/util/log.rb
335 def to_hash
336   self.to_data_hash
337 end
to_report() click to toggle source
    # File lib/puppet/util/log.rb
397 def to_report
398   "#{time} #{source} (#{level}): #{self}"
399 end
to_s() click to toggle source
    # 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
to_structured_hash() click to toggle source
    # 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