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.
@@ -7,7 +7,7 @@ require 'debug_logging/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'debug_logging'
9
9
  spec.version = DebugLogging::VERSION
10
- spec.authors = ['Peter Boling']
10
+ spec.authors = ['Peter Boling', 'guckin']
11
11
  spec.email = ['peter.boling@gmail.com']
12
12
 
13
13
  spec.summary = 'Drop-in debug logging useful when a call stack gets unruly'
@@ -24,15 +24,19 @@ Automatically log selected methods and their arguments as they are called at run
24
24
  spec.bindir = 'exe'
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ['lib']
27
- spec.required_ruby_version = '>= 2.3.0' # Uses magic comments
27
+ spec.required_ruby_version = '>= 2.4.0' # Uses magic comments
28
28
 
29
29
  spec.add_runtime_dependency 'colorize', '>= 0'
30
+ spec.add_development_dependency 'activesupport', '~> 5.2', '>= 5.2.4.4'
30
31
  spec.add_development_dependency 'bundler', '>= 2'
31
32
  spec.add_development_dependency 'byebug', '>= 11'
32
33
  spec.add_development_dependency 'rake', '>= 13'
33
34
  spec.add_development_dependency 'rspec', '>= 3'
34
35
  spec.add_development_dependency 'rspec-pending_for', '>= 0'
35
- spec.add_development_dependency 'rubocop', '>= 0'
36
- spec.add_development_dependency 'rubocop-rspec', '>= 1'
36
+ spec.add_development_dependency 'rubocop', '~> 1.0'
37
+ spec.add_development_dependency 'rubocop-md'
38
+ spec.add_development_dependency 'rubocop-performance'
39
+ spec.add_development_dependency 'rubocop-rake'
40
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.0'
37
41
  spec.add_development_dependency 'silent_stream', '>= 1'
38
42
  end
@@ -5,8 +5,12 @@ require 'colorized_string'
5
5
  require 'digest'
6
6
 
7
7
  require 'debug_logging/version'
8
+ require 'debug_logging/errors'
8
9
  require 'debug_logging/configuration'
10
+ require 'debug_logging/util'
11
+ require 'debug_logging/finalize'
9
12
  require 'debug_logging/argument_printer'
13
+ require 'debug_logging/hooks'
10
14
  require 'debug_logging/instance_logger_modulizer'
11
15
  require 'debug_logging/instance_logger'
12
16
  require 'debug_logging/class_logger'
@@ -61,6 +65,11 @@ module DebugLogging
61
65
  def self.extended(base)
62
66
  base.send(:extend, ArgumentPrinter)
63
67
  base.debug_config_reset(Configuration.new(**debug_logging_configuration.to_hash))
68
+ base.class_eval do
69
+ def base.inherited(subclass)
70
+ subclass.debug_config_reset(Configuration.new(**debug_config.to_hash))
71
+ end
72
+ end
64
73
  end
65
74
 
66
75
  #### API ####
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'debug_logging/log_subscriber'
4
+ require 'debug_logging/class_notifier'
5
+ require 'debug_logging/instance_notifier_modulizer'
6
+ require 'debug_logging/instance_notifier'
@@ -2,11 +2,13 @@
2
2
 
3
3
  module DebugLogging
4
4
  module ArgumentPrinter
5
- def debug_benchmark_to_s(tms: nil)
5
+ def debug_benchmark_to_s(tms:)
6
6
  "completed in #{format('%f', tms.real)}s (#{format('%f', tms.total)}s CPU)"
7
7
  end
8
8
 
9
9
  def debug_invocation_id_to_s(args: nil, config_proxy: nil)
10
+ return '' unless args && config_proxy
11
+
10
12
  if config_proxy.debug_add_invocation_id
11
13
  invocation = " ~#{args.object_id}@#{(Time.now.to_f.to_s % '%#-21a')[4..-4]}~"
12
14
  case config_proxy.debug_add_invocation_id
@@ -21,6 +23,8 @@ module DebugLogging
21
23
  end
22
24
 
23
25
  def debug_invocation_to_s(klass: nil, separator: nil, method_to_log: nil, config_proxy: nil)
26
+ return '' unless config_proxy
27
+
24
28
  klass_string = if config_proxy.debug_colorized_chain_for_class
