Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Unobtrusive Debug Logging that examines wonders of Ruby: Module < Class; include Module.new(*args); and more!
# Simpler version of what the debug_logging gem does; see https://github.com/pboling/debug_logging
#
############# THIS IS A BAUBLE
############# FOR EXAMINING SEVERAL OF THE WONDERS OF RUBY
############# TO ACCOMPLISH SOMETHING PRACTICAL
############# For a more robust implementation use the gem debug_logging itself,
############# which makes use of these same principles.
#
# Automatically log Class.method(arguments) as they are called at runtime (instance or singleton)!
#
# NOTE: For a more advanced version see the debug_logging gem
# NOTE: The manner this is made to work for class methods is totally different than the way this is made to work for instance methods.
# NOTE: The instance method manner of logging works on Ruby 2.0+
# NOTE: The class method manner of logging works on Ruby 2.1+
require 'benchmark'
class SimpleDebugLogging < Module
def initialize(i_methods: nil)
@instance_methods_to_log = Array(i_methods) if i_methods
end
def included(base)
instance_method_logger = InstanceMethodLoggerModulizer.to_mod(@instance_methods_to_log)
base.send(:prepend, instance_method_logger)
base.send(:extend, ClassMethodLogger)
end
module ClassMethodLogger
def logged(*methods_to_log)
methods_to_log.each do |method_to_log|
original_method = method(method_to_log)
(class << self; self; end).class_eval do
define_method(method_to_log.to_sym) do |*args|
method_return_value = nil
invocation_id = " ~#{args.object_id}@#{Time.now.to_i}~" if args
puts "#{self}.#{method_to_log}(#{args.map {|x| x.inspect}.join(", ")})#{invocation_id}"
elapsed = Benchmark.realtime do
method_return_value = original_method.call(*args)
end
puts "#{self}.#{method_to_log} ~#{args.hash}~ complete in #{elapsed}s#{invocation_id}"
method_return_value
end
end
end
end
end
module InstanceMethodLoggerModulizer
def self.to_mod(methods_to_log = [])
Module.new do
Array(methods_to_log).each do |method_to_log|
define_method(method_to_log.to_sym) do |*args, &block|
method_return_value = nil
invocation_id = " ~#{args.object_id}@#{Time.now.to_i}~" if args
puts "#{self.class}##{method_to_log}(#{args.map {|x| x.inspect}.join(", ")})#{invocation_id}"
elapsed = Benchmark.realtime do
method_return_value = super(*args, &block)
end
puts "#{self.class}##{method_to_log} ~#{args.hash}~ complete in #{elapsed}s#{invocation_id}"
method_return_value
end
end
end
end
end
end
class UnobtrusivelyLogged
def initialize(**args); end
def an_instance_method(*args); end
def self.a_class_method(*args); self; end
include SimpleDebugLogging.new(i_methods: %i( initialzie an_instance_method ))
logged :a_class_method
end
# Output will be something like:
# UnobtrusivelyLogged.a_class_method() ~70156299674920@1506415414~
# UnobtrusivelyLogged.a_class_method ~689933733604307372~ complete in 8.000002708286047e-06s ~70156299674920@1506415414~
# UnobtrusivelyLogged#an_instance_method() ~70156299673760@1506415414~
# UnobtrusivelyLogged#an_instance_method ~689933733604307372~ complete in 2.00001522898674e-06s ~70156299673760@1506415414~
UnobtrusivelyLogged.a_class_method.new.an_instance_method
# UnobtrusivelyLogged.a_class_method(1, 2, "a", "b") ~70124887408680@1506418201~
# UnobtrusivelyLogged.a_class_method ~-1310500302753052858~ complete in 2.9999646358191967e-06s ~70124887408680@1506418201~
# UnobtrusivelyLogged#an_instance_method([55, 44, 33]) ~70124896074340@1506418201~
# UnobtrusivelyLogged#an_instance_method ~180398559802792504~ complete in 2.00001522898674e-06s ~70124896074340@1506418201~
UnobtrusivelyLogged.a_class_method(1, 2, 'a', 'b').new(a: :b, c: :b).an_instance_method([55,44,33])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment