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