hanami-utils 2.0.0.alpha6 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -1
- data/README.md +9 -18
- data/hanami-utils.gemspec +0 -1
- data/lib/hanami/utils/string.rb +1 -1
- data/lib/hanami/utils/version.rb +2 -4
- metadata +2 -8
- data/lib/hanami/interactor.rb +0 -619
- data/lib/hanami/logger/colorizer.rb +0 -105
- data/lib/hanami/logger/filter.rb +0 -144
- data/lib/hanami/logger/formatter.rb +0 -183
- data/lib/hanami/logger.rb +0 -362
- data/lib/hanami/utils/basic_object.rb +0 -141
@@ -1,105 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "logger"
|
4
|
-
require "hanami/utils/shell_color"
|
5
|
-
|
6
|
-
module Hanami
|
7
|
-
class Logger < ::Logger
|
8
|
-
# Null colorizer for logger streams that aren't a TTY (eg. files)
|
9
|
-
#
|
10
|
-
# @since 1.2.0
|
11
|
-
# @api private
|
12
|
-
class NullColorizer
|
13
|
-
# @since 1.2.0
|
14
|
-
# @api private
|
15
|
-
def call(app, severity, datetime, _progname)
|
16
|
-
::Hash[
|
17
|
-
app: app,
|
18
|
-
severity: severity,
|
19
|
-
time: datetime,
|
20
|
-
]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# Hanami::Logger Default Colorizer
|
25
|
-
#
|
26
|
-
# This colorizer takes in parts of the log message and returns them with
|
27
|
-
# proper shellcode to colorize when displayed to a tty.
|
28
|
-
#
|
29
|
-
# @since 1.2.0
|
30
|
-
# @api private
|
31
|
-
class Colorizer < NullColorizer
|
32
|
-
def initialize(colors: COLORS)
|
33
|
-
@colors = colors
|
34
|
-
end
|
35
|
-
|
36
|
-
# Colorize the inputs
|
37
|
-
#
|
38
|
-
# @param app [#to_s] the app name
|
39
|
-
# @param severity [#to_s] log severity
|
40
|
-
# @param datetime [#to_s] timestamp
|
41
|
-
# @param _progname [#to_s] program name - ignored, accepted for
|
42
|
-
# compatibility with Ruby's Logger
|
43
|
-
#
|
44
|
-
# @return [::Hash] an Hash containing the keys `:app`, `:severity`, and `:time`
|
45
|
-
def call(app, severity, datetime, _progname)
|
46
|
-
::Hash[
|
47
|
-
app: app(app),
|
48
|
-
severity: severity(severity),
|
49
|
-
time: datetime(datetime),
|
50
|
-
]
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
# The colors defined for the three parts of the log message
|
56
|
-
#
|
57
|
-
# @since 1.2.0
|
58
|
-
# @api private
|
59
|
-
COLORS = ::Hash[
|
60
|
-
app: :blue,
|
61
|
-
datetime: :cyan,
|
62
|
-
].freeze
|
63
|
-
|
64
|
-
# @since 1.2.0
|
65
|
-
# @api private
|
66
|
-
LEVELS = ::Hash[
|
67
|
-
Hanami::Logger::DEBUG => :cyan,
|
68
|
-
Hanami::Logger::INFO => :magenta,
|
69
|
-
Hanami::Logger::WARN => :yellow,
|
70
|
-
Hanami::Logger::ERROR => :red,
|
71
|
-
Hanami::Logger::FATAL => :red,
|
72
|
-
Hanami::Logger::UNKNOWN => :blue,
|
73
|
-
].freeze
|
74
|
-
|
75
|
-
attr_reader :colors
|
76
|
-
|
77
|
-
# @since 1.2.0
|
78
|
-
# @api private
|
79
|
-
def app(input)
|
80
|
-
colorize(input, color: colors.fetch(:app, nil))
|
81
|
-
end
|
82
|
-
|
83
|
-
# @since 1.2.0
|
84
|
-
# @api private
|
85
|
-
def severity(input)
|
86
|
-
color = LEVELS.fetch(Hanami::Logger.level(input), :gray)
|
87
|
-
colorize(input, color: color)
|
88
|
-
end
|
89
|
-
|
90
|
-
# @since 1.2.0
|
91
|
-
# @api private
|
92
|
-
def datetime(input)
|
93
|
-
colorize(input, color: colors.fetch(:datetime, nil))
|
94
|
-
end
|
95
|
-
|
96
|
-
# @since 1.2.0
|
97
|
-
# @api private
|
98
|
-
def colorize(message, color:)
|
99
|
-
return message if color.nil?
|
100
|
-
|
101
|
-
Hanami::Utils::ShellColor.call(message, color: color)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
data/lib/hanami/logger/filter.rb
DELETED
@@ -1,144 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "logger"
|
4
|
-
|
5
|
-
module Hanami
|
6
|
-
class Logger < ::Logger
|
7
|
-
# Filtering logic
|
8
|
-
#
|
9
|
-
# @since 1.1.0
|
10
|
-
# @api private
|
11
|
-
class Filter
|
12
|
-
# @since 1.3.7
|
13
|
-
# @api private
|
14
|
-
FILTERED_VALUE = "[FILTERED]"
|
15
|
-
|
16
|
-
def initialize(filters = [], mask: FILTERED_VALUE)
|
17
|
-
@filters = filters
|
18
|
-
@mask = mask
|
19
|
-
end
|
20
|
-
|
21
|
-
# @since 1.1.0
|
22
|
-
# @api private
|
23
|
-
def call(params)
|
24
|
-
_filter(_copy_params(params))
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
# @since 1.1.0
|
30
|
-
# @api private
|
31
|
-
attr_reader :filters
|
32
|
-
|
33
|
-
# @since 1.3.7
|
34
|
-
# @api private
|
35
|
-
attr_reader :mask
|
36
|
-
|
37
|
-
# This is a simple deep merge to merge the original input
|
38
|
-
# with the filtered hash which contains '[FILTERED]' string.
|
39
|
-
#
|
40
|
-
# It only deep-merges if the conflict values are both hashes.
|
41
|
-
#
|
42
|
-
# @since 1.3.7
|
43
|
-
# @api private
|
44
|
-
def _deep_merge(original_hash, filtered_hash)
|
45
|
-
original_hash.merge(filtered_hash) do |_key, original_item, filtered_item|
|
46
|
-
if original_item.is_a?(Hash) && filtered_item.is_a?(Hash)
|
47
|
-
_deep_merge(original_item, filtered_item)
|
48
|
-
elsif filtered_item == FILTERED_VALUE
|
49
|
-
filtered_item
|
50
|
-
else
|
51
|
-
original_item
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# @since 1.1.0
|
57
|
-
# @api private
|
58
|
-
def _filtered_keys(hash)
|
59
|
-
_key_paths(hash).select { |key| filters.any? { |filter| key =~ %r{(\.|\A)#{filter}(\.|\z)} } }
|
60
|
-
end
|
61
|
-
|
62
|
-
# @since 1.1.0
|
63
|
-
# @api private
|
64
|
-
def _key_paths(hash, base = nil)
|
65
|
-
hash.inject([]) do |results, (k, v)|
|
66
|
-
results + (_key_paths?(v) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# @since 1.1.0
|
71
|
-
# @api private
|
72
|
-
def _build_path(base, key)
|
73
|
-
[base, key.to_s].compact.join(".")
|
74
|
-
end
|
75
|
-
|
76
|
-
# @since 1.1.0
|
77
|
-
# @api private
|
78
|
-
def _actual_keys(hash, keys)
|
79
|
-
search_in = hash
|
80
|
-
|
81
|
-
keys.inject([]) do |res, key|
|
82
|
-
correct_key = search_in.key?(key.to_sym) ? key.to_sym : key
|
83
|
-
search_in = search_in[correct_key]
|
84
|
-
res + [correct_key]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Check if the given value can be iterated (`Enumerable`) and that isn't a `File`.
|
89
|
-
# This is useful to detect closed `Tempfiles`.
|
90
|
-
#
|
91
|
-
# @since 1.3.5
|
92
|
-
# @api private
|
93
|
-
#
|
94
|
-
# @see https://github.com/hanami/utils/pull/342
|
95
|
-
def _key_paths?(value)
|
96
|
-
value.is_a?(Enumerable) && !value.is_a?(File)
|
97
|
-
end
|
98
|
-
|
99
|
-
# @since 1.3.7
|
100
|
-
# @api private
|
101
|
-
def _deep_dup(hash)
|
102
|
-
hash.transform_values do |value|
|
103
|
-
if value.is_a?(Hash)
|
104
|
-
_deep_dup(value)
|
105
|
-
else
|
106
|
-
_key_paths?(value) ? value.dup : value
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# @since 1.3.7
|
112
|
-
# @api private
|
113
|
-
def _copy_params(params)
|
114
|
-
case params
|
115
|
-
when Hash
|
116
|
-
_deep_dup(params)
|
117
|
-
when Array
|
118
|
-
params.map { |hash| _deep_dup(hash) }
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# @since 1.3.7
|
123
|
-
# @api private
|
124
|
-
def _filter_hash(hash)
|
125
|
-
_filtered_keys(hash).each do |key|
|
126
|
-
*keys, last = _actual_keys(hash, key.split("."))
|
127
|
-
keys.inject(hash, :fetch)[last] = mask
|
128
|
-
end
|
129
|
-
hash
|
130
|
-
end
|
131
|
-
|
132
|
-
# @since 1.3.7
|
133
|
-
# @api private
|
134
|
-
def _filter(params)
|
135
|
-
case params
|
136
|
-
when Hash
|
137
|
-
_filter_hash(params)
|
138
|
-
when Array
|
139
|
-
params.map { |hash| _filter_hash(hash) }
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
@@ -1,183 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "set"
|
4
|
-
require "json"
|
5
|
-
require "logger"
|
6
|
-
require "hanami/utils/json"
|
7
|
-
require "hanami/utils/class_attribute"
|
8
|
-
require "hanami/utils/query_string"
|
9
|
-
|
10
|
-
module Hanami
|
11
|
-
class Logger < ::Logger
|
12
|
-
# Hanami::Logger default formatter.
|
13
|
-
# This formatter returns string in key=value format.
|
14
|
-
#
|
15
|
-
# @since 0.5.0
|
16
|
-
# @api private
|
17
|
-
#
|
18
|
-
# @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html
|
19
|
-
class Formatter < ::Logger::Formatter
|
20
|
-
require "hanami/logger/filter"
|
21
|
-
require "hanami/logger/colorizer"
|
22
|
-
|
23
|
-
# @since 0.8.0
|
24
|
-
# @api private
|
25
|
-
SEPARATOR = " "
|
26
|
-
|
27
|
-
# @since 0.8.0
|
28
|
-
# @api private
|
29
|
-
NEW_LINE = $/
|
30
|
-
|
31
|
-
# @since 1.0.0
|
32
|
-
# @api private
|
33
|
-
RESERVED_KEYS = %i[app severity time].freeze
|
34
|
-
|
35
|
-
include Utils::ClassAttribute
|
36
|
-
|
37
|
-
class_attribute :subclasses
|
38
|
-
self.subclasses = Set.new
|
39
|
-
|
40
|
-
def self.fabricate(formatter, application_name, filters, colorizer)
|
41
|
-
fabricated_formatter = _formatter_instance(formatter)
|
42
|
-
|
43
|
-
fabricated_formatter.application_name = application_name
|
44
|
-
fabricated_formatter.filter = Filter.new(filters)
|
45
|
-
fabricated_formatter.colorizer = colorizer
|
46
|
-
|
47
|
-
fabricated_formatter
|
48
|
-
end
|
49
|
-
|
50
|
-
# @api private
|
51
|
-
def self.inherited(subclass)
|
52
|
-
super
|
53
|
-
subclasses << subclass
|
54
|
-
end
|
55
|
-
|
56
|
-
# @api private
|
57
|
-
def self.eligible?(name)
|
58
|
-
name == :default
|
59
|
-
end
|
60
|
-
|
61
|
-
# @api private
|
62
|
-
# @since 1.1.0
|
63
|
-
def self._formatter_instance(formatter)
|
64
|
-
case formatter
|
65
|
-
when Symbol
|
66
|
-
(subclasses.find { |s| s.eligible?(formatter) } || self).new
|
67
|
-
when nil
|
68
|
-
new
|
69
|
-
else
|
70
|
-
formatter
|
71
|
-
end
|
72
|
-
end
|
73
|
-
private_class_method :_formatter_instance
|
74
|
-
|
75
|
-
# @since 0.5.0
|
76
|
-
# @api private
|
77
|
-
attr_writer :application_name
|
78
|
-
|
79
|
-
# @since 1.0.0
|
80
|
-
# @api private
|
81
|
-
attr_reader :application_name
|
82
|
-
|
83
|
-
# @since 1.2.0
|
84
|
-
# @api private
|
85
|
-
attr_writer :filter
|
86
|
-
|
87
|
-
# @since 1.2.0
|
88
|
-
# @api private
|
89
|
-
attr_writer :colorizer
|
90
|
-
|
91
|
-
# @since 0.5.0
|
92
|
-
# @api private
|
93
|
-
#
|
94
|
-
# @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html#method-i-call
|
95
|
-
def call(severity, time, progname, msg)
|
96
|
-
colorized = @colorizer.call(application_name, severity, time, progname)
|
97
|
-
colorized.merge!(_message_hash(msg))
|
98
|
-
|
99
|
-
_format(colorized)
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
104
|
-
# @since 0.8.0
|
105
|
-
# @api private
|
106
|
-
def _message_hash(message)
|
107
|
-
case message
|
108
|
-
when ::Hash
|
109
|
-
@filter.call(message)
|
110
|
-
when Exception
|
111
|
-
::Hash[
|
112
|
-
message: message.message,
|
113
|
-
backtrace: message.backtrace || [],
|
114
|
-
error: message.class
|
115
|
-
]
|
116
|
-
else
|
117
|
-
::Hash[message: message]
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# @since 0.8.0
|
122
|
-
# @api private
|
123
|
-
def _format(hash)
|
124
|
-
"#{_line_front_matter(hash.delete(:app), hash.delete(:severity), hash.delete(:time))}#{SEPARATOR}#{_format_message(hash)}" # rubocop:disable Layout/LineLength
|
125
|
-
end
|
126
|
-
|
127
|
-
# @since 1.2.0
|
128
|
-
# @api private
|
129
|
-
def _line_front_matter(*args)
|
130
|
-
args.map { |string| "[#{string}]" }.join(SEPARATOR)
|
131
|
-
end
|
132
|
-
|
133
|
-
# @since 1.2.0
|
134
|
-
# @api private
|
135
|
-
def _format_message(hash)
|
136
|
-
if hash.key?(:error)
|
137
|
-
_format_error(hash)
|
138
|
-
elsif hash.key?(:params)
|
139
|
-
"#{hash.values.join(SEPARATOR)}#{NEW_LINE}"
|
140
|
-
else
|
141
|
-
"#{Utils::QueryString.call(hash[:message] || hash)}#{NEW_LINE}"
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# @since 1.2.0
|
146
|
-
# @api private
|
147
|
-
def _format_error(hash)
|
148
|
-
result = [hash[:error], hash[:message]].compact.join(": ").concat(NEW_LINE)
|
149
|
-
hash[:backtrace].each do |line|
|
150
|
-
result << "from #{line}#{NEW_LINE}"
|
151
|
-
end
|
152
|
-
|
153
|
-
result
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Hanami::Logger JSON formatter.
|
158
|
-
# This formatter returns string in JSON format.
|
159
|
-
#
|
160
|
-
# @since 0.5.0
|
161
|
-
# @api private
|
162
|
-
class JSONFormatter < Formatter
|
163
|
-
# @api private
|
164
|
-
def self.eligible?(name)
|
165
|
-
name == :json
|
166
|
-
end
|
167
|
-
|
168
|
-
# @api private
|
169
|
-
def colorizer=(*)
|
170
|
-
@colorizer = NullColorizer.new
|
171
|
-
end
|
172
|
-
|
173
|
-
private
|
174
|
-
|
175
|
-
# @since 0.8.0
|
176
|
-
# @api private
|
177
|
-
def _format(hash)
|
178
|
-
hash[:time] = hash[:time].utc.iso8601
|
179
|
-
Hanami::Utils::Json.generate(hash) + NEW_LINE
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|