Search code examples
clojureiolazy-sequences

Clojure, java.io.Writer f and lazy seq


Cant consume lazy-seq to write data to file, no matter what i using, "Stream closed" error raised every time.

(ns logger.core
  (:gen-class)
  (:require [clojure.java.io :as io])
  (:import java.util.Date
           java.util.TimeZone
           java.text.SimpleDateFormat))

(defn -main
  "Creating new logger"
  [name]
  ())

(defn- create-dt-formatter-with-time-zone [time-format time-zone-code]
  (let [*date-format* (SimpleDateFormat. time-format)
        time-zone (TimeZone/getTimeZone time-zone-code)]
    (.setTimeZone *date-format* time-zone)
    *date-format*))

(defn date-formatter
  ([] (create-dt-formatter-with-time-zone "MM.dd HH:mm:ss" "UTC"))
  ([time-zone] (create-dt-formatter-with-time-zone "MM.dd HH:mm:ss" time-zone))
  ([time-zone time-format] (create-dt-formatter-with-time-zone time-format time-format)))

(defn date-to-string
  ([^Date date] (let [formatter (date-formatter)] (.format formatter date)))
  ([^Date date ^SimpleDateFormat formatter] (.format formatter date)))

(def my-formatter (date-formatter "UTC" "MM.dd"))

(def log-to-file (io/writer "logs/text.log" :append true))

(defn write [^java.io.Writer writer data]
  (with-open [writer writer]
    (let [date-now (date-to-string (Date.))]
        (->> data
             (map #(str date-now " " % "\n"))
             (map #(.write writer %))
             (doall)
             (.flush writer)))))


(defn write-2 [^java.io.Writer log-writer data]
  (with-open [writer log-writer]
    (let [date-now (date-to-string (Date.))]
      #_(doseq [data-row data]
          (.write writer (str date-now " " data-row "\n")))
      (doall (map #(.write writer (str date-now " " % "\n")) data)))
    (doto writer (.flush writer))))


(write-2 log-to-file ["test" "test1" "test lonmg string"])

Here is 2 function (write, write-2), but both doesnt work. Have no idea, how to use .write with lazy-seq, what is big problem in the lazy clojure world.


Solution

  • I simplified it a bit and got it to work. You didn't show the exact error so I can't comment in more detail. Here is the code:

    (ns tst.demo.core
      (:use tupelo.core tupelo.test)
      (:require
        [clojure.java.io :as io])
      (:import
        [java.io Writer]
        [java.util Date]))
    
    (def log-to-file (io/writer "text.log" ))
    
    (defn write-2 [^java.io.Writer log-writer data]
      (with-open [writer log-writer]
        (let [date-now (str (Date.))]
          (doall (map #(.write writer (str date-now " " % "\n")) data)))
         (.flush ^java.io.Flushable writer)))
    
    (dotest
      (write-2 log-to-file ["test" "test1" "test lonmg string"])
      )
    

    it is based on my favorite template project. The output:

    ~/expr/demo > cat text.log 
    Mon Feb 22 13:39:37 PST 2021 test
    Mon Feb 22 13:39:37 PST 2021 test1
    Mon Feb 22 13:39:37 PST 2021 test lonmg string
    

    It also works if you delete the (.flush ...) expression, since a close automatically flushes the output (i.e. when the with-open form exits).

    Note that you can use mapv instead of doall to force immediate execution. Or, just wrap the final value in a (vec ...) form.

    Please see this list of documentation sources, especially the Clojure CheatSheet and the book Getting Clojure.