25
29
  config_proxy.debug_colorized_chain_for_class.call(ColorizedString[klass.to_s])
26
30
  else
@@ -34,7 +38,9 @@ module DebugLogging
34
38
  "#{klass_string}#{separator}#{method_string}"
35
39
  end
36
40
 
37
- def debug_signature_to_s(args: nil, config_proxy: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
41
+ def debug_signature_to_s(args: nil, config_proxy: nil) # rubocop:disable Metrics/CyclomaticComplexity
42
+ return '' unless args && config_proxy
43
+
38
44
  printed_args = ''
39
45
 
40
46
  add_args_ellipsis = false
@@ -65,25 +71,56 @@ module DebugLogging
65
71
  printed_args += ', ' if !other_args_string.empty? && !last_hash_args_string.empty?
66
72
  printed_args += last_hash_args_string if last_hash_args_string && !last_hash_args_string.empty?
67
73
  else
68
- printed_args += args[0..-2].map(&:inspect).join(', ').tap { |x| add_args_ellipsis = x.length > config_proxy.debug_args_max_length }[0..(config_proxy.debug_args_max_length)]
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)]
69
77
  printed_args += config_proxy.debug_ellipsis if add_args_ellipsis
70
- printed_args += ", #{config_proxy.debug_last_hash_to_s_proc.call(args[-1]).tap { |x| add_last_hash_ellipsis = x.length > config_proxy.debug_last_hash_max_length }[0..(config_proxy.debug_last_hash_max_length)]}"
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)]}"
71
81
  printed_args += config_proxy.debug_ellipsis if add_last_hash_ellipsis
72
82
  end
73
83
  else
74
- printed_args += String(config_proxy.debug_last_hash_to_s_proc.call(args[0])).tap { |x| add_last_hash_ellipsis = x.length > config_proxy.debug_last_hash_max_length }[0..(config_proxy.debug_last_hash_max_length)]
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)]
75
87
  printed_args += config_proxy.debug_ellipsis if add_last_hash_ellipsis
76
88
  end
77
89
  else
78
- if args.length == 1 && args[0].is_a?(Hash)
79
- # handle double splat
80
- printed_args += ("**#{args.map(&:inspect).join(', ').tap { |x| add_args_ellipsis = x.length > config_proxy.debug_args_max_length }}")[0..(config_proxy.debug_args_max_length)]
81
- else
82
- printed_args += args.map(&:inspect).join(', ').tap { |x| add_args_ellipsis = x.length > config_proxy.debug_args_max_length}[0..(config_proxy.debug_args_max_length)]
83
- end
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
84
100
  printed_args += config_proxy.debug_ellipsis if add_args_ellipsis
85
101
  end
86
102
  "(#{printed_args})"
87
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
88
125
  end
89
126
  end
@@ -3,50 +3,55 @@
3
3
  module DebugLogging
4
4
  module ClassLogger
5
5
  def logged(*methods_to_log)
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
- opts = methods_to_log.last.is_a?(Hash) && methods_to_log.pop
9
- if methods_to_log.first.is_a?(Array)
10
- methods_to_log = methods_to_log.shift
11
- else
12
- # logged :meth1, :meth2, :meth3 without options is valid too
13
- end
14
- methods_to_log.each do |method_to_log|
15
- # method name must be a symbol
16
- method_to_log = method_to_log.to_sym
6
+ methods_to_log, payload, config_opts = DebugLogging::Util.extract_payload_and_config(
7
+ method_names: methods_to_log,
8
+ payload: nil,
9
+ config: nil
10
+ )
11
+ Array(methods_to_log).each do |method_to_log|
12
+ method_to_log, method_payload, method_config_opts = DebugLogging::Util.extract_payload_and_config(
13
+ method_names: method_to_log,
14
+ payload: payload,
15
+ config: config_opts
16
+ )
17
17
  original_method = method(method_to_log)
18
18
  (class << self; self; end).class_eval do
19
19
  define_method(method_to_log) do |*args, &block|
