2
votes

I want to use core.async as a logger that writes out to a file, so I created a test.txt file, stuck it in my resources folder and wrote this code:

(use 'clojure.java.io)
(use 'clojure.core.async)

(def print-chan (chan))

(go (loop []
      (when-let [v (<! print-chan)]
        (with-open [wrtr (writer "resources/test.txt" :append true)]
          (.write wrtr v))
        (recur)))) 

(>!! print-chan 42)

When I run this, however, I find that it will only replace what is in the file, and not append to it. Also, sometimes the output that is written to the file is odd. Once, I tried to put 42 and I got * instead. When I use the writer without the core.async functions, it works as expected. What is the idiomatic way to write to a log file in Clojure using core.async? Thanks in advance!

*I am using light table.

2

2 Answers

4
votes

wrtr is a java.io.BufferedWriter.

When you send 42 (long) into the channel, you're calling (.write wrtr 42) which invokes this method:

write(int c)
Writes a single character.

42 represents the \* character on the ASCII table so that's what's being written.

Instead, you want to invoke this Writer method via (.write wrtr "42"):

write(String str)
Writes a string.

And you can do that by converting the values read from the channel into strings:

(.write wrtr (str v))
3
votes

You are opening the file anew in each loop/recur cycle, this is why it's not appending.

What you need to do is open the writer once for the life of the program.

(go
  (with-open [wrtr (writer "resources/test.txt" :append true)]
    (loop []
      (when-let [v (<! print-chan)]
        (.write wrtr (str v "\n"))
      (recur)))) 

I just had to write code like this today. What I found was that you have to call with-open inside the goroutine. Because of this (I assume), you can't use the with-open macro with the go-loop macro.