'How can I DRY up SemanticLogger::Loggable and SemanticLogger::Loggable::ClassMethods by extending classes?
Summary
I'm trying to use SemanticLogger 4.10.0 as a part of a dynamic tracing solution in a Ruby 3.1.1 app. However, I seem to be misunderstanding something about how or where to access the logger instance that SemanticLogger::Loggable is supposed to create on the class, and am getting NameError exceptions (documented below) when calling #logger_measure_method from inside the extended class because I can't seem to implement the class extension properly.
Code That Demonstrates the Problem
Module with CustomLogger
require 'json'
require 'semantic_logger'
module SomeGem
module CustomLogger
include SemanticLogger
class MyFormat < SemanticLogger::Formatters::Default
def call (log, logger, machine_name: nil, json: nil)
self.log = log
self.logger = logger
message = "[#{machine name}] #{message}" if machine_name
payload = JSON.parse(json)
[time, level, process.info, tags, named.tags, duration, name, message, payload, exception].compact!
end
end
def self.extended(klass)
prepend SemanticLogger, SemanticLogger::Loggable, SemanticLogger::Loggable::ClassMethods
SemanticLogger.default_level = :debug
SemanticLogger.add_appender(file_name: "/dev/stderr", formatter: SomeGem::CustomLogger::MyFormat.new,
level: :warn, filter: proc { %i[warn fatal unknown].include? _1.level }
)
SemanticLogger.add_appender(file_name: "/dev/stdout", formatter: SomeGem::CustomLogger::MyFormat.new,
level: :trace, filter: proc { %i[trace debug info error].include? _1.level }
)
define_method(:auto_measure_method_calls) do
public_instance_methods(false).
reject { _1.to_s.match? /^logger/ }.
each { logger_measure_method _1, level: :trace }
end
end
end
end
Module Extended by CustomLogger
module SomeGem
class Foo
extend CustomLogger
def alpha = 1
def beta = 2
def charlie = 3
auto_measure_method_calls if ENV['LOG_LEVEL'] == 'trace'
end
end
Exercising the Code
ENV['LOG_LEVEL'] = 'trace'
f = SomeGem::Foo.new
pp f.alpha, f.beta, f.charlie
Problems and Issues with Code
My issues are:
The underlying (and possibly X/Y) issue is that I want to DRY up the code of including SemanticLogger::Loggable and SemanticLogger::Loggable::ClassMethods in every class I'm tracing, and auto-measuring the extended classes' methods in development.
When I try to extend a class with a module that is intended to pull in SemanticLogger::Loggable, I don't seem to always have logger available as an accessor throughout the class.
I'm also concerned that including the module in multiple classes would result in duplicate appenders being added to the #appenders array, wherever that's actually stored.
Most importantly though, when I try to automagically add logging and method measurement through extending a class, I get errors like the following:
~/.gem/ruby/3.1.1/gems/semantic_logger-4.10.0/lib/semantic_logger/loggable.rb:96:in `alpha': undefined local variable or method `logger' for #SomeGem::Foo:0x00000001138f6a38 (NameError) from foo.rb:51:in `<main>'
Am I missing something obvious about how to extend the classes with SemanticLogger? If there's a better way to accomplish what I'm trying to do, that's great! If I'm doing something wrong, understanding that would be great, too. If it's a bug, or I'm using the feature wrong, that's useful information as well.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
