logging 1.7.2 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/History.txt +10 -0
- data/README.rdoc +13 -12
- data/Rakefile +3 -2
- data/examples/formatting.rb +5 -5
- data/examples/mdc.rb +83 -0
- data/lib/logging.rb +5 -1
- data/lib/logging/appender.rb +28 -0
- data/lib/logging/appenders/buffering.rb +4 -1
- data/lib/logging/appenders/console.rb +4 -0
- data/lib/logging/appenders/email.rb +2 -0
- data/lib/logging/appenders/file.rb +3 -0
- data/lib/logging/appenders/io.rb +1 -0
- data/lib/logging/appenders/rolling_file.rb +5 -3
- data/lib/logging/appenders/string_io.rb +1 -1
- data/lib/logging/color_scheme.rb +2 -3
- data/lib/logging/diagnostic_context.rb +330 -0
- data/lib/logging/layout.rb +20 -4
- data/lib/logging/layouts/parseable.rb +45 -30
- data/lib/logging/layouts/pattern.rb +61 -12
- data/test/appenders/test_buffered_io.rb +21 -0
- data/test/appenders/test_file.rb +20 -0
- data/test/appenders/test_syslog.rb +6 -9
- data/test/layouts/test_json.rb +70 -12
- data/test/layouts/test_pattern.rb +35 -0
- data/test/layouts/test_yaml.rb +49 -6
- data/test/test_layout.rb +7 -0
- data/test/test_logging.rb +6 -1
- data/test/test_mapped_diagnostic_context.rb +78 -0
- data/test/test_nested_diagnostic_context.rb +83 -0
- data/version.txt +1 -1
- metadata +57 -19
- data/.rvmrc +0 -47
- data/a.rb +0 -10
- data/tmp.txt +0 -3
data/lib/logging/layout.rb
CHANGED
@@ -22,6 +22,7 @@ class Layout
|
|
22
22
|
# * :string => to_s
|
23
23
|
# * :inspect => inspect
|
24
24
|
# * :yaml => to_yaml
|
25
|
+
# * :json => MultiJson.encode(obj)
|
25
26
|
#
|
26
27
|
# If the format is not specified then the global object format is used
|
27
28
|
# (see Logging#format_as). If the global object format is not specified
|
@@ -37,7 +38,7 @@ class Layout
|
|
37
38
|
f = f.intern if f.instance_of? String
|
38
39
|
|
39
40
|
@obj_format = case f
|
40
|
-
when :inspect, :yaml; f
|
41
|
+
when :inspect, :yaml, :json; f
|
41
42
|
else :string end
|
42
43
|
|
43
44
|
b = opts.getopt(:backtrace, ::Logging.backtrace)
|
@@ -94,23 +95,38 @@ class Layout
|
|
94
95
|
str << case @obj_format
|
95
96
|
when :inspect; obj.inspect
|
96
97
|
when :yaml; try_yaml(obj)
|
98
|
+
when :json; try_json(obj)
|
97
99
|
else obj.to_s end
|
98
100
|
str
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
102
|
-
# call-seq:
|
103
|
-
# try_yaml( obj )
|
104
|
-
#
|
105
104
|
# Attempt to format the _obj_ using yaml, but fall back to inspect style
|
106
105
|
# formatting if yaml fails.
|
107
106
|
#
|
107
|
+
# obj - The Object to format.
|
108
|
+
#
|
109
|
+
# Returns a String representation of the object.
|
110
|
+
#
|
108
111
|
def try_yaml( obj )
|
109
112
|
"\n#{obj.to_yaml}"
|
110
113
|
rescue TypeError
|
111
114
|
obj.inspect
|
112
115
|
end
|
113
116
|
|
117
|
+
# Attempt to format the given object as a JSON string, but fall back to
|
118
|
+
# inspect formatting if JSON encoding fails.
|
119
|
+
#
|
120
|
+
# obj - The Object to format.
|
121
|
+
#
|
122
|
+
# Returns a String representation of the object.
|
123
|
+
#
|
124
|
+
def try_json( obj )
|
125
|
+
MultiJson.encode(obj)
|
126
|
+
rescue StandardError
|
127
|
+
obj.inspect
|
128
|
+
end
|
129
|
+
|
114
130
|
end # class Layout
|
115
131
|
end # module Logging
|
116
132
|
|
@@ -64,12 +64,12 @@ module Logging::Layouts
|
|
64
64
|
# follows:
|
65
65
|
#
|
66
66
|
# ---
|
67
|
-
# timestamp: 2009-04-
|
67
|
+
# timestamp: 2009-04-17T16:15:42
|
68
68
|
# level: INFO
|
69
69
|
# logger: Foo::Bar
|
70
70
|
# message: this is a log message
|
71
71
|
# ---
|
72
|
-
# timestamp: 2009-04-
|
72
|
+
# timestamp: 2009-04-17T16:15:43
|
73
73
|
# level: ERROR
|
74
74
|
# logger: Foo
|
75
75
|
# message: <RuntimeError> Oooops!!
|
@@ -84,8 +84,8 @@ module Logging::Layouts
|
|
84
84
|
# it line by line and parse the individual objects. Taking the same
|
85
85
|
# example above the JSON output would be:
|
86
86
|
#
|
87
|
-
# {"timestamp":"2009-04-
|
88
|
-
# {"timestamp":"2009-04-
|
87
|
+
# {"timestamp":"2009-04-17T16:15:42","level":"INFO","logger":"Foo::Bar","message":"this is a log message"}
|
88
|
+
# {"timestamp":"2009-04-17T16:15:43","level":"ERROR","logger":"Foo","message":"<RuntimeError> Oooops!!"}
|
89
89
|
#
|
90
90
|
# The output order of the fields is guaranteed to be the same as the order
|
91
91
|
# specified in the _items_ list.
|
@@ -95,17 +95,19 @@ module Logging::Layouts
|
|
95
95
|
# :stopdoc:
|
96
96
|
# Arguments to sprintf keyed to directive letters
|
97
97
|
DIRECTIVE_TABLE = {
|
98
|
-
'logger' => 'event.logger',
|
99
|
-
'timestamp' => 'event.time',
|
100
|
-
'level' => '::Logging::LNAMES[event.level]',
|
101
|
-
'message' => 'format_obj(event.data)',
|
102
|
-
'file' => 'event.file',
|
103
|
-
'line' => 'event.line',
|
104
|
-
'method' => 'event.method',
|
105
|
-
'pid' => 'Process.pid',
|
106
|
-
'millis' => 'Integer((event.time-@created_at)*1000)',
|
107
|
-
'thread_id' => 'Thread.current.object_id',
|
108
|
-
'thread' => 'Thread.current[:name]'
|
98
|
+
'logger' => 'event.logger'.freeze,
|
99
|
+
'timestamp' => 'iso8601_format(event.time)'.freeze,
|
100
|
+
'level' => '::Logging::LNAMES[event.level]'.freeze,
|
101
|
+
'message' => 'format_obj(event.data)'.freeze,
|
102
|
+
'file' => 'event.file'.freeze,
|
103
|
+
'line' => 'event.line'.freeze,
|
104
|
+
'method' => 'event.method'.freeze,
|
105
|
+
'pid' => 'Process.pid'.freeze,
|
106
|
+
'millis' => 'Integer((event.time-@created_at)*1000)'.freeze,
|
107
|
+
'thread_id' => 'Thread.current.object_id'.freeze,
|
108
|
+
'thread' => 'Thread.current[:name]'.freeze,
|
109
|
+
'mdc' => 'Logging::MappedDiagnosticContext.context'.freeze,
|
110
|
+
'ndc' => 'Logging::NestedDiagnosticContext.context'.freeze
|
109
111
|
}
|
110
112
|
|
111
113
|
# call-seq:
|
@@ -134,14 +136,12 @@ module Logging::Layouts
|
|
134
136
|
#
|
135
137
|
def self.create_json_format_method( layout )
|
136
138
|
code = "undef :format if method_defined? :format\n"
|
137
|
-
code << "def format( event )\n
|
139
|
+
code << "def format( event )\nh = {\n"
|
138
140
|
|
139
|
-
args = []
|
140
141
|
code << layout.items.map {|name|
|
141
|
-
|
142
|
-
|
143
|
-
}.
|
144
|
-
code << "}\\n\" % [#{args.join(', ')}]\nend"
|
142
|
+
"'#{name}' => #{Parseable::DIRECTIVE_TABLE[name]}"
|
143
|
+
}.join(",\n")
|
144
|
+
code << "\n}\nMultiJson.encode(h) << \"\\n\"\nend\n"
|
145
145
|
|
146
146
|
(class << layout; self end).class_eval(code, __FILE__, __LINE__)
|
147
147
|
end
|
@@ -201,18 +201,33 @@ module Logging::Layouts
|
|
201
201
|
create_format_method
|
202
202
|
end
|
203
203
|
|
204
|
-
|
205
|
-
|
206
|
-
#
|
204
|
+
# Public: Take a given object and convert it into a format suitable for
|
205
|
+
# inclusion as a log message. The conversion allows the object to be more
|
206
|
+
# easily expressed in YAML or JSON form.
|
207
|
+
#
|
208
|
+
# If the object is an Exception, then this method will return a Hash
|
209
|
+
# containing the exception class name, message, and backtrace (if any).
|
207
210
|
#
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
211
|
+
# obj - The Object to format
|
212
|
+
#
|
213
|
+
# Returns the formatted Object.
|
214
|
+
#
|
215
|
+
def format_obj( obj )
|
216
|
+
case obj
|
217
|
+
when Exception
|
218
|
+
h = { :class => obj.class.name,
|
219
|
+
:message => obj.message }
|
220
|
+
h[:backtrace] = obj.backtrace if @backtrace && !obj.backtrace.nil?
|
221
|
+
h
|
222
|
+
when Time
|
223
|
+
iso8601_format(obj)
|
224
|
+
else
|
225
|
+
obj
|
226
|
+
end
|
214
227
|
end
|
215
228
|
|
229
|
+
private
|
230
|
+
|
216
231
|
# Call the appropriate class level create format method based on the
|
217
232
|
# style of this parseable layout.
|
218
233
|
#
|
@@ -65,7 +65,12 @@ module Logging::Layouts
|
|
65
65
|
# [T] Used to output the name of the thread that generated the log event.
|
66
66
|
# Name can be specified using Thread.current[:name] notation. Output
|
67
67
|
# empty string if name not specified. This option helps to create
|
68
|
-
# more human readable output for
|
68
|
+
# more human readable output for multi-threaded application logs.
|
69
|
+
# [X] Used to output values from the Mapped Diagnostic Context. Requires
|
70
|
+
# a key name to lookup the value from the context. More details are
|
71
|
+
# listed below.
|
72
|
+
# [x] Used to output values from the Nested Diagnostic Context. Supports
|
73
|
+
# an optional context separator string. More details are listed below.
|
69
74
|
# [%] The sequence '%%' outputs a single percent sign.
|
70
75
|
#
|
71
76
|
# The logger name directive 'c' accepts an optional precision that will
|
@@ -77,6 +82,23 @@ module Logging::Layouts
|
|
77
82
|
# events is configured to generate tracing information. If this is not
|
78
83
|
# the case these fields will always be empty.
|
79
84
|
#
|
85
|
+
# The directives for include diagnostic context information in the log
|
86
|
+
# messages are X and x. For the Mapped Diagnostic Context the directive must
|
87
|
+
# be accompanied by the key identifying the value to insert into the log
|
88
|
+
# message. The X directive can appear multiple times to include multiple
|
89
|
+
# values from the mapped context.
|
90
|
+
#
|
91
|
+
# %X{Cookie} Insert the current session cookie
|
92
|
+
# %X{X-Session} Insert a session identifier
|
93
|
+
#
|
94
|
+
# For the Nested Diagnostic Context you need only include the directive
|
95
|
+
# once. All contexts currently in the stack will be added to the log message
|
96
|
+
# separated by spaces. If spaces are not your style, a separator string can
|
97
|
+
# be given, too.
|
98
|
+
#
|
99
|
+
# %x Insert all contexts separated by spaces
|
100
|
+
# %x{, } Insert all contexts separate by a comma and a space
|
101
|
+
#
|
80
102
|
# By default the relevant information is output as is. However, with the
|
81
103
|
# aid of format modifiers it is possible to change the minimum field width,
|
82
104
|
# the maximum field width and justification.
|
@@ -103,15 +125,15 @@ module Logging::Layouts
|
|
103
125
|
# Below are various format modifier examples for the category conversion
|
104
126
|
# specifier.
|
105
127
|
#
|
106
|
-
#
|
128
|
+
# %20c Left pad with spaces if the logger name is less than 20
|
107
129
|
# characters long
|
108
|
-
#
|
130
|
+
# %-20c Right pad with spaces if the logger name is less than 20
|
109
131
|
# characters long
|
110
|
-
#
|
111
|
-
#
|
132
|
+
# %.30c Truncates the logger name if it is longer than 30 characters
|
133
|
+
# %20.30c Left pad with spaces if the logger name is shorter than
|
112
134
|
# 20 characters. However, if the logger name is longer than
|
113
135
|
# 30 characters, then truncate the name.
|
114
|
-
#
|
136
|
+
# %-20.30c Right pad with spaces if the logger name is shorter than
|
115
137
|
# 20 characters. However, if the logger name is longer than
|
116
138
|
# 30 characters, then truncate the name.
|
117
139
|
#
|
@@ -141,6 +163,8 @@ module Logging::Layouts
|
|
141
163
|
'r' => 'Integer((event.time-@created_at)*1000).to_s'.freeze,
|
142
164
|
't' => 'Thread.current.object_id.to_s'.freeze,
|
143
165
|
'T' => 'Thread.current[:name]'.freeze,
|
166
|
+
'X' => :placeholder,
|
167
|
+
'x' => :placeholder,
|
144
168
|
'%' => :placeholder
|
145
169
|
}.freeze
|
146
170
|
|
@@ -151,7 +175,7 @@ module Logging::Layouts
|
|
151
175
|
# * $3 is the directive letter
|
152
176
|
# * $4 is the precision specifier for the logger name
|
153
177
|
# * $5 is the stuff after the directive or "" if not applicable
|
154
|
-
DIRECTIVE_RGXP = %r/([^%]*)(?:(%-?\d*(?:\.\d+)?)([a-zA-Z%])(?:\{(
|
178
|
+
DIRECTIVE_RGXP = %r/([^%]*)(?:(%-?\d*(?:\.\d+)?)([a-zA-Z%])(?:\{([^\}]+)\})?)?(.*)/m
|
155
179
|
|
156
180
|
# default date format
|
157
181
|
ISO8601 = "%Y-%m-%d %H:%M:%S".freeze
|
@@ -167,7 +191,9 @@ module Logging::Layouts
|
|
167
191
|
't' => :thread_id,
|
168
192
|
'F' => :file,
|
169
193
|
'L' => :line,
|
170
|
-
'M' => :method
|
194
|
+
'M' => :method,
|
195
|
+
'X' => :mdc,
|
196
|
+
'x' => :ndc
|
171
197
|
}.freeze
|
172
198
|
|
173
199
|
# call-seq:
|
@@ -226,10 +252,15 @@ module Logging::Layouts
|
|
226
252
|
format_string << fmt
|
227
253
|
args << DIRECTIVE_TABLE[m[3]].dup
|
228
254
|
if m[4]
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
255
|
+
precision = Integer(m[4]) rescue nil
|
256
|
+
if precision
|
257
|
+
raise ArgumentError, "logger name precision must be an integer greater than zero: #{precision}" unless precision > 0
|
258
|
+
args.last <<
|
259
|
+
".split(::Logging::Repository::PATH_DELIMITER)" \
|
260
|
+
".last(#{m[4]}).join(::Logging::Repository::PATH_DELIMITER)"
|
261
|
+
else
|
262
|
+
format_string << "{#{m[4]}}"
|
263
|
+
end
|
233
264
|
end
|
234
265
|
when 'l'
|
235
266
|
if color_scheme and color_scheme.levels?
|
@@ -247,6 +278,23 @@ module Logging::Layouts
|
|
247
278
|
args << DIRECTIVE_TABLE[m[3]]
|
248
279
|
end
|
249
280
|
|
281
|
+
when 'X'
|
282
|
+
raise ArgumentError, "MDC must have a key reference" unless m[4]
|
283
|
+
fmt = m[2] + 's'
|
284
|
+
fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[m[3]]) if color_scheme and !color_scheme.lines?
|
285
|
+
|
286
|
+
format_string << fmt
|
287
|
+
args << "::Logging.mdc['#{m[4]}']"
|
288
|
+
|
289
|
+
when 'x'
|
290
|
+
fmt = m[2] + 's'
|
291
|
+
fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[m[3]]) if color_scheme and !color_scheme.lines?
|
292
|
+
|
293
|
+
format_string << fmt
|
294
|
+
separator = m[4].to_s
|
295
|
+
separator = ' ' if separator.empty?
|
296
|
+
args << "::Logging.ndc.context.join('#{separator}')"
|
297
|
+
|
250
298
|
when *DIRECTIVE_TABLE.keys
|
251
299
|
fmt = m[2] + 's'
|
252
300
|
fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[m[3]]) if color_scheme and !color_scheme.lines?
|
@@ -254,6 +302,7 @@ module Logging::Layouts
|
|
254
302
|
format_string << fmt
|
255
303
|
format_string << "{#{m[4]}}" if m[4]
|
256
304
|
args << DIRECTIVE_TABLE[m[3]]
|
305
|
+
|
257
306
|
when nil; break
|
258
307
|
else
|
259
308
|
raise ArgumentError, "illegal format character - '#{m[3]}'"
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
|
2
3
|
require File.expand_path('../setup', File.dirname(__FILE__))
|
3
4
|
|
@@ -161,6 +162,26 @@ module TestAppenders
|
|
161
162
|
assert_nil(readline)
|
162
163
|
end
|
163
164
|
|
165
|
+
if Object.const_defined?(:Encoding)
|
166
|
+
def test_force_encoding
|
167
|
+
a = 'ümlaut'
|
168
|
+
b = 'hello ümlaut'.force_encoding('BINARY')
|
169
|
+
|
170
|
+
event_a = Logging::LogEvent.new('TestLogger', @levels['info'], a, false)
|
171
|
+
event_b = Logging::LogEvent.new('TestLogger', @levels['info'], b, false)
|
172
|
+
|
173
|
+
@appender.append event_a
|
174
|
+
@appender.append event_b
|
175
|
+
assert_nil(readline)
|
176
|
+
|
177
|
+
@appender.append event_a
|
178
|
+
assert_equal " INFO TestLogger : #{a}\n", readline
|
179
|
+
assert_equal " INFO TestLogger : #{b.force_encoding('UTF-8')}\n", readline
|
180
|
+
assert_equal " INFO TestLogger : #{a}\n", readline
|
181
|
+
assert_nil(readline)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
164
185
|
private
|
165
186
|
def readline
|
166
187
|
@appender.readline
|
data/test/appenders/test_file.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
|
2
3
|
require File.expand_path('../setup', File.dirname(__FILE__))
|
3
4
|
|
@@ -95,6 +96,25 @@ module TestAppenders
|
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
99
|
+
if Object.const_defined? :Encoding
|
100
|
+
|
101
|
+
def test_encoding
|
102
|
+
log = File.join(TMP, 'file-encoding.log')
|
103
|
+
#appender = Logging.appenders.file(NAME, :filename => log, :encoding => 'ISO-8859-16')
|
104
|
+
appender = Logging.appenders.file(NAME, :filename => log, :encoding => 'ASCII')
|
105
|
+
|
106
|
+
appender << "A normal line of text\n"
|
107
|
+
appender << "ümlaut\n"
|
108
|
+
appender.close
|
109
|
+
|
110
|
+
lines = File.readlines(log)
|
111
|
+
assert_equal "A normal line of text\n", lines[0]
|
112
|
+
assert_equal "ümlaut\n", lines[1]
|
113
|
+
|
114
|
+
cleanup
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
98
118
|
private
|
99
119
|
def cleanup
|
100
120
|
unless Logging.appenders[NAME].nil?
|
@@ -52,12 +52,9 @@ module TestAppenders
|
|
52
52
|
Process.waitpid(pid)
|
53
53
|
|
54
54
|
if defined?(::Syslog::LOG_PERROR)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
stderr[0].gets)
|
59
|
-
assert_equal("syslog_test: WARN TestLogger : this is your last warning\n",
|
60
|
-
stderr[0].gets)
|
55
|
+
assert_match(%r/INFO TestLogger : <Array> #{Regexp.escape [1,2,3,4].to_s}/, stderr[0].gets)
|
56
|
+
assert_match(%r/DEBUG TestLogger : the big log message/, stderr[0].gets)
|
57
|
+
assert_match(%r/WARN TestLogger : this is your last warning/, stderr[0].gets)
|
61
58
|
end
|
62
59
|
end
|
63
60
|
|
@@ -101,9 +98,9 @@ module TestAppenders
|
|
101
98
|
Process.waitpid(pid)
|
102
99
|
|
103
100
|
if defined?(::Syslog::LOG_PERROR)
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
assert_match(%r/this is a test message/, stderr[0].gets)
|
102
|
+
assert_match(%r/this is another message/, stderr[0].gets)
|
103
|
+
assert_match(%r/some other line/, stderr[0].gets)
|
107
104
|
end
|
108
105
|
end
|
109
106
|
|
data/test/layouts/test_json.rb
CHANGED
@@ -22,29 +22,37 @@ module TestLayouts
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_format
|
25
|
-
fmt = %Q[\\{"timestamp":"#@date_fmt","level":"%s","logger":"%s","message":"%s"\\}\\n]
|
26
|
-
|
27
25
|
event = Logging::LogEvent.new('ArrayLogger', @levels['info'],
|
28
26
|
'log message', false)
|
29
|
-
|
30
|
-
assert_match
|
27
|
+
format = @layout.format(event)
|
28
|
+
assert_match %r/"timestamp":"#@date_fmt"/, format
|
29
|
+
assert_match %r/"level":"INFO"/, format
|
30
|
+
assert_match %r/"logger":"ArrayLogger"/, format
|
31
|
+
assert_match %r/"message":"log message"/, format
|
31
32
|
|
32
33
|
event.data = [1, 2, 3, 4]
|
33
|
-
|
34
|
-
|
35
|
-
assert_match
|
34
|
+
format = @layout.format(event)
|
35
|
+
assert_match %r/"timestamp":"#@date_fmt"/, format
|
36
|
+
assert_match %r/"level":"INFO"/, format
|
37
|
+
assert_match %r/"logger":"ArrayLogger"/, format
|
38
|
+
assert_match %r/"message":\[1,2,3,4\]/, format
|
36
39
|
|
37
40
|
event.level = @levels['debug']
|
38
41
|
event.data = 'and another message'
|
39
|
-
|
40
|
-
|
41
|
-
assert_match
|
42
|
+
format = @layout.format(event)
|
43
|
+
assert_match %r/"timestamp":"#@date_fmt"/, format
|
44
|
+
assert_match %r/"level":"DEBUG"/, format
|
45
|
+
assert_match %r/"logger":"ArrayLogger"/, format
|
46
|
+
assert_match %r/"message":"and another message"/, format
|
42
47
|
|
43
48
|
event.logger = 'Test'
|
44
49
|
event.level = @levels['fatal']
|
45
50
|
event.data = Exception.new
|
46
|
-
|
47
|
-
assert_match
|
51
|
+
format = @layout.format(event)
|
52
|
+
assert_match %r/"timestamp":"#@date_fmt"/, format
|
53
|
+
assert_match %r/"level":"FATAL"/, format
|
54
|
+
assert_match %r/"logger":"Test"/, format
|
55
|
+
assert_match %r/"message":\{(?:"(?:class|message)":"Exception",?){2}\}/, format
|
48
56
|
end
|
49
57
|
|
50
58
|
def test_items
|
@@ -103,6 +111,56 @@ module TestLayouts
|
|
103
111
|
assert_equal %Q[{"thread":null}\n], @layout.format(event)
|
104
112
|
Thread.current[:name] = "Main"
|
105
113
|
assert_equal %Q[{"thread":"Main"}\n], @layout.format(event)
|
114
|
+
|
115
|
+
@layout.items = %w[mdc]
|
116
|
+
assert_match %r/\A\{"mdc":\{\}\}\n\z/, @layout.format(event)
|
117
|
+
|
118
|
+
@layout.items = %w[ndc]
|
119
|
+
assert_match %r/\A\{"ndc":\[\]\}\n\z/, @layout.format(event)
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_mdc_output
|
123
|
+
event = Logging::LogEvent.new('TestLogger', @levels['info'],
|
124
|
+
'log message', false)
|
125
|
+
Logging.mdc['X-Session'] = '123abc'
|
126
|
+
Logging.mdc['Cookie'] = 'monster'
|
127
|
+
|
128
|
+
@layout.items = %w[timestamp level logger message mdc]
|
129
|
+
|
130
|
+
format = @layout.format(event)
|
131
|
+
assert_match %r/"timestamp":"#@date_fmt"/, format
|
132
|
+
assert_match %r/"level":"INFO"/, format
|
133
|
+
assert_match %r/"logger":"TestLogger"/, format
|
134
|
+
assert_match %r/"message":"log message"/, format
|
135
|
+
assert_match %r/"mdc":\{(?:(?:"X-Session":"123abc"|"Cookie":"monster"),?){2}\}/, format
|
136
|
+
|
137
|
+
Logging.mdc.delete 'Cookie'
|
138
|
+
format = @layout.format(event)
|
139
|
+
assert_match %r/"mdc":\{"X-Session":"123abc"\}/, format
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_ndc_output
|
143
|
+
event = Logging::LogEvent.new('TestLogger', @levels['info'],
|
144
|
+
'log message', false)
|
145
|
+
Logging.ndc << 'context a'
|
146
|
+
Logging.ndc << 'context b'
|
147
|
+
|
148
|
+
@layout.items = %w[timestamp level logger message ndc]
|
149
|
+
|
150
|
+
format = @layout.format(event)
|
151
|
+
assert_match %r/"timestamp":"#@date_fmt"/, format
|
152
|
+
assert_match %r/"level":"INFO"/, format
|
153
|
+
assert_match %r/"logger":"TestLogger"/, format
|
154
|
+
assert_match %r/"message":"log message"/, format
|
155
|
+
assert_match %r/"ndc":\["context a","context b"\]/, format
|
156
|
+
|
157
|
+
Logging.ndc.pop
|
158
|
+
format = @layout.format(event)
|
159
|
+
assert_match %r/"ndc":\["context a"\]/, format
|
160
|
+
|
161
|
+
Logging.ndc.pop
|
162
|
+
format = @layout.format(event)
|
163
|
+
assert_match %r/"ndc":\[\]/, format
|
106
164
|
end
|
107
165
|
|
108
166
|
end # class TestJson
|