The Crystal Programming Language Forum

Logging to a file

Can someone please point me to a simple example of writing log messages to a file? I would have thought this was a common thing, but I can’t seem to find an example in the documentation.

There’s probably a way to do this with https://crystal-lang.org/api/master/Log/IOBackend.html, i.e. pass a File.new ... object as the IO. However it probably makes more sense to create a dedicated FileBackend that extends the IO one. Maybe this is something that would be wanted in the stdlib…

Something like:

require "log"

class FileBackend < Log::IOBackend
  def initialize(file_name : String, mode : String = "a", *, formatter : Log::Formatter = Log::ShortFormat)
    super File.new(file_name, mode), formatter: formatter
  end

  def initialize(file : File, *, formatter : Log::Formatter = Log::ShortFormat)
    super file, formatter: formatter
  end

  def close
    @io.close
    super
  end
end

Log.setup :debug, FileBackend.new("app.log")

Log.info { "Log this to a file" }

Looking at it tho it’s essentially what the IOBackend is, just with an easier API… My only thinking is that we should close @io in this case because it’s a file? Any reason not to do that in IOBackend anyway?

2 Likes

I think OP doesn’t even know about the Log module.

Well if there is a way to do it with IOBackend, then I could not figure it out. And there was no example in the documentation, so I am left wondering about it.

Thanks for the code sample. I just spent the past half hour doing the same and including file rotation etc. to suit my needs.

Yes I’m aware of it and the depreciated Logger module as well. I just assumed that I was missing something, as I expected functionality as basic as logging to a file to be already in there. Seems like I was wrong.

To be clear it would just be like Log::IOBackend.new File.new("out.log", "a"). The only difference is this doesn’t close the file when the backend is closed; which probably isn’t too big of an issue as there should only be on FD open since you only instantiate the backend once. Plus the file would be closed when the program exits.

2 Likes

I didn’t know backends could be closed. There should be an option to close the underlying IO. I guess it’s not there because closing stdout would be wrong.

And I think having a simple interface to specify logging to a file would be good, because that’s probably the most typical scenario.

Sorry I assumed you didn’t know about Log, the original post didn’t mention anything so I assumed you didn’t know anything (no mention of what you were trying to do and what you wete having trouble with).

4 Likes

here is a really simple example.

require "log"

Log.setup(:info, Log::IOBackend.new(File.new("./app.log", "a+")))

Log.info { "bar" }

I use setup in most cases but there is a way to configure this with ENV. This is a very flexible and powerful Logging library but it is a little hard to approach. It would be nice if there was an entry here https://crystal-lang.org/reference/guides/index.html.

3 Likes

Thanks everyone. Your help is much appreciated.