Class: Puppet::Util::Log

Inherits:
Object show all
Extended by:
Puppet::Util, ClassGen
Includes:
Network::FormatSupport, Puppet::Util, PsychSupport, Tagging
Defined in:
lib/puppet/util/log.rb

Overview

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.

Defined Under Namespace

Classes: Destination

Constant Summary

Constants included from Puppet::Util

AbsolutePathPosix, AbsolutePathWindows, DEFAULT_POSIX_MODE, DEFAULT_WINDOWS_MODE, RFC_3986_URI_REGEX

Constants included from POSIX

POSIX::LOCALE_ENV_VARS, POSIX::USER_ENV_VARS

Constants included from SymbolicFileMode

SymbolicFileMode::SetGIDBit, SymbolicFileMode::SetUIDBit, SymbolicFileMode::StickyBit, SymbolicFileMode::SymbolicMode, SymbolicFileMode::SymbolicSpecialToBit

Constants included from Tagging

Tagging::ValidTagRegex

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Puppet::Util

absolute_path?, benchmark, chuser, clear_environment, default_env, deterministic_rand, deterministic_rand_int, exit_on_fail, get_env, get_environment, logmethods, merge_environment, path_to_uri, pretty_backtrace, replace_file, safe_posix_fork, set_env, symbolizehash, thinmark, uri_encode, uri_query_encode, uri_to_path, which, withenv, withumask

Methods included from POSIX

#get_posix_field, #gid, groups_of, #idfield, #methodbyid, #methodbyname, #search_posix_field, #uid

Methods included from SymbolicFileMode

#normalize_symbolic_mode, #symbolic_mode_to_int, #valid_symbolic_mode?

Methods included from ClassGen

genclass, genmodule, rmclass

Methods included from Network::FormatSupport

included, #mime, #render, #support_format?, #to_json, #to_msgpack, #to_pson

Methods included from Tagging

#merge_into, #merge_tags_from, #raw_tagged?, #set_tags, #tag, #tag_if_valid, #tagged?, #tags, #tags=, #valid_tag?

Methods included from PsychSupport

#encode_with, #init_with

Constructor Details

#initialize(args) ⇒ Log

Returns a new instance of Log.



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/puppet/util/log.rb', line 296

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

Class Attribute Details

.desttypesObject (readonly)

Returns the value of attribute desttypes.



49
50
51
# File 'lib/puppet/util/log.rb', line 49

def desttypes
  @desttypes
end

Instance Attribute Details

#backtraceObject

Returns the value of attribute backtrace.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def backtrace
  @backtrace
end

#environmentObject

Returns the value of attribute environment.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def environment
  @environment
end

#fileObject

Returns the value of attribute file.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def file
  @file
end

#issue_codeObject

Returns the value of attribute issue_code.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def issue_code
  @issue_code
end

#levelObject

Returns the value of attribute level.



294
295
296
# File 'lib/puppet/util/log.rb', line 294

def level
  @level
end

#lineObject

Returns the value of attribute line.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def line
  @line
end

#messageObject

Returns the value of attribute message.



294
295
296
# File 'lib/puppet/util/log.rb', line 294

def message
  @message
end

#nodeObject

Returns the value of attribute node.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def node
  @node
end

#posObject

Returns the value of attribute pos.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def pos
  @pos
end

#remoteObject

Returns the value of attribute remote.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def remote
  @remote
end

#sourceObject

Returns the value of attribute source.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def source
  @source
end

#timeObject

Returns the value of attribute time.



293
294
295
# File 'lib/puppet/util/log.rb', line 293

def time
  @time
end

Class Method Details

.autoflush=(v) ⇒ Object



77
78
79
80
81
# File 'lib/puppet/util/log.rb', line 77

def Log.autoflush=(v)
  @destinations.each do |type, dest|
    dest.autoflush = v if dest.respond_to?(:autoflush=)
  end
end

.close(destination) ⇒ Object

Reset log to basics. Basically just flushes and closes files and undefs other objects.



