mirror of https://github.com/status-im/timbre.git
[#166] Add 3rd-party logstash appender (@dfrese)
This commit is contained in:
parent
b86eb0d0e6
commit
2adbabee8d
|
@ -37,7 +37,8 @@
|
||||||
[org.graylog2/gelfclient "1.3.1"]
|
[org.graylog2/gelfclient "1.3.1"]
|
||||||
[org.julienxx/clj-slack "0.5.3"]
|
[org.julienxx/clj-slack "0.5.3"]
|
||||||
[org.clojure/java.jdbc "0.5.8"]
|
[org.clojure/java.jdbc "0.5.8"]
|
||||||
[com.mchange/c3p0 "0.9.5.2"]]}
|
[com.mchange/c3p0 "0.9.5.2"]
|
||||||
|
[cheshire "5.5.0"]]}
|
||||||
:dev
|
:dev
|
||||||
[:1.7 :test
|
[:1.7 :test
|
||||||
{:dependencies [[org.clojure/clojurescript "1.7.28"]]
|
{:dependencies [[org.clojure/clojurescript "1.7.28"]]
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
(ns taoensso.timbre.appenders.3rd-party.logstash
|
||||||
|
"Timbre appender that send output to logstash.
|
||||||
|
|
||||||
|
Requires cheshire (https://github.com/dakrone/cheshire)."
|
||||||
|
{:author "Mike Sperber (@mikesperber), David Frese (@dfrese)"}
|
||||||
|
(:require [taoensso.timbre :as timbre]
|
||||||
|
[cheshire.core :as cheshire])
|
||||||
|
(:import [java.net Socket InetAddress]
|
||||||
|
[java.io PrintWriter]))
|
||||||
|
|
||||||
|
;; Adapted from taoensso.timbre.appenders.3rd-party.server-socket
|
||||||
|
|
||||||
|
(defn connect
|
||||||
|
[host port]
|
||||||
|
(let [addr (InetAddress/getByName host)
|
||||||
|
sock (Socket. addr (int port))]
|
||||||
|
[sock
|
||||||
|
(PrintWriter. (.getOutputStream sock))]))
|
||||||
|
|
||||||
|
(defn connection-ok?
|
||||||
|
[[^Socket sock ^PrintWriter out]]
|
||||||
|
(and (not (.isClosed sock))
|
||||||
|
(.isConnected sock)
|
||||||
|
(not (.checkError out))))
|
||||||
|
|
||||||
|
(def iso-format "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||||
|
|
||||||
|
(defn data->json-stream
|
||||||
|
[data writer opts]
|
||||||
|
;; Note: this it meant to target the logstash-filter-json; especially "message" and "@timestamp" get a special meaning there.
|
||||||
|
(let [stacktrace-str (if-let [pr (:pr-stacktrace opts)]
|
||||||
|
#(with-out-str (pr %))
|
||||||
|
timbre/stacktrace)]
|
||||||
|
(cheshire/generate-stream
|
||||||
|
(merge (:context data)
|
||||||
|
{:level (:level data)
|
||||||
|
:namespace (:?ns-str data)
|
||||||
|
:file (:?file data)
|
||||||
|
:line (:?line data)
|
||||||
|
:stacktrace (some-> (force (:?err_ data)) (stacktrace-str))
|
||||||
|
:hostname (force (:hostname_ data))
|
||||||
|
:message (force (:msg_ data))
|
||||||
|
"@timestamp" (:instant data)})
|
||||||
|
writer
|
||||||
|
(merge {:date-format iso-format
|
||||||
|
:pretty false}
|
||||||
|
opts))))
|
||||||
|
|
||||||
|
(defn logstash-appender
|
||||||
|
"Returns a Logstash appender, which will send each event in JSON
|
||||||
|
format to the logstash server at `host:port`. Additionally `opts`
|
||||||
|
may be a map with `:pr-stracktrace` mapped to a function taking an
|
||||||
|
exception, which should write the stacktrace of that exception to
|
||||||
|
`*out`."
|
||||||
|
[host port & [opts]]
|
||||||
|
(let [conn (atom nil)
|
||||||
|
nl "\n"]
|
||||||
|
{:enabled? true
|
||||||
|
:async? false
|
||||||
|
:min-level nil
|
||||||
|
:rate-limit nil
|
||||||
|
:output-fn :inherit
|
||||||
|
:fn
|
||||||
|
(fn [data]
|
||||||
|
(try
|
||||||
|
(let [[sock out] (swap! conn
|
||||||
|
(fn [conn]
|
||||||
|
(or (and conn (connection-ok? conn) conn)
|
||||||
|
(connect host port))))]
|
||||||
|
(locking sock
|
||||||
|
(data->json-stream data out (select-keys opts [:pr-stacktrace]))
|
||||||
|
;; logstash tcp input plugin: "each event is assumed to be one line of text".
|
||||||
|
(.write ^java.io.Writer out nl)))
|
||||||
|
(catch java.io.IOException _
|
||||||
|
nil)))}))
|
Loading…
Reference in New Issue