debug_logging 2.0.0 → 3.1.3

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,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
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DebugLogging
4
+ class InstanceNotifier < Module
5
+ def initialize(i_methods: nil, payload: nil, config: nil)
6
+ super()
7
+ @config = config
8
+ @payload = payload
9
+ @instance_methods_to_notify = Array(i_methods) if i_methods
10
+ end
11
+
12
+ def included(base)
13
+ return unless @instance_methods_to_notify
14
+
15
+ base.send(:include, ArgumentPrinter)
16
+ instance_method_notifier = DebugLogging::InstanceNotifierModulizer.to_mod(methods_to_notify: @instance_methods_to_notify,
17
+ payload: @payload,
18
+ config: @config)
19
+ base.send(:prepend, instance_method_notifier)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DebugLogging
4
+ module InstanceNotifierModulizer
5
+ def self.to_mod(methods_to_notify: nil, payload: nil, config: nil)
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
+ )
12
+ Array(methods_to_notify).each do |method_to_notify|
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
+ )
18
+ define_method(method_to_notify) do |*args, &block|
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
31
+ end
32
+ end
33
+ paydirt = DebugLogging::Util.payload_instance_vaiable_hydration(scope: self, payload: method_payload)
34
+ ActiveSupport::Notifications.instrument(
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
39
+ ) do
40
+ super(*args, &block)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/log_subscriber'
4
+
5
+ module DebugLogging
6
+ class LogSubscriber < ActiveSupport::LogSubscriber
7
+ EXCLUDE_FROM_PAYLOAD = %i[debug_args config_proxy].freeze
8
+ extend DebugLogging::ArgumentPrinter
9
+
10
+ class << self
11
+ attr_accessor :event
12
+ end
13
+ attach_to :log
14
+
15
+ EVENT_FORMAT_STRING = '%<name>s (%<duration>.3f secs) start=%<time>s end=%<end>s args=%<args>s payload=%<payload>s'
16
+
17
+ def self.log_event(event)
18
+ @event = event
19
+ if event.payload && event.payload[:exception_object]
20
+ exception = event.payload[:exception_object]
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))
24
+ end
25
+ end
26
+
27
+ # @param [ActiveSupport::Notifications::Event]
28
+ # @return [Hash]
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) }
33
+ {
34
+ name: event.name,
35
+ duration: Rational(event.duration, 1000).to_f,
36
+ time: event.time,
37
+ end: event.end,
38
+ args: debug_signature_to_s(args: args, config_proxy: config_proxy),
39
+ payload: debug_payload_to_s(payload: payload, config_proxy: config_proxy)
40
+ }
41
+ end
42
+ end
43
+ 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 = '2.0.0'
4
+ VERSION = '3.1.3'
5
5
  end
@@ -21,8 +21,6 @@
21
21
  # logged :a_class_method
22
22
  # end
23
23
  #
24
- # Make sure you have the latest debug_logging gem (>= 1.0.10) installed for this to work
25
- #
26
24
  # In an irb session:
27
25
  # >> require_relative 'unobtrusively_logged'
28
26
  # => true
@@ -50,6 +48,7 @@ class SimpleDebugLogging < Module
50
48
  base.send(:prepend, instance_method_logger)
51
49
  base.send(:extend, ClassMethodLogger)
52
50
  end
51
+
53
52
  module ClassMethodLogger
54
53
  def logged(*methods_to_log)
55
54
  methods_to_log.each do |method_to_log|
@@ -69,6 +68,7 @@ class SimpleDebugLogging < Module
69
68
  end
70
69
  end
71
70
  end
71
+
72
72
  module InstanceMethodLoggerModulizer
73
73
  def self.to_mod(methods_to_log = [])
74
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: 2.0.0
4
+ version: 3.1.3
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-06 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
@@ -24,6 +25,26 @@ dependencies:
24
25
  - - ">="
25
26
  - !ruby/object:Gem::Version
26
27
  version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '5.2'
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 5.2.4.4
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - "~>"
43
+ - !ruby/object:Gem::Version
44
+ version: '5.2'
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 5.2.4.4
27
48
  - !ruby/object:Gem::Dependency
28
49
  name: bundler
29
50
  requirement: !ruby/object:Gem::Requirement
@@ -96,6 +117,20 @@ dependencies:
96
117
  version: '0'
97
118
  - !ruby/object:Gem::Dependency
98
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
99
134
  requirement: !ruby/object:Gem::Requirement
100
135
  requirements:
101
136
  - - ">="
@@ -109,19 +144,47 @@ dependencies:
109
144
  - !ruby/object:Gem::Version
110
145
  version: '0'
111
146
  - !ruby/object:Gem::Dependency
112
- name: rubocop-rspec
147
+ name: rubocop-performance
113
148
  requirement: !ruby/object:Gem::Requirement
114
149
  requirements:
115
150
  - - ">="
116
151
  - !ruby/object:Gem::Version
117
- version: '1'
152
+ version: '0'
118
153
  type: :development
119
154
  prerelease: false
120
155
  version_requirements: !ruby/object:Gem::Requirement
121
156
  requirements:
122
157
  - - ">="
123
158
  - !ruby/object:Gem::Version
124
- 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'
125
188
  - !ruby/object:Gem::Dependency
126
189
  name: silent_stream
127
190
  requirement: !ruby/object:Gem::Requirement
@@ -159,18 +222,27 @@ files:
159
222
  - bin/setup
160
223
  - debug_logging.gemspec
161
224
  - lib/debug_logging.rb
225
+ - lib/debug_logging/active_support_notifications.rb
162
226
  - lib/debug_logging/argument_printer.rb
163
227
  - lib/debug_logging/class_logger.rb
228
+ - lib/debug_logging/class_notifier.rb
164
229
  - lib/debug_logging/configuration.rb
230
+ - lib/debug_logging/errors.rb
231
+ - lib/debug_logging/finalize.rb
232
+ - lib/debug_logging/hooks.rb
165
233
  - lib/debug_logging/instance_logger.rb
166
234
  - lib/debug_logging/instance_logger_modulizer.rb
235
+ - lib/debug_logging/instance_notifier.rb
236
+ - lib/debug_logging/instance_notifier_modulizer.rb
237
+ - lib/debug_logging/log_subscriber.rb
238
+ - lib/debug_logging/util.rb
167
239
  - lib/debug_logging/version.rb
168
240
  - lib/simple_debug_logging.rb
169
241
  homepage: https://github.com/pboling/debug_logging
170
242
  licenses:
171
243
  - MIT
172
244
  metadata: {}
173
- post_install_message:
245
+ post_install_message:
174
246
  rdoc_options: []
175
247
  require_paths:
176
248
  - lib
@@ -178,15 +250,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
250
  requirements:
179
251
  - - ">="
180
252
  - !ruby/object:Gem::Version
181
- version: 2.3.0
253
+ version: 2.4.0
182
254
  required_rubygems_version: !ruby/object:Gem::Requirement
183
255
  requirements:
184
256
  - - ">="
185
257
  - !ruby/object:Gem::Version
186
258
  version: '0'
187
259
  requirements: []
188
- rubygems_version: 3.1.4
189
- signing_key:
260
+ rubygems_version: 3.2.1
261
+ signing_key:
190
262
  specification_version: 4
191
263
  summary: Drop-in debug logging useful when a call stack gets unruly
192
264
  test_files: []