54
55
56
57
58
59
60
# File 'lib/puppet/util/log.rb', line 54

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

.close_allObject

Raises:



62
63
64
65
66
67
68
# File 'lib/puppet/util/log.rb', line 62

def self.close_all
  @destinations.keys.each { |dest|
    close(dest)
  }
  #TRANSLATORS "Log.close_all" is a method name and should not be translated
  raise Puppet::DevError.new(_("Log.close_all failed to close %{destinations}") % { destinations: @destinations.keys.inspect }) if !@destinations.empty?
end

.create(hash) ⇒ Object

Create a new log message. The primary role of this method is to avoid creating log messages below the loglevel.

Raises:



85
86
87
88
89
# File 'lib/puppet/util/log.rb', line 85

def Log.create(hash)
  raise Puppet::DevError, _("Logs require a level") unless hash.include?(:level)
  raise Puppet::DevError, _("Invalid log level %{level}") % { level: hash[:level] } unless @levels.index(hash[:level])
  @levels.index(hash[:level]) >= @loglevel ? Puppet::Util::Log.new(hash) : nil
end

.destinationsObject



91
92
93
# File 'lib/puppet/util/log.rb', line 91

def Log.destinations
  @destinations
end

.eachlevelObject

Yield each valid level in turn



96
97
98
# File 'lib/puppet/util/log.rb', line 96

def Log.eachlevel
  @levels.each { |level| yield level }
end

.flushObject

Flush any log destinations that support such operations.



71
72
73
74
75
# File 'lib/puppet/util/log.rb', line 71

def Log.flush
  @destinations.each { |type, dest|
    dest.flush if dest.respond_to?(:flush)
  }
end

.flushqueueObject



204
205
206
207
208
209
210
# File 'lib/puppet/util/log.rb', line 204

def Log.flushqueue
  return unless @destinations.size >= 1
  @queued.each do |msg|
    Log.newmessage(msg)
  end
  @queued.clear
end

.force_flushqueueObject

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.

Returns:

  • nil



219
220
221
222
223
224
# File 'lib/puppet/util/log.rb', line 219

def Log.force_flushqueue()
  if (@destinations.empty? and !(@queued.empty?))
    newdestination(:console)
  end
  flushqueue
end

.from_data_hash(data) ⇒ Object



262
263
264
265
266
# File 'lib/puppet/util/log.rb', line 262

def self.from_data_hash(data)
  obj = allocate
  obj.initialize_from_hash(data)
  obj
end

.levelObject

Return the current log level.



101
102
103
# File 'lib/puppet/util/log.rb', line 101

def Log.level
  @levels[@loglevel]
end

.level=(level) ⇒ Object

Set the current log level.

Raises:



106
107
108
109
110
111
112
113
114
115
# File 'lib/puppet/util/log.rb', line 106

def Log.level=(level)
  level = level.intern unless level.is_a?(Symbol)

  raise Puppet::DevError, _("Invalid loglevel %{level}") % { level: level } unless @levels.include?(level)

  @loglevel = @levels.index(level)

  # Enable or disable Facter debugging
  Facter.debugging(level == :debug) if Facter.respond_to? :debugging
end

.levelsObject



117
118
119
# File 'lib/puppet/util/log.rb', line 117

def Log.levels
  @levels.dup
end

.log_func(scope, level, vals) ⇒ Object

Log output using scope and level

Parameters:

  • scope (Puppet::Parser::Scope)
  • level (Symbol)

    log level

  • vals (Array<Object>)

    the values to log (will be converted to string and joined with space)



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/puppet/util/log.rb', line 274

def self.log_func(scope, level, vals)
  # NOTE: 3x, does this: vals.join(" ")
  # New implementation uses the evaluator to get proper formatting per type
  vals = vals.map { |v| Puppet::Pops::Evaluator::EvaluatorImpl.new.string(v, scope) }

  # Bypass Puppet.<level> call since it picks up source from "self" which is not applicable in the 4x
  # Function API.
  # TODO: When a function can obtain the file, line, pos of the call merge those in (3x supports
  #       options :file, :line. (These were never output when calling the 3x logging functions since
  #       3x scope does not know about the calling location at that detailed level, nor do they
  #       appear in a report to stdout/error when included). Now, the output simply uses scope (like 3x)
  #       as this is good enough, but does not reflect the true call-stack, but is a rough estimate
  #       of where the logging call originates from).
  #
  Puppet::Util::Log.create({:level => level, :source => scope, :message => vals.join(" ")})
  nil
