Help with the Log module in version 0.34.0

Please, let me know if I should ask this elsewhere. My current app uses logging in the following manner:

Module Util
    def self.logger(output) #=> output = STDOUT or file
      log = Logger.new(output)
      log.formatter = Logger::Formatter.new do |severity, datetime, _progname, message, io|
        label = severity.unknown? ? "ANY" : severity.to_s
        io << "[" << datetime << "]"
        io << label.rjust(5) << " -- " << message
      end
      log
    end
end

Later, I call this:

 Util.logger(STDOUT).info("backup #{VERSION} : crystal #{Crystal::VERSION}")
 Util.logger(logfile).info("backup #{VERSION} : crystal #{Crystal::VERSION}")

This works for STDOUT and writing to a file. It’s simple and easy:

[2020-04-07 15:14:22 -07:00] INFO -- backup 0.8.17 : crystal 0.33.0
[2020-04-07 15:14:22 -07:00] INFO -- Performing backup operation:

The new Log module has me completely baffled! Any guidance is greatly appreciated.

Please note: I am a 71 year old retired network specialist whose first computer was an Altair 8080. I feel that Crystal deserves a place in any dev or engineer’s toolkit, i.e., I love this language! Please, temper any responses as if you’re talking to grandpa (you are)

7 Likes

Hi @lebogan,

There are two ways to migrate that to the new Log module. The new Log module has some additional features that if you don’t want to use them it might sound more complicated that writing directly to the output.

One alternative is to use the built-in configuration methods that will allow you to use environment variables to tweak how verbose you want the logs to be.

require "log"

module Util
  def self.logger(output)
    backend = Log::IOBackend.new(output)
    backend.formatter = Log::Formatter.new do |entry, io|
      label = entry.severity.label
      io << "[" << entry.timestamp << "]"
      io << label.rjust(7) << " -- " << entry.message
    end

    Log.setup_from_env(backend: backend)
  end
end

Util.logger(STDOUT)

Log.info { "lorem ipsum" }

The Util.logger will setup the logging for the whole app. The labels are a bit longer than before, hence rjust(7). You will be able to use CRYSTAL_LOG_LEVEL and CRYSTAL_LOG_SOURCES environment variables to tweak verbosity.

The other alternative, if you want to create an indepentan Log object to pass arround, the Util.logger can be converted to a factory or provider of such object.

module Util
  def self.logger(output)
    backend = Log::IOBackend.new(output)
    backend.formatter = Log::Formatter.new do |entry, io|
      label = entry.severity.label
      io << "[" << entry.timestamp << "]"
      io << label.rjust(7) << " -- " << entry.message
    end

    builder = Log::Builder.new
    builder.bind("", :info, backend)
    builder.for("")
  end
end

log = Util.logger(STDOUT)
log.info { "lorem ipsum" }

The methods for emitting a log are now .info { "message" } rather than .info("message"). This helps to avoid creating strings if the message was not to be emitted at the end of the day.


My first computer was a TI-99/4A :-) that ran Basic TI.

6 Likes

And this is why I Crystal. Thank you very much @bcardiff for this awesome answer! I have updated my app using logging and everything just works :grinning:

6 Likes