hanami-utils 1.2.0.beta2 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +8 -0
- data/lib/hanami/logger/colorizer.rb +95 -0
- data/lib/hanami/logger/filter.rb +68 -0
- data/lib/hanami/logger/formatter.rb +183 -0
- data/lib/hanami/logger.rb +62 -234
- data/lib/hanami/utils/query_string.rb +30 -0
- data/lib/hanami/utils/shell_color.rb +64 -0
- data/lib/hanami/utils/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86da2fce7ef69bfbd91366caa3abcc403dcacbf581fca38eb3b242f4dcbca5e7
|
4
|
+
data.tar.gz: 34213163f0aff44ee06516a4ceb7fdcbe96802a4b101a1bb5c45b6220c8e8736
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 607969cb2bc3673ef48a15102342409b5e383433c55dc39543f51dd4436e384f56050e8d48be9defddba15d2933644d001c59ce6863bffd203f020a58b34753d
|
7
|
+
data.tar.gz: c9374f9627a418002d7753536025f9b3724cfea6fb18a77344ccdfcc0a8d94375cf26c687fd8d91edda8fb036b75d7dda16674785260f37a8507672d62935a9c
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
# Hanami::Utils
|
2
2
|
Ruby core extentions and class utilities for Hanami
|
3
3
|
|
4
|
+
## v1.2.0.rc1 - 2018-03-30
|
5
|
+
### Added
|
6
|
+
- [Oana Sipos & Sean Collins & Luca Guidi] Colored logging
|
7
|
+
|
8
|
+
### Fixed
|
9
|
+
- [Luca Guidi] Make `Hanami::Logger` to properly log hash messages
|
10
|
+
|
4
11
|
## v1.2.0.beta2 - 2018-03-23
|
5
12
|
|
6
13
|
## v1.2.0.beta1 - 2018-02-28
|
data/README.md
CHANGED
@@ -124,6 +124,14 @@ Manage directories where to find Ruby source code or web static assets. [[API do
|
|
124
124
|
|
125
125
|
Safe logic to manage relative URLs. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/PathPrefix)]
|
126
126
|
|
127
|
+
### Hanami::Utils::QueryString
|
128
|
+
|
129
|
+
URI query string transformation [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/QueryString)]
|
130
|
+
|
131
|
+
### Hanami::Utils::ShellColor
|
132
|
+
|
133
|
+
Shell colorizer [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/ShellColor)]
|
134
|
+
|
127
135
|
### Hanami::Utils::String
|
128
136
|
|
129
137
|
Enhanced version of Ruby's `String`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/String)]
|
@@ -0,0 +1,95 @@
|
|
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 four parts of the log message
|
56
|
+
#
|
57
|
+
# These can be overridden, keeping the name keys, with acceptable values
|
58
|
+
# being any from Hanami::Utils::ShellColor::COLORS
|
59
|
+
#
|
60
|
+
# @since 1.2.0
|
61
|
+
COLORS = ::Hash[
|
62
|
+
app: :blue,
|
63
|
+
severity: :magenta,
|
64
|
+
datetime: :cyan,
|
65
|
+
].freeze
|
66
|
+
|
67
|
+
attr_reader :colors
|
68
|
+
|
69
|
+
# @since 1.2.0
|
70
|
+
# @api private
|
71
|
+
def app(input)
|
72
|
+
colorize(input, color: colors.fetch(:app, nil))
|
73
|
+
end
|
74
|
+
|
75
|
+
# @since 1.2.0
|
76
|
+
# @api private
|
77
|
+
def severity(input)
|
78
|
+
colorize(input, color: colors.fetch(:severity, nil))
|
79
|
+
end
|
80
|
+
|
81
|
+
# @since 1.2.0
|
82
|
+
# @api private
|
83
|
+
def datetime(input)
|
84
|
+
colorize(input, color: colors.fetch(:datetime, nil))
|
85
|
+
end
|
86
|
+
|
87
|
+
# @since 1.2.0
|
88
|
+
# @api private
|
89
|
+
def colorize(message, color:)
|
90
|
+
return message if color.nil?
|
91
|
+
Hanami::Utils::ShellColor.call(message, color: color)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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.1.0
|
13
|
+
# @api private
|
14
|
+
def initialize(filters = [])
|
15
|
+
@filters = filters
|
16
|
+
end
|
17
|
+
|
18
|
+
# @since 1.1.0
|
19
|
+
# @api private
|
20
|
+
def call(hash)
|
21
|
+
_filtered_keys(hash).each do |key|
|
22
|
+
*keys, last = _actual_keys(hash, key.split("."))
|
23
|
+
keys.inject(hash, :fetch)[last] = "[FILTERED]"
|
24
|
+
end
|
25
|
+
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# @since 1.1.0
|
32
|
+
# @api private
|
33
|
+
attr_reader :filters
|
34
|
+
|
35
|
+
# @since 1.1.0
|
36
|
+
# @api private
|
37
|
+
def _filtered_keys(hash)
|
38
|
+
_key_paths(hash).select { |key| filters.any? { |filter| key =~ %r{(\.|\A)#{filter}(\.|\z)} } }
|
39
|
+
end
|
40
|
+
|
41
|
+
# @since 1.1.0
|
42
|
+
# @api private
|
43
|
+
def _key_paths(hash, base = nil)
|
44
|
+
hash.inject([]) do |results, (k, v)|
|
45
|
+
results + (v.respond_to?(:each) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @since 1.1.0
|
50
|
+
# @api private
|
51
|
+
def _build_path(base, key)
|
52
|
+
[base, key.to_s].compact.join(".")
|
53
|
+
end
|
54
|
+
|
55
|
+
# @since 1.1.0
|
56
|
+
# @api private
|
57
|
+
def _actual_keys(hash, keys)
|
58
|
+
search_in = hash
|
59
|
+
|
60
|
+
keys.inject([]) do |res, key|
|
61
|
+
correct_key = search_in.key?(key.to_sym) ? key.to_sym : key
|
62
|
+
search_in = search_in[correct_key]
|
63
|
+
res + [correct_key]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,183 @@
|
|
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) # rubocop:disable Metrics/MethodLength
|
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)}"
|
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
|
data/lib/hanami/logger.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require 'hanami/utils/hash'
|
7
|
-
require 'hanami/utils/class_attribute'
|
8
|
-
require 'hanami/utils/files'
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "hanami/utils/string"
|
5
|
+
require "hanami/utils/files"
|
9
6
|
|
10
7
|
module Hanami
|
11
8
|
# Hanami logger
|
@@ -82,248 +79,65 @@ module Hanami
|
|
82
79
|
# # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
|
83
80
|
#
|
84
81
|
# @example Write to file
|
85
|
-
# require 'hanami'
|
82
|
+
# require 'hanami/logger'
|
86
83
|
#
|
87
84
|
# Hanami::Logger.new(stream: 'logfile.log').info('Hello')
|
88
85
|
# # in logfile.log
|
89
86
|
# # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
|
90
87
|
#
|
91
88
|
# @example Use JSON formatter
|
92
|
-
# require 'hanami'
|
89
|
+
# require 'hanami/logger'
|
93
90
|
#
|
94
91
|
# Hanami::Logger.new(formatter: Hanami::Logger::JSONFormatter).info('Hello')
|
95
92
|
# # => "{\"app\":\"Hanami\",\"severity\":\"INFO\",\"time\":\"1988-09-01 00:00:00 UTC\",\"message\":\"Hello\"}"
|
93
|
+
#
|
94
|
+
# @example Disable colorization
|
95
|
+
# require 'hanami/logger'
|
96
|
+
#
|
97
|
+
# Hanami::Logger.new(colorizer: false)
|
98
|
+
#
|
99
|
+
# @example Use custom colors
|
100
|
+
# require 'hanami/logger'
|
101
|
+
#
|
102
|
+
# Hanami::Logger.new(colorizer: Hanami::Logger::Colorizer.new(colors: { app: :red }))
|
103
|
+
#
|
104
|
+
# @example Use custom colorizer
|
105
|
+
# require "hanami/logger"
|
106
|
+
# require "paint" # gem install paint
|
107
|
+
#
|
108
|
+
# class LogColorizer < Hanami::Logger::Colorizer
|
109
|
+
# def initialize(colors: { app: [:red, :bright], severity: [:red, :blue], datetime: [:italic, :yellow] })
|
110
|
+
# super
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# private
|
114
|
+
#
|
115
|
+
# def colorize(message, color:)
|
116
|
+
# Paint[message, *color]
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# Hanami::Logger.new(colorizer: LogColorizer.new)
|
96
121
|
class Logger < ::Logger
|
97
|
-
|
98
|
-
|
99
|
-
#
|
100
|
-
# @since 0.5.0
|
101
|
-
# @api private
|
102
|
-
#
|
103
|
-
# @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html
|
104
|
-
class Formatter < ::Logger::Formatter
|
105
|
-
# @since 0.8.0
|
106
|
-
# @api private
|
107
|
-
SEPARATOR = ' '.freeze
|
108
|
-
|
109
|
-
# @since 0.8.0
|
110
|
-
# @api private
|
111
|
-
NEW_LINE = $/
|
112
|
-
|
113
|
-
# @since 1.0.0
|
114
|
-
# @api private
|
115
|
-
RESERVED_KEYS = %i[app severity time].freeze
|
116
|
-
|
117
|
-
include Utils::ClassAttribute
|
118
|
-
|
119
|
-
class_attribute :subclasses
|
120
|
-
self.subclasses = Set.new
|
121
|
-
|
122
|
-
def self.fabricate(formatter, application_name, filters)
|
123
|
-
fabricated_formatter = _formatter_instance(formatter)
|
124
|
-
|
125
|
-
fabricated_formatter.application_name = application_name
|
126
|
-
fabricated_formatter.hash_filter = HashFilter.new(filters)
|
127
|
-
|
128
|
-
fabricated_formatter
|
129
|
-
end
|
130
|
-
|
131
|
-
# @api private
|
132
|
-
def self.inherited(subclass)
|
133
|
-
super
|
134
|
-
subclasses << subclass
|
135
|
-
end
|
136
|
-
|
137
|
-
# @api private
|
138
|
-
def self.eligible?(name)
|
139
|
-
name == :default
|
140
|
-
end
|
141
|
-
|
142
|
-
# @api private
|
143
|
-
# @since 1.1.0
|
144
|
-
def self._formatter_instance(formatter)
|
145
|
-
case formatter
|
146
|
-
when Symbol
|
147
|
-
(subclasses.find { |s| s.eligible?(formatter) } || self).new
|
148
|
-
when nil
|
149
|
-
new
|
150
|
-
else
|
151
|
-
formatter
|
152
|
-
end
|
153
|
-
end
|
154
|
-
private_class_method :_formatter_instance
|
155
|
-
|
156
|
-
# @since 0.5.0
|
157
|
-
# @api private
|
158
|
-
attr_writer :application_name
|
159
|
-
|
160
|
-
# @since 1.0.0
|
161
|
-
# @api private
|
162
|
-
attr_reader :application_name
|
163
|
-
|
164
|
-
# @since 1.1.0
|
165
|
-
# @api private
|
166
|
-
attr_writer :hash_filter
|
167
|
-
|
168
|
-
# @since 0.5.0
|
169
|
-
# @api private
|
170
|
-
#
|
171
|
-
# @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Formatter.html#method-i-call
|
172
|
-
def call(severity, time, _progname, msg)
|
173
|
-
_format({
|
174
|
-
app: application_name,
|
175
|
-
severity: severity,
|
176
|
-
time: time
|
177
|
-
}.merge(
|
178
|
-
_message_hash(msg)
|
179
|
-
))
|
180
|
-
end
|
181
|
-
|
182
|
-
private
|
183
|
-
|
184
|
-
# @since 0.8.0
|
185
|
-
# @api private
|
186
|
-
def _message_hash(message) # rubocop:disable Metrics/MethodLength
|
187
|
-
case message
|
188
|
-
when Hash
|
189
|
-
@hash_filter.filter(message)
|
190
|
-
when Exception
|
191
|
-
Hash[
|
192
|
-
message: message.message,
|
193
|
-
backtrace: message.backtrace || [],
|
194
|
-
error: message.class
|
195
|
-
]
|
196
|
-
else
|
197
|
-
Hash[message: message]
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
# @since 0.8.0
|
202
|
-
# @api private
|
203
|
-
def _format(hash)
|
204
|
-
result = RESERVED_KEYS.map { |k| "[#{hash[k]}]" }.join(SEPARATOR)
|
205
|
-
return _format_error(result, hash) if hash.key?(:error)
|
206
|
-
|
207
|
-
values = hash.each_with_object([]) do |(k, v), memo|
|
208
|
-
memo << v unless RESERVED_KEYS.include?(k)
|
209
|
-
end
|
210
|
-
|
211
|
-
result << " #{values.join(SEPARATOR)}#{NEW_LINE}"
|
212
|
-
result
|
213
|
-
end
|
214
|
-
|
215
|
-
# @api private
|
216
|
-
def _format_error(result, hash)
|
217
|
-
result << " #{hash[:error]}:" if hash.key?(:error)
|
218
|
-
result << " #{hash[:message]}#{NEW_LINE}"
|
219
|
-
if hash.key?(:backtrace)
|
220
|
-
hash[:backtrace].each do |line|
|
221
|
-
result << "from #{line}#{NEW_LINE}"
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
result
|
226
|
-
end
|
227
|
-
|
228
|
-
# Filtering logic
|
229
|
-
#
|
230
|
-
# @since 1.1.0
|
231
|
-
# @api private
|
232
|
-
class HashFilter
|
233
|
-
# @since 1.1.0
|
234
|
-
# @api private
|
235
|
-
attr_reader :filters
|
236
|
-
|
237
|
-
# @since 1.1.0
|
238
|
-
# @api private
|
239
|
-
def initialize(filters = [])
|
240
|
-
@filters = filters
|
241
|
-
end
|
242
|
-
|
243
|
-
# @since 1.1.0
|
244
|
-
# @api private
|
245
|
-
def filter(hash)
|
246
|
-
_filtered_keys(hash).each do |key|
|
247
|
-
*keys, last = _actual_keys(hash, key.split('.'))
|
248
|
-
keys.inject(hash, :fetch)[last] = '[FILTERED]'
|
249
|
-
end
|
250
|
-
|
251
|
-
hash
|
252
|
-
end
|
253
|
-
|
254
|
-
private
|
255
|
-
|
256
|
-
# @since 1.1.0
|
257
|
-
# @api private
|
258
|
-
def _filtered_keys(hash)
|
259
|
-
_key_paths(hash).select { |key| filters.any? { |filter| key =~ %r{(\.|\A)#{filter}(\.|\z)} } }
|
260
|
-
end
|
261
|
-
|
262
|
-
# @since 1.1.0
|
263
|
-
# @api private
|
264
|
-
def _key_paths(hash, base = nil)
|
265
|
-
hash.inject([]) do |results, (k, v)|
|
266
|
-
results + (v.respond_to?(:each) ? _key_paths(v, _build_path(base, k)) : [_build_path(base, k)])
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
# @since 1.1.0
|
271
|
-
# @api private
|
272
|
-
def _build_path(base, key)
|
273
|
-
[base, key.to_s].compact.join('.')
|
274
|
-
end
|
275
|
-
|
276
|
-
# @since 1.1.0
|
277
|
-
# @api private
|
278
|
-
def _actual_keys(hash, keys)
|
279
|
-
search_in = hash
|
280
|
-
|
281
|
-
keys.inject([]) do |res, key|
|
282
|
-
correct_key = search_in.key?(key.to_sym) ? key.to_sym : key
|
283
|
-
search_in = search_in[correct_key]
|
284
|
-
res + [correct_key]
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
# Hanami::Logger JSON formatter.
|
291
|
-
# This formatter returns string in JSON format.
|
292
|
-
#
|
293
|
-
# @since 0.5.0
|
294
|
-
# @api private
|
295
|
-
class JSONFormatter < Formatter
|
296
|
-
# @api private
|
297
|
-
def self.eligible?(name)
|
298
|
-
name == :json
|
299
|
-
end
|
300
|
-
|
301
|
-
private
|
302
|
-
|
303
|
-
# @since 0.8.0
|
304
|
-
# @api private
|
305
|
-
def _format(hash)
|
306
|
-
hash[:time] = hash[:time].utc.iso8601
|
307
|
-
Hanami::Utils::Json.generate(hash) + NEW_LINE
|
308
|
-
end
|
309
|
-
end
|
122
|
+
require "hanami/logger/formatter"
|
123
|
+
require "hanami/logger/colorizer"
|
310
124
|
|
311
125
|
# Default application name.
|
312
126
|
# This is used as a fallback for tagging purposes.
|
313
127
|
#
|
314
128
|
# @since 0.5.0
|
315
129
|
# @api private
|
316
|
-
DEFAULT_APPLICATION_NAME =
|
130
|
+
DEFAULT_APPLICATION_NAME = "hanami"
|
317
131
|
|
318
132
|
# @since 0.8.0
|
319
133
|
# @api private
|
320
|
-
LEVELS = Hash[
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
134
|
+
LEVELS = ::Hash[
|
135
|
+
"debug" => DEBUG,
|
136
|
+
"info" => INFO,
|
137
|
+
"warn" => WARN,
|
138
|
+
"error" => ERROR,
|
139
|
+
"fatal" => FATAL,
|
140
|
+
"unknown" => UNKNOWN
|
327
141
|
].freeze
|
328
142
|
|
329
143
|
# @since 0.5.0
|
@@ -456,7 +270,7 @@ module Hanami
|
|
456
270
|
# # => {"app":"Hanami","severity":"DEBUG","time":"2017-03-30T13:57:59Z","message":"Hello World"}
|
457
271
|
# rubocop:disable Lint/HandleExceptions
|
458
272
|
# rubocop:disable Metrics/ParameterLists
|
459
|
-
def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [])
|
273
|
+
def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [], colorizer: nil)
|
460
274
|
begin
|
461
275
|
Utils::Files.mkdir_p(stream)
|
462
276
|
rescue TypeError
|
@@ -467,8 +281,9 @@ module Hanami
|
|
467
281
|
@level = _level(level)
|
468
282
|
@stream = stream
|
469
283
|
@application_name = application_name
|
470
|
-
@formatter = Formatter.fabricate(formatter, self.application_name, filter)
|
284
|
+
@formatter = Formatter.fabricate(formatter, self.application_name, filter, lookup_colorizer(colorizer))
|
471
285
|
end
|
286
|
+
|
472
287
|
# rubocop:enable Metrics/ParameterLists
|
473
288
|
# rubocop:enable Lint/HandleExceptions
|
474
289
|
|
@@ -500,7 +315,7 @@ module Hanami
|
|
500
315
|
# @api private
|
501
316
|
def _application_name_from_namespace
|
502
317
|
class_name = self.class.name
|
503
|
-
namespace = Utils::String.
|
318
|
+
namespace = Utils::String.namespace(class_name)
|
504
319
|
|
505
320
|
class_name != namespace and return namespace
|
506
321
|
end
|
@@ -521,5 +336,18 @@ module Hanami
|
|
521
336
|
LEVELS.fetch(level.to_s.downcase, DEBUG)
|
522
337
|
end
|
523
338
|
end
|
339
|
+
|
340
|
+
# @since 1.2.0
|
341
|
+
# @api private
|
342
|
+
def lookup_colorizer(colorizer)
|
343
|
+
return NullColorizer.new if colorizer == false
|
344
|
+
colorizer || (tty? ? Colorizer : NullColorizer).new
|
345
|
+
end
|
346
|
+
|
347
|
+
# @since 1.2.0
|
348
|
+
# @api private
|
349
|
+
def tty?
|
350
|
+
@logdev.dev.tty?
|
351
|
+
end
|
524
352
|
end
|
525
353
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module Utils
|
7
|
+
# URI query string transformations
|
8
|
+
#
|
9
|
+
# @since 1.2.0
|
10
|
+
module QueryString
|
11
|
+
# Serialize input into a query string
|
12
|
+
#
|
13
|
+
# @param input [Object] the input
|
14
|
+
#
|
15
|
+
# @return [::String] the query string
|
16
|
+
#
|
17
|
+
# @since 1.2.0
|
18
|
+
#
|
19
|
+
# TODO: this is a very basic implementation that needs to be expanded
|
20
|
+
def self.call(input)
|
21
|
+
case input
|
22
|
+
when ::Hash
|
23
|
+
input.to_a.join("=")
|
24
|
+
else
|
25
|
+
input.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Utils
|
5
|
+
# Shell helper for colorizing STDOUT
|
6
|
+
#
|
7
|
+
# It doesn't check if you're writing to a file or anything, so you have to
|
8
|
+
# check that yourself before using this module.
|
9
|
+
#
|
10
|
+
# @since 1.2.0
|
11
|
+
module ShellColor
|
12
|
+
# Unknown color code error
|
13
|
+
#
|
14
|
+
# @since 1.2.0
|
15
|
+
class UnknownColorCodeError < ::StandardError
|
16
|
+
def initialize(code)
|
17
|
+
super("unknown color code: `#{code.inspect}'")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Escape codes for terminals to output strings in colors
|
22
|
+
#
|
23
|
+
# @since 1.2.0
|
24
|
+
# @api private
|
25
|
+
COLORS = ::Hash[
|
26
|
+
black: 30,
|
27
|
+
red: 31,
|
28
|
+
green: 32,
|
29
|
+
yellow: 33,
|
30
|
+
blue: 34,
|
31
|
+
magenta: 35,
|
32
|
+
cyan: 36,
|
33
|
+
gray: 37,
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
# Colorize output
|
37
|
+
# 8 colors available: black, red, green, yellow, blue, magenta, cyan, and gray
|
38
|
+
#
|
39
|
+
# @param input [#to_s] the string to colorize
|
40
|
+
# @param color [Symbol] the color
|
41
|
+
#
|
42
|
+
# @raise [Hanami::Utils::ShellColor::UnknownColorError] if the color code is
|
43
|
+
# unknown
|
44
|
+
#
|
45
|
+
# @return [String] the colorized string
|
46
|
+
#
|
47
|
+
# @since 1.2.0
|
48
|
+
def self.call(input, color:)
|
49
|
+
"\e[#{color_code(color)}m#{input}\e[0m"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Helper method to translate between color names and terminal escape codes
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @since 1.2.0
|
56
|
+
#
|
57
|
+
# @raise [Hanami::Utils::ShellColor::UnknownColorError] if the color code is
|
58
|
+
# unknown
|
59
|
+
def self.color_code(code)
|
60
|
+
COLORS.fetch(code) { raise UnknownColorCodeError.new(code) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/hanami/utils/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.0.
|
4
|
+
version: 1.2.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: transproc
|
@@ -94,6 +94,9 @@ files:
|
|
94
94
|
- lib/hanami-utils.rb
|
95
95
|
- lib/hanami/interactor.rb
|
96
96
|
- lib/hanami/logger.rb
|
97
|
+
- lib/hanami/logger/colorizer.rb
|
98
|
+
- lib/hanami/logger/filter.rb
|
99
|
+
- lib/hanami/logger/formatter.rb
|
97
100
|
- lib/hanami/utils.rb
|
98
101
|
- lib/hanami/utils/basic_object.rb
|
99
102
|
- lib/hanami/utils/blank.rb
|
@@ -112,6 +115,8 @@ files:
|
|
112
115
|
- lib/hanami/utils/kernel.rb
|
113
116
|
- lib/hanami/utils/load_paths.rb
|
114
117
|
- lib/hanami/utils/path_prefix.rb
|
118
|
+
- lib/hanami/utils/query_string.rb
|
119
|
+
- lib/hanami/utils/shell_color.rb
|
115
120
|
- lib/hanami/utils/string.rb
|
116
121
|
- lib/hanami/utils/version.rb
|
117
122
|
homepage: http://hanamirb.org
|