TwP-logging 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +169 -0
- data/README.rdoc +102 -0
- data/Rakefile +42 -0
- data/data/bad_logging_1.rb +13 -0
- data/data/bad_logging_2.rb +21 -0
- data/data/logging.rb +42 -0
- data/data/logging.yaml +63 -0
- data/data/simple_logging.rb +13 -0
- data/lib/logging.rb +408 -0
- data/lib/logging/appender.rb +303 -0
- data/lib/logging/appenders/buffering.rb +167 -0
- data/lib/logging/appenders/console.rb +62 -0
- data/lib/logging/appenders/email.rb +75 -0
- data/lib/logging/appenders/file.rb +54 -0
- data/lib/logging/appenders/growl.rb +197 -0
- data/lib/logging/appenders/io.rb +69 -0
- data/lib/logging/appenders/rolling_file.rb +291 -0
- data/lib/logging/appenders/syslog.rb +201 -0
- data/lib/logging/config/configurator.rb +190 -0
- data/lib/logging/config/yaml_configurator.rb +195 -0
- data/lib/logging/layout.rb +119 -0
- data/lib/logging/layouts/basic.rb +34 -0
- data/lib/logging/layouts/pattern.rb +296 -0
- data/lib/logging/log_event.rb +51 -0
- data/lib/logging/logger.rb +490 -0
- data/lib/logging/repository.rb +172 -0
- data/lib/logging/root_logger.rb +61 -0
- data/lib/logging/stats.rb +278 -0
- data/lib/logging/utils.rb +130 -0
- data/logging.gemspec +41 -0
- data/test/appenders/test_buffered_io.rb +183 -0
- data/test/appenders/test_console.rb +66 -0
- data/test/appenders/test_email.rb +171 -0
- data/test/appenders/test_file.rb +93 -0
- data/test/appenders/test_growl.rb +128 -0
- data/test/appenders/test_io.rb +142 -0
- data/test/appenders/test_rolling_file.rb +207 -0
- data/test/appenders/test_syslog.rb +194 -0
- data/test/benchmark.rb +87 -0
- data/test/config/test_configurator.rb +70 -0
- data/test/config/test_yaml_configurator.rb +40 -0
- data/test/layouts/test_basic.rb +43 -0
- data/test/layouts/test_pattern.rb +177 -0
- data/test/setup.rb +74 -0
- data/test/test_appender.rb +166 -0
- data/test/test_layout.rb +110 -0
- data/test/test_log_event.rb +80 -0
- data/test/test_logger.rb +734 -0
- data/test/test_logging.rb +267 -0
- data/test/test_repository.rb +126 -0
- data/test/test_root_logger.rb +81 -0
- data/test/test_stats.rb +274 -0
- data/test/test_utils.rb +116 -0
- metadata +156 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module Logging
|
3
|
+
module Layouts
|
4
|
+
|
5
|
+
# The +Basic+ layout class provides methods for simple formatting of log
|
6
|
+
# events. The resulting string follows the format below.
|
7
|
+
#
|
8
|
+
# LEVEL LoggerName : log message
|
9
|
+
#
|
10
|
+
# _LEVEL_ is the log level of the event. _LoggerName_ is the name of the
|
11
|
+
# logger that generated the event. <em>log message</em> is the message
|
12
|
+
# or object that was passed to the logger. If multiple message or objects
|
13
|
+
# were passed to the logger then each will be printed on its own line with
|
14
|
+
# the format show above.
|
15
|
+
#
|
16
|
+
class Basic < ::Logging::Layout
|
17
|
+
|
18
|
+
# call-seq:
|
19
|
+
# format( event )
|
20
|
+
#
|
21
|
+
# Returns a string representation of the given loggging _event_. See the
|
22
|
+
# class documentation for details about the formatting used.
|
23
|
+
#
|
24
|
+
def format( event )
|
25
|
+
obj = format_obj(event.data)
|
26
|
+
sprintf("%*s %s : %s\n", ::Logging::MAX_LEVEL_LENGTH,
|
27
|
+
::Logging::LNAMES[event.level], event.logger, obj)
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class Basic
|
31
|
+
end # module Layouts
|
32
|
+
end # module Logging
|
33
|
+
|
34
|
+
# EOF
|
@@ -0,0 +1,296 @@
|
|
1
|
+
|
2
|
+
module Logging
|
3
|
+
module Layouts
|
4
|
+
|
5
|
+
# A flexible layout configurable with pattern string.
|
6
|
+
#
|
7
|
+
# The goal of this class is to format a LogEvent and return the results as
|
8
|
+
# a String. The results depend on the conversion pattern.
|
9
|
+
#
|
10
|
+
# The conversion pattern is closely related to the conversion pattern of
|
11
|
+
# the sprintf function. A conversion pattern is composed of literal text
|
12
|
+
# and format control expressions called conversion specifiers.
|
13
|
+
#
|
14
|
+
# You are free to insert any literal text within the conversion pattern.
|
15
|
+
#
|
16
|
+
# Each conversion specifier starts with a percent sign (%) and is followed
|
17
|
+
# by optional format modifiers and a conversion character. The conversion
|
18
|
+
# character specifies the type of data, e.g. logger, level, date, thread
|
19
|
+
# ID. The format modifiers control such things as field width, padding,
|
20
|
+
# left and right justification. The following is a simple example.
|
21
|
+
#
|
22
|
+
# Let the conversion pattern be "%-5l [%c]: %m\n" and assume that the
|
23
|
+
# logging environment was set to use a Pattern layout. Then the statements
|
24
|
+
#
|
25
|
+
# root = Logging::Logger[:root]
|
26
|
+
# root.debug("Message 1")
|
27
|
+
# root.warn("Message 2")
|
28
|
+
#
|
29
|
+
# would yield the output
|
30
|
+
#
|
31
|
+
# DEBUG [root]: Message 1
|
32
|
+
# WARN [root]: Message 2
|
33
|
+
#
|
34
|
+
# Note that there is no explicit separator between text and conversion
|
35
|
+
# specifiers. The pattern parser knows when it has reached the end of a
|
36
|
+
# conversion specifier when it reads a conversion character. In the example
|
37
|
+
# above the conversion specifier %-5l means the level of the logging event
|
38
|
+
# should be left justified to a width of five characters. The recognized
|
39
|
+
# conversion characters are
|
40
|
+
#
|
41
|
+
# [c] Used to output the name of the logger that generated the log
|
42
|
+
# event.
|
43
|
+
# [d] Used to output the date of the log event. The format of the
|
44
|
+
# date is specified using the :date_pattern option when the Layout
|
45
|
+
# is created. ISO8601 format is assumed if not date pattern is given.
|
46
|
+
# [F] Used to output the file name where the logging request was issued.
|
47
|
+
# [l] Used to output the level of the log event.
|
48
|
+
# [L] Used to output the line number where the logging request was
|
49
|
+
# issued.
|
50
|
+
# [m] Used to output the application supplied message associated with
|
51
|
+
# the log event.
|
52
|
+
# [M] Used to output the method name where the logging request was
|
53
|
+
# issued.
|
54
|
+
# [p] Used to output the process ID of the currently running program.
|
55
|
+
# [r] Used to output the number of milliseconds elapsed from the
|
56
|
+
# construction of the Layout until creation of the log event.
|
57
|
+
# [t] Used to output the object ID of the thread that generated the
|
58
|
+
# log event.
|
59
|
+
# [T] Used to output the name of the thread that generated the log event.
|
60
|
+
# Name can be specified using Thread.current[:name] notation. Output empty
|
61
|
+
# string if name not specified. This options helps to create more human
|
62
|
+
# readable output for multithread application log.
|
63
|
+
# [%] The sequence '%%' outputs a single percent sign.
|
64
|
+
#
|
65
|
+
# The directives F, L, and M will only work if the Logger generating the
|
66
|
+
# events is configured to generate tracing information. If this is not
|
67
|
+
# the case these fields will always be empty.
|
68
|
+
#
|
69
|
+
# By default the relevant information is output as is. However, with the
|
70
|
+
# aid of format modifiers it is possible to change the minimum field width,
|
71
|
+
# the maximum field width and justification.
|
72
|
+
#
|
73
|
+
# The optional format modifier is placed between the percent sign and the
|
74
|
+
# conversion character.
|
75
|
+
#
|
76
|
+
# The first optional format modifier is the left justification flag which
|
77
|
+
# is just the minus (-) character. Then comes the optional minimum field
|
78
|
+
# width modifier. This is a decimal constant that represents the minimum
|
79
|
+
# number of characters to output. If the data item requires fewer
|
80
|
+
# characters, it is padded on either the left or the right until the
|
81
|
+
# minimum width is reached. The default is to pad on the left (right
|
82
|
+
# justify) but you can specify right padding with the left justification
|
83
|
+
# flag. The padding character is space. If the data item is larger than the
|
84
|
+
# minimum field width, the field is expanded to accommodate the data. The
|
85
|
+
# value is never truncated.
|
86
|
+
#
|
87
|
+
# This behavior can be changed using the maximum field width modifier which
|
88
|
+
# is designated by a period followed by a decimal constant. If the data
|
89
|
+
# item is longer than the maximum field, then the extra characters are
|
90
|
+
# removed from the end of the data item.
|
91
|
+
#
|
92
|
+
# Below are various format modifier examples for the category conversion
|
93
|
+
# specifier.
|
94
|
+
#
|
95
|
+
# [%20c] Left pad with spaces if the logger name is less than 20
|
96
|
+
# characters long
|
97
|
+
# [%-20c] Right pad with spaces if the logger name is less than 20
|
98
|
+
# characters long
|
99
|
+
# [%.30c] Truncates the logger name if it is longer than 30 characters
|
100
|
+
# [%20.30c] Left pad with spaces if the logger name is shorter than
|
101
|
+
# 20 characters. However, if the logger name is longer than
|
102
|
+
# 30 characters, then truncate the name.
|
103
|
+
# [%-20.30c] Right pad with spaces if the logger name is shorter than
|
104
|
+
# 20 characters. However, if the logger name is longer than
|
105
|
+
# 30 characters, then truncate the name.
|
106
|
+
#
|
107
|
+
# Below are examples of some conversion patterns.
|
108
|
+
#
|
109
|
+
# %.1l, [%d] %5l -- %c: %m\n
|
110
|
+
#
|
111
|
+
# This is how the Logger class in the Ruby standard library formats
|
112
|
+
# messages. The main difference will be in the date format (the Pattern
|
113
|
+
# Layout uses the ISO8601 date format). Set the :date_method on the
|
114
|
+
# Pattern Layout to be 'to_s' and then the date formats will agree.
|
115
|
+
#
|
116
|
+
class Pattern < ::Logging::Layout
|
117
|
+
|
118
|
+
# :stopdoc:
|
119
|
+
|
120
|
+
# Arguments to sprintf keyed to directive letters
|
121
|
+
DIRECTIVE_TABLE = {
|
122
|
+
'c' => 'event.logger',
|
123
|
+
'd' => 'format_date',
|
124
|
+
'F' => 'event.file',
|
125
|
+
'l' => '::Logging::LNAMES[event.level]',
|
126
|
+
'L' => 'event.line',
|
127
|
+
'm' => 'format_obj(event.data)',
|
128
|
+
'M' => 'event.method',
|
129
|
+
'p' => 'Process.pid',
|
130
|
+
'r' => 'Integer((Time.now-@created_at)*1000).to_s',
|
131
|
+
't' => 'Thread.current.object_id.to_s',
|
132
|
+
'T' => 'Thread.current[:name]',
|
133
|
+
'%' => :placeholder
|
134
|
+
}
|
135
|
+
|
136
|
+
# Matches the first directive encountered and the stuff around it.
|
137
|
+
#
|
138
|
+
# * $1 is the stuff before directive or "" if not applicable
|
139
|
+
# * $2 is the %#.# match within directive group
|
140
|
+
# * $3 is the directive letter
|
141
|
+
# * $4 is the stuff after the directive or "" if not applicable
|
142
|
+
DIRECTIVE_RGXP = %r/([^%]*)(?:(%-?\d*(?:\.\d+)?)([a-zA-Z%]))?(.*)/m
|
143
|
+
|
144
|
+
# default date format
|
145
|
+
ISO8601 = "%Y-%m-%d %H:%M:%S"
|
146
|
+
|
147
|
+
# call-seq:
|
148
|
+
# Pattern.create_date_format_methods( pf )
|
149
|
+
#
|
150
|
+
# This method will create the +date_format+ method in the given Pattern
|
151
|
+
# Layout _pf_ based on the configured date patten and/or date method
|
152
|
+
# specified by the user.
|
153
|
+
#
|
154
|
+
def self.create_date_format_methods( pf )
|
155
|
+
code = "undef :format_date if method_defined? :format_date\n"
|
156
|
+
code << "def format_date\n"
|
157
|
+
if pf.date_method.nil?
|
158
|
+
if pf.date_pattern =~ %r/%s/
|
159
|
+
code << <<-CODE
|
160
|
+
now = Time.now
|
161
|
+
dp = '#{pf.date_pattern}'.gsub('%s','%06d' % now.usec)
|
162
|
+
now.strftime dp
|
163
|
+
CODE
|
164
|
+
else
|
165
|
+
code << "Time.now.strftime '#{pf.date_pattern}'\n"
|
166
|
+
end
|
167
|
+
else
|
168
|
+
code << "Time.now.#{pf.date_method}\n"
|
169
|
+
end
|
170
|
+
code << "end\n"
|
171
|
+
|
172
|
+
pf._meta_eval(code, __FILE__, __LINE__)
|
173
|
+
end
|
174
|
+
|
175
|
+
# call-seq:
|
176
|
+
# Pattern.create_format_method( pf )
|
177
|
+
#
|
178
|
+
# This method will create the +format+ method in the given Pattern
|
179
|
+
# Layout _pf_ based on the configured format pattern specified by the
|
180
|
+
# user.
|
181
|
+
#
|
182
|
+
def self.create_format_method( pf )
|
183
|
+
# Create the format(event) method
|
184
|
+
code = "undef :format if method_defined? :format\n"
|
185
|
+
code << "def format( event )\nsprintf(\""
|
186
|
+
pattern = pf.pattern.dup
|
187
|
+
args = []
|
188
|
+
|
189
|
+
while true
|
190
|
+
m = DIRECTIVE_RGXP.match(pattern)
|
191
|
+
code << m[1] unless m[1].empty?
|
192
|
+
|
193
|
+
case m[3]
|
194
|
+
when '%'; code << '%%'
|
195
|
+
when *DIRECTIVE_TABLE.keys
|
196
|
+
code << m[2] + 's'
|
197
|
+
args << DIRECTIVE_TABLE[m[3]]
|
198
|
+
when nil; break
|
199
|
+
else
|
200
|
+
raise ArgumentError, "illegal format character - '#{m[3]}'"
|
201
|
+
end
|
202
|
+
|
203
|
+
break if m[4].empty?
|
204
|
+
pattern = m[4]
|
205
|
+
end
|
206
|
+
|
207
|
+
code << '"'
|
208
|
+
code << ', ' + args.join(', ') unless args.empty?
|
209
|
+
code << ")\n"
|
210
|
+
code << "end\n"
|
211
|
+
|
212
|
+
pf._meta_eval(code, __FILE__, __LINE__)
|
213
|
+
end
|
214
|
+
# :startdoc:
|
215
|
+
|
216
|
+
# call-seq:
|
217
|
+
# Pattern.new( opts )
|
218
|
+
#
|
219
|
+
# Creates a new Pattern layout using the following options.
|
220
|
+
#
|
221
|
+
# :pattern => "[%d] %-5l -- %c : %m\n"
|
222
|
+
# :date_pattern => "%Y-%m-%d %H:%M:%S"
|
223
|
+
# :date_method => 'usec' or 'to_s'
|
224
|
+
#
|
225
|
+
# If used, :date_method will supersede :date_pattern.
|
226
|
+
#
|
227
|
+
def initialize( opts = {} )
|
228
|
+
super
|
229
|
+
@created_at = Time.now
|
230
|
+
|
231
|
+
@date_pattern = opts.getopt(:date_pattern)
|
232
|
+
@date_method = opts.getopt(:date_method)
|
233
|
+
@date_pattern = ISO8601 if @date_pattern.nil? and @date_method.nil?
|
234
|
+
|
235
|
+
@pattern = opts.getopt(:pattern,
|
236
|
+
"[%d] %-#{::Logging::MAX_LEVEL_LENGTH}l -- %c : %m\n")
|
237
|
+
|
238
|
+
Pattern.create_date_format_methods(self)
|
239
|
+
Pattern.create_format_method(self)
|
240
|
+
end
|
241
|
+
|
242
|
+
attr_reader :pattern, :date_pattern, :date_method
|
243
|
+
|
244
|
+
# call-seq:
|
245
|
+
# appender.pattern = "[%d] %-5l -- %c : %m\n"
|
246
|
+
#
|
247
|
+
# Set the message formatting pattern to be used by the layout.
|
248
|
+
#
|
249
|
+
def pattern=( var )
|
250
|
+
@pattern = var
|
251
|
+
Pattern.create_format_method(self)
|
252
|
+
end
|
253
|
+
|
254
|
+
# call-seq:
|
255
|
+
# appender.date_pattern = "%Y-%m-%d %H:%M:%S"
|
256
|
+
#
|
257
|
+
# Set the date formatting pattern to be used when outputting timestamps
|
258
|
+
# in the log messages.
|
259
|
+
#
|
260
|
+
def date_pattern=( var )
|
261
|
+
@date_pattern = var
|
262
|
+
Pattern.create_date_format_methods(self)
|
263
|
+
end
|
264
|
+
|
265
|
+
# call-seq:
|
266
|
+
# appender.date_method = 'to_s'
|
267
|
+
# appender.date_method = :usec
|
268
|
+
#
|
269
|
+
# Set the date method to be used when outputting timestamps in the log
|
270
|
+
# messages. If a date method is configured, the output of that method
|
271
|
+
# will be used in leu of the date pattern.
|
272
|
+
#
|
273
|
+
def date_method=( var )
|
274
|
+
@date_method = var
|
275
|
+
Pattern.create_date_format_methods(self)
|
276
|
+
end
|
277
|
+
|
278
|
+
# :stopdoc:
|
279
|
+
|
280
|
+
# call-seq:
|
281
|
+
# _meta_eval( code )
|
282
|
+
#
|
283
|
+
# Evaluates the given string of _code_ if the singleton class of this
|
284
|
+
# Pattern Layout object.
|
285
|
+
#
|
286
|
+
def _meta_eval( code, file = nil, line = nil )
|
287
|
+
meta = class << self; self end
|
288
|
+
meta.class_eval code, file, line
|
289
|
+
end
|
290
|
+
# :startdoc:
|
291
|
+
|
292
|
+
end # class Pattern
|
293
|
+
end # module Layouts
|
294
|
+
end # module Logging
|
295
|
+
|
296
|
+
# EOF
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
module Logging
|
3
|
+
|
4
|
+
# This class defines a logging event.
|
5
|
+
#
|
6
|
+
class LogEvent
|
7
|
+
|
8
|
+
# :stopdoc:
|
9
|
+
|
10
|
+
# Regular expression used to parse out caller information
|
11
|
+
#
|
12
|
+
# * $1 == filename
|
13
|
+
# * $2 == line number
|
14
|
+
# * $3 == method name (might be nil)
|
15
|
+
CALLER_RGXP = %r/([\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o
|
16
|
+
|
17
|
+
CALLER_INDEX = RUBY_PLATFORM[%r/^java/i] ? 1 : 2
|
18
|
+
# :startdoc:
|
19
|
+
|
20
|
+
# call-seq:
|
21
|
+
# LogEvent.new( logger, level, [data], trace )
|
22
|
+
#
|
23
|
+
# Creates a new log event with the given _logger_ name, numeric _level_,
|
24
|
+
# array of _data_ from the user to be logged, and boolean _trace_ flag.
|
25
|
+
# If the _trace_ flag is set to +true+ then Kernel::caller will be
|
26
|
+
# invoked to get the execution trace of the logging method.
|
27
|
+
#
|
28
|
+
def initialize( logger, level, data, trace )
|
29
|
+
@logger = logger
|
30
|
+
@level = level
|
31
|
+
@data = data
|
32
|
+
@file = @line = @method = ''
|
33
|
+
|
34
|
+
if trace
|
35
|
+
t = Kernel.caller[CALLER_INDEX]
|
36
|
+
return if t.nil?
|
37
|
+
|
38
|
+
m = CALLER_RGXP.match(t)
|
39
|
+
@file = m[1]
|
40
|
+
@line = m[2]
|
41
|
+
@method = m[3] unless m[3].nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_accessor :logger, :level, :data
|
46
|
+
attr_reader :file, :line, :method
|
47
|
+
|
48
|
+
end # class LogEvent
|
49
|
+
end # module Logging
|
50
|
+
|
51
|
+
# EOF
|
@@ -0,0 +1,490 @@
|
|
1
|
+
|
2
|
+
module Logging
|
3
|
+
|
4
|
+
# The +Logger+ class is the primary interface to the +Logging+ framework.
|
5
|
+
# It provides the logging methods that will be called from user methods,
|
6
|
+
# and it generates logging events that are sent to the appenders (the
|
7
|
+
# appenders take care of sending the log events to the logging
|
8
|
+
# destinations -- files, sockets, etc).
|
9
|
+
#
|
10
|
+
# +Logger+ instances are obtained from the +Repository+ and should
|
11
|
+
# not be directly created by users.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# log = Logging::Logger['my logger']
|
16
|
+
# log.add_appenders( Logging::Appender.stdout ) # append to STDOUT
|
17
|
+
# log.level = :info # log 'info' and above
|
18
|
+
#
|
19
|
+
# log.info 'starting foo operation'
|
20
|
+
# ...
|
21
|
+
# log.info 'finishing foo operation'
|
22
|
+
# ...
|
23
|
+
# log.fatal 'unknown exception', exception
|
24
|
+
#
|
25
|
+
class Logger
|
26
|
+
|
27
|
+
@mutex = Mutex.new # :nodoc:
|
28
|
+
|
29
|
+
class << self
|
30
|
+
|
31
|
+
# call-seq:
|
32
|
+
# Logger.root
|
33
|
+
#
|
34
|
+
# Returns the root logger.
|
35
|
+
#
|
36
|
+
def root
|
37
|
+
::Logging::Repository.instance[:root]
|
38
|
+
end
|
39
|
+
|
40
|
+
# :stopdoc:
|
41
|
+
|
42
|
+
# Overrides the new method such that only one Logger will be created
|
43
|
+
# for any given logger name.
|
44
|
+
#
|
45
|
+
def new( *args )
|
46
|
+
return super if args.empty?
|
47
|
+
|
48
|
+
repo = ::Logging::Repository.instance
|
49
|
+
name = repo.to_key(args.shift)
|
50
|
+
|
51
|
+
@mutex.synchronize do
|
52
|
+
logger = repo[name]
|
53
|
+
if logger.nil?
|
54
|
+
logger = super(name, *args)
|
55
|
+
repo[name] = logger
|
56
|
+
repo.children(name).each {|c| c.__send__(:parent=, logger)}
|
57
|
+
end
|
58
|
+
logger
|
59
|
+
end
|
60
|
+
end
|
61
|
+
alias :[] :new
|
62
|
+
|
63
|
+
# This is where the actual logging methods are defined. Two methods
|
64
|
+
# are created for each log level. The first is a query method used to
|
65
|
+
# determine if that perticular logging level is enabled. The second is
|
66
|
+
# the actual logging method that accepts a list of objects to be
|
67
|
+
# logged or a block. If a block is given, then the object returned
|
68
|
+
# from the block will be logged.
|
69
|
+
#
|
70
|
+
# Example
|
71
|
+
#
|
72
|
+
# log = Logging::Logger['my logger']
|
73
|
+
# log.level = :warn
|
74
|
+
#
|
75
|
+
# log.info? # => false
|
76
|
+
# log.warn? # => true
|
77
|
+
# log.warn 'this is your last warning'
|
78
|
+
# log.fatal 'I die!', exception
|
79
|
+
#
|
80
|
+
# log.debug do
|
81
|
+
# # expensive method to construct log message
|
82
|
+
# msg
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
def define_log_methods( logger )
|
86
|
+
::Logging::LEVELS.each do |name,num|
|
87
|
+
code = "undef :#{name} if method_defined? :#{name}\n"
|
88
|
+
code << "undef :#{name}? if method_defined? :#{name}?\n"
|
89
|
+
|
90
|
+
if logger.level > num
|
91
|
+
code << <<-CODE
|
92
|
+
def #{name}?( ) false end
|
93
|
+
def #{name}( data = nil ) false end
|
94
|
+
CODE
|
95
|
+
else
|
96
|
+
code << <<-CODE
|
97
|
+
def #{name}?( ) true end
|
98
|
+
def #{name}( data = nil )
|
99
|
+
data = yield if block_given?
|
100
|
+
log_event(::Logging::LogEvent.new(@name, #{num}, data, @trace))
|
101
|
+
true
|
102
|
+
end
|
103
|
+
CODE
|
104
|
+
end
|
105
|
+
|
106
|
+
logger._meta_eval(code, __FILE__, __LINE__)
|
107
|
+
end
|
108
|
+
logger
|
109
|
+
end
|
110
|
+
# :startdoc:
|
111
|
+
|
112
|
+
end # class << self
|
113
|
+
|
114
|
+
attr_reader :name, :parent, :additive, :trace
|
115
|
+
|
116
|
+
# call-seq:
|
117
|
+
# Logger.new( name )
|
118
|
+
# Logger[name]
|
119
|
+
#
|
120
|
+
# Returns the logger identified by _name_.
|
121
|
+
#
|
122
|
+
# When _name_ is a +String+ or a +Symbol+ it will be used "as is" to
|
123
|
+
# retrieve the logger. When _name_ is a +Class+ the class name will be
|
124
|
+
# used to retrieve the logger. When _name_ is an object the name of the
|
125
|
+
# object's class will be used to retrieve the logger.
|
126
|
+
#
|
127
|
+
# Example:
|
128
|
+
#
|
129
|
+
# obj = MyClass.new
|
130
|
+
#
|
131
|
+
# log1 = Logger.new(obj)
|
132
|
+
# log2 = Logger.new(MyClass)
|
133
|
+
# log3 = Logger['MyClass']
|
134
|
+
#
|
135
|
+
# log1.object_id == log2.object_id # => true
|
136
|
+
# log2.object_id == log3.object_id # => true
|
137
|
+
#
|
138
|
+
def initialize( name )
|
139
|
+
case name
|
140
|
+
when String
|
141
|
+
raise(ArgumentError, "logger must have a name") if name.empty?
|
142
|
+
else raise(ArgumentError, "logger name must be a String") end
|
143
|
+
|
144
|
+
repo = ::Logging::Repository.instance
|
145
|
+
_setup(name, :parent => repo.parent(name))
|
146
|
+
end
|
147
|
+
|
148
|
+
# call-seq:
|
149
|
+
# log <=> other
|
150
|
+
#
|
151
|
+
# Compares this logger by name to another logger. The normal return codes
|
152
|
+
# for +String+ objects apply.
|
153
|
+
#
|
154
|
+
def <=>( other )
|
155
|
+
case other
|
156
|
+
when self; 0
|
157
|
+
when ::Logging::RootLogger; 1
|
158
|
+
when ::Logging::Logger; @name <=> other.name
|
159
|
+
else raise ArgumentError, 'expecting a Logger instance' end
|
160
|
+
end
|
161
|
+
|
162
|
+
# call-seq:
|
163
|
+
# log << "message"
|
164
|
+
#
|
165
|
+
# Log the given message without any formatting and without performing any
|
166
|
+
# level checks. The message is logged to all appenders. The message is
|
167
|
+
# passed up the logger tree if this logger's additivity is +true+.
|
168
|
+
#
|
169
|
+
def <<( msg )
|
170
|
+
@appenders.each {|a| a << msg}
|
171
|
+
@parent << msg if @additive
|
172
|
+
end
|
173
|
+
|
174
|
+
# call-seq:
|
175
|
+
# add( severity, message = nil ) {block}
|
176
|
+
#
|
177
|
+
# Log a message if the given severity is high enough. This is the generic
|
178
|
+
# logging method. Users will be more inclined to use #debug, #info, #warn,
|
179
|
+
# #error, and #fatal.
|
180
|
+
#
|
181
|
+
# <b>Message format</b>: +message+ can be any object, but it has to be
|
182
|
+
# converted to a String in order to log it. The Logging::format_as
|
183
|
+
# method is used to determine how objects chould be converted to
|
184
|
+
# strings. Generally, +inspect+ is used.
|
185
|
+
#
|
186
|
+
# A special case is an +Exception+ object, which will be printed in
|
187
|
+
# detail, including message, class, and backtrace.
|
188
|
+
#
|
189
|
+
# If a _message_ is not given, then the return value from the block is
|
190
|
+
# used as the message to log. This is useful when creating the actual
|
191
|
+
# message is an expensive operation. This allows the logger to check the
|
192
|
+
# severity against the configured level before actually constructing the
|
193
|
+
# message.
|
194
|
+
#
|
195
|
+
# This method returns +true+ if the message was logged, and +false+ is
|
196
|
+
# returned if the message was not logged.
|
197
|
+
#
|
198
|
+
def add( lvl, data = nil )
|
199
|
+
lvl = Integer(lvl)
|
200
|
+
return false if lvl < level
|
201
|
+
|
202
|
+
data = yield if block_given?
|
203
|
+
log_event(::Logging::LogEvent.new(@name, lvl, data, @trace))
|
204
|
+
true
|
205
|
+
end
|
206
|
+
|
207
|
+
# call-seq:
|
208
|
+
# additive = true
|
209
|
+
#
|
210
|
+
# Sets the additivity of the logger. Acceptable values are +true+,
|
211
|
+
# 'true', +false+, 'false', or +nil+. In this case +nil+ does not
|
212
|
+
# change the additivity
|
213
|
+
#
|
214
|
+
def additive=( val )
|
215
|
+
@additive = case val
|
216
|
+
when true, 'true'; true
|
217
|
+
when false, 'false'; false
|
218
|
+
when nil; @additive
|
219
|
+
else raise ArgumentError, 'expecting a boolean' end
|
220
|
+
end
|
221
|
+
|
222
|
+
# call-seq:
|
223
|
+
# trace = true
|
224
|
+
#
|
225
|
+
# Sets the tracing of the logger. Acceptable values are +true+,
|
226
|
+
# 'true', +false+, 'false', or +nil+. In this case +nil+ does not
|
227
|
+
# change the tracing.
|
228
|
+
#
|
229
|
+
def trace=( val )
|
230
|
+
@trace = case val
|
231
|
+
when true, 'true'; true
|
232
|
+
when false, 'false'; false
|
233
|
+
when nil; @trace
|
234
|
+
else raise ArgumentError, 'expecting a boolean' end
|
235
|
+
end
|
236
|
+
|
237
|
+
# call-seq:
|
238
|
+
# level => integer
|
239
|
+
#
|
240
|
+
# Returns an integer which is the defined log level for this logger.
|
241
|
+
#
|
242
|
+
def level
|
243
|
+
return @level unless @level.nil?
|
244
|
+
@parent.level
|
245
|
+
end
|
246
|
+
|
247
|
+
# call-seq:
|
248
|
+
# level = :all
|
249
|
+
#
|
250
|
+
# Set the level for this logger. The level can be either a +String+, a
|
251
|
+
# +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
|
252
|
+
# the case.
|
253
|
+
#
|
254
|
+
# There are two special levels -- "all" and "off". The former will
|
255
|
+
# enable log messages from this logger. The latter will disable all log
|
256
|
+
# messages from this logger.
|
257
|
+
#
|
258
|
+
# Setting the logger level to +nil+ will cause the parent's logger level
|
259
|
+
# to be used.
|
260
|
+
#
|
261
|
+
# Example:
|
262
|
+
#
|
263
|
+
# log.level = :debug
|
264
|
+
# log.level = "INFO"
|
265
|
+
# log.level = 4
|
266
|
+
# log.level = 'off'
|
267
|
+
# log.level = :all
|
268
|
+
#
|
269
|
+
# These prodcue an +ArgumentError+
|
270
|
+
#
|
271
|
+
# log.level = Object
|
272
|
+
# log.level = -1
|
273
|
+
# log.level = 1_000_000_000_000
|
274
|
+
#
|
275
|
+
def level=( level )
|
276
|
+
@level =
|
277
|
+
if level.nil? then level
|
278
|
+
else
|
279
|
+
lvl = case level
|
280
|
+
when String, Symbol; ::Logging::level_num(level)
|
281
|
+
when Fixnum; level
|
282
|
+
else
|
283
|
+
raise ArgumentError,
|
284
|
+
"level must be a String, Symbol, or Integer"
|
285
|
+
end
|
286
|
+
if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
|
287
|
+
raise ArgumentError, "unknown level was given '#{level}'"
|
288
|
+
end
|
289
|
+
lvl
|
290
|
+
end
|
291
|
+
|
292
|
+
define_log_methods(true)
|
293
|
+
self.level
|
294
|
+
end
|
295
|
+
|
296
|
+
# call-seq:
|
297
|
+
# appenders = app
|
298
|
+
#
|
299
|
+
# Clears the current list of appenders and replaces them with _app_,
|
300
|
+
# where _app_ can be either a single appender or an array of appenders.
|
301
|
+
#
|
302
|
+
def appenders=( args )
|
303
|
+
@appenders.clear
|
304
|
+
add_appenders(*args) unless args.nil?
|
305
|
+
end
|
306
|
+
|
307
|
+
# call-seq:
|
308
|
+
# add_appenders( appenders )
|
309
|
+
#
|
310
|
+
# Add the given _appenders_ to the list of appenders, where _appenders_
|
311
|
+
# can be either a single appender or an array of appenders.
|
312
|
+
#
|
313
|
+
def add_appenders( *args )
|
314
|
+
args.flatten.each do |arg|
|
315
|
+
o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appender[arg]
|
316
|
+
raise ArgumentError, "unknown appender '#{arg}'" if o.nil?
|
317
|
+
@appenders << o unless @appenders.include?(o)
|
318
|
+
end
|
319
|
+
self
|
320
|
+
end
|
321
|
+
|
322
|
+
# call-seq:
|
323
|
+
# remove_appenders( appenders )
|
324
|
+
#
|
325
|
+
# Remove the given _appenders_ from the list of appenders. The appenders
|
326
|
+
# to remove can be identified either by name using a +String+ or by
|
327
|
+
# passing the appender instance. _appenders_ can be a single appender or
|
328
|
+
# an array of appenders.
|
329
|
+
#
|
330
|
+
def remove_appenders( *args )
|
331
|
+
args.flatten.each do |arg|
|
332
|
+
@appenders.delete_if do |a|
|
333
|
+
case arg
|
334
|
+
when String; arg == a.name
|
335
|
+
when ::Logging::Appender; arg.object_id == a.object_id
|
336
|
+
else
|
337
|
+
raise ArgumentError, "#{arg.inspect} is not a 'Logging::Appender'"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
self
|
342
|
+
end
|
343
|
+
|
344
|
+
# call-seq:
|
345
|
+
# clear_appenders
|
346
|
+
#
|
347
|
+
# Remove all appenders from this logger.
|
348
|
+
#
|
349
|
+
def clear_appenders( ) @appenders.clear end
|
350
|
+
|
351
|
+
# call-seq:
|
352
|
+
# inspect => string
|
353
|
+
#
|
354
|
+
# Returns a string representation of the logger.
|
355
|
+
#
|
356
|
+
def inspect
|
357
|
+
"<%s:0x%x name=\"%s\">" % [self.class.name, self.object_id, self.name]
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
protected
|
362
|
+
|
363
|
+
# call-seq:
|
364
|
+
# parent = ParentLogger
|
365
|
+
#
|
366
|
+
# Set the parent logger for this logger. This method will be invoked by
|
367
|
+
# the +Repository+ class when a parent or child is added to the
|
368
|
+
# hierarchy.
|
369
|
+
#
|
370
|
+
def parent=( parent ) @parent = parent end
|
371
|
+
|
372
|
+
# call-seq:
|
373
|
+
# log_event( event )
|
374
|
+
#
|
375
|
+
# Send the given _event_ to the appenders for logging, and pass the
|
376
|
+
# _event_ up to the parent if additive mode is enabled. The log level has
|
377
|
+
# already been checked before this method is called.
|
378
|
+
#
|
379
|
+
def log_event( event )
|
380
|
+
@appenders.each {|a| a.append(event)}
|
381
|
+
@parent.log_event(event) if @additive
|
382
|
+
end
|
383
|
+
|
384
|
+
# call-seq:
|
385
|
+
# define_log_methods( force = false )
|
386
|
+
#
|
387
|
+
# Define the logging methods for this logger based on the configured log
|
388
|
+
# level. If the level is nil, then we will ask our parent for it's level
|
389
|
+
# and define log levels accordingly. The force flag will skip this
|
390
|
+
# check.
|
391
|
+
#
|
392
|
+
# Recursively call this method on all our children loggers.
|
393
|
+
#
|
394
|
+
def define_log_methods( force = false )
|
395
|
+
return if @level and !force
|
396
|
+
|
397
|
+
::Logging::Logger.define_log_methods(self)
|
398
|
+
::Logging::Repository.instance.children(name).each do |c|
|
399
|
+
c.define_log_methods
|
400
|
+
end
|
401
|
+
self
|
402
|
+
end
|
403
|
+
|
404
|
+
# :stopdoc:
|
405
|
+
public
|
406
|
+
|
407
|
+
# call-seq:
|
408
|
+
# _meta_eval( code )
|
409
|
+
#
|
410
|
+
# Evaluates the given string of _code_ if the singleton class of this
|
411
|
+
# Logger object.
|
412
|
+
#
|
413
|
+
def _meta_eval( code, file = nil, line = nil )
|
414
|
+
meta = class << self; self end
|
415
|
+
meta.class_eval code, file, line
|
416
|
+
end
|
417
|
+
|
418
|
+
# call-seq:
|
419
|
+
# _setup( name, opts = {} )
|
420
|
+
#
|
421
|
+
# Configures internal variables for the logger. This method can be used
|
422
|
+
# to avoid storing the logger in the repository.
|
423
|
+
#
|
424
|
+
def _setup( name, opts = {} )
|
425
|
+
@name = name
|
426
|
+
@parent = opts.getopt(:parent)
|
427
|
+
@appenders = opts.getopt(:appenders, [])
|
428
|
+
@additive = opts.getopt(:additive, true)
|
429
|
+
@trace = opts.getopt(:trace, false)
|
430
|
+
@level = opts.getopt(:level)
|
431
|
+
::Logging::Logger.define_log_methods(self)
|
432
|
+
end
|
433
|
+
|
434
|
+
# call-seq:
|
435
|
+
# _dump_configuration( io = STDOUT, indent = 0 )
|
436
|
+
#
|
437
|
+
# An internal method that is used to dump this logger's configuration to
|
438
|
+
# the given _io_ stream. The configuration includes the logger's name,
|
439
|
+
# level, additivity, and trace settings. The configured appenders are
|
440
|
+
# also printed to the _io_ stream.
|
441
|
+
#
|
442
|
+
def _dump_configuration( io = STDOUT, indent = 0 )
|
443
|
+
str, spacer, base = '', ' ', 50
|
444
|
+
indent_str = indent == 0 ? '' : ' ' * indent
|
445
|
+
|
446
|
+
str << indent_str
|
447
|
+
str << self.name.reduce(base - indent)
|
448
|
+
if (str.length + spacer.length) < base
|
449
|
+
str << spacer
|
450
|
+
str << '.' * (base - str.length)
|
451
|
+
end
|
452
|
+
io.write(str.ljust(base))
|
453
|
+
io.write(spacer)
|
454
|
+
|
455
|
+
level_str = @level.nil? ? '' : '*'
|
456
|
+
level_str << if level < ::Logging::LEVELS.length
|
457
|
+
::Logging.levelify(::Logging::LNAMES[level])
|
458
|
+
else
|
459
|
+
'off'
|
460
|
+
end
|
461
|
+
level_len = ::Logging::MAX_LEVEL_LENGTH + 1
|
462
|
+
|
463
|
+
io.write("%#{level_len}s" % level_str)
|
464
|
+
io.write(spacer)
|
465
|
+
|
466
|
+
if self.respond_to?(:additive)
|
467
|
+
io.write(additive ? '+A' : '-A')
|
468
|
+
else
|
469
|
+
io.write(' ')
|
470
|
+
end
|
471
|
+
|
472
|
+
io.write(spacer)
|
473
|
+
io.write(trace ? '+T' : '-T')
|
474
|
+
io.write("\n")
|
475
|
+
|
476
|
+
@appenders.each do |appender|
|
477
|
+
io.write(indent_str)
|
478
|
+
io.write('- ')
|
479
|
+
io.write(appender.inspect)
|
480
|
+
io.write("\n")
|
481
|
+
end
|
482
|
+
|
483
|
+
return io
|
484
|
+
end
|
485
|
+
# :startdoc:
|
486
|
+
|
487
|
+
end # class Logger
|
488
|
+
end # module Logging
|
489
|
+
|
490
|
+
# EOF
|