debug_logging 2.0.0 → 3.1.3

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