debug_logging 3.1.0 → 3.1.5

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.
@@ -0,0 +1,7 @@
1
+ module DebugLogging
2
+ class Error < StandardError; end
3
+
4
+ class TimeoutError < Error; end
5
+
6
+ class NoBlockGiven < Error; end
7
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # From: https://stackoverflow.com/a/34559282
4
+ # License: https://creativecommons.org/licenses/by-sa/4.0/
5
+ module DebugLogging
6
+ module Finalize
7
+ def self.extended(obj)
8
+ TracePoint.trace(:end) do |t|
9
+ if obj == t.self
10
+ if obj.respond_to?(:debug_finalize)
11
+ obj.debug_finalize
12
+ else
13
+ warn "#{obj} does not define a debug_finalize"
14
+ end
15
+ t.disable
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,82 @@
1
+ require 'debug_logging/errors'
2
+ require 'timeout'
3
+
4
+ module DebugLogging
5
+ module Hooks
6
+ def self.included(mod)
7
+ mod.extend(ClassMethods)
8
+ end
9
+
10
+ def self.extend(mod)
11
+ mod.extend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ def debug_time_box(time, *names, &blk)
16
+ names.each do |name|
17
+ meth = instance_method(name)
18
+ define_method(name) do |*args, &block|
19
+ begin
20
+ Timeout.timeout(time) do
21
+ meth.bind(self).call(*args, &block)
22
+ end
23
+ rescue Timeout::Error
24
+ error_args = [TimeoutError, 'execution expired', caller]
25
+ raise(*error_args) unless blk
26
+
27
+ instance_exec(*error_args, &blk)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def debug_rescue_on_fail(*names, &blk)
34
+ unless blk
35
+ raise NoBlockGiven,
36
+ '.rescue_on_fail must be called with a block',
37
+ caller
38
+ end
39
+ names.each do |name|
40
+ meth = instance_method(name)
41
+ define_method(name) do |*args, &block|
42
+ begin
43
+ meth.bind(self).call(*args, &block)
44
+ rescue StandardError => e
45
+ instance_exec(e, &blk)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def debug_before(*names, &blk)
52
+ unless blk
53
+ raise NoBlockGiven,
54
+ '.before must be called with a block',
55
+ caller
56
+ end
57
+ names.each do |name|
58
+ meth = instance_method(name)
59
+ define_method name do |*args, &block|
60
+ instance_exec(name, *args, block, &blk)
61
+ meth.bind(self).call(*args, &block)
62
+ end
63
+ end
64
+ end
65
+
66
+ def debug_after(*names, &blk)
67
+ unless blk
68
+ raise NoBlockGiven,
69
+ '.after must be called with a block',
70
+ caller
71
+ end
72
+ names.each do |name|
73
+ meth = instance_method(name)
74
+ define_method name do |*args, &block|
75
+ result = meth.bind(self).call(*args, &block)
76
+ instance_exec(result, &blk)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -2,9 +2,10 @@
2
2
 
3
3
  module DebugLogging
4
4
  class InstanceLogger < Module
5
- def initialize(i_methods: nil, config: nil)
5
+ def initialize(i_methods: nil, payload: nil, config: nil)
6
6
  super()
7
7
  @config = config
8
+ @payload = payload
8
9
  @instance_methods_to_log = Array(i_methods) if i_methods
9
10
  end
10
11
 
@@ -12,7 +13,9 @@ module DebugLogging
12
13
  return unless @instance_methods_to_log
13
14
 
14
15
  base.send(:include, ArgumentPrinter)
15
- instance_method_logger = DebugLogging::InstanceLoggerModulizer.to_mod(methods_to_log: @instance_methods_to_log, config: @config)
16
+ instance_method_logger = DebugLogging::InstanceLoggerModulizer.to_mod(methods_to_log: @instance_methods_to_log,
17
+ payload: @payload,
18
+ config: @config)
16
19
  base.send(:prepend, instance_method_logger)
17
20
  end
18
21
  end
@@ -2,29 +2,35 @@
2
2
 
3
3
  module DebugLogging
4
4
  module InstanceLoggerModulizer
5
- def self.to_mod(methods_to_log: nil, config: nil)
5
+ def self.to_mod(methods_to_log: nil, payload: nil, config: nil)
6
6
  Module.new do
7
+ methods_to_log, payload, config_opts = DebugLogging::Util.extract_payload_and_config(
8
+ method_names: Array(methods_to_log),
9
+ payload: payload,
10
+ config: config
11
+ )
7
12
  Array(methods_to_log).each do |method_to_log|
