debug_logging 3.1.0 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []