ruby_smart-simple_logger 1.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 +7 -0
- data/.github/workflows/ruby.yml +38 -0
- data/.gitignore +25 -0
- data/.rspec +3 -0
- data/.yardopts +5 -0
- data/Gemfile +7 -0
- data/README.md +739 -0
- data/Rakefile +8 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/docs/CHANGELOG.md +17 -0
- data/docs/CODE_OF_CONDUCT.md +84 -0
- data/docs/LICENSE.txt +21 -0
- data/lib/debugger.rb +20 -0
- data/lib/ruby_smart/simple_logger/core_ext/ruby/string.rb +43 -0
- data/lib/ruby_smart/simple_logger/devices/memory_device.rb +43 -0
- data/lib/ruby_smart/simple_logger/devices/multi_device.rb +69 -0
- data/lib/ruby_smart/simple_logger/devices/proc_device.rb +37 -0
- data/lib/ruby_smart/simple_logger/extensions/helper.rb +259 -0
- data/lib/ruby_smart/simple_logger/extensions/logs.rb +26 -0
- data/lib/ruby_smart/simple_logger/extensions/mask.rb +53 -0
- data/lib/ruby_smart/simple_logger/extensions/scene.rb +77 -0
- data/lib/ruby_smart/simple_logger/extensions/severity.rb +62 -0
- data/lib/ruby_smart/simple_logger/extensions/simple_log.rb +224 -0
- data/lib/ruby_smart/simple_logger/extensions/timer.rb +63 -0
- data/lib/ruby_smart/simple_logger/formatter.rb +153 -0
- data/lib/ruby_smart/simple_logger/gem_version.rb +23 -0
- data/lib/ruby_smart/simple_logger/klass_logger.rb +45 -0
- data/lib/ruby_smart/simple_logger/logger.rb +74 -0
- data/lib/ruby_smart/simple_logger/scenes.rb +288 -0
- data/lib/ruby_smart/simple_logger/version.rb +12 -0
- data/lib/ruby_smart/simple_logger.rb +25 -0
- data/lib/ruby_smart-simple_logger.rb +3 -0
- data/lib/simple_logger.rb +7 -0
- data/ruby_smart-simple_logger.gemspec +43 -0
- metadata +167 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
module Extensions
|
6
|
+
module Mask
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.include InstanceMethods
|
10
|
+
base.class_eval do
|
11
|
+
# defines the default mask to be used
|
12
|
+
#
|
13
|
+
# @option [String] :char - the character to be used as mask
|
14
|
+
# @option [Integer] :length - the mask length (amount of mask chars be line)
|
15
|
+
# @option [Symbol] :clr - the color to be used by printing the mask
|
16
|
+
self.mask = { char: '=', length: 100, clr: :blue }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def mask
|
22
|
+
class_variable_get('@@mask')
|
23
|
+
end
|
24
|
+
|
25
|
+
def mask=(mask)
|
26
|
+
class_variable_set('@@mask', mask)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module InstanceMethods
|
31
|
+
# combined getter & setter for instances mask
|
32
|
+
# new mask is merged with existing
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# mask
|
36
|
+
# > {char: '=', length: 100}
|
37
|
+
#
|
38
|
+
# mask(clr: :blue, length: 10)
|
39
|
+
# mask
|
40
|
+
# > {char: '=', length: 10, clr: :blue}
|
41
|
+
#
|
42
|
+
# @param [nil, Hash] mask
|
43
|
+
# @return [Hash] mask
|
44
|
+
def mask(mask = nil)
|
45
|
+
return (@mask || self.class.mask) if mask.nil?
|
46
|
+
|
47
|
+
@mask = (@mask || self.class.mask).merge(mask)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
module Extensions
|
6
|
+
module Scene
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.include InstanceMethods
|
10
|
+
base.class_eval do
|
11
|
+
# holds the default scene options
|
12
|
+
# [Hash] scenes
|
13
|
+
self.scenes = {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# returns all registered scene default options
|
19
|
+
# @return [Hash] scenes
|
20
|
+
def scenes
|
21
|
+
class_variable_get('@@scenes')
|
22
|
+
end
|
23
|
+
|
24
|
+
# sets scene options
|
25
|
+
# @param [Hash] scenes
|
26
|
+
def scenes=(scenes)
|
27
|
+
class_variable_set('@@scenes', scenes)
|
28
|
+
end
|
29
|
+
|
30
|
+
# registers a new scene by provided key & options
|
31
|
+
# also defines this method by provided block
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# scene :line, { level: :debug } do |data, opts = {}|
|
35
|
+
# self.log data, _scene_opt(:line, opts)
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @param [Symbol] key - name of the scene method
|
39
|
+
# @param [Hash] opts - scene default options
|
40
|
+
# @param [Proc] block - scene block to define a appropriate method
|
41
|
+
# @return [Boolean] created result
|
42
|
+
def scene(key, opts = {}, &block)
|
43
|
+
# protect overwrite existing methods
|
44
|
+
# but allow all severities (levels)
|
45
|
+
return false if instance_methods.include?(key) && !self::LEVEL.key?(key)
|
46
|
+
|
47
|
+
# register scene default options
|
48
|
+
self.scenes[key] = opts
|
49
|
+
|
50
|
+
# define (or overwrite) this method, if a block was provided
|
51
|
+
define_method(key, &block) if block_given?
|
52
|
+
|
53
|
+
# returns success result
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module InstanceMethods
|
59
|
+
# returns all registered scene default options
|
60
|
+
# @return [Hash] scenes
|
61
|
+
def scenes
|
62
|
+
self.class.scenes
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# resolves scene options by provided key & merges them with additional options
|
68
|
+
# @param [Symbol] key
|
69
|
+
# @param [Array<Hash>] opts
|
70
|
+
def _scene_opt(key, *opts)
|
71
|
+
_opt((scenes[key] || {}), *opts)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module RubySmart
|
6
|
+
module SimpleLogger
|
7
|
+
module Extensions
|
8
|
+
module Severity
|
9
|
+
# include log level constants from original logger severity
|
10
|
+
include ::Logger::Severity
|
11
|
+
|
12
|
+
# add severity success (sub-kind of info = 1)
|
13
|
+
SUCCESS = 1.1
|
14
|
+
|
15
|
+
# creates an severity hash
|
16
|
+
# {
|
17
|
+
# 0 => 'DEBUG',
|
18
|
+
# 1 => 'INFO',
|
19
|
+
# 1.1 => 'SUCCESS',
|
20
|
+
# 2 => 'WARN',
|
21
|
+
# ...
|
22
|
+
# }
|
23
|
+
SEVERITIES = %w(DEBUG INFO SUCCESS WARN ERROR FATAL UNKNOWN).map { |sev| [const_get(sev), sev] }.to_h.freeze
|
24
|
+
|
25
|
+
# creates an level hash from SEVERITIES
|
26
|
+
LEVEL = SEVERITIES.reduce({}) { |m, (lvl, sev)| m[sev.downcase.to_sym] = lvl; m }.freeze
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# overwrite original method to provide additional severities
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# format_severity(1.1)
|
34
|
+
# > 'SUCCESS'
|
35
|
+
#
|
36
|
+
# @param [Numeric] severity
|
37
|
+
# @return [String (frozen)] severity name
|
38
|
+
def format_severity(severity)
|
39
|
+
self.class::SEVERITIES[severity] || 'UNKNOWN'
|
40
|
+
end
|
41
|
+
|
42
|
+
# resolves the severity level by provided Number, Symbol or String
|
43
|
+
#
|
44
|
+
# @param [Numeric, String, Symbol] sev - severity to resolve
|
45
|
+
# @return [Numeric,nil] severity level
|
46
|
+
def _level(sev)
|
47
|
+
# no sev provided
|
48
|
+
return UNKNOWN if sev.nil?
|
49
|
+
# numeric and valid
|
50
|
+
return sev if sev.is_a?(Numeric) && SEVERITIES.key?(sev)
|
51
|
+
|
52
|
+
key = sev.to_s.downcase.to_sym
|
53
|
+
# symbol (:success) and valid
|
54
|
+
return LEVEL[key] if LEVEL.key?(key)
|
55
|
+
|
56
|
+
# fallback to unknown
|
57
|
+
UNKNOWN
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
module Extensions
|
6
|
+
module SimpleLog
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.include InstanceMethods
|
10
|
+
base.class_eval do
|
11
|
+
# defines the default inspector to be used
|
12
|
+
#
|
13
|
+
# :auto
|
14
|
+
self.inspector = :auto
|
15
|
+
|
16
|
+
# this will overwrite the default log method from the Ruby Logger
|
17
|
+
# but makes it still accessible through: #_log
|
18
|
+
alias_method :_log, :log
|
19
|
+
alias_method :log, :simple_log
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def inspector
|
25
|
+
class_variable_get('@@inspector')
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspector=(inspector)
|
29
|
+
class_variable_set('@@inspector', inspector)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module InstanceMethods
|
34
|
+
# Acts as the MAIN logging method and sends the data to the device
|
35
|
+
# - checks provided level before logging
|
36
|
+
# - creates & formats payload
|
37
|
+
#
|
38
|
+
# @param [Object] data
|
39
|
+
# @param [Hash] opts
|
40
|
+
# @option opts [Symbol, Numeric, String] :level - which level is logging
|
41
|
+
# @option opts [Array] :payload - payload for logging multilines (scene)
|
42
|
+
# @option opts [Symbol] :inspect - inspection method for data
|
43
|
+
# @option opts [Symbol] :formatter - formatter type
|
44
|
+
# @option opts [Hash] :mask - mask data
|
45
|
+
# @return [Boolean] logging result
|
46
|
+
def simple_log(data, opts = {})
|
47
|
+
# resolve & check level
|
48
|
+
level = _level(opts[:level])
|
49
|
+
return false if level < self.level
|
50
|
+
|
51
|
+
# prevents data from being transformed into payload
|
52
|
+
if ignore_payload? || opts[:payload].is_a?(FalseClass)
|
53
|
+
# prevent logging nil data
|
54
|
+
return false if data.nil?
|
55
|
+
|
56
|
+
add level, data
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
# create a default payload, if nothing was provided
|
61
|
+
# :_ -> alias for log data
|
62
|
+
opts[:payload] ||= [self.class::PAYLOAD_DATA_KEY]
|
63
|
+
|
64
|
+
# create a default mask, if nothing was provided
|
65
|
+
opts[:mask] ||= self.mask
|
66
|
+
|
67
|
+
# create payloads and log each payload
|
68
|
+
# returns the payload boolean result
|
69
|
+
_payloads(opts.delete(:payload), opts, data) do |p|
|
70
|
+
add level, p
|
71
|
+
end
|
72
|
+
|
73
|
+
# returns true as logging result
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
# returns true if no payload should be created - instead the data will be send directly to the logdev
|
78
|
+
# forces the *simple_log* method to prevent building payloads from schemes - it just forwards the level & data to the logdev
|
79
|
+
#
|
80
|
+
# @return [Boolean]
|
81
|
+
def ignore_payload?
|
82
|
+
!!@ignore_payload
|
83
|
+
end
|
84
|
+
|
85
|
+
# resolve an inspector method for data inspection
|
86
|
+
# @return [Symbol, nil]
|
87
|
+
def inspector
|
88
|
+
# return or resolve inspector
|
89
|
+
@inspector ||= if self.class.inspector == :auto
|
90
|
+
# provide awesome_print support
|
91
|
+
Object.respond_to?(:ai) ? :ai : :inspect
|
92
|
+
else
|
93
|
+
self.class.inspector || :inspect
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# parses each payload and creates a callback
|
100
|
+
#
|
101
|
+
# @param [Array] payloads
|
102
|
+
# @param [Hash] opts
|
103
|
+
# @param [Object] data
|
104
|
+
def _payloads(payloads, opts, data)
|
105
|
+
payloads.each do |payload|
|
106
|
+
# IMPORTANT: Do NOT remove this - prevents frozen string literal problem on other file sources
|
107
|
+
str = ''
|
108
|
+
if payload == self.class::PAYLOAD_DATA_KEY
|
109
|
+
# checks, if we should inspect the data
|
110
|
+
str << _parse_data(data, (opts[:inspect] ? (opts[:inspector] || self.inspector) : nil))
|
111
|
+
else
|
112
|
+
str << _parse_payload(payload, opts)
|
113
|
+
end
|
114
|
+
|
115
|
+
# always append newline - except it is forced excluded
|
116
|
+
str << "\n" if opts[:nl] != false
|
117
|
+
|
118
|
+
yield str
|
119
|
+
end
|
120
|
+
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
# just parses the data and calls an inspection method, if provided
|
125
|
+
#
|
126
|
+
# @param [Object] data
|
127
|
+
# @param [Symbol, nil] inspector - the inspection method to be called (e.g. :ai, :inspect, :to_s) - if not provided it tries to auto-resolve
|
128
|
+
# @return [String] stringified data
|
129
|
+
def _parse_data(data, inspector = nil)
|
130
|
+
(inspector ? data.send(inspector) : data).to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
# parses a single payload with provided options
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# _parse_payload(:mask, opts)
|
137
|
+
#
|
138
|
+
# _parse_payload({mask: ' [%{subject}] '}, opts)
|
139
|
+
#
|
140
|
+
# _parse_payload([:mask, ' [%{subject}] '], opts)
|
141
|
+
#
|
142
|
+
# @param [Array, Hash, Symbol, String] payload
|
143
|
+
# @param [Hash] opts
|
144
|
+
# @return [String] str
|
145
|
+
def _parse_payload(payload, opts)
|
146
|
+
# resolve type & additional sub-payloads (fraction)
|
147
|
+
payload = payload.to_a[0] if payload.is_a?(Hash)
|
148
|
+
type, fraction = (payload.is_a?(Array) ? payload : [payload, nil])
|
149
|
+
|
150
|
+
# prevent type case - type is the fraction
|
151
|
+
return type if type.is_a?(String)
|
152
|
+
|
153
|
+
case type
|
154
|
+
when :mask, :_mask
|
155
|
+
# resolve mask data
|
156
|
+
mask = self.mask.merge(opts[:mask])
|
157
|
+
|
158
|
+
# resolve text from fraction
|
159
|
+
# this could also be a payload
|
160
|
+
txt = _parse_payload({ _txt: fraction }, opts)
|
161
|
+
|
162
|
+
# clean possible colored text - sucks but necessary :(
|
163
|
+
txt.gsub!(/\e\[[\d;]+m?/, '')
|
164
|
+
|
165
|
+
# check for provided txt length - this will decide if it's a full-line mask
|
166
|
+
if txt.length == 0
|
167
|
+
_clr((mask[:char] * mask[:length]), mask[:clr])
|
168
|
+
else
|
169
|
+
# text size is to large for mask
|
170
|
+
txt = txt[0, (mask[:length] - 1)] if txt.length > mask[:length]
|
171
|
+
txt_lh = (txt.length / 2).floor
|
172
|
+
|
173
|
+
left_mask = mask[:char] * ((mask[:length] / 2) - txt_lh)
|
174
|
+
right_mask = mask[:char] * (mask[:length] - left_mask.length - txt.length)
|
175
|
+
|
176
|
+
_clr("#{left_mask}#{txt}#{right_mask}", mask[:clr])
|
177
|
+
# _clr(left_mask, mask[:clr]) + _clr(txt, mask[:clr]) + _clr(right_mask, mask[:clr])
|
178
|
+
end
|
179
|
+
when :txt, :_txt
|
180
|
+
txt = _parse_payload(fraction, opts)
|
181
|
+
txt = _parse_opts(txt, opts)
|
182
|
+
return '' if txt.length == 0
|
183
|
+
|
184
|
+
# force string to exact length
|
185
|
+
txt = txt.ljust(opts[:length], ' ')[0..(opts[:length] - 1)] if opts[:length]
|
186
|
+
|
187
|
+
_clr(txt, opts[:clr])
|
188
|
+
when :concat
|
189
|
+
fraction = [fraction] unless fraction.is_a?(Array)
|
190
|
+
fraction.map { |f| _parse_payload(f, opts) }.join
|
191
|
+
when :blank, :_blank
|
192
|
+
''
|
193
|
+
else
|
194
|
+
# unknown type will be resolved by returning as string
|
195
|
+
fraction.nil? ? type.to_s : fraction.to_s
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# parses wildcards ( %{xyz} ) from provided text by using the option keys
|
200
|
+
#
|
201
|
+
# @param [String] str
|
202
|
+
# @param [Hash] opts
|
203
|
+
# @return [String] parsed txt
|
204
|
+
def _parse_opts(str, opts = {})
|
205
|
+
mask = opts[:mask] || { length: 0 }
|
206
|
+
str = str.to_s
|
207
|
+
txt = str.dup
|
208
|
+
|
209
|
+
# SPECIAL: prevent subject being parsed longer as the mask#length
|
210
|
+
opts[:subject] = opts[:subject][0, (mask[:length] - 4 - mask[:char].length * 2)] if opts[:subject] && mask[:length] && opts[:subject].length > mask[:length]
|
211
|
+
|
212
|
+
str.scan(/%\{(\w+)\}/) do |mm|
|
213
|
+
next unless mm.length > 0 && mm[0]
|
214
|
+
m = mm[0].to_sym
|
215
|
+
txt.gsub!("%{#{m}}", (opts[m] ? opts[m].to_s : ''))
|
216
|
+
end
|
217
|
+
|
218
|
+
txt
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
module Extensions
|
6
|
+
module Timer
|
7
|
+
def timer(action, key = :default, opts = {})
|
8
|
+
@timers ||= {}
|
9
|
+
@timers[key] ||= {
|
10
|
+
start: nil,
|
11
|
+
stop: nil,
|
12
|
+
measure: 0
|
13
|
+
}
|
14
|
+
|
15
|
+
case action
|
16
|
+
when :restart
|
17
|
+
@timers[key][:start] = Time.now
|
18
|
+
@timers[key][:stop] = nil
|
19
|
+
@timers[key][:measure] = 0
|
20
|
+
|
21
|
+
true
|
22
|
+
when :start, :continue
|
23
|
+
@timers[key][:start] = Time.now
|
24
|
+
@timers[key][:stop] = nil
|
25
|
+
|
26
|
+
true
|
27
|
+
when :stop
|
28
|
+
return false if !@timers[key][:start] || @timers[key][:stop]
|
29
|
+
@timers[key][:stop] = Time.now
|
30
|
+
@timers[key][:measure] += @timers[key][:stop] - @timers[key][:start]
|
31
|
+
|
32
|
+
true
|
33
|
+
when :pause
|
34
|
+
return false if !@timers[key][:start] || @timers[key][:stop]
|
35
|
+
|
36
|
+
@timers[key][:measure] += Time.now - @timers[key][:start]
|
37
|
+
@timers[key][:start] = nil
|
38
|
+
@timers[key][:stop] = nil
|
39
|
+
|
40
|
+
true
|
41
|
+
when :clear
|
42
|
+
self.timer(:stop, key)
|
43
|
+
current = self.timer(:current, key)
|
44
|
+
@timers.delete(key)
|
45
|
+
|
46
|
+
# time_ago_in_words in only available if activesupport & actionview gems are loaded
|
47
|
+
if opts[:humanized] && respond_to?(:time_ago_in_words)
|
48
|
+
time_ago_in_words(current.to_i.seconds.from_now, include_seconds: true)
|
49
|
+
else
|
50
|
+
current
|
51
|
+
end
|
52
|
+
when :current
|
53
|
+
current = @timers[key][:measure]
|
54
|
+
current += Time.now - @timers[key][:start] if @timers[key][:start] && @timers[key][:stop].nil?
|
55
|
+
current
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
class Formatter
|
6
|
+
class << self
|
7
|
+
# returns all registered formats
|
8
|
+
# @return [Hash] formats
|
9
|
+
def formats
|
10
|
+
class_variable_get('@@formats')
|
11
|
+
end
|
12
|
+
|
13
|
+
# sets formats
|
14
|
+
# @param [Hash] formats
|
15
|
+
def formats=(formats)
|
16
|
+
class_variable_set('@@formats', formats)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# set default formats
|
21
|
+
self.formats = {
|
22
|
+
# the ruby default's logging format (except to format the severity to 7-chars)
|
23
|
+
default: {
|
24
|
+
str: "%s, [%s #%d] %7s -- %s: %s",
|
25
|
+
cb: lambda { |severity, time, progname, data| [severity[0], format_datetime(time), $$, severity, progname, msg2str(data)] }
|
26
|
+
},
|
27
|
+
# all provided args as array
|
28
|
+
passthrough: {
|
29
|
+
str: false, # no formatting
|
30
|
+
cb: lambda { |*args| args }
|
31
|
+
},
|
32
|
+
# the plain data (msg) only, no severity, etc.
|
33
|
+
plain: {
|
34
|
+
str: false, # no formatting
|
35
|
+
cb: lambda { |_severity, _time, _progname, data| data }
|
36
|
+
},
|
37
|
+
# special array data for memory-logging
|
38
|
+
memory: {
|
39
|
+
str: false, # no formatting
|
40
|
+
cb: lambda { |severity, time, _progname, data| [severity.downcase.to_sym, time, data] }
|
41
|
+
},
|
42
|
+
# special datalog data with all provided data in additional brackets -> [data] [data] [data]
|
43
|
+
datalog: {
|
44
|
+
str: "[%7s] [%s] [#%d] [%s]",
|
45
|
+
cb: lambda { |severity, time, _progname, data| [severity, format_datetime(time, true), $$, msg2str(data, true)] }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
# defines the severity colors
|
50
|
+
SEVERITY_COLORS = {
|
51
|
+
'DEBUG' => :blue,
|
52
|
+
'INFO' => :cyan,
|
53
|
+
'WARN' => :yellow,
|
54
|
+
'ERROR' => :red,
|
55
|
+
'FATAL' => :bg_red,
|
56
|
+
'SUCCESS' => :green
|
57
|
+
}
|
58
|
+
|
59
|
+
# initialize with options
|
60
|
+
# @param [Hash] opts
|
61
|
+
# @option opts [Symbol] :format - define other format
|
62
|
+
# @option opts [Boolean] :nl - create newline after each call (default: true)
|
63
|
+
# @option opts [Boolean] :clr - colorizes the whole output (default: false)
|
64
|
+
def initialize(opts = {})
|
65
|
+
# set default opts
|
66
|
+
opts[:nl] = true if opts[:nl].nil?
|
67
|
+
opts[:format] = :default if opts[:format].nil?
|
68
|
+
|
69
|
+
@opts = opts
|
70
|
+
end
|
71
|
+
|
72
|
+
# standard call method - used to format provided terms
|
73
|
+
def call(severity, time, progname, data)
|
74
|
+
if current_format_str
|
75
|
+
str = current_format_str % instance_exec(severity, time, progname, data, ¤t_format_cb)
|
76
|
+
str << "\n" if opts[:nl]
|
77
|
+
|
78
|
+
# check for colorized output
|
79
|
+
(opts[:clr] && SEVERITY_COLORS[severity]) ? str.send(SEVERITY_COLORS[severity]) : str
|
80
|
+
else
|
81
|
+
instance_exec(severity, time, progname, data, ¤t_format_cb)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# returns all formats
|
86
|
+
# @return [Hash] formats
|
87
|
+
def formats
|
88
|
+
self.class.formats
|
89
|
+
end
|
90
|
+
|
91
|
+
# combined getter & setter for options
|
92
|
+
# new options are merged with existing
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# opts
|
96
|
+
# > {formatter: :default, nl: true}
|
97
|
+
#
|
98
|
+
# opts(nl: false, test: 45)
|
99
|
+
# opts
|
100
|
+
# > {formatter: :default, nl: false, test: 45}
|
101
|
+
#
|
102
|
+
# @param [nil, Hash] opts
|
103
|
+
# @return [Hash] opts
|
104
|
+
def opts(opts = nil)
|
105
|
+
return @opts if opts.nil?
|
106
|
+
|
107
|
+
clear!
|
108
|
+
|
109
|
+
@opts.merge!(opts)
|
110
|
+
end
|
111
|
+
|
112
|
+
# clears auto-generated / cached data
|
113
|
+
def clear!
|
114
|
+
@current_format = nil
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def current_format
|
120
|
+
@current_format ||= self.formats.key?(opts[:format]) ? self.formats[opts[:format]] : self.formats.values.first
|
121
|
+
end
|
122
|
+
|
123
|
+
def current_format_str
|
124
|
+
current_format[:str]
|
125
|
+
end
|
126
|
+
|
127
|
+
def current_format_cb
|
128
|
+
current_format[:cb]
|
129
|
+
end
|
130
|
+
|
131
|
+
def format_datetime(time, short = false)
|
132
|
+
if short
|
133
|
+
time.strftime("%Y-%m-%d %H:%M:%S")
|
134
|
+
else
|
135
|
+
time.strftime("%Y-%m-%dT%H:%M:%S.%6N")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def msg2str(msg, join = false)
|
140
|
+
case msg
|
141
|
+
when ::String
|
142
|
+
msg
|
143
|
+
when ::Array
|
144
|
+
join ? msg.join('] [') : msg.inspect
|
145
|
+
when ::Exception
|
146
|
+
"#{ msg.message } (#{ msg.class })\n" + (msg.backtrace || []).join("\n")
|
147
|
+
else
|
148
|
+
msg.inspect
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
# Returns the version of the currently loaded module as a <tt>Gem::Version</tt>
|
6
|
+
def self.gem_version
|
7
|
+
Gem::Version.new VERSION::STRING
|
8
|
+
end
|
9
|
+
|
10
|
+
module VERSION
|
11
|
+
MAJOR = 1
|
12
|
+
MINOR = 0
|
13
|
+
TINY = 0
|
14
|
+
PRE = nil
|
15
|
+
|
16
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
17
|
+
|
18
|
+
def self.to_s
|
19
|
+
STRING
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module RubySmart
|
4
|
+
module SimpleLogger
|
5
|
+
module KlassLogger
|
6
|
+
def self.extended(base)
|
7
|
+
base.send(:include, RubySmart::SimpleLogger::Logger::Severity)
|
8
|
+
base.class_eval do
|
9
|
+
self.klass_logger_opts = {}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def klass_logger_opts
|
14
|
+
class_variable_get('@@klass_logger_opts')
|
15
|
+
end
|
16
|
+
|
17
|
+
def klass_logger_opts=(opts)
|
18
|
+
class_variable_set('@@klass_logger_opts', opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
# delegate new method to logger
|
22
|
+
def new(*args)
|
23
|
+
args = [nil, self.klass_logger_opts] if args.length == 0
|
24
|
+
RubySmart::SimpleLogger::Logger.new(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def klass_logger
|
28
|
+
@klass_logger ||= self.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear!
|
32
|
+
@klass_logger = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(name, *args, &block)
|
36
|
+
return self.klass_logger.send(name, *args) if self.klass_logger.respond_to? name
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def respond_to?(method_name, _include_private = false)
|
41
|
+
self.klass_logger.respond_to? method_name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|