tty-logger 0.2.0 → 0.3.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/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)
|