I'm discovering/learning Clojure with Riemann and I have written the following code to aggregate my CPU metrics per host :
(streams
(by [:host]
smap (aggregate-cpu-metrics "user" folds/mean)
smap (aggregate-cpu-metrics "nice" folds/mean)
smap (aggregate-cpu-metrics "system" folds/mean)
smap (aggregate-cpu-metrics "idle" folds/mean)
smap (aggregate-cpu-metrics "wait" folds/mean)
smap (aggregate-cpu-metrics "interrupt" folds/mean)
smap (aggregate-cpu-metrics "softirq" folds/mean)
smap (aggregate-cpu-metrics "steal" folds/mean)))
(defn aggregate-cpu-metrics
[name, aggr]
(where (service (re-pattern (str "cpu-[0-9]+ " name)))
(coalesce 10
(smap aggr
(with :service (str "cpu-average " name) reinject)))))
To explain the code a little bit, I'm receiving events like these :
And my goal is to calculate the average and to reinject this event in riemann :
It's working, that's not the problem. But as you can see in lines 3 to 10, there is a lot of duplicate code here. I'm looking for a way to refactor this code, but I'm stuck.
I would like to define a vector with my metrics names :
(def cpu-metrics ["user", "nice", "system", "idle", "interrupt", "softirq", "steal"])
...and to use it to call smap(aggregate-cpu-metrics...
But I don't know how to do that. I've tried map or doseq, but without any success.
How would you do it ?
(Update / Solution) :
Here is my refactored version, after reading Arthur's answer.
(streams
(where
(service #"^cpu-[0-9]+ ")
(adjust
[:service #(clojure.string/replace % #"^cpu-[0-9]+" "cpu-average")]
(by [:host :service]
(fixed-time-window 10 (smap folds/mean reinject))))))
I might turn the function somewhat inside out by first extracting the name and creating the new service name, then using the service name and the host to split the event streams.
Something like this:
(streams
(where (service #"cpu-[0-9]+ ")
(adjust [:service (fn [service]
(str "cpu-average "
(second (clojure.string/split service #"cpu-[0-9]+ "))))]
(by [:host :service] ... )))
This has a side effect of allowing any cpu service that gets reported to show up in the stats without changing your monitoring code. If you don't want this you can add another where
to explicitly accept the ones you want. I don't have a setup here to test this so please edit if the code is broken :-)