hanami-utils 2.0.0.beta1 → 2.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 +12 -0
- data/hanami-utils.gemspec +1 -1
- data/lib/hanami/middleware.rb +10 -3
- data/lib/hanami/utils/class.rb +0 -27
- data/lib/hanami/utils/hash.rb +1 -1
- data/lib/hanami/utils/json.rb +10 -0
- data/lib/hanami/utils/kernel.rb +1 -0
- data/lib/hanami/utils/shell_color.rb +5 -1
- data/lib/hanami/utils/string.rb +1 -1
- data/lib/hanami/utils/version.rb +3 -2
- metadata +13 -11
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2575ff2a640a9ffcddf8a9930a887d536ec9da4b64bc4a2883849ed261f57b43
|
4
|
+
data.tar.gz: 70fbfe55f826d940c0f5decad39840990925e061de975e53d2b672fdae9873df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73d5a9fade1cb961ac4be22b8967507fceebde1a9d8edb33d71634b06ef6c06538c8d0ef2b1a32a33e8442c7609b675ae99bd63780c353c6db02485ddbfd3432
|
7
|
+
data.tar.gz: 368ffe941852e67432d9574b79220b1807b1529cbdf4289fb2fd2db92299888617bc3779bd6e0c3f03b81d5fbcb9725b8b36364733d262a3c5fa2cb257db91b1
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
Ruby core extensions and class utilities for Hanami
|
4
4
|
|
5
|
+
## v2.0.0 - 2022-11-22
|
6
|
+
|
7
|
+
## v2.0.0.rc1 - 2022-11-08
|
8
|
+
|
9
|
+
### Fixed
|
10
|
+
|
11
|
+
- [Benjamin Klotz] Ensure `Hanami::Utils::String.underscore` to replace `"."` (dot character) into underscore
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
- [Luca Guidi] Removed `Hanami::Logger` in favor of `Dry::Logger`
|
16
|
+
|
5
17
|
## v2.0.0.beta1 - 2022-07-20
|
6
18
|
|
7
19
|
### Changed
|
data/hanami-utils.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.metadata["rubygems_mfa_required"] = "true"
|
21
21
|
spec.required_ruby_version = ">= 3.0"
|
22
22
|
|
23
|
-
spec.add_dependency "dry-transformer", "~> 0
|
23
|
+
spec.add_dependency "dry-transformer", "~> 1.0", "< 2"
|
24
24
|
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", ">= 1.6", "< 3"
|
data/lib/hanami/middleware.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Hanami
|
4
|
+
# Shared Rack middleware for Hanami apps.
|
5
|
+
#
|
6
|
+
# This module is defined in hanami-utils so that any gem providing middleware can use its
|
7
|
+
# resources.
|
8
|
+
#
|
9
|
+
# @since 2.0.0
|
4
10
|
module Middleware
|
5
|
-
#
|
6
|
-
|
11
|
+
# Base class for all errors raised during middleware loading.
|
12
|
+
#
|
7
13
|
# @since 2.0.0
|
8
|
-
|
14
|
+
# @api public
|
15
|
+
class Error < StandardError
|
9
16
|
end
|
10
17
|
end
|
11
18
|
end
|
data/lib/hanami/utils/class.rb
CHANGED
@@ -71,33 +71,6 @@ module Hanami
|
|
71
71
|
def self.load(name, namespace = Object)
|
72
72
|
load!(name, namespace) if namespace.const_defined?(name.to_s, false)
|
73
73
|
end
|
74
|
-
|
75
|
-
def self.tokenize(pattern)
|
76
|
-
if match = TOKENIZE_REGEXP.match(pattern)
|
77
|
-
pre = match.pre_match
|
78
|
-
post = match.post_match
|
79
|
-
tokens = match[1].split(TOKENIZE_SEPARATOR)
|
80
|
-
tokens.each do |token|
|
81
|
-
yield("#{pre}#{token}#{post}")
|
82
|
-
end
|
83
|
-
else
|
84
|
-
yield(pattern)
|
85
|
-
end
|
86
|
-
|
87
|
-
nil
|
88
|
-
end
|
89
|
-
|
90
|
-
# Regexp for .tokenize
|
91
|
-
#
|
92
|
-
# @since 1.3.0
|
93
|
-
# @api private
|
94
|
-
TOKENIZE_REGEXP = /\((.*)\)/
|
95
|
-
|
96
|
-
# Separator for .tokenize
|
97
|
-
#
|
98
|
-
# @since 1.3.0
|
99
|
-
# @api private
|
100
|
-
TOKENIZE_SEPARATOR = "|"
|
101
74
|
end
|
102
75
|
end
|
103
76
|
end
|
data/lib/hanami/utils/hash.rb
CHANGED
data/lib/hanami/utils/json.rb
CHANGED
@@ -35,10 +35,20 @@ module Hanami
|
|
35
35
|
|
36
36
|
# rubocop:disable Style/ClassVars
|
37
37
|
if defined?(MultiJson)
|
38
|
+
# @since 0.8.0
|
39
|
+
# @api private
|
38
40
|
@@engine = MultiJsonAdapter.new
|
41
|
+
|
42
|
+
# @since 0.8.0
|
43
|
+
# @api private
|
39
44
|
ParserError = MultiJson::ParseError
|
40
45
|
else
|
46
|
+
# @since 0.8.0
|
47
|
+
# @api private
|
41
48
|
@@engine = ::JSON
|
49
|
+
|
50
|
+
# @since 0.8.0
|
51
|
+
# @api private
|
42
52
|
ParserError = ::JSON::ParserError
|
43
53
|
end
|
44
54
|
# rubocop:enable Style/ClassVars
|
data/lib/hanami/utils/kernel.rb
CHANGED
@@ -7,12 +7,16 @@ module Hanami
|
|
7
7
|
# It doesn't check if you're writing to a file or anything, so you have to
|
8
8
|
# check that yourself before using this module.
|
9
9
|
#
|
10
|
+
# @api public
|
10
11
|
# @since 1.2.0
|
11
12
|
module ShellColor
|
12
|
-
#
|
13
|
+
# Error raised for unknown color codes.
|
13
14
|
#
|
14
15
|
# @since 1.2.0
|
16
|
+
# @api public
|
15
17
|
class UnknownColorCodeError < ::StandardError
|
18
|
+
# @since 1.2.0
|
19
|
+
# @api private
|
16
20
|
def initialize(code)
|
17
21
|
super("unknown color code: `#{code.inspect}'")
|
18
22
|
end
|
data/lib/hanami/utils/string.rb
CHANGED
@@ -231,7 +231,7 @@ module Hanami
|
|
231
231
|
string.gsub!(NAMESPACE_SEPARATOR, UNDERSCORE_SEPARATOR)
|
232
232
|
string.gsub!(/([A-Z\d]+)([A-Z][a-z])/, UNDERSCORE_DIVISION_TARGET)
|
233
233
|
string.gsub!(/([a-z\d])([A-Z])/, UNDERSCORE_DIVISION_TARGET)
|
234
|
-
string.gsub!(/[[:space:]]
|
234
|
+
string.gsub!(/[[:space:]]|-|\./, UNDERSCORE_DIVISION_TARGET)
|
235
235
|
string.downcase
|
236
236
|
end
|
237
237
|
|
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: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-transformer
|
@@ -16,14 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
19
|
+
version: '1.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
29
|
+
version: '1.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: concurrent-ruby
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,10 +118,6 @@ files:
|
|
112
118
|
- README.md
|
113
119
|
- hanami-utils.gemspec
|
114
120
|
- lib/hanami-utils.rb
|
115
|
-
- lib/hanami/logger.rb
|
116
|
-
- lib/hanami/logger/colorizer.rb
|
117
|
-
- lib/hanami/logger/filter.rb
|
118
|
-
- lib/hanami/logger/formatter.rb
|
119
121
|
- lib/hanami/middleware.rb
|
120
122
|
- lib/hanami/utils.rb
|
121
123
|
- lib/hanami/utils/blank.rb
|
@@ -153,11 +155,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
153
155
|
version: '3.0'
|
154
156
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
157
|
requirements:
|
156
|
-
- - "
|
158
|
+
- - ">="
|
157
159
|
- !ruby/object:Gem::Version
|
158
|
-
version:
|
160
|
+
version: '0'
|
159
161
|
requirements: []
|
160
|
-
rubygems_version: 3.3.
|
162
|
+
rubygems_version: 3.3.7
|
161
163
|
signing_key:
|
162
164
|
specification_version: 4
|
163
165
|
summary: Ruby core extentions and Hanami utilities
|
@@ -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
|
data/lib/hanami/logger.rb
DELETED
@@ -1,362 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "logger"
|
4
|
-
require "hanami/utils/string"
|
5
|
-
require "hanami/utils/files"
|
6
|
-
|
7
|
-
module Hanami
|
8
|
-
# Hanami logger
|
9
|
-
#
|
10
|
-
# Implementation with the same interface of Ruby std lib `Logger`.
|
11
|
-
# It uses `STDOUT`, `STDERR`, file name or open file as output stream.
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# When a Hanami application is initialized, it creates a logger for that specific application.
|
15
|
-
# For instance for a `Bookshelf::Application` a `Bookshelf::Logger` will be available.
|
16
|
-
#
|
17
|
-
# This is useful for auto-tagging the output. Eg (`app=Booshelf`).
|
18
|
-
#
|
19
|
-
# When used standalone (eg. `Hanami::Logger.info`), it tags lines with `app=Shared`.
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# The available severity levels are the same of `Logger`:
|
23
|
-
#
|
24
|
-
# * DEBUG
|
25
|
-
# * INFO
|
26
|
-
# * WARN
|
27
|
-
# * ERROR
|
28
|
-
# * FATAL
|
29
|
-
# * UNKNOWN
|
30
|
-
#
|
31
|
-
# Those levels are available both as class and instance methods.
|
32
|
-
#
|
33
|
-
# Also Hanami::Logger supports different formatters. Now available only two:
|
34
|
-
#
|
35
|
-
# * Formatter (default)
|
36
|
-
# * JSONFormatter
|
37
|
-
#
|
38
|
-
# And if you want to use custom formatter you need create new class inherited from
|
39
|
-
# `Formatter` class and define `_format` private method like this:
|
40
|
-
#
|
41
|
-
# class CustomFormatter < Formatter
|
42
|
-
# private
|
43
|
-
# def _format(hash)
|
44
|
-
# # ...
|
45
|
-
# end
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# @since 0.5.0
|
49
|
-
#
|
50
|
-
# @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html
|
51
|
-
# @see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger/Severity.html
|
52
|
-
#
|
53
|
-
# @example Basic usage
|
54
|
-
# require 'hanami'
|
55
|
-
#
|
56
|
-
# module Bookshelf
|
57
|
-
# class Application < Hanami::Application
|
58
|
-
# end
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# # Initialize the application with the following code:
|
62
|
-
# Bookshelf::Application.load!
|
63
|
-
# # or
|
64
|
-
# Bookshelf::Application.new
|
65
|
-
#
|
66
|
-
# Bookshelf::Logger.new.info('Hello')
|
67
|
-
# # => app=Bookshelf severity=INFO time=1988-09-01 00:00:00 UTC message=Hello
|
68
|
-
#
|
69
|
-
# @example Standalone usage
|
70
|
-
# require 'hanami/logger'
|
71
|
-
#
|
72
|
-
# Hanami::Logger.new.info('Hello')
|
73
|
-
# # => app=Hanami severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
|
74
|
-
#
|
75
|
-
# @example Custom tagging
|
76
|
-
# require 'hanami/logger'
|
77
|
-
#
|
78
|
-
# Hanami::Logger.new('FOO').info('Hello')
|
79
|
-
# # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
|
80
|
-
#
|
81
|
-
# @example Write to file
|
82
|
-
# require 'hanami/logger'
|
83
|
-
#
|
84
|
-
# Hanami::Logger.new(stream: 'logfile.log').info('Hello')
|
85
|
-
# # in logfile.log
|
86
|
-
# # => app=FOO severity=INFO time=2016-05-27 10:14:42 UTC message=Hello
|
87
|
-
#
|
88
|
-
# @example Use JSON formatter
|
89
|
-
# require 'hanami/logger'
|
90
|
-
#
|
91
|
-
# Hanami::Logger.new(formatter: Hanami::Logger::JSONFormatter).info('Hello')
|
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)
|
121
|
-
class Logger < ::Logger
|
122
|
-
require "hanami/logger/formatter"
|
123
|
-
require "hanami/logger/colorizer"
|
124
|
-
|
125
|
-
# Default application name.
|
126
|
-
# This is used as a fallback for tagging purposes.
|
127
|
-
#
|
128
|
-
# @since 0.5.0
|
129
|
-
# @api private
|
130
|
-
DEFAULT_APPLICATION_NAME = "hanami"
|
131
|
-
|
132
|
-
# @since 0.8.0
|
133
|
-
# @api private
|
134
|
-
LEVELS = ::Hash[
|
135
|
-
"debug" => DEBUG,
|
136
|
-
"info" => INFO,
|
137
|
-
"warn" => WARN,
|
138
|
-
"error" => ERROR,
|
139
|
-
"fatal" => FATAL,
|
140
|
-
"unknown" => UNKNOWN
|
141
|
-
].freeze
|
142
|
-
|
143
|
-
# @since 1.2.0
|
144
|
-
# @api private
|
145
|
-
def self.level(level)
|
146
|
-
case level
|
147
|
-
when DEBUG..UNKNOWN
|
148
|
-
level
|
149
|
-
else
|
150
|
-
LEVELS.fetch(level.to_s.downcase, DEBUG)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# @since 0.5.0
|
155
|
-
# @api private
|
156
|
-
attr_writer :application_name
|
157
|
-
|
158
|
-
# Initialize a logger
|
159
|
-
#
|
160
|
-
# @param application_name [String] an optional application name used for
|
161
|
-
# tagging purposes
|
162
|
-
#
|
163
|
-
# @param args [Array<Object>] an optional set of arguments to honor Ruby's
|
164
|
-
# `Logger#initialize` arguments. See Ruby documentation for details.
|
165
|
-
#
|
166
|
-
# @param stream [String, IO, StringIO, Pathname] an optional log stream.
|
167
|
-
# This is a filename (`String`) or `IO` object (typically `$stdout`,
|
168
|
-
# `$stderr`, or an open file). It defaults to `$stderr`.
|
169
|
-
#
|
170
|
-
# @param level [Integer,String] logging level. It can be expressed as an
|
171
|
-
# integer, according to Ruby's `Logger` from standard library or as a
|
172
|
-
# string with the name of the level
|
173
|
-
#
|
174
|
-
# @param formatter [Symbol,#_format] a formatter - We support `:json` as
|
175
|
-
# JSON formatter or an object that respond to `#_format(data)`
|
176
|
-
#
|
177
|
-
# @since 0.5.0
|
178
|
-
#
|
179
|
-
# @see https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html#class-Logger-label-How+to+create+a+logger
|
180
|
-
#
|
181
|
-
# @example Basic usage
|
182
|
-
# require 'hanami/logger'
|
183
|
-
#
|
184
|
-
# logger = Hanami::Logger.new
|
185
|
-
# logger.info "Hello World"
|
186
|
-
#
|
187
|
-
# # => [Hanami] [DEBUG] [2017-03-30 15:41:01 +0200] Hello World
|
188
|
-
#
|
189
|
-
# @example Custom application name
|
190
|
-
# require 'hanami/logger'
|
191
|
-
#
|
192
|
-
# logger = Hanami::Logger.new('bookshelf')
|
193
|
-
# logger.info "Hello World"
|
194
|
-
#
|
195
|
-
# # => [bookshelf] [DEBUG] [2017-03-30 15:44:23 +0200] Hello World
|
196
|
-
#
|
197
|
-
# @example Logger level (Integer)
|
198
|
-
# require 'hanami/logger'
|
199
|
-
#
|
200
|
-
# logger = Hanami::Logger.new(level: 2) # WARN
|
201
|
-
# logger.info "Hello World"
|
202
|
-
# # => true
|
203
|
-
#
|
204
|
-
# logger.info "Hello World"
|
205
|
-
# # => true
|
206
|
-
#
|
207
|
-
# logger.warn "Hello World"
|
208
|
-
# # => [Hanami] [WARN] [2017-03-30 16:00:48 +0200] Hello World
|
209
|
-
#
|
210
|
-
# @example Logger level (Constant)
|
211
|
-
# require 'hanami/logger'
|
212
|
-
#
|
213
|
-
# logger = Hanami::Logger.new(level: Hanami::Logger::WARN)
|
214
|
-
# logger.info "Hello World"
|
215
|
-
# # => true
|
216
|
-
#
|
217
|
-
# logger.info "Hello World"
|
218
|
-
# # => true
|
219
|
-
#
|
220
|
-
# logger.warn "Hello World"
|
221
|
-
# # => [Hanami] [WARN] [2017-03-30 16:00:48 +0200] Hello World
|
222
|
-
#
|
223
|
-
# @example Logger level (String)
|
224
|
-
# require 'hanami/logger'
|
225
|
-
#
|
226
|
-
# logger = Hanami::Logger.new(level: 'warn')
|
227
|
-
# logger.info "Hello World"
|
228
|
-
# # => true
|
229
|
-
#
|
230
|
-
# logger.info "Hello World"
|
231
|
-
# # => true
|
232
|
-
#
|
233
|
-
# logger.warn "Hello World"
|
234
|
-
# # => [Hanami] [WARN] [2017-03-30 16:00:48 +0200] Hello World
|
235
|
-
#
|
236
|
-
# @example Use a file
|
237
|
-
# require 'hanami/logger'
|
238
|
-
#
|
239
|
-
# logger = Hanami::Logger.new(stream: "development.log")
|
240
|
-
# logger.info "Hello World"
|
241
|
-
#
|
242
|
-
# # => true
|
243
|
-
#
|
244
|
-
# File.read("development.log")
|
245
|
-
# # =>
|
246
|
-
# # # Logfile created on 2017-03-30 15:52:48 +0200 by logger.rb/56815
|
247
|
-
# # [Hanami] [DEBUG] [2017-03-30 15:52:54 +0200] Hello World
|
248
|
-
#
|
249
|
-
# @example Period rotation
|
250
|
-
# require 'hanami/logger'
|
251
|
-
#
|
252
|
-
# # Rotate daily
|
253
|
-
# logger = Hanami::Logger.new('bookshelf', 'daily', stream: 'development.log')
|
254
|
-
#
|
255
|
-
# @example File size rotation
|
256
|
-
# require 'hanami/logger'
|
257
|
-
#
|
258
|
-
# # leave 10 old log files where the size is about 1,024,000 bytes
|
259
|
-
# logger = Hanami::Logger.new('bookshelf', 10, 1024000, stream: 'development.log')
|
260
|
-
#
|
261
|
-
# @example Use a StringIO
|
262
|
-
# require 'hanami/logger'
|
263
|
-
#
|
264
|
-
# stream = StringIO.new
|
265
|
-
# logger = Hanami::Logger.new(stream: stream)
|
266
|
-
# logger.info "Hello World"
|
267
|
-
#
|
268
|
-
# # => true
|
269
|
-
#
|
270
|
-
# stream.rewind
|
271
|
-
# stream.read
|
272
|
-
#
|
273
|
-
# # => "[Hanami] [DEBUG] [2017-03-30 15:55:22 +0200] Hello World\n"
|
274
|
-
#
|
275
|
-
# @example JSON formatter
|
276
|
-
# require 'hanami/logger'
|
277
|
-
#
|
278
|
-
# logger = Hanami::Logger.new(formatter: :json)
|
279
|
-
# logger.info "Hello World"
|
280
|
-
#
|
281
|
-
# # => {"app":"Hanami","severity":"DEBUG","time":"2017-03-30T13:57:59Z","message":"Hello World"}
|
282
|
-
# rubocop:disable Lint/SuppressedException
|
283
|
-
# rubocop:disable Metrics/ParameterLists
|
284
|
-
def initialize(application_name = nil, *args, stream: $stdout, level: DEBUG, formatter: nil, filter: [], colorizer: nil) # rubocop:disable Layout/LineLength
|
285
|
-
begin
|
286
|
-
Utils::Files.mkdir_p(stream)
|
287
|
-
rescue TypeError
|
288
|
-
end
|
289
|
-
|
290
|
-
super(stream, *args)
|
291
|
-
|
292
|
-
@level = _level(level)
|
293
|
-
@stream = stream
|
294
|
-
@application_name = application_name
|
295
|
-
@formatter = Formatter.fabricate(formatter, self.application_name, filter, lookup_colorizer(colorizer))
|
296
|
-
end
|
297
|
-
|
298
|
-
# rubocop:enable Metrics/ParameterLists
|
299
|
-
# rubocop:enable Lint/SuppressedException
|
300
|
-
|
301
|
-
# Returns the current application name, this is used for tagging purposes
|
302
|
-
#
|
303
|
-
# @return [String] the application name
|
304
|
-
#
|
305
|
-
# @since 0.5.0
|
306
|
-
def application_name
|
307
|
-
@application_name || _application_name_from_namespace || _default_application_name
|
308
|
-
end
|
309
|
-
|
310
|
-
# @since 0.8.0
|
311
|
-
# @api private
|
312
|
-
def level=(value)
|
313
|
-
super _level(value)
|
314
|
-
end
|
315
|
-
|
316
|
-
# Closes the logging stream if this stream isn't an STDOUT
|
317
|
-
#
|
318
|
-
# @since 0.8.0
|
319
|
-
def close
|
320
|
-
super unless [STDOUT, $stdout].include?(@stream) # rubocop:disable Style/GlobalStdStream
|
321
|
-
end
|
322
|
-
|
323
|
-
private
|
324
|
-
|
325
|
-
# @since 0.5.0
|
326
|
-
# @api private
|
327
|
-
def _application_name_from_namespace
|
328
|
-
class_name = self.class.name
|
329
|
-
namespace = Utils::String.namespace(class_name)
|
330
|
-
|
331
|
-
class_name != namespace and return namespace
|
332
|
-
end
|
333
|
-
|
334
|
-
# @since 0.5.0
|
335
|
-
# @api private
|
336
|
-
def _default_application_name
|
337
|
-
DEFAULT_APPLICATION_NAME
|
338
|
-
end
|
339
|
-
|
340
|
-
# @since 0.8.0
|
341
|
-
# @api private
|
342
|
-
def _level(level)
|
343
|
-
self.class.level(level)
|
344
|
-
end
|
345
|
-
|
346
|
-
# @since 1.2.0
|
347
|
-
# @api private
|
348
|
-
def lookup_colorizer(colorizer)
|
349
|
-
return NullColorizer.new if colorizer == false
|
350
|
-
|
351
|
-
colorizer || (tty? ? Colorizer : NullColorizer).new
|
352
|
-
end
|
353
|
-
|
354
|
-
# @since 1.2.0
|
355
|
-
# @api private
|
356
|
-
def tty?
|
357
|
-
return false if @logdev.nil?
|
358
|
-
|
359
|
-
@logdev.dev.tty?
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|