Code:
class C
getter log : Log
def initialize(log = nil)
# @log = log || Log.for(self)
@log = Log.for(self)
end
end
C.new
Output:
> 4 | @log : Log
^---
Error: instance variable '@log' of C was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.
Crystal 0.35.0-dev [74b7aa188] (2020-04-12)
Because self is passed to a method, there's a chance @log is read before it's used. The compiler prevents this.
One way to solve it is to make @log nilable. You can use getter! log : Log for this.
Using a nilable ivar won't help in this case. Log.for(self) only works in class scope, not inside a method. Log.for receives either a string or a class (which is then transformed into a string).
I'm not sure if you actually need the log as an instance variable. Usually you would just do Log = ::Log.for(self) at the class scope.
Either way, the equivalent invocation would be @log = Log.for(C), though @straight-shoota is correct that that's rather unneccesary vs a constant.
Main benefit would be able to inject a test logger. Otherwise currently there isn't a way, that I can think of ATM, that would allow you to mock it out for unit tests.
I suppose you would have to reopen the type in the spec and change the Log = xx const. Although wouldn't it complain that that constant is already defined?
MemoryBackend is provided by std, imagined for testing purposes.
Right yea, but how would you change the logger instance to use that backend if it is a constant? As you would run into Error: already initialized constant Foo::Log.
That's not how Log works. You would route the log source to the memory backend using the top-level Log as shown in the docs:
https://crystal-lang.org/api/0.34.0/Log.html#configure-logging-explicitly-in-the-code
Thank you all. The solution i went with:
class C
Log = ::Log.for(self)
def initialize(@log = Log)
end
end
Why? I have a class with 2 distinct uses where I need debugging on one or the other but rarely both (Mostly just one). Turning them both on adds too much noise.
Most helpful comment
That's not how
Logworks. You would route the log source to the memory backend using the top-levelLogas shown in the docs:https://crystal-lang.org/api/0.34.0/Log.html#configure-logging-explicitly-in-the-code