20
- config_proxy = if (proxy = instance_variable_get(DebugLogging::Configuration.config_pointer('k', method_to_log)))
21
- proxy
22
- else
23
- proxy = if opts
24
- Configuration.new(**debug_config.to_hash.merge(opts))
25
- else
26
- debug_config
27
- end
28
- proxy.register(method_to_log)
29
- instance_variable_set(DebugLogging::Configuration.config_pointer('k', method_to_log), proxy)
30
- proxy
31
- end
20
+ config_proxy = DebugLogging::Util.config_proxy_finder(
21
+ scope: self,
22
+ config_opts: method_config_opts,
23
+ method_name: method_to_log,
24
+ proxy_ref: 'kl'
25
+ )
32
26
  method_return_value = nil
33
27
  log_prefix = nil
34
28
  invocation_id = nil
35
29
  config_proxy.log do
36
- log_prefix = debug_invocation_to_s(klass: to_s, separator: '.', method_to_log: method_to_log, config_proxy: config_proxy)
30
+ paydirt = DebugLogging::Util.payload_instance_vaiable_hydration(scope: self, payload: method_payload)
31
+ log_prefix = debug_invocation_to_s(klass: to_s, separator: '.', method_to_log: method_to_log,
32
+ config_proxy: config_proxy)
37
33
  invocation_id = debug_invocation_id_to_s(args: args, config_proxy: config_proxy)
38
34
  signature = debug_signature_to_s(args: args, config_proxy: config_proxy)
39
- "#{log_prefix}#{signature}#{invocation_id}"
35
+ paymud = debug_payload_to_s(payload: paydirt, config_proxy: config_proxy)
36
+ "#{log_prefix}#{signature}#{invocation_id} debug: #{paymud}"
40
37
  end
41
38
  if config_proxy.benchmarkable_for?(:debug_class_benchmarks)
42
39
  tms = Benchmark.measure do
43
- method_return_value = original_method.call(*args, &block)
40
+ method_return_value = if args.size == 1 && (harsh = args[0]) && harsh.is_a?(Hash)
41
+ original_method.call(**harsh, &block)
42
+ else
43
+ original_method.call(*args, &block)
44
+ end
44
45
  end
45
46
  config_proxy.log do
46
47
  "#{log_prefix} #{debug_benchmark_to_s(tms: tms)}#{invocation_id}"
47
48
  end
48
49
  else
49
- method_return_value = original_method.call(*args, &block)
50
+ method_return_value = if args.size == 1 && (harsh = args[0]) && harsh.is_a?(Hash)
51
+ original_method.call(**harsh, &block)
52
+ else
53
+ original_method.call(*args, &block)
54
+ end
50
55
  if config_proxy.exit_scope_markable? && invocation_id && !invocation_id.empty?
51
56
  config_proxy.log do
52
57
  "#{log_prefix} completed#{invocation_id}"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DebugLogging
4
+ module ClassNotifier
5
+ def notifies(*methods_to_notify)
6
+ methods_to_notify, payload, config_opts = DebugLogging::Util.extract_payload_and_config(
7
+ method_names: methods_to_notify,
8
+ payload: nil,
9
+ config: nil
10
+ )
11
+ Array(methods_to_notify).each do |method_to_notify|
12
+ method_to_notify, method_payload, method_config_opts = DebugLogging::Util.extract_payload_and_config(
13
+ method_names: method_to_notify,
14
+ payload: payload,
15
+ config: config_opts
16
+ )
17
+ original_method = method(method_to_notify)
18
+ (class << self; self; end).class_eval do
19
+ define_method(method_to_notify) do |*args, &block|
20
+ config_proxy = DebugLogging::Util.config_proxy_finder(
21
+ scope: self,
22
+ config_opts: method_config_opts,
23
+ method_name: method_to_notify,
24
+ proxy_ref: 'kn'
25
+ ) do |proxy|
26
+ ActiveSupport::Notifications.subscribe(
27
+ DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify)
28
+ ) do |*debug_args|
29
+ proxy.log do
30
+ DebugLogging::LogSubscriber.log_event(ActiveSupport::Notifications::Event.new(*debug_args))
31
+ end
32
+ end
33
+ end
34
+ paydirt = DebugLogging::Util.payload_instance_vaiable_hydration(scope: self, payload: method_payload)
35
+ ActiveSupport::Notifications.instrument(
36
+ DebugLogging::ArgumentPrinter.debug_event_name_to_s(method_to_notify: method_to_notify),
37
+ {
38
+ debug_args: args,
39
+ config_proxy: config_proxy,
40
+ **paydirt
41
+ }
42
+ ) do
43
+ if args.size == 1 && (harsh = args[0]) && harsh.is_a?(Hash)
44
+ original_method.call(**harsh, &block)
45
+ else
46
+ original_method.call(*args, &block)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ 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 :enabled
9
- attr_accessor :logger, :log_level, :multiple_last_hashes, :last_hash_to_s_proc, :last_hash_max_length,
10
- :args_max_length, :colorized_chain_for_method, :colorized_chain_for_class, :add_invocation_id,
11
- :ellipsis, :mark_scope_exit
12
- attr_reader :instance_benchmarks, :class_benchmarks, :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
- alias debug_enabled enabled
32
- alias debug_logger logger
33
- alias debug_log_level log_level
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_method :"debug_#{key}", :"#{key}"
55
+ end
45
56
 