8
- # method name must be a symbol
9
- define_method(method_to_log.to_sym) do |*args, &block|
13
+ method_to_log, method_payload, method_config_opts = DebugLogging::Util.extract_payload_and_config(
14
+ method_names: method_to_log,
15
+ payload: payload,
16
+ config: config_opts
17
+ )
18
+ define_method(method_to_log) do |*args, &block|
10
19
  method_return_value = nil
11
- config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('i', method_to_log)))
12
- proxy
13
- else
14
- proxy = if config
15
- Configuration.new(**self.class.debug_config.to_hash.merge(config))
16
- else
17
- self.class.debug_config
18
- end
19
- proxy.register(method_to_log)
20
- instance_variable_set(DebugLogging::Configuration.config_pointer('i', method_to_log), proxy)
21
- proxy
22
- end
23
- log_prefix = self.class.debug_invocation_to_s(klass: self.class.to_s, separator: '#', method_to_log: method_to_log, config_proxy: config_proxy)
20
+ config_proxy = DebugLogging::Util.config_proxy_finder(
21
+ scope: self.class,
22
+ config_opts: method_config_opts,
23
+ method_name: method_to_log,
24
+ proxy_ref: 'ilm'
25
+ )
26
+ log_prefix = self.class.debug_invocation_to_s(klass: self.class.to_s, separator: '#',
27
+ method_to_log: method_to_log, config_proxy: config_proxy)
24
28
  invocation_id = self.class.debug_invocation_id_to_s(args: args, config_proxy: config_proxy)
25
29
  config_proxy.log do
30
+ paydirt = DebugLogging::Util.payload_instance_vaiable_hydration(scope: self, payload: method_payload)
26
31
  signature = self.class.debug_signature_to_s(args: args, config_proxy: config_proxy)
27
- "#{log_prefix}#{signature}#{invocation_id}"
32
+ paymud = debug_payload_to_s(payload: paydirt, config_proxy: config_proxy)
33
+ "#{log_prefix}#{signature}#{invocation_id} debug: #{paymud}"
28
34
  end
29
35
  if config_proxy.benchmarkable_for?(:debug_instance_benchmarks)
30
36
  tms = Benchmark.measure do
@@ -2,8 +2,10 @@
2
2
 
3
3
  module DebugLogging
4
4
  class InstanceNotifier < Module
5
- def initialize(i_methods: nil)
5
+ def initialize(i_methods: nil, payload: nil, config: nil)
6
6
  super()
7
+ @config = config
8
+ @payload = payload
7
9
  @instance_methods_to_notify = Array(i_methods) if i_methods
8
10
  end
9
11
 
@@ -11,7 +13,9 @@ module DebugLogging
11
13
  return unless @instance_methods_to_notify
12
14
 
13
15
  base.send(:include, ArgumentPrinter)
14
- instance_method_notifier = DebugLogging::InstanceNotifierModulizer.to_mod(methods_to_notify: @instance_methods_to_notify)
16
+ instance_method_notifier = DebugLogging::InstanceNotifierModulizer.to_mod(methods_to_notify: @instance_methods_to_notify,
17
+ payload: @payload,
18
+ config: @config)
15
19
  base.send(:prepend, instance_method_notifier)
16
20
  end
17
21
  end
@@ -2,48 +2,44 @@
2
2
 
3
3
  module DebugLogging
4
4
  module InstanceNotifierModulizer
5
- def self.to_mod(methods_to_notify: nil)
5
+ def self.to_mod(methods_to_notify: nil, payload: nil, config: nil)
6
6
  Module.new do
7
+ methods_to_notify, payload, config_opts = DebugLogging::Util.extract_payload_and_config(
8
+ method_names: Array(methods_to_notify),
9
+ payload: payload,
10
+ config: config
11
+ )
7
12
  Array(methods_to_notify).each do |method_to_notify|
8
- # 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
- if method_to_notify.is_a?(Array)
11
- method_to_notify = method_to_notify.first&.to_sym
12
- else
13
- method_to_notify.to_sym
14
- end
15
- config_proxy = nil
13
+ method_to_notify, method_payload, method_config_opts = DebugLogging::Util.extract_payload_and_config(
14
+ method_names: method_to_notify,
15
+ payload: payload,
16
+ config: config_opts
17
+ )
16
18
  define_method(method_to_notify) do |*args, &block|
