hanami-utils 2.0.0.alpha6 → 2.0.0.rc1

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.
@@ -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
@@ -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