46
57
  class << self
47
58
  def config_pointer(type, method_to_log)
@@ -52,20 +63,12 @@ module DebugLogging
52
63
  end
53
64
  end
54
65
  def initialize(**options)
55
- @enabled = options.key?(:enabled) ? options[:enabled] : true
56
- @logger = options.key?(:logger) ? options[:logger] : Logger.new($stdout)
57
- @log_level = options.key?(:log_level) ? options[:log_level] : :debug
58
- @multiple_last_hashes = options.key?(:multiple_last_hashes) ? options[:multiple_last_hashes] : false
59
- @last_hash_to_s_proc = options.key?(:last_hash_to_s_proc) ? options[:last_hash_to_s_proc] : nil
60
- @last_hash_max_length = options.key?(:last_hash_max_length) ? options[:last_hash_max_length] : 1_000
61
- @args_max_length = options.key?(:args_max_length) ? options[:args_max_length] : 1_000
62
- @instance_benchmarks = options.key?(:instance_benchmarks) ? options[:instance_benchmarks] : false
63
- @class_benchmarks = options.key?(:class_benchmarks) ? options[:class_benchmarks] : false
64
- @colorized_chain_for_method = options.key?(:colorized_chain_for_method) ? options[:colorized_chain_for_method] : false
65
- @colorized_chain_for_class = options.key?(:colorized_chain_for_class) ? options[:colorized_chain_for_class] : false
66
- @add_invocation_id = options.key?(:add_invocation_id) ? options[:add_invocation_id] : true
67
- @ellipsis = options.key?(:ellipsis) ? options[:ellipsis] : DEFAULT_ELLIPSIS
68
- @mark_scope_exit = options.key?(:mark_scope_exit) ? options[:mark_scope_exit] : 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
69
72
  @methods_to_log = []
70
73
  end
71
74
 
@@ -73,7 +76,7 @@ module DebugLogging
73
76
  return unless enabled
74
77
  return unless logger
75
78
 
76
- if block_given?
79
+ if block
77
80
  logger.send(log_level, &block)
78
81
  else
79
82
  logger.send(log_level, message)
@@ -108,26 +111,29 @@ module DebugLogging
108
111
  @class_benchmarks = class_benchmarks
109
112
  end
110
113
 
114
+ def active_support_notifications=(active_support_notifications)
115
+ require 'debug_logging/active_support_notifications' if active_support_notifications
116
+ @active_support_notifications = active_support_notifications
117
+ end
118
+
111
119
  def to_hash
112
- {
113
- logger: logger,
114
- log_level: log_level,
115
- multiple_last_hashes: multiple_last_hashes,
116
- last_hash_to_s_proc: last_hash_to_s_proc,
117
- last_hash_max_length: last_hash_max_length,
118
- args_max_length: args_max_length,
119
- instance_benchmarks: instance_benchmarks,
120
- class_benchmarks: class_benchmarks,
121
- colorized_chain_for_method: colorized_chain_for_method,
122
- colorized_chain_for_class: colorized_chain_for_class,
123
- add_invocation_id: add_invocation_id,
124
- ellipsis: ellipsis,
125
- mark_scope_exit: mark_scope_exit
126
- }
120
+ CONFIG_KEYS.each_with_object({}) do |key, hash|
121
+ hash[key] = instance_variable_get("@#{key}")
122
+ end
127
123
  end
128
124
 
129
125
  def register(method_lo_log)
130
126
  @methods_to_log << method_lo_log
131
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
132
138
  end
133
139
  end
@@ -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