Christoph finds exceptional log lines and takes a more literal approach.
map
and reduce
!"parse-line
on the inputs before doing their specific parsing.parse-line
is :raw/line
, which is the entire line, and that's always there.parse-line
will always return a map for each line.:raw/line
:log/date
), that means it didn't parse, and is probably a continuation of a previous line.some->>
, which shortcuts on nil
.take-while
to find all the lines that are bare.:log/message
key.Related episodes:
Clojure in this episode:
some->>
, take-while
Code sample from this episode:
(ns devops.week-06
(:require
[clojure.string :as string]
[devops.week-02 :refer [process-log]]
[devops.week-05 :refer [parse-357-error parse-sprinkle]]
))
(def general-re #"(\d\d\d\d-\d\d-\d\d)\s+(\d\d:\d\d:\d\d)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s(.*)")
(defn parse-line
[line]
(if-let [[whole dt tm thread-name level ns message] (re-matches general-re line)]
{:raw/line whole
:log/date dt
:log/time tm
:log/thread thread-name
:log/level level
:log/namespace ns
:log/message message}
{:raw/line line
:log/message line}))
(defn bare-line?
[line]
(nil? (:log/date line)))
(defn parse-exception-info
[lines]
(let [first-line (first lines)
[_whole classname] (some->> first-line :log/message (re-matches #"([^ ]+) #error \{"))]
(when classname
(let [error-lines (cons first-line (take-while bare-line? (rest lines)))
error-str (string/join "\n" (map :log/message error-lines))]
(merge first-line
{:kind :error
:error/class classname
:log/message error-str})))))
(defn parse-next
[lines]
(or (parse-357-error lines)
(parse-sprinkle lines)
(parse-exception-info lines)))
(defn parse-all
[lines]
(lazy-seq
(when (seq lines)
(if-some [found (parse-next lines)]
(cons found (parse-all (rest lines)))
(parse-all (rest lines))))))
(comment
(process-log "sample.log" #(->> % (map parse-line) parse-all doall))
)
Log file sample:
2019-05-14 16:48:57 | process-Poster | INFO | com.donutgram.poster | transaction failed while updating user joe: code 357
2019-05-14 16:48:55 | process-Poster | INFO | com.donutgram.poster | failed to add sprinkle to donut 50493
2019-05-14 16:48:55 | process-Poster | INFO | com.donutgram.poster | sprinkle fail reason: unknown state
2019-05-14 16:48:55 | process-Poster | INFO | com.donutgram.poster | Poster #error {
:cause "Failed to lock the synchronizer"
:data {}
:via
[{:type clojure.lang.ExceptionInfo
:message "Failed to lock the synchronizer"
:data {}
:at [process.poster$eval50560 invokeStatic "poster.clj" 40]}]
:trace
[[process.poster$eval50560 invokeStatic "poster.clj" 40]
[clojure.lang.AFn run "AFn.java" 22]
[java.lang.Thread run "Thread.java" 748]]}