-
Notifications
You must be signed in to change notification settings - Fork 0
/
hookd.clj
112 lines (99 loc) · 4.54 KB
/
hookd.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
(ns com.github.ivarref.hookd
(:require [clojure.spec.alpha :as s])
(:import (com.github.ivarref.hookd JavaAgent)
(java.util.function Consumer)))
(s/def :hookd/pre? boolean?)
(s/def :hookd/post? boolean?)
(s/def :hookd/error? boolean?)
(s/def :hookd/error #(instance? Throwable %))
(s/def :hookd/start int?)
(s/def :hookd/stop int?)
(s/def :hookd/spent-ns int?)
(s/def :hookd/spent-ms int?)
(s/def :hookd/args vector?)
(s/def :hookd/id string?)
(s/def :hookd/ctx-map
(s/and
(s/keys
:req-un [:hookd/pre?
:hookd/post?
:hookd/start
:hookd/args
:hookd/id
:hookd/this])
(s/keys
:opt-un [:hookd/stop
:hookd/error?
:hookd/error
:hookd/result
:hookd/spent-ns
:hookd/spent-ms])))
(defn produce-wiretap-ctx-map
[java-str-map]
{:post [(s/valid? :hookd/ctx-map %)]}
(let [{:keys [start stop] :as m} (reduce-kv (fn [o k v]
(assoc o (keyword k)
(cond (= "args" k)
(into [] v)
:else
v)))
(sorted-map)
(into {} java-str-map))]
(cond-> m
stop (assoc :spent-ns (- stop start))
stop (assoc :spent-ms (long (/ (- stop start)
1000000))))))
(comment
(produce-wiretap-ctx-map {"pre?" true
"post?" false
"start" 10
"args" []
"id" "janei"
"this" nil
"error" (Exception. "boom")
"stop" 20}))
(defn uninstall!
([]
(JavaAgent/clearAll))
([className]
(JavaAgent/clear className)))
(defn install! [f classes-and-methods]
(doseq [[className methodName] classes-and-methods]
(JavaAgent/addPrePost
className
methodName
(reify Consumer
(accept [_ java-map]
(f (produce-wiretap-ctx-map java-map)))))))
(defn install-post!
"Like `install!` but ensures that `f` is only called **post** invocation.
The following contextual data is will **always** be present in the map passed
to `f`:
| Key | Value |
| ----------- | ---------------------------------------------------------------- |
| `:id` | Uniquely identifies the call. Same value for pre and post calls. |
| `:args` | The vector of args |
| `:start` | Nanoseconds since some fixed but arbitrary origin time. |
| `:post?` | `true` |
| `:stop` | Nanoseconds since some fixed but arbitrary origin time. |
| `:spent-ns` | Number of nanoseconds elapsed. |
| `:spent-ms` | Number of milliseconds elapsed. Coerced to long. |
| `:result` | The result of applying the value of `:function` to `:args`. |
| `:error` | Any exception caught during computation of the result. |
| `:error?` | True if an exception was thrown. |
| `:this` | The object `this`. Will be nil for static methods. |"
[f classes-and-methods]
(install! #(when (:post? %) (f %)) classes-and-methods))
(defn install-pre!
"Like `install!` but ensures that `f` is only called **pre** invocation.
The following contextual data is will **always** be present in the map passed
to `f`:
| Key | Value |
| ----------- | ---------------------------------------------------------------- |
| `:id` | Uniquely identifies the call. Same value for pre and post calls. |
| `:args` | The vector of args |
| `:start` | Nanoseconds since some fixed but arbitrary origin time. |
| `:pre?` | `true` |
| `:this` | The object `this`. Will be nil for static methods. |"
[f classes-and-methods]
(install! #(when (:pre? %) (f %)) classes-and-methods))