dry-logger 1.0.0.rc1 → 1.0.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 +16 -2
- data/lib/dry/logger/backends/core.rb +38 -0
- data/lib/dry/logger/backends/proxy.rb +38 -0
- data/lib/dry/logger/backends/stream.rb +6 -11
- data/lib/dry/logger/clock.rb +47 -0
- data/lib/dry/logger/constants.rb +55 -3
- data/lib/dry/logger/dispatcher.rb +85 -14
- data/lib/dry/logger/entry.rb +27 -33
- data/lib/dry/logger/formatters/colors.rb +84 -0
- data/lib/dry/logger/formatters/json.rb +27 -1
- data/lib/dry/logger/formatters/rack.rb +10 -2
- data/lib/dry/logger/formatters/string.rb +93 -26
- data/lib/dry/logger/formatters/structured.rb +16 -5
- data/lib/dry/logger/formatters/template.rb +92 -0
- data/lib/dry/logger/global.rb +123 -0
- data/lib/dry/logger/version.rb +1 -1
- data/lib/dry/logger.rb +45 -59
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 730cb488952659e0a7499948d6f77e3ed61b7be38e462d7a000479d7e6a8c7f2
|
4
|
+
data.tar.gz: 81ddc5b02fc960cb3650f1305e19bfc33d21488722455c8bfdcb3e411cb07bf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02d8e2eb6e2029a3c29269db001e5e3f50eacf1605efa4cdd62ae01a1e8349b80073e0b591761ed3bc82f7acda5b139a2a03c120c32b3716c27c2d054eee0a64
|
7
|
+
data.tar.gz: 2f11fec53ac321f0157f3c620944d460a189fa4da112a5b2d617a21171533dcacf17370bf1288d2ef1e4574ab4f35518aca2e280a7063df669768d1c3dda73b6
|
data/CHANGELOG.md
CHANGED
@@ -1,13 +1,27 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
-
## 1.0.0
|
3
|
+
## 1.0.0
|
4
4
|
|
5
5
|
This is a port of the original Hanami logger from hanami-utils extended with support for logging
|
6
|
-
dispatchers that can log to different destinations.
|
6
|
+
dispatchers that can log to different destinations and plenty more.
|
7
7
|
|
8
8
|
|
9
9
|
### Added
|
10
10
|
|
11
|
+
- Support arbitrary logging backends through proxy (via #12) (@solnic)
|
12
|
+
- Support for conditional logging when using arbitrary logging backends (via #13) (@solnic)
|
13
|
+
- Support for registering templates via `Dry::Logger.register_template` (via #14) (@solnic)
|
14
|
+
- Support for payload keys as template tokens (via #14) (@solnic)
|
15
|
+
- Support for payload value formatter methods, ie if there's `:verb` token your formatter can implement `format_verb(value)` (via #14) (@solnic)
|
16
|
+
- Support block-based setup (via #16) (@solnic)
|
17
|
+
- Support for defining cherry-picked keys from the payload in string templates (via #17) (@solnic)
|
18
|
+
- Support for `%<payload>s` template token. It will be replaced by a formatted payload, excluding any key that you specified explicitly in the template (via #17) (@solnic)
|
19
|
+
- Support for colorized output using color tags in templates (via #18) (@solnic)
|
20
|
+
- Support for `colorize: true` logger option which enables severity coloring in string formatter (via #18) (@solnic)
|
21
|
+
- `:details` template: `"[%<progname>s] [%<severity>s] [%<time>s] %<message>s %<payload>s"` (@solnic)
|
22
|
+
- A new option `on_crash` for setting up a logger-crash handling proc (via #21) (@solnic)
|
23
|
+
- Handle logger crashes by default using a simple `$stdout` logger (via #21) (@solnic)
|
24
|
+
- Support for regular logger backends that don't support `log?` predicate (@solnic)
|
11
25
|
- Support for providing a string template for log entries via `template` option (via #7) (@solnic)
|
12
26
|
- `:rack` string log formatter which inlines request info and displays params at the end (@solnic)
|
13
27
|
- Conditional log dispatch via `#log_if` backend's predicate (via #9) (@solnic)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logger/constants"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Logger
|
7
|
+
module Backends
|
8
|
+
module Core
|
9
|
+
# Return a proc used by the log? predicate
|
10
|
+
#
|
11
|
+
# @since 1.0.0
|
12
|
+
# @api private
|
13
|
+
attr_reader :log_if
|
14
|
+
|
15
|
+
# Set a predicate proc that checks if an entry should be logged by a given backend
|
16
|
+
#
|
17
|
+
# The predicate will receive {Entry} as its argument and should return true/false
|
18
|
+
#
|
19
|
+
# @param [Proc, #to_proc] spec A proc-like object
|
20
|
+
# @since 1.0.0
|
21
|
+
# @api public
|
22
|
+
def log_if=(spec)
|
23
|
+
@log_if = spec&.to_proc
|
24
|
+
end
|
25
|
+
|
26
|
+
# @since 1.0.0
|
27
|
+
# @api private
|
28
|
+
def log?(entry)
|
29
|
+
if log_if
|
30
|
+
log_if.call(entry)
|
31
|
+
else
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
|
5
|
+
require "dry/logger/constants"
|
6
|
+
require "dry/logger/backends/core"
|
7
|
+
|
8
|
+
module Dry
|
9
|
+
module Logger
|
10
|
+
module Backends
|
11
|
+
# Logger proxy is used for regular loggers that don't work with log entries
|
12
|
+
#
|
13
|
+
# @since 1.0.0
|
14
|
+
# @api private
|
15
|
+
class Proxy < SimpleDelegator
|
16
|
+
include Core
|
17
|
+
|
18
|
+
# @since 0.1.0
|
19
|
+
# @api public
|
20
|
+
attr_accessor :log_if
|
21
|
+
|
22
|
+
LOG_METHODS.each do |method|
|
23
|
+
define_method(method) { |entry| __getobj__.public_send(method, entry.message) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# @since 1.0.0
|
27
|
+
# @api private
|
28
|
+
def log?(entry)
|
29
|
+
if log_if
|
30
|
+
log_if.call(entry)
|
31
|
+
else
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -3,11 +3,14 @@
|
|
3
3
|
require "logger"
|
4
4
|
|
5
5
|
require "dry/logger/constants"
|
6
|
+
require "dry/logger/backends/core"
|
6
7
|
|
7
8
|
module Dry
|
8
9
|
module Logger
|
9
10
|
module Backends
|
10
11
|
class Stream < ::Logger
|
12
|
+
include Core
|
13
|
+
|
11
14
|
# @since 0.1.0
|
12
15
|
# @api private
|
13
16
|
attr_reader :stream
|
@@ -16,10 +19,6 @@ module Dry
|
|
16
19
|
# @api private
|
17
20
|
attr_reader :level
|
18
21
|
|
19
|
-
# @since 0.1.0
|
20
|
-
# @api public
|
21
|
-
attr_accessor :log_if
|
22
|
-
|
23
22
|
# @since 0.1.0
|
24
23
|
# @api private
|
25
24
|
def initialize(stream:, formatter:, level: DEFAULT_LEVEL, progname: nil, log_if: nil)
|
@@ -33,13 +32,9 @@ module Dry
|
|
33
32
|
end
|
34
33
|
|
35
34
|
# @since 1.0.0
|
36
|
-
# @api
|
37
|
-
def
|
38
|
-
|
39
|
-
log_if.call(entry)
|
40
|
-
else
|
41
|
-
true
|
42
|
-
end
|
35
|
+
# @api public
|
36
|
+
def inspect
|
37
|
+
%(#<#{self.class} stream=#{stream} level=#{level} log_if=#{log_if}>)
|
43
38
|
end
|
44
39
|
end
|
45
40
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Logger
|
5
|
+
# @since 1.0.0
|
6
|
+
# @api private
|
7
|
+
class Clock
|
8
|
+
# @since 1.0.0
|
9
|
+
# @api private
|
10
|
+
attr_reader :unit
|
11
|
+
|
12
|
+
# @since 1.0.0
|
13
|
+
# @api private
|
14
|
+
def initialize(unit: :nanosecond)
|
15
|
+
@unit = unit
|
16
|
+
end
|
17
|
+
|
18
|
+
# @since 1.0.0
|
19
|
+
# @api private
|
20
|
+
def now
|
21
|
+
Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
# @since 1.0.0
|
25
|
+
# @api private
|
26
|
+
def now_utc
|
27
|
+
now.getutc
|
28
|
+
end
|
29
|
+
|
30
|
+
# @since 1.0.0
|
31
|
+
# @api private
|
32
|
+
def measure
|
33
|
+
start = current
|
34
|
+
result = yield
|
35
|
+
[result, current - start]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# @since 1.0.0
|
41
|
+
# @api private
|
42
|
+
def current
|
43
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/dry/logger/constants.rb
CHANGED
@@ -4,22 +4,67 @@ require "logger"
|
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Logger
|
7
|
-
|
7
|
+
# @since 1.0.0
|
8
|
+
# @api private
|
9
|
+
NEW_LINE = $/ # rubocop:disable Style/SpecialGlobalVars
|
10
|
+
|
11
|
+
# @since 1.0.0
|
12
|
+
# @api private
|
13
|
+
SEPARATOR = " "
|
14
|
+
|
15
|
+
# @since 1.0.0
|
16
|
+
# @api private
|
17
|
+
TAB = SEPARATOR * 2
|
18
|
+
|
19
|
+
# @since 1.0.0
|
20
|
+
# @api private
|
21
|
+
EMPTY_ARRAY = [].freeze
|
8
22
|
|
23
|
+
# @since 1.0.0
|
24
|
+
# @api private
|
25
|
+
EMPTY_HASH = {}.freeze
|
26
|
+
|
27
|
+
# @since 1.0.0
|
28
|
+
# @api private
|
29
|
+
LOG_METHODS = %i[debug info warn error fatal unknown].freeze
|
30
|
+
|
31
|
+
# @since 1.0.0
|
32
|
+
# @api private
|
9
33
|
BACKEND_METHODS = %i[close].freeze
|
10
34
|
|
35
|
+
# @since 1.0.0
|
36
|
+
# @api private
|
11
37
|
DEBUG = ::Logger::DEBUG
|
38
|
+
|
39
|
+
# @since 1.0.0
|
40
|
+
# @api private
|
12
41
|
INFO = ::Logger::INFO
|
42
|
+
|
43
|
+
# @since 1.0.0
|
44
|
+
# @api private
|
13
45
|
WARN = ::Logger::WARN
|
46
|
+
|
47
|
+
# @since 1.0.0
|
48
|
+
# @api private
|
14
49
|
ERROR = ::Logger::ERROR
|
50
|
+
|
51
|
+
# @since 1.0.0
|
52
|
+
# @api private
|
15
53
|
FATAL = ::Logger::FATAL
|
54
|
+
|
55
|
+
# @since 1.0.0
|
56
|
+
# @api private
|
16
57
|
UNKNOWN = ::Logger::UNKNOWN
|
17
58
|
|
59
|
+
# @since 1.0.0
|
60
|
+
# @api private
|
18
61
|
LEVEL_RANGE = (DEBUG..UNKNOWN).freeze
|
19
62
|
|
63
|
+
# @since 1.0.0
|
64
|
+
# @api private
|
20
65
|
DEFAULT_LEVEL = INFO
|
21
66
|
|
22
|
-
# @since
|
67
|
+
# @since 1.0.0
|
23
68
|
# @api private
|
24
69
|
LEVELS = Hash
|
25
70
|
.new { |levels, key|
|
@@ -35,9 +80,16 @@ module Dry
|
|
35
80
|
)
|
36
81
|
.freeze
|
37
82
|
|
38
|
-
|
83
|
+
# @since 1.0.0
|
84
|
+
# @api private
|
85
|
+
DEFAULT_OPTS = {level: DEFAULT_LEVEL, formatter: nil, progname: nil, log_if: nil}.freeze
|
39
86
|
|
87
|
+
# @since 1.0.0
|
88
|
+
# @api private
|
40
89
|
BACKEND_OPT_KEYS = DEFAULT_OPTS.keys.freeze
|
90
|
+
|
91
|
+
# @since 1.0.0
|
92
|
+
# @api private
|
41
93
|
FORMATTER_OPT_KEYS = %i[filter].freeze
|
42
94
|
end
|
43
95
|
end
|
@@ -4,6 +4,7 @@ require "logger"
|
|
4
4
|
require "pathname"
|
5
5
|
|
6
6
|
require "dry/logger/constants"
|
7
|
+
require "dry/logger/backends/proxy"
|
7
8
|
require "dry/logger/entry"
|
8
9
|
|
9
10
|
module Dry
|
@@ -37,21 +38,48 @@ module Dry
|
|
37
38
|
# @api private
|
38
39
|
attr_reader :options
|
39
40
|
|
41
|
+
# @since 1.0.0
|
42
|
+
# @api private
|
43
|
+
attr_reader :clock
|
44
|
+
|
45
|
+
# @since 1.0.0
|
46
|
+
# @api private
|
47
|
+
attr_reader :on_crash
|
48
|
+
|
40
49
|
# @since 1.0.0
|
41
50
|
# @api private
|
42
51
|
attr_reader :mutex
|
43
52
|
|
53
|
+
# @since 1.0.0
|
54
|
+
# @api private
|
55
|
+
CRASH_LOGGER = ::Logger.new($stdout).tap { |logger|
|
56
|
+
logger.formatter = -> (_, _, _, message) { "#{message}#{NEW_LINE}" }
|
57
|
+
logger.level = FATAL
|
58
|
+
}.freeze
|
59
|
+
|
60
|
+
# @since 1.0.0
|
61
|
+
# @api private
|
62
|
+
ON_CRASH = -> (progname:, exception:, message:, payload:) {
|
63
|
+
CRASH_LOGGER.fatal(Logger.templates[:crash] % {
|
64
|
+
severity: "FATAL",
|
65
|
+
progname: progname,
|
66
|
+
time: Time.now,
|
67
|
+
log_entry: [message, payload].map(&:to_s).reject(&:empty?).join(SEPARATOR),
|
68
|
+
exception: exception.class,
|
69
|
+
message: exception.message,
|
70
|
+
backtrace: TAB + exception.backtrace.join(NEW_LINE + TAB)
|
71
|
+
})
|
72
|
+
}
|
73
|
+
|
44
74
|
# Set up a dispatcher
|
45
75
|
#
|
46
76
|
# @since 1.0.0
|
47
|
-
#
|
48
|
-
# @param [String, Symbol] id The dispatcher id, can be used as progname in log entries
|
49
|
-
# @param [Hash] options Options that can be used for both the backend and formatter
|
77
|
+
# @api private
|
50
78
|
#
|
51
79
|
# @return [Dispatcher]
|
52
|
-
# @api public
|
53
80
|
def self.setup(id, **options)
|
54
81
|
dispatcher = new(id, **DEFAULT_OPTS, **options)
|
82
|
+
yield(dispatcher) if block_given?
|
55
83
|
dispatcher.add_backend if dispatcher.backends.empty?
|
56
84
|
dispatcher
|
57
85
|
end
|
@@ -64,12 +92,17 @@ module Dry
|
|
64
92
|
|
65
93
|
# @since 1.0.0
|
66
94
|
# @api private
|
67
|
-
def initialize(
|
95
|
+
def initialize(
|
96
|
+
id, backends: [], tags: [], context: self.class.default_context, **options
|
97
|
+
)
|
68
98
|
@id = id
|
69
99
|
@backends = backends
|
70
100
|
@options = {**options, progname: id}
|
71
101
|
@mutex = Mutex.new
|
72
102
|
@context = context
|
103
|
+
@tags = tags
|
104
|
+
@clock = Clock.new(**(options[:clock] || EMPTY_HASH))
|
105
|
+
@on_crash = options[:on_crash] || ON_CRASH
|
73
106
|
end
|
74
107
|
|
75
108
|
# Log an entry with UNKNOWN severity
|
@@ -128,7 +161,7 @@ module Dry
|
|
128
161
|
|
129
162
|
BACKEND_METHODS.each do |name|
|
130
163
|
define_method(name) do
|
131
|
-
|
164
|
+
forward(name)
|
132
165
|
end
|
133
166
|
end
|
134
167
|
|
@@ -143,8 +176,25 @@ module Dry
|
|
143
176
|
|
144
177
|
# Pass logging to all configured backends
|
145
178
|
#
|
179
|
+
# @example logging a message
|
180
|
+
# logger.log(:info, "Hello World")
|
181
|
+
#
|
182
|
+
# @example logging payload
|
183
|
+
# logger.log(:info, verb: "GET", path: "/users")
|
184
|
+
#
|
185
|
+
# @example logging message and payload
|
186
|
+
# logger.log(:info, "User index request", verb: "GET", path: "/users")
|
187
|
+
#
|
188
|
+
# @example logging exception
|
189
|
+
# begin
|
190
|
+
# # things that may raise
|
191
|
+
# rescue => e
|
192
|
+
# logger.log(:error, e)
|
193
|
+
# raise e
|
194
|
+
# end
|
195
|
+
#
|
146
196
|
# @param [Symbol] severity The log severity name
|
147
|
-
# @param [String
|
197
|
+
# @param [String] message Optional message
|
148
198
|
# @param [Hash] payload Optional log entry payload
|
149
199
|
#
|
150
200
|
# @since 1.0.0
|
@@ -155,15 +205,24 @@ module Dry
|
|
155
205
|
when Hash then log(severity, nil, **message)
|
156
206
|
else
|
157
207
|
entry = Entry.new(
|
208
|
+
clock: clock,
|
158
209
|
progname: id,
|
159
210
|
severity: severity,
|
211
|
+
tags: @tags,
|
160
212
|
message: message,
|
161
213
|
payload: {**context, **payload}
|
162
214
|
)
|
163
215
|
|
164
|
-
each_backend
|
216
|
+
each_backend do |backend|
|
217
|
+
backend.__send__(severity, entry) if backend.log?(entry)
|
218
|
+
rescue StandardError => e
|
219
|
+
on_crash.(progname: id, exception: e, message: message, payload: payload)
|
220
|
+
end
|
165
221
|
end
|
166
222
|
|
223
|
+
true
|
224
|
+
rescue StandardError => e
|
225
|
+
on_crash.(progname: id, exception: e, message: message, payload: payload)
|
167
226
|
true
|
168
227
|
end
|
169
228
|
|
@@ -180,11 +239,11 @@ module Dry
|
|
180
239
|
#
|
181
240
|
# @since 1.0.0
|
182
241
|
# @api public
|
183
|
-
def tagged(
|
184
|
-
|
242
|
+
def tagged(*tags)
|
243
|
+
@tags.concat(tags)
|
185
244
|
yield
|
186
245
|
ensure
|
187
|
-
|
246
|
+
@tags = []
|
188
247
|
end
|
189
248
|
|
190
249
|
# Add a new backend to an existing dispatcher
|
@@ -198,15 +257,27 @@ module Dry
|
|
198
257
|
# @return [Dispatcher]
|
199
258
|
# @api public
|
200
259
|
def add_backend(instance = nil, **backend_options)
|
201
|
-
backend =
|
260
|
+
backend =
|
261
|
+
case (instance ||= Dry::Logger.new(**options, **backend_options))
|
262
|
+
when Backends::Stream then instance
|
263
|
+
else Backends::Proxy.new(instance)
|
264
|
+
end
|
265
|
+
|
202
266
|
yield(backend) if block_given?
|
267
|
+
|
203
268
|
backends << backend
|
204
269
|
self
|
205
270
|
end
|
206
271
|
|
272
|
+
# @since 1.0.0
|
273
|
+
# @api public
|
274
|
+
def inspect
|
275
|
+
%(#<#{self.class} id=#{id} options=#{options} backends=#{backends}>)
|
276
|
+
end
|
277
|
+
|
207
278
|
# @since 1.0.0
|
208
279
|
# @api private
|
209
|
-
def each_backend(
|
280
|
+
def each_backend(&block)
|
210
281
|
mutex.synchronize do
|
211
282
|
backends.each(&block)
|
212
283
|
end
|
@@ -217,7 +288,7 @@ module Dry
|
|
217
288
|
# @since 1.0.0
|
218
289
|
# @return [true]
|
219
290
|
# @api private
|
220
|
-
def
|
291
|
+
def forward(meth, ...)
|
221
292
|
each_backend { |backend| backend.public_send(meth, ...) }
|
222
293
|
true
|
223
294
|
end
|
data/lib/dry/logger/entry.rb
CHANGED
@@ -10,14 +10,6 @@ module Dry
|
|
10
10
|
class Entry
|
11
11
|
include Enumerable
|
12
12
|
|
13
|
-
# @since 1.0.0
|
14
|
-
# @api private
|
15
|
-
EMPTY_PAYLOAD = {}.freeze
|
16
|
-
|
17
|
-
# @since 1.0.0
|
18
|
-
# @api private
|
19
|
-
EMPTY_BACKTRACE = [].freeze
|
20
|
-
|
21
13
|
# @since 1.0.0
|
22
14
|
# @api public
|
23
15
|
attr_reader :progname
|
@@ -28,16 +20,19 @@ module Dry
|
|
28
20
|
|
29
21
|
# @since 1.0.0
|
30
22
|
# @api public
|
31
|
-
attr_reader :
|
23
|
+
attr_reader :tags
|
32
24
|
|
33
25
|
# @since 1.0.0
|
34
26
|
# @api public
|
35
|
-
attr_reader :
|
27
|
+
attr_reader :level
|
36
28
|
|
37
29
|
# @since 1.0.0
|
38
30
|
# @api public
|
39
31
|
attr_reader :message
|
40
|
-
|
32
|
+
|
33
|
+
# @since 1.0.0
|
34
|
+
# @api public
|
35
|
+
attr_reader :exception
|
41
36
|
|
42
37
|
# @since 1.0.0
|
43
38
|
# @api public
|
@@ -45,14 +40,23 @@ module Dry
|
|
45
40
|
|
46
41
|
# @since 1.0.0
|
47
42
|
# @api private
|
48
|
-
|
43
|
+
attr_reader :clock
|
44
|
+
|
45
|
+
# @since 1.0.0
|
46
|
+
# @api private
|
47
|
+
# rubocop:disable Metrics/ParameterLists
|
48
|
+
def initialize(clock:, progname:, severity:, tags: EMPTY_ARRAY, message: nil,
|
49
|
+
payload: EMPTY_HASH)
|
50
|
+
@clock = clock
|
49
51
|
@progname = progname
|
50
|
-
@severity = severity.to_s
|
52
|
+
@severity = severity.to_s
|
53
|
+
@tags = tags
|
51
54
|
@level = LEVELS.fetch(severity.to_s)
|
52
|
-
@
|
53
|
-
@
|
55
|
+
@message = message unless message.is_a?(Exception)
|
56
|
+
@exception = message if message.is_a?(Exception)
|
54
57
|
@payload = build_payload(payload)
|
55
58
|
end
|
59
|
+
# rubocop:enable Metrics/ParameterLists
|
56
60
|
|
57
61
|
# @since 1.0.0
|
58
62
|
# @api public
|
@@ -99,7 +103,7 @@ module Dry
|
|
99
103
|
# @since 1.0.0
|
100
104
|
# @api public
|
101
105
|
def exception?
|
102
|
-
|
106
|
+
!exception.nil?
|
103
107
|
end
|
104
108
|
|
105
109
|
# @since 1.0.0
|
@@ -109,28 +113,21 @@ module Dry
|
|
109
113
|
end
|
110
114
|
|
111
115
|
# @since 1.0.0
|
112
|
-
# @api
|
113
|
-
def
|
114
|
-
|
116
|
+
# @api public
|
117
|
+
def tag?(value)
|
118
|
+
tags.include?(value)
|
115
119
|
end
|
116
120
|
|
117
121
|
# @since 1.0.0
|
118
122
|
# @api private
|
119
123
|
def meta
|
120
|
-
@meta ||= {progname: progname, severity: severity, time:
|
124
|
+
@meta ||= {progname: progname, severity: severity, time: clock.now}
|
121
125
|
end
|
122
126
|
|
123
127
|
# @since 1.0.0
|
124
128
|
# @api private
|
125
|
-
def
|
126
|
-
@
|
127
|
-
end
|
128
|
-
|
129
|
-
# @since 1.0.0
|
130
|
-
# @api private
|
131
|
-
def as_json
|
132
|
-
# TODO: why are we enforcing UTC in JSON but not in String?
|
133
|
-
@as_json ||= to_h.merge(message: message, time: utc_time).compact
|
129
|
+
def to_h
|
130
|
+
@to_h ||= meta.merge(message: message, **payload)
|
134
131
|
end
|
135
132
|
|
136
133
|
# @since 1.0.0
|
@@ -146,10 +143,7 @@ module Dry
|
|
146
143
|
# @api private
|
147
144
|
def build_payload(payload)
|
148
145
|
if exception?
|
149
|
-
{
|
150
|
-
backtrace: exception.backtrace || EMPTY_BACKTRACE,
|
151
|
-
error: exception.class,
|
152
|
-
**payload}
|
146
|
+
{exception: exception, **payload}
|
153
147
|
else
|
154
148
|
payload
|
155
149
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Logger
|
5
|
+
module Formatters
|
6
|
+
# Shell colorizer
|
7
|
+
#
|
8
|
+
# This was ported from hanami-utils
|
9
|
+
#
|
10
|
+
# @since 1.0.0
|
11
|
+
# @api private
|
12
|
+
class Colors
|
13
|
+
# Unknown color code error
|
14
|
+
#
|
15
|
+
# @since 1.0.0
|
16
|
+
class UnknownColorCodeError < StandardError
|
17
|
+
def initialize(code)
|
18
|
+
super("Unknown color code: `#{code.inspect}'")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Escapes codes for terminals to output strings in colors
|
23
|
+
#
|
24
|
+
# @since 1.2.0
|
25
|
+
# @api private
|
26
|
+
COLORS = {black: 30,
|
27
|
+
red: 31,
|
28
|
+
green: 32,
|
29
|
+
yellow: 33,
|
30
|
+
blue: 34,
|
31
|
+
magenta: 35,
|
32
|
+
cyan: 36,
|
33
|
+
gray: 37}.freeze
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def self.evaluate(input)
|
37
|
+
COLORS.keys.reduce(input.dup) { |output, color|
|
38
|
+
output.gsub!("<#{color}>", start(color))
|
39
|
+
output.gsub!("</#{color}>", stop)
|
40
|
+
output
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Colorizes output
|
45
|
+
# 8 colors available: black, red, green, yellow, blue, magenta, cyan, and gray
|
46
|
+
#
|
47
|
+
# @param input [#to_s] the string to colorize
|
48
|
+
# @param color [Symbol] the color
|
49
|
+
#
|
50
|
+
# @raise [UnknownColorError] if the color code is unknown
|
51
|
+
#
|
52
|
+
# @return [String] the colorized string
|
53
|
+
#
|
54
|
+
# @since 1.0.0
|
55
|
+
# @api private
|
56
|
+
def self.call(color, input)
|
57
|
+
"#{start(color)}#{input}#{stop}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# @since 1.0.0
|
61
|
+
# @api private
|
62
|
+
def self.start(color)
|
63
|
+
"\e[#{self[color]}m"
|
64
|
+
end
|
65
|
+
|
66
|
+
# @since 1.0.0
|
67
|
+
# @api private
|
68
|
+
def self.stop
|
69
|
+
"\e[0m"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Helper method to translate between color names and terminal escape codes
|
73
|
+
#
|
74
|
+
# @since 1.0.0
|
75
|
+
# @api private
|
76
|
+
#
|
77
|
+
# @raise [UnknownColorError] if the color code is unknown
|
78
|
+
def self.[](code)
|
79
|
+
COLORS.fetch(code) { raise UnknownColorCodeError, code }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|