-
Notifications
You must be signed in to change notification settings - Fork 3
/
fields.clj
108 lines (90 loc) · 3.57 KB
/
fields.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
(ns clj-honeycomb.fields
"Fields are the data that are sent as part of an event. Events are just maps,
where the keys are either keywords or strings and the values may be anything.
Since clj-honeycomb wraps the Java Honeycomb SDK, we must transform some of
the values from Clojure types into more Java-friendly types where possible.
Only the ->ValueSupplier function in this namespace should be considered part
of the public API for clj-honeycomb."
(:import (clojure.lang IBlockingDeref
IDeref
IPending
Repeat)
(io.honeycomb.libhoney ValueSupplier)))
(defn ->ValueSupplier
"Convert a function to a ValueSupplier so that it can act as a dynamic field."
[f & args]
(reify ValueSupplier
(supply [_this]
(apply f args))))
(defn- stringify-key
"Field names must be strings but Clojure maps typically have keywords for keys
so here we coalesce strings and keywords to strings."
[k]
(cond (keyword? k) (if-let [n (namespace k)]
(str n "/" (name k))
(name k))
(string? k) k
:else (throw (IllegalArgumentException. "Field names must be keywords or strings"))))
(defn- realize-value
"Clojure has several types which are not trivially transformed into JSON in
the libhoney-java library. Things we're trying to avoid here are:
- Blocking on undelivered promises
- Serializing infinite/lazy sequences
- Sending Clojure ratios as clojure.lang.Ratio rather than a float"
[v]
(cond (instance? IBlockingDeref v)
(realize-value (deref v 0 nil))
(instance? IDeref v)
(realize-value (deref v))
(instance? Repeat v)
(mapv realize-value (take 1000 v))
(instance? ValueSupplier v)
(realize-value (.supply ^ValueSupplier v))
(and (instance? IPending v) (sequential? v))
(mapv realize-value (take 1000 v))
(sequential? v)
(mapv realize-value v)
(map? v)
(->> v
(map (fn [[k v]]
[(stringify-key k) (realize-value v)]))
(into {}))
(set? v)
(mapv realize-value v)
(ratio? v)
(float v)
(keyword? v)
(str v)
:else v))
(defn- maybe-value-supplier
"Convert anything that is delay-like into a ValueSupplier for the Honeycomb
Java SDK. This delays the evaluation of these things until the point of event
creation."
[x]
(if (or (instance? IBlockingDeref x)
(instance? IDeref x)
(instance? IPending x)
(instance? Repeat x))
(->ValueSupplier realize-value x)
x))
(defn separate
"Given a map, stringify the keys and convert any values that should be
ValueSuppliers into them. Then return a tuple where the first item is the
submap of the input where no values are ValueSuppliers and the second item is
the submap of the input where all the values are ValueSuppliers."
[m]
(let [m (->> m
(map (fn [[k v]]
[(stringify-key k) (maybe-value-supplier v)]))
(into {}))]
[(->> m (remove (comp (partial instance? ValueSupplier) val)) (into {}))
(->> m (filter (comp (partial instance? ValueSupplier) val)) (into {}))]))
(defn realize
"Given a map, stringify the keys and realize all the values. This must be done
at the last minute before sending an event so that any dynamic/delayed fields
are computed as late as possible."
[m]
(->> m
(map (fn [[k v]]
[(stringify-key k) (realize-value v)]))
(into {})))