1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Christopher Brown (<cb@chef.io>)
# Copyright:: Copyright (c) 2008-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "logger"
require "mixlib/log/version"
require "mixlib/log/formatter"
require "mixlib/log/child"
require "mixlib/log/logging"
require "mixlib/log/logger"
module Mixlib
module Log
include Logging
def reset!
@logger ||= nil
@loggers ||= []
close!
@logger = @loggers = nil
@metadata = {}
end
# An Array of log devices that will be logged to. Defaults to just the default
# \@logger log device, but you can push to this array to add more devices.
def loggers
@loggers ||= [logger]
end
##
# init always returns a configured logger
# and creates a new one if it doesn't yet exist
##
def logger
@logger ||= init
end
# Sets the log device to +new_log_device+. Any additional loggers
# that had been added to the +loggers+ array will be cleared.
def logger=(new_log_device)
reset!
@logger = new_log_device
end
def use_log_devices(other)
if other.respond_to?(:loggers) && other.respond_to?(:logger)
@loggers = other.loggers
@logger = other.logger
elsif other.kind_of?(Array)
@loggers = other
@logger = other.first
else
msg = "#use_log_devices takes a Mixlib::Log object or array of log devices. " <<
"You gave: #{other.inspect}"
raise ArgumentError, msg
end
@configured = true
end
# Use Mixlib::Log.init when you want to set up the logger manually. Arguments to this method
# get passed directly to Logger.new, so check out the documentation for the standard Logger class
# to understand what to do here.
#
# If this method is called with no arguments, it will log to STDOUT at the :warn level.
#
# It also configures the Logger instance it creates to use the custom Mixlib::Log::Formatter class.
def init(*opts)
reset!
@logger = logger_for(*opts)
@logger.formatter = Mixlib::Log::Formatter.new() if @logger.respond_to?(:formatter=)
@logger.level = Logger::WARN
@configured = true
@parent = nil
@metadata = {}
@logger
end
# Let the application query if logging objects have been set up
def configured?
@configured
end
attr_accessor :metadata
# Sets the level for the Logger object by symbol. Valid arguments are:
#
# :trace
# :debug
# :info
# :warn
# :error
# :fatal
#
# Throws an ArgumentError if you feed it a bogus log level.
def level=(new_level)
level_int = LEVEL_NAMES.key?(new_level) ? new_level : LEVELS[new_level]
raise ArgumentError, "Log level must be one of :trace, :debug, :info, :warn, :error, or :fatal" if level_int.nil?
loggers.each { |l| l.level = level_int }
end
def level(new_level = nil)
if new_level.nil?
LEVEL_NAMES[logger.level]
else
self.level = (new_level)
end
end
# Define the methods to interrogate the logger for the current log level.
# Note that we *only* query the default logger (@logger) and not any other
# loggers that may have been added, even though it is possible to configure
# two (or more) loggers at different log levels.
[:trace?, :debug?, :info?, :warn?, :error?, :fatal?].each do |method_name|
define_method(method_name) do
logger.send(method_name)
end
end
def <<(msg)
loggers.each { |l| l << msg }
end
def add(severity, message = nil, progname = nil, data: {}, &block)
message, progname, data = yield if block_given?
data = metadata.merge(data) if metadata.kind_of?(Hash) && data.kind_of?(Hash)
loggers.each do |l|
# if we don't have any metadata, let's not do the potentially expensive
# merging and managing that this call requires
if l.respond_to?(:add_data) && !data.nil? && !data.empty?
l.add_data(severity, message, progname, data: data)
else
l.add(severity, message, progname)
end
end
end
alias :log :add
def with_child(metadata = {})
child = Child.new(self, metadata)
if block_given?
yield child
else
child
end
end
# Passes any other method calls on directly to the underlying Logger object created with init. If
# this method gets hit before a call to Mixlib::Logger.init has been made, it will call
# Mixlib::Logger.init() with no arguments.
def method_missing(method_symbol, *args, &block)
loggers.each { |l| l.send(method_symbol, *args, &block) }
end
private
def logger_for(*opts)
if opts.empty?
Mixlib::Log::Logger.new($stdout)
elsif LEVELS.keys.inject(true) { |quacks, level| quacks && opts.first.respond_to?(level) }
opts.first
else
Mixlib::Log::Logger.new(*opts)
end
end
def all_loggers
[@logger, *@loggers].uniq
end
# select all loggers with File log devices
def loggers_to_close
loggers_to_close = []
all_loggers.each do |logger|
# unfortunately Logger does not provide access to the logdev
# via public API. In order to reduce amount of impact and
# handle only File type log devices I had to use this method
# to get access to it.
next unless logger.instance_variable_defined?(:"@logdev")
next unless (logdev = logger.instance_variable_get(:"@logdev"))
loggers_to_close << logger if logdev.filename
end
loggers_to_close
end
def close!
# try to close all file loggers
loggers_to_close.each do |l|
l.close rescue nil
end
end
end
end
|