tty-logger 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +69 -8
- data/lib/tty/logger.rb +14 -10
- data/lib/tty/logger/config.rb +34 -3
- data/lib/tty/logger/data_filter.rb +121 -0
- data/lib/tty/logger/handlers/console.rb +13 -3
- data/lib/tty/logger/version.rb +1 -1
- data/tty-logger.gemspec +4 -6
- metadata +4 -38
- data/Rakefile +0 -8
- data/examples/child.rb +0 -9
- data/examples/console.rb +0 -22
- data/examples/custom_type.rb +0 -27
- data/examples/error.rb +0 -11
- data/examples/handler.rb +0 -19
- data/examples/log.rb +0 -9
- data/examples/output.rb +0 -15
- data/examples/override.rb +0 -29
- data/examples/stream.rb +0 -22
- data/spec/perf/json_formatter_spec.rb +0 -29
- data/spec/perf/log_spec.rb +0 -21
- data/spec/perf/text_formatter_spec.rb +0 -29
- data/spec/spec_helper.rb +0 -31
- data/spec/unit/add_handler_spec.rb +0 -25
- data/spec/unit/config_spec.rb +0 -109
- data/spec/unit/copy_spec.rb +0 -27
- data/spec/unit/event_spec.rb +0 -22
- data/spec/unit/exception_spec.rb +0 -45
- data/spec/unit/filter_spec.rb +0 -32
- data/spec/unit/formatter_spec.rb +0 -70
- data/spec/unit/formatters/json_spec.rb +0 -41
- data/spec/unit/formatters/text_spec.rb +0 -82
- data/spec/unit/handler_spec.rb +0 -83
- data/spec/unit/handlers/custom_spec.rb +0 -26
- data/spec/unit/handlers/null_spec.rb +0 -15
- data/spec/unit/handlers/stream_spec.rb +0 -72
- data/spec/unit/levels_spec.rb +0 -40
- data/spec/unit/log_at_spec.rb +0 -34
- data/spec/unit/log_metadata_spec.rb +0 -55
- data/spec/unit/log_spec.rb +0 -203
- data/spec/unit/output_spec.rb +0 -40
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af3524110f6873609fc2086063371a728bd63d08d9f9160c8469cef4e5e50d94
|
4
|
+
data.tar.gz: 4991ed5e387b34fbe8c2e1887ba36ce53e5bc3bc120f957eda6a778202abcf35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66c6892da31f674bcdee76a92334976f19f4447e341cf61c5fce106d5ad7085035d5a580c80f614bc564f4f6567ae6a446aacfe17d7499fb7c65fd4e800d0003
|
7
|
+
data.tar.gz: 45ee01d044d21a79a85fe55933ef3da2c3aefd58cea31453d61f7007768687e9256447e214de0f90973dc4de136f35eba735e9619c0c17aa9d74b05a32d84090
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## [v0.3.0] - 2020-01-01
|
4
|
+
|
5
|
+
### Added
|
6
|
+
* Add ability to filter sensitive information out of structured data
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
* Remove the test and task files from the gemspec
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
* Fix console handler highlighting of nested hash keys
|
13
|
+
|
3
14
|
## [v0.2.0] - 2019-09-30
|
4
15
|
|
5
16
|
### Added
|
@@ -16,5 +27,6 @@
|
|
16
27
|
|
17
28
|
* Initial implementation and release
|
18
29
|
|
30
|
+
[v0.3.0]: https://github.com/piotrmurach/tty-logger/compare/v0.2.0..v0.3.0
|
19
31
|
[v0.2.0]: https://github.com/piotrmurach/tty-logger/compare/v0.1.0..v0.2.0
|
20
32
|
[v0.1.0]: https://github.com/piotrmurach/tty-logger/compare/v0.1.0
|
data/README.md
CHANGED
@@ -375,15 +375,17 @@ The `:metdata` configuration option can include the following symbols:
|
|
375
375
|
|
376
376
|
### 2.4.2 Filters
|
377
377
|
|
378
|
-
You can filter sensitive data out of log output with `filters` configuration option.
|
378
|
+
You can filter sensitive data out of log output with `filters` configuration option. The `filters` can be further configured to remove info from log message with `message` or structured data with `data`. Both methods, as a value accept a list of sensitive items to search for.
|
379
|
+
|
380
|
+
If you want to filter sensitive information from log messages use `message`:
|
379
381
|
|
380
382
|
```ruby
|
381
383
|
logger = TTY::Logger.new(output: output) do |config|
|
382
|
-
config.filters = [
|
384
|
+
config.filters.message = %w[secret password]
|
383
385
|
end
|
384
386
|
```
|
385
387
|
|
386
|
-
Which by default will replace each
|
388
|
+
Which by default will replace each matching string with `[FILTERED]` placeholder:
|
387
389
|
|
388
390
|
```ruby
|
389
391
|
logger.info("Super secret info with password")
|
@@ -391,13 +393,14 @@ logger.info("Super secret info with password")
|
|
391
393
|
# ℹ info Super [FILTERED] info with [FILTERED]
|
392
394
|
```
|
393
395
|
|
394
|
-
You can also replace each data item with a custom placeholder. To do so use a
|
396
|
+
You can also replace each data item with a custom placeholder. To do so use a `:mask` keyword with a replacement placeholder.
|
395
397
|
|
396
|
-
For example, to replace "secret" content with placeholder "<SECRET>" do:
|
398
|
+
For example, to replace "secret" content with placeholder `"<SECRET>"` do:
|
397
399
|
|
398
400
|
```ruby
|
399
401
|
logger = TTY::Logger.new do |config|
|
400
|
-
config.filters =
|
402
|
+
config.filters.message = %w[secret]
|
403
|
+
config.filters.mask = "<SECRET>"
|
401
404
|
end
|
402
405
|
```
|
403
406
|
|
@@ -409,6 +412,64 @@ logger.info("Super secret info")
|
|
409
412
|
# ℹ info Super <SECRET> info
|
410
413
|
```
|
411
414
|
|
415
|
+
To filter out sensitive information out of structured data use `data` method. By default any value matching a parameter name will be filtered regardless of the level of nesting. If you wish to filter only a specific deeply nested key use a dot notation like `params.card.password` to only filter `{params: {card: {password: "Secret123"}}}`.
|
416
|
+
|
417
|
+
For example to filter out a `:password` from data do:
|
418
|
+
|
419
|
+
```ruby
|
420
|
+
logger = TTY::Logger.new do |config|
|
421
|
+
config.filters.data = %i[password]
|
422
|
+
end
|
423
|
+
```
|
424
|
+
|
425
|
+
This will filter out any key matching password:
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
logger.info("Secret info", password: "Secret123", email: "")
|
429
|
+
# =>
|
430
|
+
# ℹ info Secret info password="[FILTERED]" email="secret@example.com"
|
431
|
+
```
|
432
|
+
|
433
|
+
But also any nested data item:
|
434
|
+
|
435
|
+
```ruby
|
436
|
+
logger.info("Secret info", params: {password: "Secret123", email: ""})
|
437
|
+
# =>
|
438
|
+
# ℹ info Secret info params={password="[FILTERED]" email="secret@example.com"}
|
439
|
+
```
|
440
|
+
|
441
|
+
You're not limited to using only direct string comparison. You can also match based on regular expressions. For example, to match keys starting with `ba` we can add a following filter:
|
442
|
+
|
443
|
+
```ruby
|
444
|
+
logger = TTY::Logger.new do |config|
|
445
|
+
config.filters.data = [/ba/]
|
446
|
+
end
|
447
|
+
```
|
448
|
+
|
449
|
+
Then appropriate values will be masked:
|
450
|
+
|
451
|
+
```ruby
|
452
|
+
logger.info("Filtering data", {"foo" => {"bar" => "val", "baz" => "val"}})
|
453
|
+
# =>
|
454
|
+
# ℹ info Filtering data foo={bar="[FILTERED]" baz="[FILTERED]"}
|
455
|
+
```
|
456
|
+
|
457
|
+
You can mix and match. To filter keys based on pattern inside a deeply nested hash use dot notation with regular expression. For example, to find keys for the `:foo` parent key that starts with `:b` character, we could do:
|
458
|
+
|
459
|
+
```ruby
|
460
|
+
logger = TTY::Logger.new do |config|
|
461
|
+
config.filters.data = [/^foo\.b/]
|
462
|
+
end
|
463
|
+
```
|
464
|
+
|
465
|
+
Then only keys under the `:foo` key will be filtered:
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
logger.info("Filtering data", {"foo" => {"bar" => "val"}, "baz" => {"bar" => val"}})
|
469
|
+
# =>
|
470
|
+
# ℹ info Filtering data foo={bar="[FILTERED]"} baz={bar=val}
|
471
|
+
```
|
472
|
+
|
412
473
|
### 2.5 Cloning
|
413
474
|
|
414
475
|
You can create a copy of a logger with the current configuration using the `copy` method.
|
@@ -547,7 +608,7 @@ end
|
|
547
608
|
By default, the output will be a plain text streamed to console. The text contains key and value pairs of all the metadata and the message of the log event.
|
548
609
|
|
549
610
|
```ruby
|
550
|
-
|
611
|
+
logger.info("Info about the deploy", app:"myap", env:"prod")
|
551
612
|
# =>
|
552
613
|
# pid=18315 date="2019-07-21" time="15:42:12.463" path="examples/stream.rb:17:in`<main>`"
|
553
614
|
# level=info message="Info about the deploy" app=myapp env=prod
|
@@ -565,7 +626,7 @@ end
|
|
565
626
|
This will output JSON formatted text streamed to console.
|
566
627
|
|
567
628
|
```ruby
|
568
|
-
|
629
|
+
logger.info("Info about the deploy", app="myap", env="prod")
|
569
630
|
# =>
|
570
631
|
# {"pid":18513,"date":"2019-07-21","time":"15:54:09.924","path":"examples/stream.rb:17:in`<main>`",
|
571
632
|
# "level":"info","message":"Info about the deploy","app":"myapp","env":"prod"}
|
data/lib/tty/logger.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "logger/config"
|
4
|
+
require_relative "logger/data_filter"
|
4
5
|
require_relative "logger/event"
|
5
6
|
require_relative "logger/formatters/json"
|
6
7
|
require_relative "logger/formatters/text"
|
@@ -17,8 +18,6 @@ module TTY
|
|
17
18
|
# Error raised by this logger
|
18
19
|
class Error < StandardError; end
|
19
20
|
|
20
|
-
FILTERED = "[FILTERED]"
|
21
|
-
|
22
21
|
LOG_TYPES = {
|
23
22
|
debug: { level: :debug },
|
24
23
|
info: { level: :info },
|
@@ -35,7 +34,7 @@ module TTY
|
|
35
34
|
def self.define_level(name, log_level = nil)
|
36
35
|
const_level = (LOG_TYPES[name.to_sym] || log_level)[:level]
|
37
36
|
|
38
|
-
loc = caller_locations(0,1)[0]
|
37
|
+
loc = caller_locations(0, 1)[0]
|
39
38
|
if loc
|
40
39
|
file, line = loc.path, loc.lineno + 7
|
41
40
|
else
|
@@ -95,6 +94,8 @@ module TTY
|
|
95
94
|
@handlers = @config.handlers
|
96
95
|
@output = output || @config.output
|
97
96
|
@ready_handlers = []
|
97
|
+
@data_filter = DataFilter.new(@config.filters.data,
|
98
|
+
mask: @config.filters.mask)
|
98
99
|
|
99
100
|
@config.types.each do |name, log_level|
|
100
101
|
add_type(name, log_level)
|
@@ -129,7 +130,7 @@ module TTY
|
|
129
130
|
name = coerce_handler(h)
|
130
131
|
global_opts = { output: @output, config: @config }
|
131
132
|
opts = global_opts.merge(options)
|
132
|
-
ready_handler = name.new(opts)
|
133
|
+
ready_handler = name.new(**opts)
|
133
134
|
@ready_handlers << ready_handler
|
134
135
|
end
|
135
136
|
|
@@ -212,7 +213,8 @@ module TTY
|
|
212
213
|
# logger.log(:info) { "Deployed successfully" }
|
213
214
|
#
|
214
215
|
# @api public
|
215
|
-
def log(current_level, *msg
|
216
|
+
def log(current_level, *msg)
|
217
|
+
scoped_fields = msg.last.is_a?(::Hash) ? msg.pop : {}
|
216
218
|
fields_copy = scoped_fields.dup
|
217
219
|
if msg.empty? && block_given?
|
218
220
|
msg = []
|
@@ -220,8 +222,8 @@ module TTY
|
|
220
222
|
el.is_a?(::Hash) ? fields_copy.merge!(el) : msg << el
|
221
223
|
end
|
222
224
|
end
|
223
|
-
top_caller = caller_locations(1,1)[0]
|
224
|
-
loc = caller_locations(2,1)[0] || top_caller
|
225
|
+
top_caller = caller_locations(1, 1)[0]
|
226
|
+
loc = caller_locations(2, 1)[0] || top_caller
|
225
227
|
label = top_caller.label
|
226
228
|
metadata = {
|
227
229
|
level: current_level,
|
@@ -232,7 +234,9 @@ module TTY
|
|
232
234
|
lineno: loc.lineno,
|
233
235
|
method: loc.base_label
|
234
236
|
}
|
235
|
-
event = Event.new(filter(*msg),
|
237
|
+
event = Event.new(filter(*msg),
|
238
|
+
@data_filter.filter(@fields.merge(fields_copy)),
|
239
|
+
metadata)
|
236
240
|
|
237
241
|
@ready_handlers.each do |handler|
|
238
242
|
level = handler.respond_to?(:level) ? handler.level : @config.level
|
@@ -271,8 +275,8 @@ module TTY
|
|
271
275
|
def filter(*messages)
|
272
276
|
messages.reduce([]) do |acc, msg|
|
273
277
|
acc << msg.dup.tap do |msg_copy|
|
274
|
-
@config.filters.each do |text
|
275
|
-
msg_copy.gsub!(text,
|
278
|
+
@config.filters.message.each do |text|
|
279
|
+
msg_copy.gsub!(text, @config.filters.mask)
|
276
280
|
end
|
277
281
|
end
|
278
282
|
acc
|
data/lib/tty/logger/config.rb
CHANGED
@@ -5,13 +5,15 @@ require_relative "handlers/console"
|
|
5
5
|
module TTY
|
6
6
|
class Logger
|
7
7
|
class Config
|
8
|
+
FILTERED = "[FILTERED]"
|
9
|
+
|
8
10
|
# The format used for date display
|
9
11
|
attr_accessor :date_format
|
10
12
|
|
11
13
|
# The format used for time display
|
12
14
|
attr_accessor :time_format
|
13
15
|
|
14
|
-
# The
|
16
|
+
# The filters to hide sensitive data from the messages and data.
|
15
17
|
attr_accessor :filters
|
16
18
|
|
17
19
|
# The format used for displaying structured data
|
@@ -46,7 +48,6 @@ module TTY
|
|
46
48
|
@max_depth = options.fetch(:max_depth) { 3 }
|
47
49
|
@level = options.fetch(:level) { :info }
|
48
50
|
@metadata = options.fetch(:metadata) { [] }
|
49
|
-
@filters = options.fetch(:filters) { {} }
|
50
51
|
@handlers = options.fetch(:handlers) { [:console] }
|
51
52
|
@formatter = options.fetch(:formatter) { :text }
|
52
53
|
@date_format = options.fetch(:date_format) { "%F" }
|
@@ -55,6 +56,36 @@ module TTY
|
|
55
56
|
@types = options.fetch(:types) { {} }
|
56
57
|
end
|
57
58
|
|
59
|
+
class FiltersProvider
|
60
|
+
attr_accessor :message, :data, :mask
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
@message = []
|
64
|
+
@data = []
|
65
|
+
@mask = FILTERED
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_h
|
69
|
+
{message: @message, data: @data, mask: @mask}
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
to_h.inspect
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# The filters to hide sensitive data from the message(s) and data.
|
78
|
+
#
|
79
|
+
# @return [FiltersProvider]
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
def filters
|
83
|
+
@filters ||= FiltersProvider.new
|
84
|
+
end
|
85
|
+
|
86
|
+
# Allow to overwirte filters
|
87
|
+
attr_writer :filters
|
88
|
+
|
58
89
|
# Clone settings
|
59
90
|
#
|
60
91
|
# @api public
|
@@ -83,7 +114,7 @@ module TTY
|
|
83
114
|
def to_h
|
84
115
|
{
|
85
116
|
date_format: date_format,
|
86
|
-
filters: filters,
|
117
|
+
filters: filters.to_h,
|
87
118
|
formatter: formatter,
|
88
119
|
handlers: handlers,
|
89
120
|
level: level,
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Logger
|
5
|
+
class DataFilter
|
6
|
+
FILTERED = "[FILTERED]"
|
7
|
+
DOT = "."
|
8
|
+
|
9
|
+
attr_reader :filters, :compiled_filters, :mask
|
10
|
+
|
11
|
+
# Create a data filter instance with filters.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# TTY::Logger::DataFilter.new(%w[foo], mask: "<SECRET>")
|
15
|
+
#
|
16
|
+
# @param [String] mask
|
17
|
+
# the mask to replace object with. Defaults to `"[FILTERED]"`
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
def initialize(filters = [], mask: nil)
|
21
|
+
@mask = mask || FILTERED
|
22
|
+
@filters = filters
|
23
|
+
@compiled_filters = compile(filters)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Filter object for keys matching provided filters.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# data_filter = TTY::Logger::DataFilter.new(%w[foo])
|
30
|
+
# data_filter.filter({"foo" => "bar"})
|
31
|
+
# # => {"foo" => "[FILTERED]"}
|
32
|
+
#
|
33
|
+
# @param [Object] obj
|
34
|
+
# the object to filter
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def filter(obj)
|
38
|
+
return obj if filters.empty?
|
39
|
+
|
40
|
+
hash = obj.reduce({}) do |acc, (k, v)|
|
41
|
+
acc[k] = filter_val(k, v)
|
42
|
+
acc
|
43
|
+
end
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def compile(filters)
|
50
|
+
compiled = {
|
51
|
+
regexps: [],
|
52
|
+
nested_regexps: [],
|
53
|
+
blocks: [],
|
54
|
+
}
|
55
|
+
strings = []
|
56
|
+
nested_strings = []
|
57
|
+
|
58
|
+
filters.each do |filter|
|
59
|
+
case filter
|
60
|
+
when Proc
|
61
|
+
compiled[:blocks] << filter
|
62
|
+
when Regexp
|
63
|
+
if filter.to_s.include?(DOT)
|
64
|
+
compiled[:nested_regexps] << filter
|
65
|
+
else
|
66
|
+
compiled[:regexps] << filter
|
67
|
+
end
|
68
|
+
else
|
69
|
+
exp = Regexp.escape(filter)
|
70
|
+
if exp.include?(DOT)
|
71
|
+
nested_strings << exp
|
72
|
+
else
|
73
|
+
strings << exp
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if !strings.empty?
|
79
|
+
compiled[:regexps] << /^(#{strings.join("|")})$/
|
80
|
+
end
|
81
|
+
|
82
|
+
if !nested_strings.empty?
|
83
|
+
compiled[:nested_regexps] << /^(#{nested_strings.join("|")})$/
|
84
|
+
end
|
85
|
+
|
86
|
+
compiled
|
87
|
+
end
|
88
|
+
|
89
|
+
def filter_val(key, val, composite = [])
|
90
|
+
return mask if filtered?(key, composite)
|
91
|
+
|
92
|
+
case val
|
93
|
+
when Hash then filter_obj(key, val, composite << key)
|
94
|
+
when Array then filter_arr(key, val, composite)
|
95
|
+
else val
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def filtered?(key, composite)
|
100
|
+
composite_key = composite + [key]
|
101
|
+
joined_key = composite_key.join(DOT)
|
102
|
+
@compiled_filters[:regexps].any? { |reg| !!reg.match(key.to_s) } ||
|
103
|
+
@compiled_filters[:nested_regexps].any? { |reg| !!reg.match(joined_key) } ||
|
104
|
+
@compiled_filters[:blocks].any? { |block| block.(composite_key.dup) }
|
105
|
+
end
|
106
|
+
|
107
|
+
def filter_obj(_key, obj, composite)
|
108
|
+
obj.reduce({}) do |acc, (k, v)|
|
109
|
+
acc[k] = filter_val(k, v, composite)
|
110
|
+
acc
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def filter_arr(key, obj, composite)
|
115
|
+
obj.reduce([]) do |acc, v|
|
116
|
+
acc << filter_val(key, v, composite)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end # DataFilter
|
120
|
+
end # Logger
|
121
|
+
end # TTY
|
@@ -57,6 +57,14 @@ module TTY
|
|
57
57
|
}
|
58
58
|
}
|
59
59
|
|
60
|
+
TEXT_REGEXP = /([{}()\[\]])?(["']?)(\S+?)(["']?=)/.freeze
|
61
|
+
JSON_REGEXP = /\"([^,]+?)\"(?=:)/.freeze
|
62
|
+
|
63
|
+
COLOR_PATTERNS = {
|
64
|
+
text: [TEXT_REGEXP, ->(c) { "\\1\\2" + c.("\\3") + "\\4" }],
|
65
|
+
json: [JSON_REGEXP, ->(c) { "\"" + c.("\\1") + "\"" }]
|
66
|
+
}.freeze
|
67
|
+
|
60
68
|
attr_reader :output
|
61
69
|
|
62
70
|
attr_reader :config
|
@@ -67,6 +75,8 @@ module TTY
|
|
67
75
|
styles: {})
|
68
76
|
@output = Array[output].flatten
|
69
77
|
@formatter = coerce_formatter(formatter || config.formatter).new
|
78
|
+
@formatter_name = @formatter.class.name.split("::").last.downcase
|
79
|
+
@color_pattern = COLOR_PATTERNS[@formatter_name.to_sym]
|
70
80
|
@config = config
|
71
81
|
@styles = styles
|
72
82
|
@level = level || @config.level
|
@@ -108,10 +118,10 @@ module TTY
|
|
108
118
|
fmt << color.(style[:label]) + (" " * style[:levelpad])
|
109
119
|
fmt << "%-25s" % event.message.join(" ")
|
110
120
|
unless event.fields.empty?
|
121
|
+
pattern, replacement = *@color_pattern
|
111
122
|
fmt << @formatter.dump(event.fields, max_bytes: config.max_bytes,
|
112
|
-
|
113
|
-
|
114
|
-
gsub(/\"([^,]+?)\"(?=:)/, "\"" + color.("\\1") + "\"")
|
123
|
+
max_depth: config.max_depth)
|
124
|
+
.gsub(pattern, replacement.(color))
|
115
125
|
end
|
116
126
|
unless event.backtrace.empty?
|
117
127
|
fmt << "\n" + format_backtrace(event)
|