17
- config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('i', method_to_notify)))
18
- proxy
19
- else
20
- proxy = if !payload.empty?
21
- Configuration.new(**self.class.debug_config.to_hash.merge(payload))
22
- else
23
- self.class.debug_config
24
- end
25
- proxy.register(method_to_notify)
26
- instance_variable_set(DebugLogging::Configuration.config_pointer('i', method_to_notify), proxy)
27
- proxy
28
- end
29
- if payload.key?(:instance_variables)
30
- payload[:instance_variables].each do |k|
31
- payload[k] = instance_variable_get("@#{k}") if instance_variable_get("@#{k}")
19
+ config_proxy = DebugLogging::Util.config_proxy_finder(
20
+ scope: self.class,
21
+ config_opts: method_config_opts,
22
+ method_name: method_to_notify,
23
+ proxy_ref: 'inm'
24
+ ) do |config_proxy|
25
+ ActiveSupport::Notifications.subscribe(
26
+ DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify)
27
+ ) do |*args|
28
+ config_proxy&.log do
29
+ DebugLogging::LogSubscriber.log_event(ActiveSupport::Notifications::Event.new(*args))
30
+ end
32
31
  end
33
- payload.delete(:instance_variables)
34
32
  end
33
+ paydirt = DebugLogging::Util.payload_instance_vaiable_hydration(scope: self, payload: method_payload)
35
34
  ActiveSupport::Notifications.instrument(
36
- self.class.debug_event_name_to_s(method_to_notify: method_to_notify), { args: args }.merge(payload)
35
+ DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify),
36
+ debug_args: args,
37
+ config_proxy: config_proxy,
38
+ **paydirt
37
39
  ) do
38
40
  super(*args, &block)
39
41
  end
40
42
  end
41
-
42
- ActiveSupport::Notifications.subscribe(/log/) do |*args|
43
- config_proxy&.log do
44
- DebugLogging::LogSubscriber.log_event(ActiveSupport::Notifications::Event.new(*args))
45
- end
46
- end
47
43
  end
48
44
  end
49
45
  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
- payload: event.payload
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
@@ -0,0 +1,75 @@
1
+ module DebugLogging
2
+ module Util
3
+ module_function
4
+
5
+ # methods_to_log may be an array of a single method name, followed by config options and payload,
6
+ # or it could be an array of method names followed by config options and payload to be shared by the whole set.
7
+ def extract_payload_and_config(method_names:, payload: nil, config: nil)
8
+ # When scoped config is present it will always be a new configuration instance per method
9
+ # When scoped config is not present it will reuse the class' configuration object
10
+ scoped_payload = (method_names.is_a?(Array) && method_names.last.is_a?(Hash) && method_names.pop.clone(freeze: false)) || {}
11
+ payload = if payload
12
+ payload.merge(scoped_payload)
13
+ else
14
+ scoped_payload
15
+ end
16
+ config_opts = config&.clone(freeze: false) || {}
17
+ unless payload.empty?
18
+ DebugLogging::Configuration::CONFIG_KEYS.each { |k| config_opts[k] = payload.delete(k) if payload.key?(k) }
19
+ end
20
+ method_names =
21
+ case method_names
22
+ when Symbol
23
+ method_names
24
+ when String
25
+ method_names.to_sym
26
+ when Array
27
+ if method_names.first.is_a?(Array)
28
+ # Array of arrays?
29
+ method_names.shift
30
+ elsif method_names.size == 1 && method_names.first.is_a?(Symbol)
31
+ # when set as i_methods: [[:i_with_dsplat_payload, { tags: %w[blue green] }], ...]
32
+ method_names.shift.to_sym
33
+ else
34
+ # Or an array of method name symbols?
35
+ # logged :meth1, :meth2, :meth3 without options is valid
36
+ method_names
37
+ end
38
+ end
39
+ [method_names, payload, config_opts]
40
+ end
41
+
42
+ def payload_instance_vaiable_hydration(scope:, payload:)
43
+ paydirt = {}
44
+ # TODO: Could make instance variable introspection configurable before or after method execution
45
+ if payload.key?(:instance_variables)
46
+ paydirt.merge!(payload.reject { |k| k == :instance_variables })
47
+ payload[:instance_variables].each do |k|
48
+ paydirt[k] = scope.send(:instance_variable_get, "@#{k}") if scope.send(:instance_variable_defined?, "@#{k}")
49
+ end
50
+ else
51
+ paydirt.merge!(payload)
52
+ end
53
+ paydirt
54
+ end
55
+
56
+ def config_proxy_finder(scope:, method_name:, proxy_ref:, config_opts: {}, &block)
57
+ if (proxy = scope.send(:instance_variable_get, DebugLogging::Configuration.config_pointer(proxy_ref,
58
+ method_name)))
59
+ proxy
60
+ else
61
+ base = scope.respond_to?(:debug_config) ? scope.debug_config : DebugLogging.debug_logging_configuration
62
+ proxy = if config_opts.empty?
63
+ base
64
+ else
65
+ DebugLogging::Configuration.new(**base.to_hash.merge(config_opts))
66
+ end
67
+ proxy.register(method_name)
68
+ scope.send(:instance_variable_set, DebugLogging::Configuration.config_pointer(proxy_ref, method_name),
69
+ proxy)
70
+ yield proxy if block
71
+ proxy
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DebugLogging
4
- VERSION = '3.1.0'
4
+ VERSION = '3.1.5'
5
5
  end
