logging 1.7.2 → 1.8.0
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/.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
|