Skip to content

Commit

Permalink
Merge pull request #11 from Yleisradio/feature/async-wrap
Browse files Browse the repository at this point in the history
async implementation for wrap-apm-transaction
  • Loading branch information
AriPaaWun authored Oct 12, 2023
2 parents f83c3ed + 0362564 commit 77e9598
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 26 deletions.
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject clojure-elastic-apm "0.8.0"
(defproject clojure-elastic-apm "0.9.0"
:description "Clojure wrapper for Elastic APM Java Agent"
:url "https://github.com/Yleisradio/clojure-elastic-apm"
:license {:name "Eclipse Public License"
Expand Down
75 changes: 51 additions & 24 deletions src/clojure_elastic_apm/ring.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(ns clojure-elastic-apm.ring
(:require [clojure-elastic-apm.core :refer [with-apm-transaction
type-request]]))
(:require
[clojure-elastic-apm.core :refer [type-request with-apm-transaction]]))

(defn match-uri [pattern uri]
(let [pattern-segs (clojure.string/split pattern #"/")
Expand All @@ -20,28 +20,55 @@
(clojure.string/join "/" matches)
matched?)))))

(defn match-patterns [patterns uri]
(->> patterns
(map #(match-uri % uri))
(drop-while false?)
(first)))

(defn wrap-apm-transaction
([handler]
(fn [{:keys [request-method uri headers] :as request}]
(let [tx-name (str (.toUpperCase (name request-method)) " " uri)
traceparent (get-in headers ["traceparent"])]
(with-apm-transaction [tx {:name tx-name :type type-request :traceparent traceparent}]
(let [{:keys [status] :as response} (handler (assoc request :clojure-elastic-apm/transaction tx))]
(when status
(.setResult tx (str "HTTP " status)))
response)))))
(fn
([{:keys [request-method uri headers] :as request}]
(let [tx-name (str (.toUpperCase (name request-method)) " " uri)
traceparent (get-in headers ["traceparent"])]
(with-apm-transaction [tx {:name tx-name :type type-request :traceparent traceparent}]
(let [{:keys [status] :as response} (handler (assoc request :clojure-elastic-apm/transaction tx))]
(when status
(.setResult tx (str "HTTP " status)))
response))))
([{:keys [request-method uri headers] :as request} respond raise]
(let [tx-name (str (.toUpperCase (name request-method)) " " uri)
traceparent (get-in headers ["traceparent"])]
(with-apm-transaction [tx {:name tx-name :type type-request :traceparent traceparent}]
(let [req (assoc request :clojure-elastic-apm/transaction tx)]
(handler req (fn [{:keys [status] :as response}]
(when status
(.setResult tx (str "HTTP " status)))
(respond response)) raise)))))))
([handler patterns]
(fn [{:keys [request-method uri headers] :as request}]
(let [matched (->> patterns
(map #(match-uri % uri))
(drop-while false?)
(first))
tx-name (str (.toUpperCase (name request-method)) " " matched)
traceparent (get-in headers ["traceparent"])]
(if matched
(with-apm-transaction [tx {:name tx-name :type type-request :traceparent traceparent}]
(let [{:keys [status] :as response} (handler (assoc request :clojure-elastic-apm/transaction tx))]
(when status
(.setResult tx (str "HTTP " status)))
response))
(handler request))))))
(fn
([{:keys [request-method uri headers] :as request}]
(let [matched (match-patterns patterns uri)
tx-name (str (.toUpperCase (name request-method)) " " matched)
traceparent (get-in headers ["traceparent"])]
(if matched
(with-apm-transaction [tx {:name tx-name :type type-request :traceparent traceparent}]
(let [{:keys [status] :as response} (handler (assoc request :clojure-elastic-apm/transaction tx))]
(when status
(.setResult tx (str "HTTP " status)))
response))
(handler request))))
([{:keys [request-method uri headers] :as request} respond raise]

(let [matched (match-patterns patterns uri)
tx-name (str (.toUpperCase (name request-method)) " " matched)
traceparent (get-in headers ["traceparent"])]
(if matched
(with-apm-transaction [tx {:name tx-name :type type-request :traceparent traceparent}]
(let [req (assoc request :clojure-elastic-apm/transaction tx)]
(handler req (fn [{:keys [status] :as response}]
(when status
(.setResult tx (str "HTTP " status)))
(respond response)) raise)))
(handler respond raise)))))))
2 changes: 1 addition & 1 deletion test/clojure_elastic_apm/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
(let [span-details (es-find-first-document (str "(processor.event:span%20AND%20span.id:" @span-id ")"))]
(is (= "TestExitSpan" (get-in span-details [:span :name])))
(is (= "ext" (get-in span-details [:span :type])))
(is (= "undefined" (get-in span-details [:span :subtype])))
(is (= "undefined subtype" (get-in span-details [:span :subtype])))
(is (nil? (get-in span-details [:span :action]))))))

