3

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.

kurofune
  • 1,055
  • 12
  • 26

2 Answers2

4

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))
danneu
  • 9,244
  • 3
  • 35
  • 63
  • Thanks for this super simple and helpful answer. The only problem I have now is getting it to print each item to a new line. Should I look in the Clojure or Java documentation to figure this out? – kurofune May 13 '14 at 05:08
  • 1
    Just write a `newline` after your log line. Either include it in the message: `(.write wrtr "42\n")`, or write it explicitly: `(.write wrtr "\n")`. – liwp May 13 '14 at 07:17
  • Exactly. Just change the line to `(.write wrtr (str v "\n"))`. – danneu May 13 '14 at 21:25
3

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.

mattdeboard
  • 700
  • 1
  • 7
  • 17