Skip to content

Support async middleware for ring async servers #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 38 additions & 15 deletions metrics-clojure-ring/src/metrics/ring/expose.clj
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,31 @@
(into {} (map #(render-metric % unit-context) (->> registry
(all-metrics)
(filter-metrics filter))))))

(defn serve-metrics
([request]
(serve-metrics request default-registry))
([request registry]
(serve-metrics request registry false))
(defn- serve-metrics*
([request registry {:keys [pretty-print? filter rate-unit duration-unit]}]
(let [metrics-map (render-metrics registry filter (unit/build-options rate-unit duration-unit))
json (generate-string metrics-map {:pretty pretty-print?})]
(-> (response json)
(header "Content-Type" "application/json")))))

(defn serve-metrics
([request]
(let [^String filter (get-in request [:params :filter])]
(serve-metrics* request default-registry {:pretty-print? false
:filter filter
:rate-unit TimeUnit/SECONDS
:duration-unit TimeUnit/NANOSECONDS})))
([request respond raise]
(let [^String filter (get-in request [:params :filter])]
(try
(respond
(serve-metrics* request default-registry {:pretty-print? false
:filter filter
:rate-unit TimeUnit/SECONDS
:duration-unit TimeUnit/NANOSECONDS}))
(catch Exception e (raise e))))))


(defn expose-metrics-as-json
([handler]
(expose-metrics-as-json handler "/metrics"))
Expand All @@ -134,12 +147,22 @@
([handler uri registry]
(expose-metrics-as-json handler uri registry {:pretty-print? false}))
([handler uri registry opts]
(fn [request]
(let [^String request-uri (:uri request)
^String filter (get-in request [:params :filter])]
(if (or (.startsWith request-uri (sanitize-uri uri))
(= request-uri uri))
(serve-metrics request registry (merge {:filter filter
:rate-unit TimeUnit/SECONDS
:duration-unit TimeUnit/NANOSECONDS} opts))
(handler request))))))
(fn
([request]
(let [^String request-uri (:uri request)
^String filter (get-in request [:params :filter])]
(if (or (.startsWith request-uri (sanitize-uri uri))
(= request-uri uri))
(serve-metrics* request registry (merge {:filter filter
:rate-unit TimeUnit/SECONDS
:duration-unit TimeUnit/NANOSECONDS} opts))
(handler request))))
([request respond raise]
(let [^String request-uri (:uri request)
^String filter (get-in request [:params :filter])]
(if (or (.startsWith request-uri (sanitize-uri uri))
(= request-uri uri))
(serve-metrics* request registry (merge {:filter filter
:rate-unit TimeUnit/SECONDS
:duration-unit TimeUnit/NANOSECONDS} opts))
(handler request respond raise)))))))
71 changes: 44 additions & 27 deletions metrics-clojure-ring/src/metrics/ring/instrument.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
(:require [metrics.core :refer [default-registry]]
[metrics.counters :refer (counter inc! dec!)]
[metrics.meters :refer (meter mark!)]
[metrics.timers :refer (timer time!)]
[metrics.timers :refer (timer time! start stop)]
[clojure.string :as string])
(:import [com.codahale.metrics MetricRegistry]))

Expand Down Expand Up @@ -51,23 +51,31 @@
:times times
:request-methods request-methods}))

(defn handle-request [handler metrics request]
(let [{:keys [active-requests requests responses
schemes statuses times request-methods]} metrics]
(inc! active-requests)
(try
(let [request-method (:request-method request)
request-scheme (:scheme request)]
(mark! requests)
(mark-in! request-methods request-method)
(mark-in! schemes request-scheme)
(let [resp (time! (times request-method (times :other))
(handler request))
^{:tag "int"} status-code (or (:status resp) 404)]
(mark! responses)
(mark-in! statuses (int (/ status-code 100)))
resp))
(finally (dec! active-requests)))))
(defn- time-request [thunk metrics request]
(let [{:keys [active-requests requests responses
schemes statuses times request-methods]} metrics]
(inc! active-requests)
(try
(let [request-method (:request-method request)
request-scheme (:scheme request)]
(mark! requests)
(mark-in! request-methods request-method)
(mark-in! schemes request-scheme)
(let [resp (time! (times request-method (times :other))
(thunk))
^{:tag "int"} status-code (or (:status resp) 404)]
(mark! responses)
(mark-in! statuses (int (/ status-code 100)))
resp))
(finally (dec! active-requests)))))

(defn handle-request
([handler metrics request]
(time-request #(handler request) metrics request))
([handler metrics request respond raise]
(try
(time-request #(handler request respond raise) metrics request)
(catch Exception e (raise e)))))

(defn metrics-for
[metrics-db prefix registry]
Expand All @@ -87,10 +95,15 @@
([handler]
(instrument handler default-registry))
([handler ^MetricRegistry reg]
(fn [request]
(handle-request handler
(ring-metrics reg {:prefix []})
request))))
(fn
([request]
(handle-request handler
(ring-metrics reg {:prefix []})
request))
([request respond raise]
(handle-request handler
(ring-metrics reg {:prefix []})
request respond raise)))))

(defn instrument-by
"Instrument a ring handler using the metrics returned by the `metrics-for`
Expand All @@ -107,8 +120,12 @@
(instrument-by handler default-registry metrics-prefix))
([handler ^MetricRegistry reg metrics-prefix]
(let [metrics-db (atom {})]
(fn [request]
(let [prefix (metrics-prefix request)]
(handle-request handler
(metrics-for metrics-db prefix reg)
request))))))
(fn
([request]
(handle-request handler
(metrics-for metrics-db (metrics-prefix request) reg)
request))
([request respond raise]
(handle-request handler
(metrics-for metrics-db (metrics-prefix request) reg)
request respond raise))))))
32 changes: 27 additions & 5 deletions metrics-clojure-ring/test/metrics/test/instrument_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
(def dummy-handler (fn [req]
req))

(def dummy-handler-async (fn [req respond raise]
(respond req)))

(defn tracked? [^MetricRegistry reg metric]
(->> (.getMetrics reg)
(map (fn [[k v]] k))
Expand Down Expand Up @@ -54,6 +57,15 @@

(app (mock/request :get "/yolo" {}))

(doseq [m expected-metrics]
(is (tracked? reg m)))))

(testing "instrument without custom prefix and async handler"
(let [reg (MetricRegistry. )
app (instrument dummy-handler-async reg)]

(app (mock/request :get "/yolo" {}) identity identity)

(doseq [m expected-metrics]
(is (tracked? reg m))))))

Expand All @@ -64,10 +76,20 @@
(uri-prefix (mock/request :get "/foo/bar/baz")))))

(deftest test-instrument-by-uri-prefix
(let [reg (MetricRegistry.)
app (instrument-by dummy-handler reg uri-prefix)]
(testing "instrument by uri prefix"
(let [reg (MetricRegistry.)
app (instrument-by dummy-handler reg uri-prefix)]

(app (mock/request :get "/yolo" {}))
(app (mock/request :get "/yolo" {}))

(doseq [m expected-metrics]
(is (tracked? reg (str "yolo." m))))))

(doseq [m expected-metrics]
(is (tracked? reg (str "yolo." m))))))
(testing "instrument by uri prefix and async handler"
(let [reg (MetricRegistry.)
app (instrument-by dummy-handler-async reg uri-prefix)]

(app (mock/request :get "/yolo" {}) identity identity)

(doseq [m expected-metrics]
(is (tracked? reg (str "yolo." m)))))))