mirror of
https://github.com/taoensso/telemere.git
synced 2026-01-01 15:28:25 +00:00
[new] Add postal (email) handler
This commit is contained in:
parent
1d4cdb8a3c
commit
2ba23ee7f7
3 changed files with 145 additions and 6 deletions
11
project.clj
11
project.clj
|
|
@ -46,11 +46,12 @@
|
||||||
[org.clojure/tools.logging "1.3.0"]
|
[org.clojure/tools.logging "1.3.0"]
|
||||||
[org.slf4j/slf4j-api "2.0.13"]
|
[org.slf4j/slf4j-api "2.0.13"]
|
||||||
[com.taoensso/slf4j-telemere "1.0.0-beta3"]
|
[com.taoensso/slf4j-telemere "1.0.0-beta3"]
|
||||||
;; [org.slf4j/slf4j-simple "2.0.13"]
|
#_[org.slf4j/slf4j-simple "2.0.13"]
|
||||||
;; [org.slf4j/slf4j-nop "2.0.13"]
|
#_[org.slf4j/slf4j-nop "2.0.13"]
|
||||||
[io.opentelemetry/opentelemetry-api "1.37.0"]
|
[com.draines/postal "2.0.5"]
|
||||||
[io.opentelemetry/opentelemetry-sdk-extension-autoconfigure "1.37.0"]
|
[io.opentelemetry/opentelemetry-api "1.37.0"]
|
||||||
[io.opentelemetry/opentelemetry-exporter-otlp "1.37.0"]]
|
#_[io.opentelemetry/opentelemetry-sdk-extension-autoconfigure "1.37.0"]
|
||||||
|
#_[io.opentelemetry/opentelemetry-exporter-otlp "1.37.0"]]
|
||||||
|
|
||||||
:plugins
|
:plugins
|
||||||
[[lein-pprint "1.3.2"]
|
[[lein-pprint "1.3.2"]
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@
|
||||||
(enc/assert-min-encore-version [3 105 1])
|
(enc/assert-min-encore-version [3 105 1])
|
||||||
|
|
||||||
;;;; TODO
|
;;;; TODO
|
||||||
;; - Add email handler
|
|
||||||
;; - Native OpenTelemetry traces and spans
|
;; - Native OpenTelemetry traces and spans
|
||||||
;; - Update Tufte (signal API, config API, signal keys, etc.)
|
;; - Update Tufte (signal API, config API, signal keys, etc.)
|
||||||
;; - Update Timbre (signal API, config API, signal keys, backport improvements)
|
;; - Update Timbre (signal API, config API, signal keys, backport improvements)
|
||||||
|
|
|
||||||
139
src/taoensso/telemere/postal.clj
Normal file
139
src/taoensso/telemere/postal.clj
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
(ns taoensso.telemere.postal
|
||||||
|
"Email handler using `postal`,
|
||||||
|
Ref. <https://github.com/drewr/postal>."
|
||||||
|
(:require
|
||||||
|
[taoensso.encore :as enc :refer [have have?]]
|
||||||
|
[taoensso.telemere.utils :as utils]
|
||||||
|
[postal.core :as postal]))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(require '[taoensso.telemere :as tel])
|
||||||
|
(remove-ns 'taoensso.telemere.postal)
|
||||||
|
(:api (enc/interns-overview)))
|
||||||
|
|
||||||
|
;;;; Implementation
|
||||||
|
|
||||||
|
(defn format-signal->subject-fn
|
||||||
|
"Experimental, subject to change.
|
||||||
|
Returns a (fn format [signal]) that:
|
||||||
|
- Takes a Telemere signal.
|
||||||
|
- Returns a formatted email subject like:
|
||||||
|
\"INFO EVENT :taoensso.telemere.postal/ev-id1 - msg\""
|
||||||
|
([] (format-signal->subject-fn nil))
|
||||||
|
([{:keys [max-len subject-signal-key]
|
||||||
|
:or
|
||||||
|
{max-len 128
|
||||||
|
subject-signal-key :postal/subject}}]
|
||||||
|
|
||||||
|
(fn format-signal->subject [signal]
|
||||||
|
(or
|
||||||
|
(get signal subject-signal-key) ; Custom subject
|
||||||
|
|
||||||
|
;; Simplified `format-signal->prelude-fn`
|
||||||
|
(let [{:keys [level kind #_ns id msg_]} signal
|
||||||
|
sb (enc/str-builder)
|
||||||
|
s+spc (enc/sb-appender sb " ")]
|
||||||
|
|
||||||
|
(when level (s+spc (utils/format-level level)))
|
||||||
|
(when kind (s+spc (utils/upper-qn kind)))
|
||||||
|
(when id (s+spc (utils/format-id nil id)))
|
||||||
|
(when-let [msg (force msg_)] (s+spc "- " msg))
|
||||||
|
|
||||||
|
(enc/get-substr-by-len (str sb) 0 max-len))))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
((format-signal->subject-fn)
|
||||||
|
(tel/with-signal (tel/event! ::ev-id1 #_{:postal/subject "My subject"}))))
|
||||||
|
|
||||||
|
;;;; Handler
|
||||||
|
|
||||||
|
(defn handler:postal
|
||||||
|
"Experimental, subject to change. Feedback welcome!
|
||||||
|
|
||||||
|
Needs `postal`,
|
||||||
|
Ref. <https://github.com/drewr/postal>.
|
||||||
|
|
||||||
|
Returns a (fn handler [signal]) that:
|
||||||
|
- Takes a Telemere signal.
|
||||||
|
- Sends an email with formatted signal content to the configured recipient.
|
||||||
|
|
||||||
|
Useful for emailing important alerts to admins, etc.
|
||||||
|
|
||||||
|
NB can incur financial costs!!
|
||||||
|
See tips section re: protecting against unexpected costs.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
`:postal/conn-opts` - Map of connection opts provided to `postal`
|
||||||
|
Examples:
|
||||||
|
{:host \"mail.isp.net\", :user \"jsmith\", :pass \"a-secret\"},
|
||||||
|
{:host \"smtp.gmail.com\", :user \"jsmith@gmail.com\", :pass \"a-secret\" :port 587 :tls true},
|
||||||
|
{:host \"email-smtp.us-east-1.amazonaws.com\", :port 587, :tls true
|
||||||
|
:user \"AKIAIDTP........\" :pass \"AikCFhx1P.......\"}
|
||||||
|
|
||||||
|
`:postal/msg-opts` - Map of message options
|
||||||
|
Examples:
|
||||||
|
{:from \"foo@example.com\", :to \"bar@example.com\"},
|
||||||
|
{:from \"Alice <foo@example.com\", :to \"Bob <bar@example.com>\"},
|
||||||
|
{:from \"no-reply@example.com\", :to [\"first-responders@example.com\",
|
||||||
|
\"devops@example.com\"],
|
||||||
|
:cc \"engineering@example.com\"
|
||||||
|
:X-MyHeader \"A custom header\"}
|
||||||
|
|
||||||
|
`:format-signal-fn` - (fn [signal]) => output, see `help:signal-formatters`
|
||||||
|
`:format-signal->subject-fn` - (fn [signal]) => email subject string
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
|
||||||
|
- Sending emails can incur financial costs!
|
||||||
|
Use appropriate dispatch filtering options when calling `add-handler!` to prevent
|
||||||
|
handler from sending unnecessary emails!
|
||||||
|
|
||||||
|
At least ALWAYS set an appropriate `:rate-limit` option, e.g.:
|
||||||
|
(add-handler! :my-postal-handler (handler:postal {<my-handler-opts})
|
||||||
|
{:rate-limit {\"Max 1 per min\" [1 (enc/msecs :mins 1)]
|
||||||
|
\"Max 3 per 15 mins\" [3 (enc/msecs :mins 15)]
|
||||||
|
\"Max 5 per hour\" [5 (enc/msecs :hours 1)]}, ...}), etc.
|
||||||
|
|
||||||
|
- Sending emails is slow!
|
||||||
|
Use appropriate async dispatch options when calling `add-handler!` to prevent
|
||||||
|
handler from blocking signal creator calls, e.g.:
|
||||||
|
(add-handler! :my-postal-handler (handler:postal {<my-handler-opts>})
|
||||||
|
{:async {:mode :dropping, :buffer-size 128, :n-threads 4} ...}), etc.
|
||||||
|
|
||||||
|
- Ref. <https://github.com/drewr/postal> for more info on `postal` options."
|
||||||
|
|
||||||
|
([] (handler:postal nil))
|
||||||
|
([{:keys
|
||||||
|
[postal/conn-opts
|
||||||
|
postal/msg-opts
|
||||||
|
format-signal-fn
|
||||||
|
format-signal->subject-fn]
|
||||||
|
|
||||||
|
:or
|
||||||
|
{format-signal-fn (utils/format-signal->str-fn)
|
||||||
|
format-signal->subject-fn (format-signal->subject-fn)}}]
|
||||||
|
|
||||||
|
(when-not conn-opts (throw (ex-info "No `:postal/conn-opts` was provided" {})))
|
||||||
|
(when-not msg-opts (throw (ex-info "No `:postal/msg-opts` was provided" {})))
|
||||||
|
|
||||||
|
(let []
|
||||||
|
(defn a-handler:postal
|
||||||
|
([]) ; Shut down (no-op)
|
||||||
|
([signal]
|
||||||
|
(let [msg
|
||||||
|
(assoc msg-opts
|
||||||
|
:subject (format-signal->subject-fn)
|
||||||
|
:body
|
||||||
|
[{:type "text/plain; charset=utf-8"
|
||||||
|
:content (format-signal-fn signal)}])
|
||||||
|
|
||||||
|
[result ex]
|
||||||
|
(try
|
||||||
|
[(postal/send-message conn-opts msg) nil]
|
||||||
|
(catch Exception ex [nil ex]))
|
||||||
|
|
||||||
|
success? (= (get result :code) 0)]
|
||||||
|
|
||||||
|
(when-not success?
|
||||||
|
(throw (ex-info "Failed to send email" result ex)))))))))
|
||||||
Loading…
Reference in a new issue