end

.newdestination(dest) ⇒ Object

Create a new log destination.

Raises:



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/puppet/util/log.rb', line 122

def Log.newdestination(dest)
  # Each destination can only occur once.
  if @destinations.find { |name, obj| obj.name == dest }
    return
  end

  _, 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}") % { dest: 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.
    if destinations.empty? && dest.intern != :console
      newdestination(:console)
    end

    # Re-raise (end exit Puppet) because we could not set up logging correctly.
    raise detail
  end
end

.newdesttype(name, options = {}, &block) ⇒ Object

Create a new destination type.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/puppet/util/log.rb', line 23

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

.newmessage(msg) ⇒ Object

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.



187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/puppet/util/log.rb', line 187

def Log.newmessage(msg)
  return if @levels.index(msg.level) < @loglevel

  msg.message = coerce_string(msg.message)
  msg.source = coerce_string(msg.source)

  queuemessage(msg) if @destinations.length == 0

  @destinations.each do |name, dest|
    dest.handle(msg)
  end
end

.queuemessage(msg) ⇒ Object



200
201
202
# File 'lib/puppet/util/log.rb', line 200

def Log.queuemessage(msg)
  @queued.push(msg)
end

.reopenObject

Reopen all of our logs.



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/puppet/util/log.rb', line 231

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

.sendlevel?(level) ⇒ Boolean

Returns:

  • (Boolean)


226
227
228
# File 'lib/puppet/util/log.rb', line 226

def Log.sendlevel?(level)
  @levels.index(level) >= @loglevel
end

.setup_defaultObject



251
252
253
254
255
# File 'lib/puppet/util/log.rb', line 251

def self.setup_default
  Log.newdestination(
    (Puppet.features.syslog?   ? :syslog   :
    (Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog])))
end

.validlevel?(level) ⇒ Boolean

Is the passed level a valid log level?

Returns:

  • (Boolean)


258
259
260
# File 'lib/puppet/util/log.rb', line 258

def self.validlevel?(level)
  @levels.include?(level)
end

.with_destination(destination, &block) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/puppet/util/log.rb', line 159

def Log.with_destination(destination, &block)
  if @destinations.include?(destination)
    yield
  else
    newdestination(destination)
    begin
      yield
    ensure
      close(destination)
    end
  end
end

Instance Method Details

#initialize_from_hash(data) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/puppet/util/log.rb', line 316

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

#to_data_hashObject



336
337
338
339
340
341
342
343
344
345
346
# File 'lib/puppet/util/log.rb', line 336

def to_data_hash
  {
    'level' => @level.to_s,
    'message' => to_s,
    'source' => @source,
    'tags' => @tags.to_a,
    'time' => @time.iso8601(9),
    'file' => @file,
    'line' => @line,
  }
end

#to_hashObject



332
333
334
# File 'lib/puppet/util/log.rb', line 332

def to_hash
  self.to_data_hash
end

#to_reportObject



394
395
396
# File 'lib/puppet/util/log.rb', line 394

def to_report
  "#{time} #{source} (#{level}): #{to_s}"
end

#to_sObject



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/puppet/util/log.rb', line 398

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}") % { environment: environment, msg: msg } unless environment.nil?
    msg += Puppet::Util::Errors.error_location_with_space(file, line, pos)
    msg = _("%{msg} on node %{node}") % { msg: msg, node: node } unless node.nil?
    if @backtrace.is_a?(Array)
      msg += "\n"
      msg += @backtrace.join("\n")
    end
  end
  msg
end

#to_structured_hashObject



348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/puppet/util/log.rb', line 348

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