Log is a class and the Log constants inside other types are to make it look like classes…
In the case of ameba, I would just make an exception for Log.
I think maybe ameba should allow UpperCamelCase since Log is probably not the first or last thing to be used in this way. For example, in Avram we already have a different constant name because we use 2 loggers in the same class: QueryLog and FailedQueryLog.
So that’d be my vote. Allow by default, or have a setting that you can enable/disable for UpperCamel constants
I did exactly that, allowed UpperCamelCase (or PascalCase). You can try it out in the latest Ameba (v0.12.1). Will think about the setting to turn it off in the future. Thanks!
Hey! I’ve pointed out this issue to @veelenga over Ameba.
Now I’m playing around with the Log notation, I’m not 100% sure it’s a great idea. Here an example of code which doesn’t compile:
require "log"
module Main
extend self
Log = ::Log.for(self)
def main
Log.builder.bind "*", :debug, Log::IOBackend.new
Log.warn { "This is a warning" }
end
end
Main.main
The Log object shadow the Log class. But the Log IS an object, not a class, and doesn’t provides access to subclasses and tutti quanti.
And this shadowing is in my opinion quite bad (plus the error message can confuse a beginner).
I have similar concerns as I’ve been migrating to the new logger. There is a lot that I love about it, but the Log constant reassignment has been fairly confusing in terms of inheritance and what works and doesn’t. I never really know which log I’m working with and a lot of times I’ve ended up having to create differently named logs anyway (FailedQueryLog). I'm planning to put together a post with some thoughts on the new Log` and ways that we may want to change/add some things
Ah, nevermind, disregard my comment. I think the idea is to shadow things. But also the idea is not to configure log in places where you would use them for logging. That way there’s no conflict. And if there is, you can always do ::Log.builder.bind
@asterite this is a simplified example. I just want to point out two things:
The Log here is not a module but a constant, and can confuse the beginner 100% sure.
Error: undefined method 'builder' for Log is the current error message. This one is confusing too. I guess Error: undefined method 'builder' for Main::Log would definitely give more insight on what’s going on here
In this case, I feel like we’re making an exception in conventions we follow everywhere, just because it’s more convenient (and actually it’s not so much more convenient).
In this early days of the module I think is common for people to configure the logging and use the logging at the same time.
But as frameworks incorporate it, and after a configuration module appears, there will be less confusion since the usages will be mostly for declaring Log sources and emitting logs.
class Log
class_getter sources : Array(String)
@@sources = [File.basename(PROGRAM_NAME)] of String
macro for(source)
class Log < ::Log
class_getter sources : Array(String)
@@sources = ::{{@type}}.sources + [{{source.id.stringify}}]
end
end
def self.info(message)
puts "[" + sources.join(":") + "]: #{message}"
end
end
Log.info("top")
module X
Log.for("x")
def self.y
Log.info("x")
end
class Z
Log.for("z")
def self.z
Log.info("z")
end
class A
Log.for("a")
def a
Log.info("a")
end
end
end
end
X.y
X::Z.z
X::Z::A.new.a