lib/semantic_logger/loggable.rb in semantic_logger-4.1.1 vs lib/semantic_logger/loggable.rb in semantic_logger-4.2.0

- old
+ new

@@ -3,33 +3,38 @@ # Lazy initialize a logger class variable with instance accessor # # By including this mix-in into any class it will define a class level logger # and also make it accessible via instance methods # -# Example +# Example: +# require 'semantic_logger' +# SemanticLogger.default_level = :debug +# SemanticLogger.add_appender(io: STDOUT, formatter: :color) # -# require 'semantic_logger' -# SemanticLogger.default_level = :debug -# SemanticLogger.add_appender(io: STDOUT, formatter: :color) +# class ExternalSupplier +# # Create class and instance logger methods +# include SemanticLogger::Loggable # -# class ExternalSupplier -# # Create class and instance logger methods -# include SemanticLogger::Loggable +# def call_supplier(amount, name) +# logger.debug "Calculating with amount", { amount: amount, name: name } # -# def call_supplier(amount, name) -# logger.debug "Calculating with amount", { amount: amount, name: name } +# # Measure and log on completion how long the call took to the external supplier +# logger.measure_info "Calling external interface" do +# # Code to call the external supplier ... +# end +# end +# end # -# # Measure and log on completion how long the call took to the external supplier -# logger.measure_info "Calling external interface" do -# # Code to call the external supplier ... -# end -# end -# end +# Notes: +# * To forcibly replace Rails or any other existing logging methods +# use `prepend` instead of `include`. For example: +# ExternalSupplier.prepend SemanticLogger::Loggable module SemanticLogger module Loggable def self.included(base) + base.extend ClassMethods base.class_eval do # Returns [SemanticLogger::Logger] class level logger def self.logger @semantic_logger ||= SemanticLogger[self] end @@ -47,9 +52,77 @@ # Replace instance level logger def logger=(logger) @semantic_logger = logger end end + end + + module ClassMethods + # Measure and log the performance of an instance method. + # + # Parameters: + # method_name: [Symbol] + # The name of the method that should be measured. + # + # options: [Hash] + # Any valid options that can be passed to measure. + # + # Approximate overhead when logging a method call with a metric: + # 0.044 ms per method call. + # 0.009 ms per method call. If `min_duration` is not met + # 0.0005 ms per method call. If `level` is not met + def logger_measure_method(method_name, + min_duration: 0.0, + metric: "#{name}/#{method_name}", + log_exception: :partial, + on_exception_level: nil, + message: "##{method_name}", + level: :info) + + # unless visibility = Utils.method_visibility(self, method_name) + # logger.warn("Unable to measure method: #{name}##{method_name} since it does not exist") + # return false + # end + + index = SemanticLogger.level_to_index(level) + + logger_measure_module.module_eval(<<-EOT, __FILE__, __LINE__ + 1) + def #{method_name}(*args, &block) + if logger.send(:level_index) <= #{index} + logger.send( + :measure_method, + index: #{index}, + level: #{level.inspect}, + message: #{message.inspect}, + min_duration: #{min_duration}, + metric: #{metric.inspect}, + log_exception: #{log_exception.inspect}, + on_exception_level: #{on_exception_level.inspect} + ) do + super(*args, &block) + end + else + super(*args, &block) + end + end + EOT + #{"#{visibility} :#{method_name}" unless visibility == :public} + true + end + + private + + # Dynamic Module to intercept method calls for measuring purposes. + def logger_measure_module + if const_defined?(:SemanticLoggerMeasure, _search_ancestors = false) + const_get(:SemanticLoggerMeasure) + else + mod = const_set(:SemanticLoggerMeasure, Module.new) + prepend mod + mod + end + end + end end end