lumberjack 2.0.5 → 2.1.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.
- checksums.yaml +4 -4
- data/ARCHITECTURE.md +0 -17
- data/CHANGELOG.md +52 -0
- data/README.md +35 -0
- data/UPGRADE_GUIDE.md +1 -1
- data/VERSION +1 -1
- data/lib/lumberjack/attribute_formatter.rb +0 -51
- data/lib/lumberjack/context.rb +6 -1
- data/lib/lumberjack/context_locals.rb +10 -2
- data/lib/lumberjack/context_logger.rb +14 -4
- data/lib/lumberjack/device/buffer.rb +52 -37
- data/lib/lumberjack/device/multi.rb +2 -2
- data/lib/lumberjack/device/test.rb +70 -13
- data/lib/lumberjack/device/writer.rb +35 -12
- data/lib/lumberjack/device.rb +0 -2
- data/lib/lumberjack/device_registry.rb +7 -6
- data/lib/lumberjack/entry_formatter.rb +4 -2
- data/lib/lumberjack/formatter/structured_formatter.rb +4 -4
- data/lib/lumberjack/formatter/tags_formatter.rb +3 -3
- data/lib/lumberjack/formatter.rb +0 -11
- data/lib/lumberjack/formatter_registry.rb +7 -6
- data/lib/lumberjack/log_entry.rb +13 -46
- data/lib/lumberjack/log_entry_matcher/indifferent_hash.rb +83 -0
- data/lib/lumberjack/log_entry_matcher/score.rb +12 -4
- data/lib/lumberjack/log_entry_matcher.rb +229 -28
- data/lib/lumberjack/logger.rb +19 -185
- data/lib/lumberjack/template.rb +65 -42
- data/lib/lumberjack/template_registry.rb +7 -6
- data/lib/lumberjack/utils.rb +1 -26
- data/lib/lumberjack.rb +5 -25
- data/lumberjack.gemspec +0 -2
- metadata +3 -22
- data/lib/lumberjack/device/date_rolling_log_file.rb +0 -22
- data/lib/lumberjack/device/size_rolling_log_file.rb +0 -22
- data/lib/lumberjack/formatter/tagged_message.rb +0 -17
- data/lib/lumberjack/tag_context.rb +0 -15
- data/lib/lumberjack/tag_formatter.rb +0 -31
- data/lib/lumberjack/tags.rb +0 -36
|
@@ -54,7 +54,11 @@ module Lumberjack
|
|
|
54
54
|
# @option options [Boolean] :colorize (false) Whether to colorize log output
|
|
55
55
|
def initialize(stream, options = {})
|
|
56
56
|
@stream = stream
|
|
57
|
-
@
|
|
57
|
+
@autoflush = options[:autoflush] != false
|
|
58
|
+
# Only turn sync on; the stream may be shared with other code (i.e. $stdout), so
|
|
59
|
+
# disabling autoflush must not change how anything else writing to it behaves.
|
|
60
|
+
@stream.sync = true if @autoflush && @stream.respond_to?(:sync=)
|
|
61
|
+
@lock = Mutex.new
|
|
58
62
|
|
|
59
63
|
@binmode = options[:binmode]
|
|
60
64
|
|
|
@@ -107,8 +111,10 @@ module Lumberjack
|
|
|
107
111
|
#
|
|
108
112
|
# @return [void]
|
|
109
113
|
def close
|
|
114
|
+
# Call the public flush so that subclasses that override it still get a chance to
|
|
115
|
+
# drain whatever they buffer. It must be called outside the lock since it takes it.
|
|
110
116
|
flush
|
|
111
|
-
stream.close
|
|
117
|
+
@lock.synchronize { stream.close }
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
# Flush the underlying stream to ensure all buffered data is written to the
|
|
@@ -117,7 +123,9 @@ module Lumberjack
|
|
|
117
123
|
#
|
|
118
124
|
# @return [void]
|
|
119
125
|
def flush
|
|
120
|
-
|
|
126
|
+
@lock.synchronize do
|
|
127
|
+
stream.flush if stream.respond_to?(:flush)
|
|
128
|
+
end
|
|
121
129
|
end
|
|
122
130
|
|
|
123
131
|
# Get the current datetime format from the template if supported. Returns the
|
|
@@ -164,7 +172,20 @@ module Lumberjack
|
|
|
164
172
|
# The underlying stream object that is being written to.
|
|
165
173
|
#
|
|
166
174
|
# @return [IO] The current stream object
|
|
167
|
-
|
|
175
|
+
attr_reader :stream
|
|
176
|
+
|
|
177
|
+
# Replace the underlying stream. The swap is synchronized against in-flight
|
|
178
|
+
# writes so a log line is never split across the old and new streams, and the
|
|
179
|
+
# autoflush setting is re-applied to the new stream.
|
|
180
|
+
#
|
|
181
|
+
# @param value [IO, #write] The new stream to write to
|
|
182
|
+
# @return [void]
|
|
183
|
+
def stream=(value)
|
|
184
|
+
@lock.synchronize do
|
|
185
|
+
value.sync = true if @autoflush && value.respond_to?(:sync=)
|
|
186
|
+
@stream = value
|
|
187
|
+
end
|
|
188
|
+
end
|
|
168
189
|
|
|
169
190
|
private
|
|
170
191
|
|
|
@@ -177,16 +198,18 @@ module Lumberjack
|
|
|
177
198
|
def write_to_stream(line)
|
|
178
199
|
out = line.end_with?(Lumberjack::LINE_SEPARATOR) ? line : "#{line}#{Lumberjack::LINE_SEPARATOR}"
|
|
179
200
|
begin
|
|
180
|
-
|
|
181
|
-
stream
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
201
|
+
@lock.synchronize do
|
|
202
|
+
target = stream
|
|
203
|
+
begin
|
|
204
|
+
target.write(out)
|
|
205
|
+
rescue IOError => e
|
|
206
|
+
raise e if target.closed?
|
|
207
|
+
|
|
208
|
+
target.write(out)
|
|
209
|
+
end
|
|
186
210
|
end
|
|
187
211
|
rescue => e
|
|
188
|
-
$stderr.write(error_message(e))
|
|
189
|
-
$stderr.write(out)
|
|
212
|
+
$stderr.write("#{error_message(e)}#{out}")
|
|
190
213
|
end
|
|
191
214
|
end
|
|
192
215
|
|
data/lib/lumberjack/device.rb
CHANGED
|
@@ -32,8 +32,6 @@ module Lumberjack
|
|
|
32
32
|
require_relative "device/null"
|
|
33
33
|
require_relative "device/test"
|
|
34
34
|
require_relative "device/buffer"
|
|
35
|
-
require_relative "device/size_rolling_log_file"
|
|
36
|
-
require_relative "device/date_rolling_log_file"
|
|
37
35
|
|
|
38
36
|
class << self
|
|
39
37
|
# Open a logging device with the given options.
|
|
@@ -17,6 +17,7 @@ module Lumberjack
|
|
|
17
17
|
# logger = Lumberjack::Logger.new(:my_device)
|
|
18
18
|
module DeviceRegistry
|
|
19
19
|
@registry = {stdout: :stdout, stderr: :stderr}
|
|
20
|
+
@lock = Mutex.new
|
|
20
21
|
|
|
21
22
|
class << self
|
|
22
23
|
# Register a device name. Device names can be used to associate a symbol with a device
|
|
@@ -31,7 +32,7 @@ module Lumberjack
|
|
|
31
32
|
def add(name, klass)
|
|
32
33
|
raise ArgumentError.new("name must be a symbol") unless name.is_a?(Symbol)
|
|
33
34
|
|
|
34
|
-
@registry[name] = klass
|
|
35
|
+
@lock.synchronize { @registry[name] = klass }
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
# Remove a device from the registry.
|
|
@@ -39,7 +40,7 @@ module Lumberjack
|
|
|
39
40
|
# @param name [Symbol] The name of the device to remove
|
|
40
41
|
# @return [void]
|
|
41
42
|
def remove(name)
|
|
42
|
-
@registry.delete(name)
|
|
43
|
+
@lock.synchronize { @registry.delete(name) }
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
# Check if a device is registered.
|
|
@@ -47,7 +48,7 @@ module Lumberjack
|
|
|
47
48
|
# @param name [Symbol] The name of the device
|
|
48
49
|
# @return [Boolean] True if the device is registered, false otherwise
|
|
49
50
|
def registered?(name)
|
|
50
|
-
@registry.include?(name)
|
|
51
|
+
@lock.synchronize { @registry.include?(name) }
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
# Instantiate a new device with the specified options from the device registry.
|
|
@@ -58,7 +59,7 @@ module Lumberjack
|
|
|
58
59
|
def new_device(name, options)
|
|
59
60
|
klass = device_class(name)
|
|
60
61
|
unless klass
|
|
61
|
-
valid_names =
|
|
62
|
+
valid_names = registered_devices.keys.map(&:inspect).join(", ")
|
|
62
63
|
raise ArgumentError.new("#{name.inspect} is not registered as a device name; valid names are: #{valid_names}")
|
|
63
64
|
end
|
|
64
65
|
|
|
@@ -76,14 +77,14 @@ module Lumberjack
|
|
|
76
77
|
# @param name [Symbol] The name of the device
|
|
77
78
|
# @return [Class, nil] The registered device class or nil if not found
|
|
78
79
|
def device_class(name)
|
|
79
|
-
@registry[name]
|
|
80
|
+
@lock.synchronize { @registry[name] }
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
# Return the map of registered device class names.
|
|
83
84
|
#
|
|
84
85
|
# @return [Hash]
|
|
85
86
|
def registered_devices
|
|
86
|
-
@registry.dup
|
|
87
|
+
@lock.synchronize { @registry.dup }
|
|
87
88
|
end
|
|
88
89
|
end
|
|
89
90
|
end
|
|
@@ -293,8 +293,10 @@ module Lumberjack
|
|
|
293
293
|
message_attributes = Utils.flatten_attributes(message_attributes) if message_attributes
|
|
294
294
|
|
|
295
295
|
attributes = merge_attributes(attributes, message_attributes) if message_attributes
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
if attributes
|
|
297
|
+
attributes = AttributesHelper.expand_runtime_values(attributes)
|
|
298
|
+
attributes = attribute_formatter.format(attributes) if attribute_formatter
|
|
299
|
+
end
|
|
298
300
|
|
|
299
301
|
[message, attributes]
|
|
300
302
|
end
|
|
@@ -16,7 +16,7 @@ module Lumberjack
|
|
|
16
16
|
|
|
17
17
|
# Exception raised when a circular reference is detected during traversal.
|
|
18
18
|
# This prevents infinite recursion when formatting objects that reference themselves.
|
|
19
|
-
class
|
|
19
|
+
class RecursiveReferenceError < StandardError
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# @param formatter [Formatter, nil] The formatter to call on each element
|
|
@@ -42,7 +42,7 @@ module Lumberjack
|
|
|
42
42
|
hash = {}
|
|
43
43
|
obj.each do |name, value|
|
|
44
44
|
value = call_with_references(value, references)
|
|
45
|
-
hash[name.to_s] = value unless value.is_a?(
|
|
45
|
+
hash[name.to_s] = value unless value.is_a?(RecursiveReferenceError)
|
|
46
46
|
end
|
|
47
47
|
hash
|
|
48
48
|
end
|
|
@@ -51,7 +51,7 @@ module Lumberjack
|
|
|
51
51
|
array = []
|
|
52
52
|
obj.each do |value|
|
|
53
53
|
value = call_with_references(value, references)
|
|
54
|
-
array << value unless value.is_a?(
|
|
54
|
+
array << value unless value.is_a?(RecursiveReferenceError)
|
|
55
55
|
end
|
|
56
56
|
array
|
|
57
57
|
end
|
|
@@ -64,7 +64,7 @@ module Lumberjack
|
|
|
64
64
|
|
|
65
65
|
def with_object_reference(obj, references)
|
|
66
66
|
if obj.is_a?(Enumerable)
|
|
67
|
-
return
|
|
67
|
+
return RecursiveReferenceError.new if references.include?(obj.object_id)
|
|
68
68
|
|
|
69
69
|
references << obj.object_id
|
|
70
70
|
begin
|
|
@@ -13,7 +13,7 @@ module Lumberjack
|
|
|
13
13
|
def call(tags)
|
|
14
14
|
tags = tags.collect { |key, value| "#{key}=#{value}" } if tags.is_a?(Hash)
|
|
15
15
|
if tags.is_a?(Array)
|
|
16
|
-
tags.collect { |tag| format_tag(tag) }.join(" ")
|
|
16
|
+
tags.collect { |tag| format_tag(tag) }.join(" ")
|
|
17
17
|
else
|
|
18
18
|
format_tag(tags)
|
|
19
19
|
end
|
|
@@ -23,9 +23,9 @@ module Lumberjack
|
|
|
23
23
|
|
|
24
24
|
def format_tag(tag)
|
|
25
25
|
if tag.is_a?(Hash)
|
|
26
|
-
tag.collect { |key, value| "[#{key}=#{value.strip}]" }.join(" ")
|
|
26
|
+
tag.collect { |key, value| "[#{key}=#{value.to_s.strip}]" }.join(" ")
|
|
27
27
|
else
|
|
28
|
-
"[#{tag.strip}]"
|
|
28
|
+
"[#{tag.to_s.strip}]"
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
data/lib/lumberjack/formatter.rb
CHANGED
|
@@ -46,7 +46,6 @@ module Lumberjack
|
|
|
46
46
|
require_relative "formatter/structured_formatter"
|
|
47
47
|
require_relative "formatter/tags_formatter"
|
|
48
48
|
require_relative "formatter/truncate_formatter"
|
|
49
|
-
require_relative "formatter/tagged_message"
|
|
50
49
|
|
|
51
50
|
class << self
|
|
52
51
|
# Build a new formatter using a configuration block. The block receives the new formatter
|
|
@@ -67,16 +66,6 @@ module Lumberjack
|
|
|
67
66
|
formatter
|
|
68
67
|
end
|
|
69
68
|
|
|
70
|
-
# Create a new empty formatter with no mappings. This is an alias for #new.
|
|
71
|
-
#
|
|
72
|
-
# @return [Lumberjack::Formatter] A new formatter with no default mappings.
|
|
73
|
-
# @deprecated Use #new instead.
|
|
74
|
-
def empty
|
|
75
|
-
Utils.deprecated("Formatter.empty", "Lumberjack::Formatter.empty is deprecated and will be removed in version 2.1; use new instead.") do
|
|
76
|
-
new
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
69
|
# Create a new formatter with default mappings.
|
|
81
70
|
#
|
|
82
71
|
# Object: inspect formatter
|
|
@@ -18,6 +18,7 @@ module Lumberjack
|
|
|
18
18
|
# end
|
|
19
19
|
module FormatterRegistry
|
|
20
20
|
@registry = {}
|
|
21
|
+
@lock = Mutex.new
|
|
21
22
|
|
|
22
23
|
class << self
|
|
23
24
|
# Register a formatter name. Formatter names can be used to associate a symbol with a formatter
|
|
@@ -37,7 +38,7 @@ module Lumberjack
|
|
|
37
38
|
formatter ||= block
|
|
38
39
|
raise ArgumentError.new("formatter must be a class or respond to call") unless formatter.is_a?(Class) || formatter.respond_to?(:call)
|
|
39
40
|
|
|
40
|
-
@registry[name] = formatter
|
|
41
|
+
@lock.synchronize { @registry[name] = formatter }
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
# Remove a formatter from the registry.
|
|
@@ -45,7 +46,7 @@ module Lumberjack
|
|
|
45
46
|
# @param name [Symbol] The name of the formatter to remove
|
|
46
47
|
# @return [void]
|
|
47
48
|
def remove(name)
|
|
48
|
-
@registry.delete(name)
|
|
49
|
+
@lock.synchronize { @registry.delete(name) }
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
# Check if a formatter is registered.
|
|
@@ -53,7 +54,7 @@ module Lumberjack
|
|
|
53
54
|
# @param name [Symbol] The name of the formatter
|
|
54
55
|
# @return [Boolean] True if the formatter is registered, false otherwise
|
|
55
56
|
def registered?(name)
|
|
56
|
-
@registry.include?(name)
|
|
57
|
+
@lock.synchronize { @registry.include?(name) }
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
# Retrieve the formatter registered with the given name or nil if the name is not defined.
|
|
@@ -61,10 +62,10 @@ module Lumberjack
|
|
|
61
62
|
# @param name [Symbol] The name of the formatter
|
|
62
63
|
# @return [#call, nil] The registered formatter class or nil if not found
|
|
63
64
|
def formatter(name, *args)
|
|
64
|
-
instance = @registry[name]
|
|
65
|
+
instance = @lock.synchronize { @registry[name] }
|
|
65
66
|
|
|
66
67
|
if instance.nil?
|
|
67
|
-
valid_names =
|
|
68
|
+
valid_names = registered_formatters.keys.map(&:inspect).join(", ")
|
|
68
69
|
raise ArgumentError.new("#{name.inspect} is not registered as a formatter name; valid names are: #{valid_names}")
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -77,7 +78,7 @@ module Lumberjack
|
|
|
77
78
|
#
|
|
78
79
|
# @return [Hash]
|
|
79
80
|
def registered_formatters
|
|
80
|
-
@registry.dup
|
|
81
|
+
@lock.synchronize { @registry.dup }
|
|
81
82
|
end
|
|
82
83
|
end
|
|
83
84
|
end
|
data/lib/lumberjack/log_entry.rb
CHANGED
|
@@ -94,17 +94,6 @@ module Lumberjack
|
|
|
94
94
|
attributes == other.attributes
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
# Alias for tags to provide backward compatibility with version 1.x API. This method
|
|
98
|
-
# will eventually be removed.
|
|
99
|
-
#
|
|
100
|
-
# @return [Hash, nil] The attributes of the log entry.
|
|
101
|
-
# @deprecated Use {#attributes} instead.
|
|
102
|
-
def tags
|
|
103
|
-
Utils.deprecated("LogEntry#tags", "Lumberjack::LogEntry#tags is deprecated and will be removed in version 2.1; use attributes instead.") do
|
|
104
|
-
attributes
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
97
|
# Access an attribute value by name. Supports both simple and nested attribute
|
|
109
98
|
# access using dot notation for hierarchical data structures.
|
|
110
99
|
#
|
|
@@ -116,17 +105,6 @@ module Lumberjack
|
|
|
116
105
|
AttributesHelper.new(attributes)[name]
|
|
117
106
|
end
|
|
118
107
|
|
|
119
|
-
# Alias method for #[] to provide backward compatibility with version 1.x API. This
|
|
120
|
-
# method will eventually be removed.
|
|
121
|
-
#
|
|
122
|
-
# @return [Hash]
|
|
123
|
-
# @deprecated Use {#[]} instead.
|
|
124
|
-
def tag(name)
|
|
125
|
-
Utils.deprecated("LogEntry#tag", "Lumberjack::LogEntry#tag is deprecated and will be removed in version 2.1; use [] instead.") do
|
|
126
|
-
self[name]
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
108
|
# Expand flat attributes with dot notation into a nested hash structure.
|
|
131
109
|
# Attributes containing dots in their names are converted into hierarchical
|
|
132
110
|
# nested hashes for structured data representation.
|
|
@@ -136,17 +114,6 @@ module Lumberjack
|
|
|
136
114
|
Utils.expand_attributes(attributes)
|
|
137
115
|
end
|
|
138
116
|
|
|
139
|
-
# Alias for nested_attributes to provide API compatibility with version 1.x.
|
|
140
|
-
# This method will eventually be removed.
|
|
141
|
-
#
|
|
142
|
-
# @return [Hash]
|
|
143
|
-
# @deprecated Use {#nested_attributes} instead.
|
|
144
|
-
def nested_tags
|
|
145
|
-
Utils.deprecated("LogEntry#nested_tags", "Lumberjack::LogEntry#nested_tags is deprecated and will be removed in version 2.1; use nested_attributes instead.") do
|
|
146
|
-
nested_attributes
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
117
|
# Determine if the log entry contains no meaningful content. An entry is
|
|
151
118
|
# considered empty if it has no message content and no attributes.
|
|
152
119
|
#
|
|
@@ -183,30 +150,30 @@ module Lumberjack
|
|
|
183
150
|
attributes_string
|
|
184
151
|
end
|
|
185
152
|
|
|
186
|
-
# Flatten nested attributes and remove empty values.
|
|
153
|
+
# Flatten nested attributes and remove empty values. The returned hash is always
|
|
154
|
+
# a new copy so that the entry does not alias a hash owned by the caller (such as
|
|
155
|
+
# a live logger context hash that could be mutated after the entry is created).
|
|
187
156
|
#
|
|
188
157
|
# @param attributes [Hash] The attributes hash to compact
|
|
189
158
|
# @return [Hash] The flattened attributes with empty values removed
|
|
190
159
|
def flatten_attributes(attributes)
|
|
191
|
-
|
|
192
|
-
attributes = Utils.flatten_attributes(attributes)
|
|
193
|
-
end
|
|
194
|
-
|
|
160
|
+
needs_flattening = false
|
|
195
161
|
delete_keys = nil
|
|
196
162
|
attributes.each do |key, value|
|
|
197
|
-
if
|
|
198
|
-
|
|
199
|
-
delete_keys << key
|
|
200
|
-
elsif value.is_a?(Array) && value.empty?
|
|
163
|
+
needs_flattening = true if !key.is_a?(String) || value.is_a?(Hash)
|
|
164
|
+
if value.nil? || value == "" || (value.is_a?(Array) && value.empty?)
|
|
201
165
|
delete_keys ||= []
|
|
202
166
|
delete_keys << key
|
|
203
167
|
end
|
|
204
168
|
end
|
|
205
169
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
170
|
+
if needs_flattening
|
|
171
|
+
attributes = Utils.flatten_attributes(attributes)
|
|
172
|
+
attributes.delete_if { |_key, value| value.nil? || value == "" || (value.is_a?(Array) && value.empty?) }
|
|
173
|
+
else
|
|
174
|
+
attributes = attributes.dup
|
|
175
|
+
delete_keys&.each { |key| attributes.delete(key) }
|
|
176
|
+
end
|
|
210
177
|
|
|
211
178
|
attributes
|
|
212
179
|
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A minimal hash implementation that allows values to be looked up with either
|
|
4
|
+
# string or symbol keys.
|
|
5
|
+
#
|
|
6
|
+
# The keys stored in the hash are left exactly as they are; only lookups are
|
|
7
|
+
# indifferent. Log entry attributes are matched with string keys, but matchers
|
|
8
|
+
# that do their own key lookups on the attributes hash (i.e. RSpec's
|
|
9
|
+
# `hash_including`) have no way of knowing that. Wrapping the attributes in this
|
|
10
|
+
# class lets those matchers use either form.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
class Lumberjack::LogEntryMatcher::IndifferentHash < Hash
|
|
14
|
+
class << self
|
|
15
|
+
# Recursively wrap a value so that any hashes in it, including hashes nested
|
|
16
|
+
# inside arrays, allow indifferent key lookups. Values that are not hashes or
|
|
17
|
+
# arrays are returned as is.
|
|
18
|
+
#
|
|
19
|
+
# @param value [Object] The value to wrap.
|
|
20
|
+
# @return [Object] The wrapped value.
|
|
21
|
+
def wrap(value)
|
|
22
|
+
case value
|
|
23
|
+
when self
|
|
24
|
+
value
|
|
25
|
+
when Hash
|
|
26
|
+
value.each_with_object(new) { |(key, val), hash| hash[key] = wrap(val) }
|
|
27
|
+
when Array
|
|
28
|
+
value.collect { |val| wrap(val) }
|
|
29
|
+
else
|
|
30
|
+
value
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Capture the unaliased implementation so the overrides below can check for a
|
|
36
|
+
# key without recursing back into themselves.
|
|
37
|
+
alias_method :stored_key?, :key?
|
|
38
|
+
private :stored_key?
|
|
39
|
+
|
|
40
|
+
def [](key)
|
|
41
|
+
super(resolve_key(key))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fetch(key, *args, &block)
|
|
45
|
+
super(resolve_key(key), *args, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def dig(key, *rest)
|
|
49
|
+
super(resolve_key(key), *rest)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def values_at(*keys)
|
|
53
|
+
super(*keys.collect { |key| resolve_key(key) })
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def key?(key)
|
|
57
|
+
stored_key?(resolve_key(key))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
alias_method :has_key?, :key?
|
|
61
|
+
alias_method :include?, :key?
|
|
62
|
+
alias_method :member?, :key?
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# Return the key as it is stored in the hash. If the key is not present in
|
|
67
|
+
# either its string or symbol form, then the key itself is returned so that
|
|
68
|
+
# normal missing key semantics apply.
|
|
69
|
+
#
|
|
70
|
+
# @param key [Object] The key being looked up.
|
|
71
|
+
# @return [Object] The key to use for the lookup.
|
|
72
|
+
def resolve_key(key)
|
|
73
|
+
return key if stored_key?(key)
|
|
74
|
+
|
|
75
|
+
alternate = if key.is_a?(String)
|
|
76
|
+
key.to_sym
|
|
77
|
+
elsif key.is_a?(Symbol)
|
|
78
|
+
key.to_s
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
(alternate && stored_key?(alternate)) ? alternate : key
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -47,9 +47,15 @@ class Lumberjack::LogEntryMatcher::Score
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Check attributes match
|
|
50
|
-
if attributes.is_a?(Hash)
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
if attributes.is_a?(Hash)
|
|
51
|
+
unless attributes.empty?
|
|
52
|
+
scores << calculate_attributes_score(entry.attributes, attributes)
|
|
53
|
+
weights << 0.3
|
|
54
|
+
end
|
|
55
|
+
elsif attributes
|
|
56
|
+
# Matchers like RSpec's hash_including are applied to the attributes hash as a whole.
|
|
57
|
+
entry_attributes = Lumberjack::LogEntryMatcher::IndifferentHash.wrap(Lumberjack::Utils.expand_attributes(entry.attributes))
|
|
58
|
+
scores << calculate_field_score(entry_attributes, attributes)
|
|
53
59
|
weights << 0.3
|
|
54
60
|
end
|
|
55
61
|
|
|
@@ -132,7 +138,9 @@ class Lumberjack::LogEntryMatcher::Score
|
|
|
132
138
|
return 0.0 unless entry_attributes && attributes_filter.is_a?(Hash)
|
|
133
139
|
|
|
134
140
|
attributes_filter = deep_stringify_keys(Lumberjack::Utils.expand_attributes(attributes_filter))
|
|
135
|
-
attributes =
|
|
141
|
+
attributes = Lumberjack::LogEntryMatcher::IndifferentHash.wrap(
|
|
142
|
+
deep_stringify_keys(Lumberjack::Utils.expand_attributes(entry_attributes))
|
|
143
|
+
)
|
|
136
144
|
|
|
137
145
|
total_attribute_filters = count_attribute_filters(attributes_filter)
|
|
138
146
|
return 0.0 if total_attribute_filters == 0
|