@@ -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,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debug_logging
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Boling
8
- autorequire:
8
+ - guckin
9
+ autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2020-10-24 00:00:00.000000000 Z
12
+ date: 2020-12-18 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: colorize
@@ -116,32 +117,74 @@ dependencies:
116
117
  version: '0'
117
118
  - !ruby/object:Gem::Dependency
118
119
  name: rubocop
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ - !ruby/object:Gem::Dependency
133
+ name: rubocop-md
119
134
  requirement: !ruby/object:Gem::Requirement
120
135
  requirements:
121
136
  - - ">="
122
137
  - !ruby/object:Gem::Version
123
- version: '0.92'
138
+ version: '0'
124
139
  type: :development
125
140
  prerelease: false
126
141
  version_requirements: !ruby/object:Gem::Requirement
127
142
  requirements:
128
143
  - - ">="
129
144
  - !ruby/object:Gem::Version
130
- version: '0.92'
145
+ version: '0'
131
146
  - !ruby/object:Gem::Dependency
132
- name: rubocop-rspec
147
+ name: rubocop-performance
133
148
  requirement: !ruby/object:Gem::Requirement
134
149
  requirements:
135
150
  - - ">="
136
151
  - !ruby/object:Gem::Version
137
- version: '1'
152
+ version: '0'
138
153
  type: :development
139
154
  prerelease: false
140
155
  version_requirements: !ruby/object:Gem::Requirement
141
156
  requirements:
142
157
  - - ">="
143
158
  - !ruby/object:Gem::Version
144
- version: '1'
159
+ version: '0'
160
+ - !ruby/object:Gem::Dependency
161
+ name: rubocop-rake
162
+ requirement: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: rubocop-rspec
176
+ requirement: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '2.0'
181
+ type: :development
182
+ prerelease: false
183
+ version_requirements: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '2.0'
145
188
  - !ruby/object:Gem::Dependency
146
189
  name: silent_stream
147
190
  requirement: !ruby/object:Gem::Requirement
@@ -184,18 +227,22 @@ files:
184
227
  - lib/debug_logging/class_logger.rb
185
228
  - lib/debug_logging/class_notifier.rb
186
229
  - lib/debug_logging/configuration.rb
230
+ - lib/debug_logging/errors.rb
231
+ - lib/debug_logging/finalize.rb
232
+ - lib/debug_logging/hooks.rb
187
233
  - lib/debug_logging/instance_logger.rb
188
234
  - lib/debug_logging/instance_logger_modulizer.rb
189
235
  - lib/debug_logging/instance_notifier.rb
190
236
  - lib/debug_logging/instance_notifier_modulizer.rb
191
237
  - lib/debug_logging/log_subscriber.rb
238
+ - lib/debug_logging/util.rb
192
239
  - lib/debug_logging/version.rb
193
240
  - lib/simple_debug_logging.rb
194
241
  homepage: https://github.com/pboling/debug_logging
195
242
  licenses:
196
243
  - MIT
197
244
  metadata: {}
198
- post_install_message:
245
+ post_install_message:
199
246
  rdoc_options: []
200
247
  require_paths:
201
248
  - lib
@@ -210,8 +257,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
210
257
  - !ruby/object:Gem::Version
211
258
  version: '0'
212
259
  requirements: []
213
- rubygems_version: 3.1.4
214
- signing_key:
260
+ rubygems_version: 3.2.1
261
+ signing_key:
215
262
  specification_version: 4
216
263
  summary: Drop-in debug logging useful when a call stack gets unruly
217
264
  test_files: []