-
Notifications
You must be signed in to change notification settings - Fork 1
/
timed_metric.clj
189 lines (164 loc) · 6.49 KB
/
timed_metric.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
(ns active.clojure.logger.timed-metric
"Common functionality to ease logging of timed metrics."
(:require [active.clojure.logger.event :as event]
[active.clojure.logger.metric :as metric]
[active.clojure.logger.time :as time]
[active.clojure.monad :as monad]
[active.clojure.record :refer [define-record-type]]))
;; Simple timing
(define-record-type TimerName
(make-timer-name namespace metric map)
timer-name?
[^{:doc "String"}
namespace timer-name-namespace
^{:doc "Name of the metric that will be logged."}
metric timer-name-metric
^{:doc "Map with log context info, keywords to strings."}
map timer-name-map])
(define-record-type LogTimedMetric
^{:doc "Monadic command for logging a timed metric."}
log-timed-metric
log-timed-metric?
[timer-name log-timed-metric-timer-name
timer log-timed-metric-timer])
(defmacro log-time-metric!
[?log-metric-fn! ?body]
`(let [s# (time/get-milli-time!)
r# ~?body
e# (time/get-milli-time!)]
(~?log-metric-fn! (- e# s#))
r#))
(declare start-metric-timer-1)
(declare stop-and-log-metric-timer-1)
(defn logging-timing*
([origin metric m]
(logging-timing* origin metric {} m))
([origin metric more m]
(monad/monadic
;; TimerName: ns metric map
[timer-name (start-metric-timer-1 origin metric more)
r m
_ (stop-and-log-metric-timer-1 timer-name)]
(monad/return r))))
(defmacro logging-timing
"A monadic command that executes `m` and returns its result, and
also makes a debug log message about the time it took to execute
it, where the message contains the given `metric` as name and
help string and `more`."
([?metric ?m]
`(logging-timing* ~(str *ns*) ~?metric ~?m))
([?metric ?more ?m]
`(logging-timing* ~(str *ns*) ~?metric ~?more ~?m)))
;; More sophisticated API
(defn start-metric-timer-1
"Starts a metric timer, identified by `ns`, `metric` and `more`.
`more` is a map that is merged with the log context that's already active,
if present."
[ns metric more]
(monad/monadic
[time time/get-elapsed-time]
(let [name (make-timer-name ns metric more)])
(monad/update-state-component! ::timers
(fn [timers]
(assoc timers name time)))
(monad/return name)))
(defn cancel-metric-timer-1
"Cancels a metric timer, identified by `timer-name`."
[timer-name]
(monad/update-state-component! ::timers
(fn [timers]
(dissoc timers timer-name))))
(defn stop-metric-timer
[timer-name]
(monad/monadic
[end-time time/get-elapsed-time]
[timers (monad/get-state-component ::timers)]
(let [start-time (get timers timer-name)])
(if start-time
(monad/monadic
(monad/update-state-component! ::timers
(fn [timers]
(dissoc timers timer-name)))
(monad/return (- end-time start-time)))
(monad/monadic
(event/log-event* :error (event/log-msg "metric timer has not been started"
(timer-name-namespace timer-name) (timer-name-metric timer-name) (timer-name-map timer-name))
(timer-name-map timer-name))
(monad/return nil)))))
(defn stop-and-log-metric-timer-1
"Stops a metric timer and logs the time as a metric."
[timer-name]
(monad/monadic
[timer (stop-metric-timer timer-name)]
(if timer
(monad/monadic
(log-timed-metric timer-name timer)
(monad/return timer))
(monad/return nil))))
(defmacro start-metric-timer
"Starts a metric timer, identified by `metric` and `more`.
`more` is a map that is merged with the log context that's already active,
if present.
Returns a timer-name object that can be fed to
[[cancel-metric-timer]] or [[stop-and-log-metric-timer]]."
[?metric & [?more]]
`(start-metric-timer-1 ~(str *ns*) ~?metric ~?more))
(defmacro cancel-metric-timer
"Cancels a metric timer. It can either be identified by a timer name
returned by [[start-metric-timer]] or by current namespace, metric, and
(optional) addtional context map."
([?timer-name-or-metric]
`(let [thing# ~?timer-name-or-metric]
(if (timer-name? thing#)
(cancel-metric-timer-1 thing#)
(cancel-metric-timer-1 (make-timer-name ~(str *ns*) thing# nil)))))
([?metric ?more]
`(cancel-metric-timer-1 (make-timer-name ~(str *ns*) ~?metric ~?more))))
(defmacro stop-and-log-metric-timer
"Stops a metric timer and logs the time. The timer can either be identified by a timer name
returned by [[start-metric-timer]] or by current namespace, metric, and
(optional) addtional context map.
Returns the elapsed time in milliseconds."
([?timer-name-or-metric]
`(let [thing# ~?timer-name-or-metric]
(if (timer-name? thing#)
(stop-and-log-metric-timer-1 thing#)
(stop-and-log-metric-timer-1 (make-timer-name ~(str *ns*) thing# nil)))))
([?metric ?more]
`(stop-and-log-metric-timer-1 (make-timer-name ~(str *ns*) ~?metric ~?more))))
(defn run-timed-metrics-as-gauges
[run-any env state m]
(cond
(log-timed-metric? m)
(let [timer-name (log-timed-metric-timer-name m)
timer (log-timed-metric-timer m)]
(run-any env state
;; use label as metric's name and help string
(metric/log-gauge-metric (timer-name-metric timer-name) (timer-name-map timer-name) (timer-name-metric timer-name) timer nil (timer-name-namespace timer-name))))
:else
monad/unknown-command))
(def timed-metrics-as-gauges-command-config
(monad/combine-monad-command-configs
(monad/make-monad-command-config
run-timed-metrics-as-gauges
{} {})
time/monad-command-config
metric/monad-command-config))
(defn run-timed-metrics-as-histograms
[run-any env state m]
(cond
(log-timed-metric? m)
(let [timer-name (log-timed-metric-timer-name m)
timer (log-timed-metric-timer m)]
(run-any env state
;; use label as metric's name and help string
(metric/log-histogram-metric (timer-name-metric timer-name) [] (timer-name-map timer-name) (timer-name-metric timer-name) timer nil (timer-name-namespace timer-name))))
:else
monad/unknown-command))
(def timed-metrics-as-histograms-command-config
(monad/combine-monad-command-configs
(monad/make-monad-command-config
run-timed-metrics-as-histograms
{} {})
time/monad-command-config
metric/monad-command-config))