(deftest with-apm-span-no-activation-test
Expand Down
40 changes: 40 additions & 0 deletions test/clojure_elastic_apm/ring_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,46 @@
(is (= "GET /foo/bar" (get-in tx-details [:transaction :name])))
(is (nil? (:parent tx-details))))))

(deftest wrap-apm-transaction-with-pattern-async-test
(let [transaction-id (atom nil)
request {:request-method :get, :uri "/foo/bar"}
response {:status 200 :body "Ok."}
respond (fn [r]
(is (= (:status r) 200))
r)
handler (fn [request respond _]
(is (not= "" (.getId (apm/current-apm-transaction))) "transaction should've been activated for the duration of the request")
(is (= (.getId (:clojure-elastic-apm/transaction request)) (.getId (apm/current-apm-transaction))) "transaction should've been activated for the duration of the request")
(reset! transaction-id (.getId (:clojure-elastic-apm/transaction request)))
(respond response))
wrapped-handler (apm-ring/wrap-apm-transaction handler ["/*/*"])]
(is (= (wrapped-handler request respond nil) response))
(let [tx-details (es-find-first-document (str "(processor.event:transaction%20AND%20transaction.id:" @transaction-id ")"))]
(is (= apm/type-request (get-in tx-details [:transaction :type])))
(is (= "HTTP 200" (get-in tx-details [:transaction :result])))
(is (= "GET /foo/bar" (get-in tx-details [:transaction :name])))
(is (nil? (:parent tx-details))))))

(deftest wrap-apm-transaction-async-test
(let [transaction-id (atom nil)
request {:request-method :get, :uri "/foo/bar"}
response {:status 200 :body "Ok."}
respond (fn [r]
(is (= (:status r) 200))
r)
handler (fn [request respond _]
(is (not= "" (.getId (apm/current-apm-transaction))) "transaction should've been activated for the duration of the request")
(is (= (.getId (:clojure-elastic-apm/transaction request)) (.getId (apm/current-apm-transaction))) "transaction should've been activated for the duration of the request")
(reset! transaction-id (.getId (:clojure-elastic-apm/transaction request)))
(respond response))
wrapped-handler (apm-ring/wrap-apm-transaction handler)]
(is (= (wrapped-handler request respond nil) response))
(let [tx-details (es-find-first-document (str "(processor.event:transaction%20AND%20transaction.id:" @transaction-id ")"))]
(is (= apm/type-request (get-in tx-details [:transaction :type])))
(is (= "HTTP 200" (get-in tx-details [:transaction :result])))
(is (= "GET /foo/bar" (get-in tx-details [:transaction :name])))
(is (nil? (:parent tx-details))))))

(deftest wrap-apm-remote-transaction-test
(let [transaction-id (atom nil)
parent-id "3574dfeefa12d57e"
Expand Down

0 comments on commit 77e9598

Please sign in to comment.