debug_logging 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +83 -33
- data/lib/debug_logging/argument_printer.rb +46 -13
- data/lib/debug_logging/class_logger.rb +36 -10
- data/lib/debug_logging/class_notifier.rb +32 -15
- data/lib/debug_logging/configuration.rb +49 -50
- data/lib/debug_logging/instance_logger.rb +2 -1
- data/lib/debug_logging/instance_logger_modulizer.rb +32 -6
- data/lib/debug_logging/instance_notifier_modulizer.rb +31 -16
- data/lib/debug_logging/log_subscriber.rb +11 -3
- data/lib/debug_logging/version.rb +1 -1
- data/lib/simple_debug_logging.rb +2 -0
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a39fc11d3fc815e37db037fd9ce8b08e74e5d98e7612a50f191231ba3ce95231
|
4
|
+
data.tar.gz: 99ef91cf9302dfc413ebba74435464933524cfa1a2e70f9623b97ff64c849729
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22ec37827bdf34889a023d235315ee4ca85642cdc070a22751be328f58c8f4b8b22c952da52c2d6900f3bc44431d8d0dd5800483419532f3fd5a5aca9227d529
|
7
|
+
data.tar.gz: 5b5456b83dfff252840e31ea81af17e5d3b50bdc025e090d55731fcd16353d41352b2304ba53818c791f9ec3970a447e4b01624fd269d6663951a444a876ac6d
|
data/README.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
Unobtrusive, inheritable-overridable-configurable, drop-in debug logging, that won't leave a mess behind when it is time to remove it.
|
4
4
|
|
5
|
+
## What do I mean by "unobtrusive"?
|
6
|
+
|
7
|
+
**Ugly** debug logging is added inside the body of a method, so it runs when a method is called. This can create a mess of your git history, and can even introduce new bugs to your code.
|
8
|
+
|
9
|
+
**Unobtrusive** debug logging stays out of the method, changes no logic, can't break your code, and yet it still runs when your method is called, and tells you everything you wanted to know. It doesn't mess with the git history of the method at all!
|
10
|
+
|
5
11
|
| Project | DebugLogging |
|
6
12
|
|------------------------ | ----------------------- |
|
7
13
|
| gem name | [debug_logging](https://rubygems.org/gems/debug_logging) |
|
@@ -43,9 +49,9 @@ Herein you will find:
|
|
43
49
|
* 100% clean, 0% obtrusive
|
44
50
|
* 100% tested
|
45
51
|
* 100% Ruby 2.1+ compatible
|
46
|
-
- use version
|
47
|
-
- use version
|
48
|
-
- use version
|
52
|
+
- use version `gem "debug_logging", "~> 1.0"` for Ruby < 2.3
|
53
|
+
- use version `gem "debug_logging", "~> 2.0"` for Ruby 2.3
|
54
|
+
- use version `gem "debug_logging", "~> 3.0"` for Ruby 2.4+
|
49
55
|
|
50
56
|
NOTE: The manner this is made to work for class methods is totally different than the way this is made to work for instance methods.
|
51
57
|
|
@@ -136,29 +142,45 @@ Every time a method is called, you can now get logs, optionally with arguments,
|
|
136
142
|
|
137
143
|
```ruby
|
138
144
|
class Car
|
139
|
-
|
140
|
-
#
|
141
|
-
# except for the logged
|
145
|
+
# Adds the helper methods to the class.
|
146
|
+
# All helpers prefixed with debug_*,
|
147
|
+
# except for the *logged* decorator, which comes from extending DebugLogging::ClassLogger
|
142
148
|
extend DebugLogging
|
143
149
|
|
144
150
|
# per class configuration overrides!
|
145
151
|
self.debug_class_benchmarks = true
|
146
152
|
self.debug_instance_benchmarks = true
|
147
153
|
|
148
|
-
# For
|
149
|
-
#
|
150
|
-
|
151
|
-
|
152
|
-
# Provides the `logged` method decorator
|
154
|
+
# For class methods
|
155
|
+
# Provides the versatile `logged` method decorator / macro
|
156
|
+
# For instance methods
|
157
|
+
# Provides the versatile `i_logged` method decorator / macro
|
153
158
|
extend DebugLogging::ClassLogger
|
154
159
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
def
|
160
|
+
# == BEGIN CLASS METHODS ==
|
161
|
+
# For class methods:
|
162
|
+
# Option 1: Use *logged* as a method decorator
|
163
|
+
logged def self.make; new; end
|
164
|
+
def self.design(*args); new; end
|
165
|
+
def self.safety(*args); new; end
|
166
|
+
def self.dealer_options(*args); new; end
|
167
|
+
|
168
|
+
# Option 2: Use *logged* as a macro
|
159
169
|
logged :design, :safety
|
160
|
-
#
|
161
|
-
|
170
|
+
# Override configuration options for any class method(s), by passing a hash as the last argument
|
171
|
+
# In the last hash any non-Configuration keys will be data that gets logged,
|
172
|
+
# and also made available to last_hash_to_s_proc
|
173
|
+
logged :dealer_options, {
|
174
|
+
something: 'here', # <= will be logged, and available to last_hash_to_s_proc
|
175
|
+
multiple_last_hashes: true # <= Overrides config
|
176
|
+
}
|
177
|
+
def self.will_not_be_logged; false; end
|
178
|
+
# == END CLASS METHODS ==
|
179
|
+
|
180
|
+
# == BEGIN INSTANCE METHODS ==
|
181
|
+
# For instance methods:
|
182
|
+
# Option 1: specify the exact method(s) to add logging to
|
183
|
+
include DebugLogging::InstanceLogger.new(i_methods: [:drive, :stop])
|
162
184
|
|
163
185
|
def drive(speed); speed; end
|
164
186
|
def stop(**opts); 0; end
|
@@ -167,11 +189,15 @@ class Car
|
|
167
189
|
# Option 2: add logging to all instance methods defined above (but *not* defined below)
|
168
190
|
include DebugLogging::InstanceLogger.new(i_methods: self.instance_methods(false))
|
169
191
|
|
170
|
-
|
171
|
-
include DebugLogging::InstanceLogger.new(i_methods: [:stop], config: { multiple_last_hashes: true })
|
192
|
+
def faster(**opts); 0; end
|
172
193
|
|
173
|
-
|
194
|
+
# Override configuration options for any instance method(s), by passing a hash as the last argument
|
195
|
+
# In the last hash any non-Configuration keys will be data that gets logged,
|
196
|
+
# and also made available to last_hash_to_s_proc
|
197
|
+
include DebugLogging::InstanceLogger.new(i_methods: [:faster], config: { add_invocation_id: false })
|
174
198
|
|
199
|
+
def will_not_be_logged; false; end
|
200
|
+
# == END INSTANCE METHODS ==
|
175
201
|
end
|
176
202
|
```
|
177
203
|
|
@@ -195,28 +221,45 @@ Every time a method is called, class and instance method events are instrumented
|
|
195
221
|
|
196
222
|
```ruby
|
197
223
|
class Car
|
198
|
-
|
199
|
-
#
|
200
|
-
# except for the
|
224
|
+
# Adds the helper methods to the class.
|
225
|
+
# All helpers prefixed with debug_*,
|
226
|
+
# except for the *notifies* decorator, which comes from extending DebugLogging::ClassNotifier
|
201
227
|
extend DebugLogging
|
202
228
|
|
203
229
|
# For instance methods:
|
204
|
-
# Option 1: specify the exact method(s) to add instrumentation to
|
230
|
+
# Option 1: specify the exact method(s) to add instrumentation to
|
231
|
+
# NOTE: You can capture instance variable values as part of the event payload
|
205
232
|
include DebugLogging::InstanceNotifier.new(i_methods: [:drive,
|
206
233
|
:stop,
|
207
234
|
[:turn, { instance_variables: %i[direction angle] }]])
|
208
235
|
|
209
|
-
#
|
236
|
+
# For class methods
|
237
|
+
# Provides the versatile `notifies` method decorator / macro
|
238
|
+
# For instance methods
|
239
|
+
# Provides the versatile `i_notifies` method decorator / macro
|
210
240
|
extend DebugLogging::ClassNotifier
|
211
241
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
def
|
216
|
-
|
217
|
-
|
218
|
-
|
242
|
+
# == BEGIN CLASS METHODS ==
|
243
|
+
# For class methods:
|
244
|
+
# Option 1: Use *notifies* as a method decorator
|
245
|
+
notifies def self.make; new; end
|
246
|
+
def self.design(*args); new; end
|
247
|
+
def self.safety(*args); new; end
|
248
|
+
def self.dealer_options(*args); new; end
|
219
249
|
|
250
|
+
# Option 2: Use *logged* as a macro
|
251
|
+
notifies :design, :safety
|
252
|
+
# Override configuration options for any class method(s), by passing a hash as the last argument
|
253
|
+
# In the last hash any non-Configuration keys will be data that gets added to the event payload,
|
254
|
+
# and also made available to last_hash_to_s_proc
|
255
|
+
notifies :dealer_options, {
|
256
|
+
something: 'here', # <= will be added to the event payload, and be available to last_hash_to_s_proc
|
257
|
+
add_invocation_id: false # <= Overrides config
|
258
|
+
}
|
259
|
+
def self.will_not_be_notified; false; end
|
260
|
+
# == END CLASS METHODS ==
|
261
|
+
|
262
|
+
# == BEGIN INSTANCE METHODS ==
|
220
263
|
def drive(speed); speed; end
|
221
264
|
def stop(**opts); 0; end
|
222
265
|
|
@@ -224,8 +267,15 @@ class Car
|
|
224
267
|
# Option 2: add instrumentation to all instance methods defined above (but *not* defined below)
|
225
268
|
include DebugLogging::InstanceNotifier.new(i_methods: self.instance_methods(false))
|
226
269
|
|
227
|
-
def
|
270
|
+
def faster(**opts); 0; end
|
271
|
+
|
272
|
+
# Override options for any instance method(s), by passing a hash as the last argument
|
273
|
+
# In the last hash any non-Configuration keys will be data that gets added to the event payload,
|
274
|
+
# and also made available to last_hash_to_s_proc
|
275
|
+
include DebugLogging::InstanceNotifier.new(i_methods: [:faster], config: { add_invocation_id: false })
|
228
276
|
|
277
|
+
def will_not_be_notified; false; end
|
278
|
+
# == END INSTANCE METHODS ==
|
229
279
|
end
|
230
280
|
```
|
231
281
|
|
@@ -6,11 +6,9 @@ module DebugLogging
|
|
6
6
|
"completed in #{format('%f', tms.real)}s (#{format('%f', tms.total)}s CPU)"
|
7
7
|
end
|
8
8
|
|
9
|
-
def debug_event_name_to_s(method_to_notify: nil)
|
10
|
-
"#{method_to_notify}.log"
|
11
|
-
end
|
12
|
-
|
13
9
|
def debug_invocation_id_to_s(args: nil, config_proxy: nil)
|
10
|
+
return '' unless args && config_proxy
|
11
|
+
|
14
12
|
if config_proxy.debug_add_invocation_id
|
15
13
|
invocation = " ~#{args.object_id}@#{(Time.now.to_f.to_s % '%#-21a')[4..-4]}~"
|
16
14
|
case config_proxy.debug_add_invocation_id
|
@@ -25,6 +23,8 @@ module DebugLogging
|
|
25
23
|
end
|
26
24
|
|
27
25
|
def debug_invocation_to_s(klass: nil, separator: nil, method_to_log: nil, config_proxy: nil)
|
26
|
+
return '' unless config_proxy
|
27
|
+
|
28
28
|
klass_string = if config_proxy.debug_colorized_chain_for_class
|
29
29
|
config_proxy.debug_colorized_chain_for_class.call(ColorizedString[klass.to_s])
|
30
30
|
else
|
@@ -39,6 +39,8 @@ module DebugLogging
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def debug_signature_to_s(args: nil, config_proxy: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
42
|
+
return '' unless args && config_proxy
|
43
|
+
|
42
44
|
printed_args = ''
|
43
45
|
|
44
46
|
add_args_ellipsis = false
|
@@ -69,25 +71,56 @@ module DebugLogging
|
|
69
71
|
printed_args += ', ' if !other_args_string.empty? && !last_hash_args_string.empty?
|
70
72
|
printed_args += last_hash_args_string if last_hash_args_string && !last_hash_args_string.empty?
|
71
73
|
else
|
72
|
-
printed_args += args[0..-2].map(&:inspect).join(', ').tap
|
74
|
+
printed_args += args[0..-2].map(&:inspect).join(', ').tap do |x|
|
75
|
+
add_args_ellipsis = x.length > config_proxy.debug_args_max_length
|
76
|
+
end [0..(config_proxy.debug_args_max_length)]
|
73
77
|
printed_args += config_proxy.debug_ellipsis if add_args_ellipsis
|
74
|
-
printed_args += ", #{config_proxy.debug_last_hash_to_s_proc.call(args[-1]).tap
|
78
|
+
printed_args += ", #{config_proxy.debug_last_hash_to_s_proc.call(args[-1]).tap do |x|
|
79
|
+
add_last_hash_ellipsis = x.length > config_proxy.debug_last_hash_max_length
|
80
|
+
end [0..(config_proxy.debug_last_hash_max_length)]}"
|
75
81
|
printed_args += config_proxy.debug_ellipsis if add_last_hash_ellipsis
|
76
82
|
end
|
77
83
|
else
|
78
|
-
printed_args += String(config_proxy.debug_last_hash_to_s_proc.call(args[0])).tap
|
84
|
+
printed_args += String(config_proxy.debug_last_hash_to_s_proc.call(args[0])).tap do |x|
|
85
|
+
add_last_hash_ellipsis = x.length > config_proxy.debug_last_hash_max_length
|
86
|
+
end [0..(config_proxy.debug_last_hash_max_length)]
|
79
87
|
printed_args += config_proxy.debug_ellipsis if add_last_hash_ellipsis
|
80
88
|
end
|
81
89
|
else
|
82
|
-
if args.length == 1 && args[0].is_a?(Hash)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
90
|
+
printed_args += if args.length == 1 && args[0].is_a?(Hash)
|
91
|
+
# handle double splat
|
92
|
+
("**#{args.map(&:inspect).join(', ').tap do |x|
|
93
|
+
add_args_ellipsis = x.length > config_proxy.debug_args_max_length
|
94
|
+
end }")[0..(config_proxy.debug_args_max_length)]
|
95
|
+
else
|
96
|
+
args.map(&:inspect).join(', ').tap do |x|
|
97
|
+
add_args_ellipsis = x.length > config_proxy.debug_args_max_length
|
98
|
+
end [0..(config_proxy.debug_args_max_length)]
|
99
|
+
end
|
88
100
|
printed_args += config_proxy.debug_ellipsis if add_args_ellipsis
|
89
101
|
end
|
90
102
|
"(#{printed_args})"
|
91
103
|
end
|
104
|
+
|
105
|
+
def debug_payload_to_s(payload: nil, config_proxy: nil)
|
106
|
+
return '' unless payload && config_proxy
|
107
|
+
|
108
|
+
if payload
|
109
|
+
case config_proxy.debug_add_payload
|
110
|
+
when true
|
111
|
+
payload.inspect
|
112
|
+
else
|
113
|
+
config_proxy.debug_add_payload.call(**payload)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
''
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
module_function
|
121
|
+
|
122
|
+
def debug_event_name_to_s(method_to_notify: nil)
|
123
|
+
"#{method_to_notify}.log"
|
124
|
+
end
|
92
125
|
end
|
93
126
|
end
|
@@ -5,7 +5,11 @@ module DebugLogging
|
|
5
5
|
def logged(*methods_to_log)
|
6
6
|
# When opts are present it will always be a new configuration instance per method
|
7
7
|
# When opts are not present it will reuse the class' configuration object
|
8
|
-
|
8
|
+
payload = methods_to_log.last.is_a?(Hash) && methods_to_log.pop.dup || {}
|
9
|
+
config_opts = {}
|
10
|
+
unless payload.empty?
|
11
|
+
DebugLogging::Configuration::CONFIG_KEYS.each { |k| config_opts[k] = payload.delete(k) if payload.key?(k) }
|
12
|
+
end
|
9
13
|
if methods_to_log.first.is_a?(Array)
|
10
14
|
methods_to_log = methods_to_log.shift
|
11
15
|
else
|
@@ -17,36 +21,58 @@ module DebugLogging
|
|
17
21
|
original_method = method(method_to_log)
|
18
22
|
(class << self; self; end).class_eval do
|
19
23
|
define_method(method_to_log) do |*args, &block|
|
20
|
-
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('
|
24
|
+
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('kl',
|
25
|
+
method_to_log)))
|
21
26
|
proxy
|
22
27
|
else
|
23
|
-
proxy = if
|
24
|
-
Configuration.new(**debug_config.to_hash.merge(opts))
|
25
|
-
else
|
28
|
+
proxy = if config_opts.empty?
|
26
29
|
debug_config
|
30
|
+
else
|
31
|
+
DebugLogging::Configuration.new(**debug_config.to_hash.merge(config_opts))
|
27
32
|
end
|
28
33
|
proxy.register(method_to_log)
|
29
|
-
instance_variable_set(DebugLogging::Configuration.config_pointer('
|
34
|
+
instance_variable_set(DebugLogging::Configuration.config_pointer('kl', method_to_log),
|
35
|
+
proxy)
|
30
36
|
proxy
|
31
37
|
end
|
32
38
|
method_return_value = nil
|
33
39
|
log_prefix = nil
|
34
40
|
invocation_id = nil
|
35
41
|
config_proxy.log do
|
36
|
-
|
42
|
+
paydirt = {}
|
43
|
+
# TODO: Could make instance variable introspection configurable before or after method execution
|
44
|
+
if payload.key?(:instance_variables)
|
45
|
+
paydirt.merge!(payload.reject { |k| k == :instance_variables })
|
46
|
+
payload[:instance_variables].each do |k|
|
47
|
+
paydirt[k] = instance_variable_get("@#{k}") if instance_variable_defined?("@#{k}")
|
48
|
+
end
|
49
|
+
else
|
50
|
+
paydirt.merge!(payload)
|
51
|
+
end
|
52
|
+
log_prefix = debug_invocation_to_s(klass: to_s, separator: '.', method_to_log: method_to_log,
|
53
|
+
config_proxy: config_proxy)
|
37
54
|
invocation_id = debug_invocation_id_to_s(args: args, config_proxy: config_proxy)
|
38
55
|
signature = debug_signature_to_s(args: args, config_proxy: config_proxy)
|
39
|
-
|
56
|
+
paymud = debug_payload_to_s(payload: paydirt, config_proxy: config_proxy)
|
57
|
+
"#{log_prefix}#{signature}#{invocation_id} debug: #{paymud}"
|
40
58
|
end
|
41
59
|
if config_proxy.benchmarkable_for?(:debug_class_benchmarks)
|
42
60
|
tms = Benchmark.measure do
|
43
|
-
method_return_value =
|
61
|
+
method_return_value = if args.size == 1 && (harsh = args[0]) && harsh.is_a?(Hash)
|
62
|
+
original_method.call(**harsh, &block)
|
63
|
+
else
|
64
|
+
original_method.call(*args, &block)
|
65
|
+
end
|
44
66
|
end
|
45
67
|
config_proxy.log do
|
46
68
|
"#{log_prefix} #{debug_benchmark_to_s(tms: tms)}#{invocation_id}"
|
47
69
|
end
|
48
70
|
else
|
49
|
-
method_return_value =
|
71
|
+
method_return_value = if args.size == 1 && (harsh = args[0]) && harsh.is_a?(Hash)
|
72
|
+
original_method.call(**harsh, &block)
|
73
|
+
else
|
74
|
+
original_method.call(*args, &block)
|
75
|
+
end
|
50
76
|
if config_proxy.exit_scope_markable? && invocation_id && !invocation_id.empty?
|
51
77
|
config_proxy.log do
|
52
78
|
"#{log_prefix} completed#{invocation_id}"
|
@@ -3,7 +3,13 @@
|
|
3
3
|
module DebugLogging
|
4
4
|
module ClassNotifier
|
5
5
|
def notifies(*methods_to_notify)
|
6
|
-
|
6
|
+
# When opts are present it will always be a new configuration instance per method
|
7
|
+
# When opts are not present it will reuse the class' configuration object
|
8
|
+
payload = methods_to_notify.last.is_a?(Hash) && methods_to_notify.pop.dup || {}
|
9
|
+
config_opts = {}
|
10
|
+
unless payload.empty?
|
11
|
+
DebugLogging::Configuration::CONFIG_KEYS.each { |k| config_opts[k] = payload.delete(k) if payload.key?(k) }
|
12
|
+
end
|
7
13
|
if methods_to_notify.first.is_a?(Array)
|
8
14
|
methods_to_notify = methods_to_notify.shift
|
9
15
|
else
|
@@ -13,34 +19,45 @@ module DebugLogging
|
|
13
19
|
# method name must be a symbol
|
14
20
|
method_to_notify = method_to_notify.to_sym
|
15
21
|
original_method = method(method_to_notify)
|
16
|
-
config_proxy = nil
|
17
22
|
(class << self; self; end).class_eval do
|
18
23
|
define_method(method_to_notify) do |*args, &block|
|
19
|
-
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('
|
24
|
+
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('kn',
|
25
|
+
method_to_notify)))
|
20
26
|
proxy
|
21
27
|
else
|
22
|
-
proxy = if
|
23
|
-
Configuration.new(**debug_config.to_hash.merge(payload))
|
24
|
-
else
|
28
|
+
proxy = if config_opts.empty?
|
25
29
|
debug_config
|
30
|
+
else
|
31
|
+
DebugLogging::Configuration.new(**debug_config.to_hash.merge(config_opts))
|
26
32
|
end
|
27
33
|
proxy.register(method_to_notify)
|
28
|
-
instance_variable_set(DebugLogging::Configuration.config_pointer('
|
34
|
+
instance_variable_set(DebugLogging::Configuration.config_pointer('kn', method_to_notify),
|
35
|
+
proxy)
|
36
|
+
ActiveSupport::Notifications.subscribe(
|
37
|
+
DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify)
|
38
|
+
) do |*debug_args|
|
39
|
+
proxy.log do
|
40
|
+
DebugLogging::LogSubscriber.log_event(ActiveSupport::Notifications::Event.new(*debug_args))
|
41
|
+
end
|
42
|
+
end
|
29
43
|
proxy
|
30
44
|
end
|
31
45
|
ActiveSupport::Notifications.instrument(
|
32
|
-
debug_event_name_to_s(method_to_notify: method_to_notify),
|
46
|
+
DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify),
|
47
|
+
{
|
48
|
+
debug_args: args,
|
49
|
+
config_proxy: config_proxy,
|
50
|
+
**payload
|
51
|
+
}
|
33
52
|
) do
|
34
|
-
|
53
|
+
if args.size == 1 && (harsh = args[0]) && harsh.is_a?(Hash)
|
54
|
+
original_method.call(**harsh, &block)
|
55
|
+
else
|
56
|
+
original_method.call(*args, &block)
|
57
|
+
end
|
35
58
|
end
|
36
59
|
end
|
37
60
|
end
|
38
|
-
|
39
|
-
ActiveSupport::Notifications.subscribe(/log/) do |*args|
|
40
|
-
config_proxy&.log do
|
41
|
-
DebugLogging::LogSubscriber.log_event(ActiveSupport::Notifications::Event.new(*args))
|
42
|
-
end
|
43
|
-
end
|
44
61
|
end
|
45
62
|
end
|
46
63
|
end
|
@@ -3,13 +3,35 @@
|
|
3
3
|
module DebugLogging
|
4
4
|
class Configuration
|
5
5
|
DEFAULT_ELLIPSIS = ' ✂️ …'
|
6
|
+
CONFIG_ATTRS_DEFAULTS = {
|
7
|
+
enabled: true,
|
8
|
+
logger: Logger.new($stdout),
|
9
|
+
log_level: :debug,
|
10
|
+
multiple_last_hashes: false,
|
11
|
+
last_hash_to_s_proc: nil,
|
12
|
+
last_hash_max_length: 1_000,
|
13
|
+
args_max_length: 1_000,
|
14
|
+
colorized_chain_for_method: false,
|
15
|
+
colorized_chain_for_class: false,
|
16
|
+
add_invocation_id: true,
|
17
|
+
ellipsis: DEFAULT_ELLIPSIS,
|
18
|
+
mark_scope_exit: false,
|
19
|
+
add_payload: true
|
20
|
+
}.freeze
|
21
|
+
CONFIG_ATTRS = CONFIG_ATTRS_DEFAULTS.keys
|
22
|
+
CONFIG_READERS_DEFAULTS = {
|
23
|
+
instance_benchmarks: false,
|
24
|
+
class_benchmarks: false,
|
25
|
+
active_support_notifications: false
|
26
|
+
}.freeze
|
27
|
+
CONFIG_READERS = CONFIG_READERS_DEFAULTS.keys
|
28
|
+
CONFIG_KEYS = CONFIG_ATTRS + CONFIG_READERS
|
29
|
+
|
6
30
|
# For reference, log levels as integers mapped to symbols:
|
7
31
|
# LEVELS = { 0 => :debug, 1 => :info, 2 => :warn, 3 => :error, 4 => :fatal, 5 => :unknown }
|
8
|
-
attr_accessor
|
9
|
-
|
10
|
-
|
11
|
-
:ellipsis, :mark_scope_exit
|
12
|
-
attr_reader :instance_benchmarks, :class_benchmarks, :active_support_notifications, :methods_to_log
|
32
|
+
attr_accessor(*CONFIG_ATTRS)
|
33
|
+
attr_reader :methods_to_log, *CONFIG_READERS
|
34
|
+
|
13
35
|
# alias the readers to the debug_* prefix so an instance of this class
|
14
36
|
# can have the same API granted by `extend DebugLogging`
|
15
37
|
#
|
@@ -28,20 +50,9 @@ module DebugLogging
|
|
28
50
|
# }
|
29
51
|
# )
|
30
52
|
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
alias debug_multiple_last_hashes multiple_last_hashes
|
35
|
-
alias debug_last_hash_to_s_proc last_hash_to_s_proc
|
36
|
-
alias debug_last_hash_max_length last_hash_max_length
|
37
|
-
alias debug_args_max_length args_max_length
|
38
|
-
alias debug_instance_benchmarks instance_benchmarks
|
39
|
-
alias debug_class_benchmarks class_benchmarks
|
40
|
-
alias debug_colorized_chain_for_method colorized_chain_for_method
|
41
|
-
alias debug_colorized_chain_for_class colorized_chain_for_class
|
42
|
-
alias debug_add_invocation_id add_invocation_id
|
43
|
-
alias debug_ellipsis ellipsis
|
44
|
-
alias debug_mark_scope_exit mark_scope_exit
|
53
|
+
CONFIG_KEYS.each do |key|
|
54
|
+
alias :"debug_#{key}" :"#{key}"
|
55
|
+
end
|
45
56
|
|
46
57
|
class << self
|
47
58
|
def config_pointer(type, method_to_log)
|
@@ -52,21 +63,12 @@ module DebugLogging
|
|
52
63
|
end
|
53
64
|
end
|
54
65
|
def initialize(**options)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@args_max_length = options.key?(:args_max_length) ? options[:args_max_length] : 1_000
|
62
|
-
@colorized_chain_for_method = options.key?(:colorized_chain_for_method) ? options[:colorized_chain_for_method] : false
|
63
|
-
@colorized_chain_for_class = options.key?(:colorized_chain_for_class) ? options[:colorized_chain_for_class] : false
|
64
|
-
@add_invocation_id = options.key?(:add_invocation_id) ? options[:add_invocation_id] : true
|
65
|
-
@ellipsis = options.key?(:ellipsis) ? options[:ellipsis] : DEFAULT_ELLIPSIS
|
66
|
-
@mark_scope_exit = options.key?(:mark_scope_exit) ? options[:mark_scope_exit] : false
|
67
|
-
self.instance_benchmarks = options.key?(:instance_benchmarks) ? options[:instance_benchmarks] : false
|
68
|
-
self.class_benchmarks = options.key?(:class_benchmarks) ? options[:class_benchmarks] : false
|
69
|
-
self.active_support_notifications = options.key?(:active_support_notifications) ? options[:active_support_notifications] : false
|
66
|
+
CONFIG_ATTRS.each do |key|
|
67
|
+
send("#{key}=", get_attr_from_options(options, key))
|
68
|
+
end
|
69
|
+
CONFIG_READERS.each do |key|
|
70
|
+
send("#{key}=", get_reader_from_options(options, key))
|
71
|
+
end
|
70
72
|
@methods_to_log = []
|
71
73
|
end
|
72
74
|
|
@@ -115,26 +117,23 @@ module DebugLogging
|
|
115
117
|
end
|
116
118
|
|
117
119
|
def to_hash
|
118
|
-
{
|
119
|
-
|
120
|
-
|
121
|
-
multiple_last_hashes: multiple_last_hashes,
|
122
|
-
last_hash_to_s_proc: last_hash_to_s_proc,
|
123
|
-
last_hash_max_length: last_hash_max_length,
|
124
|
-
args_max_length: args_max_length,
|
125
|
-
instance_benchmarks: instance_benchmarks,
|
126
|
-
class_benchmarks: class_benchmarks,
|
127
|
-
colorized_chain_for_method: colorized_chain_for_method,
|
128
|
-
colorized_chain_for_class: colorized_chain_for_class,
|
129
|
-
add_invocation_id: add_invocation_id,
|
130
|
-
ellipsis: ellipsis,
|
131
|
-
mark_scope_exit: mark_scope_exit,
|
132
|
-
active_support_notifications: active_support_notifications
|
133
|
-
}
|
120
|
+
CONFIG_KEYS.each_with_object({}) do |key, hash|
|
121
|
+
hash[key] = instance_variable_get("@#{key}")
|
122
|
+
end
|
134
123
|
end
|
135
124
|
|
136
125
|
def register(method_lo_log)
|
137
126
|
@methods_to_log << method_lo_log
|
138
127
|
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def get_attr_from_options(options, key)
|
132
|
+
options.key?(key) ? options[key] : CONFIG_ATTRS_DEFAULTS[key]
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_reader_from_options(options, key)
|
136
|
+
options.key?(key) ? options[key] : CONFIG_READERS_DEFAULTS[key]
|
137
|
+
end
|
139
138
|
end
|
140
139
|
end
|
@@ -12,7 +12,8 @@ module DebugLogging
|
|
12
12
|
return unless @instance_methods_to_log
|
13
13
|
|
14
14
|
base.send(:include, ArgumentPrinter)
|
15
|
-
instance_method_logger = DebugLogging::InstanceLoggerModulizer.to_mod(methods_to_log: @instance_methods_to_log,
|
15
|
+
instance_method_logger = DebugLogging::InstanceLoggerModulizer.to_mod(methods_to_log: @instance_methods_to_log,
|
16
|
+
config: @config)
|
16
17
|
base.send(:prepend, instance_method_logger)
|
17
18
|
end
|
18
19
|
end
|
@@ -5,26 +5,52 @@ module DebugLogging
|
|
5
5
|
def self.to_mod(methods_to_log: nil, config: nil)
|
6
6
|
Module.new do
|
7
7
|
Array(methods_to_log).each do |method_to_log|
|
8
|
+
payload = (method_to_log.is_a?(Array) && method_to_log.last.is_a?(Hash) && method_to_log.pop.dup) || {}
|
9
|
+
config_opts = {}
|
10
|
+
unless payload.empty?
|
11
|
+
DebugLogging::Configuration::CONFIG_KEYS.each { |k| config_opts[k] = payload.delete(k) if payload.key?(k) }
|
12
|
+
end
|
8
13
|
# method name must be a symbol
|
9
|
-
|
14
|
+
method_to_log = if method_to_log.is_a?(Array)
|
15
|
+
method_to_log.first&.to_sym
|
16
|
+
else
|
17
|
+
method_to_log.to_sym
|
18
|
+
end
|
19
|
+
define_method(method_to_log) do |*args, &block|
|
10
20
|
method_return_value = nil
|
11
|
-
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('
|
21
|
+
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('ilm',
|
22
|
+
method_to_log)))
|
12
23
|
proxy
|
13
24
|
else
|
14
25
|
proxy = if config
|
15
|
-
Configuration.new(**self.class.debug_config.to_hash.merge(config))
|
26
|
+
Configuration.new(**self.class.debug_config.to_hash.merge(config.merge(config_opts)))
|
27
|
+
elsif !config_opts.empty?
|
28
|
+
Configuration.new(**self.class.debug_config.to_hash.merge(config_opts))
|
16
29
|
else
|
17
30
|
self.class.debug_config
|
18
31
|
end
|
19
32
|
proxy.register(method_to_log)
|
20
|
-
instance_variable_set(DebugLogging::Configuration.config_pointer('
|
33
|
+
instance_variable_set(DebugLogging::Configuration.config_pointer('ilm', method_to_log),
|
34
|
+
proxy)
|
21
35
|
proxy
|
22
36
|
end
|
23
|
-
log_prefix = self.class.debug_invocation_to_s(klass: self.class.to_s, separator: '#',
|
37
|
+
log_prefix = self.class.debug_invocation_to_s(klass: self.class.to_s, separator: '#',
|
38
|
+
method_to_log: method_to_log, config_proxy: config_proxy)
|
24
39
|
invocation_id = self.class.debug_invocation_id_to_s(args: args, config_proxy: config_proxy)
|
25
40
|
config_proxy.log do
|
41
|
+
paydirt = {}
|
42
|
+
# TODO: Could make instance variable introspection configurable before or after method execution
|
43
|
+
if payload.key?(:instance_variables)
|
44
|
+
paydirt.merge!(payload.reject { |k| k == :instance_variables })
|
45
|
+
payload[:instance_variables].each do |k|
|
46
|
+
paydirt[k] = instance_variable_get("@#{k}") if instance_variable_defined?("@#{k}")
|
47
|
+
end
|
48
|
+
else
|
49
|
+
paydirt.merge!(payload)
|
50
|
+
end
|
26
51
|
signature = self.class.debug_signature_to_s(args: args, config_proxy: config_proxy)
|
27
|
-
|
52
|
+
paymud = debug_payload_to_s(payload: paydirt, config_proxy: config_proxy)
|
53
|
+
"#{log_prefix}#{signature}#{invocation_id} debug: #{paymud}"
|
28
54
|
end
|
29
55
|
if config_proxy.benchmarkable_for?(:debug_instance_benchmarks)
|
30
56
|
tms = Benchmark.measure do
|
@@ -4,42 +4,57 @@ module DebugLogging
|
|
4
4
|
module InstanceNotifierModulizer
|
5
5
|
def self.to_mod(methods_to_notify: nil)
|
6
6
|
Module.new do
|
7
|
+
config_proxy = nil
|
8
|
+
|
7
9
|
Array(methods_to_notify).each do |method_to_notify|
|
8
10
|
# method name must be a symbol
|
9
|
-
payload = method_to_notify.is_a?(Array) && method_to_notify.last.is_a?(Hash) && method_to_notify.pop || {}
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
method_to_notify.to_sym
|
11
|
+
payload = (method_to_notify.is_a?(Array) && method_to_notify.last.is_a?(Hash) && method_to_notify.pop.dup) || {}
|
12
|
+
config_opts = {}
|
13
|
+
unless payload.empty?
|
14
|
+
DebugLogging::Configuration::CONFIG_KEYS.each { |k| config_opts[k] = payload.delete(k) if payload.key?(k) }
|
14
15
|
end
|
15
|
-
|
16
|
+
method_to_notify = if method_to_notify.is_a?(Array)
|
17
|
+
method_to_notify.first&.to_sym
|
18
|
+
else
|
19
|
+
method_to_notify.to_sym
|
20
|
+
end
|
16
21
|
define_method(method_to_notify) do |*args, &block|
|
17
|
-
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('
|
22
|
+
config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('inm',
|
23
|
+
method_to_notify)))
|
18
24
|
proxy
|
19
25
|
else
|
20
|
-
proxy = if
|
21
|
-
Configuration.new(**self.class.debug_config.to_hash.merge(payload))
|
22
|
-
else
|
26
|
+
proxy = if config_opts.empty?
|
23
27
|
self.class.debug_config
|
28
|
+
else
|
29
|
+
Configuration.new(**self.class.debug_config.to_hash.merge(config_opts))
|
24
30
|
end
|
25
31
|
proxy.register(method_to_notify)
|
26
|
-
instance_variable_set(
|
32
|
+
instance_variable_set(
|
33
|
+
DebugLogging::Configuration.config_pointer('inm', method_to_notify), proxy
|
34
|
+
)
|
27
35
|
proxy
|
28
36
|
end
|
37
|
+
paydirt = {}
|
29
38
|
if payload.key?(:instance_variables)
|
39
|
+
paydirt.merge!(payload.reject { |k| k == :instance_variables })
|
30
40
|
payload[:instance_variables].each do |k|
|
31
|
-
|
41
|
+
paydirt[k] = instance_variable_get("@#{k}") if instance_variable_defined?("@#{k}")
|
32
42
|
end
|
33
|
-
|
43
|
+
else
|
44
|
+
paydirt.merge!(payload)
|
34
45
|
end
|
35
46
|
ActiveSupport::Notifications.instrument(
|
36
|
-
|
47
|
+
DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify),
|
48
|
+
debug_args: args,
|
49
|
+
config_proxy: config_proxy,
|
50
|
+
**paydirt
|
37
51
|
) do
|
38
52
|
super(*args, &block)
|
39
53
|
end
|
40
54
|
end
|
41
|
-
|
42
|
-
|
55
|
+
ActiveSupport::Notifications.subscribe(
|
56
|
+
DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify)
|
57
|
+
) do |*args|
|
43
58
|
config_proxy&.log do
|
44
59
|
DebugLogging::LogSubscriber.log_event(ActiveSupport::Notifications::Event.new(*args))
|
45
60
|
end
|
@@ -4,31 +4,39 @@ require 'active_support/log_subscriber'
|
|
4
4
|
|
5
5
|
module DebugLogging
|
6
6
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
7
|
+
EXCLUDE_FROM_PAYLOAD = %i[debug_args config_proxy].freeze
|
8
|
+
extend DebugLogging::ArgumentPrinter
|
9
|
+
|
7
10
|
class << self
|
8
11
|
attr_accessor :event
|
9
12
|
end
|
10
13
|
attach_to :log
|
11
14
|
|
12
|
-
EVENT_FORMAT_STRING = '%<name>s (%<duration>.3f secs) start=%<time>s end=%<end>s payload=%<payload>s'
|
15
|
+
EVENT_FORMAT_STRING = '%<name>s (%<duration>.3f secs) start=%<time>s end=%<end>s args=%<args>s payload=%<payload>s'
|
13
16
|
|
14
17
|
def self.log_event(event)
|
15
18
|
@event = event
|
16
19
|
if event.payload && event.payload[:exception_object]
|
17
20
|
exception = event.payload[:exception_object]
|
18
21
|
"#{event.name} [ERROR] : \n#{exception.class} : #{exception.message}\n" + exception.backtrace.join("\n")
|
22
|
+
else
|
23
|
+
format(EVENT_FORMAT_STRING, event_to_format_options(event))
|
19
24
|
end
|
20
|
-
format(EVENT_FORMAT_STRING, event_to_format_options(event))
|
21
25
|
end
|
22
26
|
|
23
27
|
# @param [ActiveSupport::Notifications::Event]
|
24
28
|
# @return [Hash]
|
25
29
|
def self.event_to_format_options(event)
|
30
|
+
args = event.payload[:debug_args]
|
31
|
+
config_proxy = event.payload[:config_proxy]
|
32
|
+
payload = event.payload.reject { |k, _| EXCLUDE_FROM_PAYLOAD.include?(k) }
|
26
33
|
{
|
27
34
|
name: event.name,
|
28
35
|
duration: Rational(event.duration, 1000).to_f,
|
29
36
|
time: event.time,
|
30
37
|
end: event.end,
|
31
|
-
|
38
|
+
args: debug_signature_to_s(args: args, config_proxy: config_proxy),
|
39
|
+
payload: debug_payload_to_s(payload: payload, config_proxy: config_proxy)
|
32
40
|
}
|
33
41
|
end
|
34
42
|
end
|
data/lib/simple_debug_logging.rb
CHANGED
@@ -48,6 +48,7 @@ class SimpleDebugLogging < Module
|
|
48
48
|
base.send(:prepend, instance_method_logger)
|
49
49
|
base.send(:extend, ClassMethodLogger)
|
50
50
|
end
|
51
|
+
|
51
52
|
module ClassMethodLogger
|
52
53
|
def logged(*methods_to_log)
|
53
54
|
methods_to_log.each do |method_to_log|
|
@@ -67,6 +68,7 @@ class SimpleDebugLogging < Module
|
|
67
68
|
end
|
68
69
|
end
|
69
70
|
end
|
71
|
+
|
70
72
|
module InstanceMethodLoggerModulizer
|
71
73
|
def self.to_mod(methods_to_log = [])
|
72
74
|
Module.new do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debug_logging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Boling
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -195,7 +195,7 @@ homepage: https://github.com/pboling/debug_logging
|
|
195
195
|
licenses:
|
196
196
|
- MIT
|
197
197
|
metadata: {}
|
198
|
-
post_install_message:
|
198
|
+
post_install_message:
|
199
199
|
rdoc_options: []
|
200
200
|
require_paths:
|
201
201
|
- lib
|
@@ -211,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
211
|
version: '0'
|
212
212
|
requirements: []
|
213
213
|
rubygems_version: 3.1.4
|
214
|
-
signing_key:
|
214
|
+
signing_key:
|
215
215
|
specification_version: 4
|
216
216
|
summary: Drop-in debug logging useful when a call stack gets unruly
|
217
217
|
test_files: []
|