Event count at certain time interval in riemann - clojure

I have to check the number of count appearing in an event at each interval of every 30 seconds. If the count is greater than 5 means, I need to trigger an email.
I am using the below code, but email didn't get triggered.
(let [userindex1 (default :ttl 300 (update-index (index)))]
(streams
prn
userindex1))
(streams
(where (and (service "system_log")
(not (expired? event)))
; fixed-time-window sends a vector of events out every 30 seconds
(fixed-time-window
30
; smap passes those events into a function
(smap
(fn [events]
;Calculate the no of count of events for failure
(let [numberofFailure (count (filter #(="IE" (:description %)) events))]
{:status "login failures"
:metric numberofFailure
:totalFail (boolean(numberofFailure > 5))}
(streams
prn
numberofFailure))))
;check if the variable status is true if condition satisfied then trigger an email
(let [email (mailer {:host "smtp.gmail.com"
:port 25
:user "aaaaa"
:pass "bbbbb"
:auth "true"
:subject (fn [events]
(clojure.string/join ", "
(map :service events)))
:from "abc#gmail.com"})]
(streams
(where (and (:status "login failures")
(:totalFail true))
(email "123#gmail.com")))))))
Where am I going wrong?

There are a couple of issues here. I'll try to address some of them, then post a minimal working example:
The first fn passed to smap should return an event. That event can be created with event or by assoc'ing into one of the received events. In your sample a plain map is created (which would not work, it's not a proper event), but that's even lost because then streams is called (which AFAIK should only be called at the top level). So instead of:
(smap
(fn [events]
(let [numberofFailure ...]
{:status "login failures"
:metric numberofFailure
:totalFail (boolean ...)}
(streams
prn
numberofFailure)))
...)
You should do something like:
(smap
(fn [events]
(let [numberofFailure ...]
(event {:status "login failures"
:metric numberofFailure
:totalFail (boolean ...)}))
...)
To calculate totalFail remember that you need to use prefix notation to call >, so it must be (> totalFail 5). And boolean is not needed, as > will already return a boolean.
I would initialize the mailer out of the top-level streams call, as an enclosing scope using let or with a def. But it should work as it is.
You should pass the last where as a children stream to smap, so it must be the second argument to smap. Let's recall the smap docs:
(smap f & children)
Streaming map. Calls children with (f event), whenever (f event) is non-nil.
Prefer this to (adjust f) and (combine f). Example:
(smap :metric prn) ; prints the metric of each event.
(smap #(assoc % :state "ok") index) ; Indexes each event with state "ok"
The last where should not be enclosed by streams, and the and sentence must work on the event, so it must be:
(where (and (= (:status event) "login failures")
(:total-fail event))
(email "123#gmail.com"))
The :subject fn for mailer should be passed as part of a second map, as explained in the mailer documentation
There's an open issue on fixed-time-window which makes it a bit unreliable: it doesn't fire as soon as the time window is due but waits until a new event is fired, so you might want to use a different windowing strategy until that get's fixed.
Here goes a full minimal working example based on yours:
(let [email (mailer {:host "localhost"
:port 1025
:from "abc#gmail.com"})]
(streams
(where (and (service "system_log")
(not (expired? event)))
(fixed-time-window
5
(smap
(fn [events]
(let [count-of-failures (count (filter #(= "IE" (:description %)) events))]
(event
{:status "login failures"
:metric count-of-failures
:total-fail (>= count-of-failures 2)})))
(where (and (= (:status event) "login failures")
(:total-fail event))
(email "hello123#gmail.com")))))))

Related

How to dispatch an event from an event in re-frame

I followed this example:
https://github.com/Day8/re-frame/blob/master/docs/FAQs/PollADatabaseEvery60.md
And here is my interval handler
(defonce interval-handler
(fn [{:keys [action id frequency event]}]
(let [live-intervals (atom {})]
(condp = action
:start (swap! live-intervals assoc id (js/setInterval #(re-frame/dispatch event) frequency))
:end (do (js/clearInterval (get live-intervals id))
(swap! live-intervals dissoc id))))))
(re-frame/reg-fx
:interval
interval-handler)
I'm trying to dispatch this interval event from another event right here:
(re-frame/reg-event-db
:start-playing
(fn [db _]
(re-frame/dispatch [:interval {:action :start
:id :some-awesome-id
:frequency 1000
:event [:tick]}])
(assoc db :is-playing? true
:fake (random-active-color db)
:real (random-active-color db))))
but it says re-frame: no :event handler registered for: :interval
Is this not possible to do?
:interval is an effect, not an event. To invoke an effect, you need to include it as a key in the effects map returned by your event handler - not emit another event with the effect's key:
(re-frame/reg-event-fx
:start-playing
(fn [{:keys [db]} _]
{:interval {:action :start
:id :some-awesome-id
:event [:tick]}]
:db (assoc db :is-playing? true
:fake (random-active-color db)
:real (random-active-color db))}))
Above event handler will return a map describing two effects:
:db - updating the app db to a new value (provided as the :db value)
:interval - re-frame will call your effect handler (interval-handler) with the value of :interval entry in the effects map

How should one handle AJAX success/error responses in Clojure re-frame?

I love re-frame, but I realize that I'm having a bit of trouble finding a nice pattern for handling AJAX responses.
My situation is the following:
I have a "global" event handler that triggers some AJAX call and dispatches to some other global event handler, depending on whether that call was successful, e.g.:
(reg-event-db :store-value
(fn [db [_ value]]
(do-ajax-store value
:on-success #(dispatch [:store-value-success %])
:on-error #(dispatch [:store-value-error %])
db))
(reg-event-db :store-value-success
(fn [db [_ result]]
(assoc db :foobar result)))
(reg-event-db :store-value-error
(fn [db [_ error]]
(assoc db :foobar nil
:last-error error)))
(I am aware of reg-event-fx and stuff, I'm just avoiding it here for the sake of brevity and because I think it does not make any difference for my problem).
I also have (multiple, distinct) UI components that might trigger the :store-value event, like so:
(defn button []
(let [processing? (reagent/atom false)]
(fn button-render []
[:button {:class (when #processing? "processing")
:on-click (fn []
(reset! processing? true)
(dispatch [:store-value 123]))}])))
So in this case the component has local state (processing?) that is supposed to depend on whether the AJAX call is still in progress or not.
Now, what is the proper pattern here to have the button component react to the events :store-value-success and :store-value-error in order to reset the processing? flag back to false after the AJAX call has finished?
Currently, I'm working around that problem by passing down callbacks but that seems really ugly because it clutters the event handlers' code with stuff that does not really belong there.
The best solution that I've thought of would be to have the button component being able to hook into the :store-value-success and :store-value-error events and install its own handler for those events, like this:
(defn button []
(let [processing? (reagent/atom false)]
(reg-event-db :store-value-success
(fn [db _]
(reset! processing? false)))
(reg-event-db :store-value-error
(fn [db _]
(reset! processing? false)))
(fn button-render []
[:button {:class (when #processing? "processing")
:on-click (fn []
(reset! processing? true)
(dispatch [:store-value 123]))}])))
However, that does not work. As it seems, re-frame does not allow multiple event handlers per event. Instead, a subsequent invocation of reg-event-db on one single event id will replace the previous event handler.
So how do you guys handle situations like this?
I think reg-event-fx (src) might indeed help solve your problem.
You could add a subscription that watches app-state e.g.
(rf/reg-sub
:app-state
(fn [db]
(get db :app-state)))
and add this to your button, perhaps with a state function e.g.
(defn btn-state [state]
(if (= state :processing)
"processing"))
And then in the AJAX handler, you could add an fx to update the state-
(reg-event-fx ;; -fx registration, not -db registration
:ajax-success
(fn [{:keys [db]} [_ result]]
{:db (assoc db :app-state :default)
:dispatch [:store-value-success result]}))
(reg-event-fx ;; -fx registration, not -db registration
:ajax-error
(fn [{:keys [db]} [_ result]]
{:db (assoc db :app-state :default)
:dispatch [:store-value-error result]}))
and update the AJAX handler
(reg-event-db :store-value
(fn [db [_ value]]
(do-ajax-store value
:on-success #(dispatch [:ajax-success %])
:on-error #(dispatch [:ajax-error %])
db))
This would be one way to handle it via -fx. I think you have already started to see the need for tracking app state, and I think bumping it up into the subscriptions would help with complexity, at which point your button render is greatly simplified.
(defn button []
[:button {:class (btn-state #app-state)
:on-click (dispatch [:store-value 123]))}])))
As others have mentioned, I would recommend to use http-fx and make processing? part of your global state. The code would look like this:
Events:
(reg-event-fx
:request
(fn [{:keys [db]} [_ method url data]]
{:http-xhrio {:method method
:uri url
:params data
:format (ajax/json-request-format)
:response-format (ajax/json-response-format {:keywords? true})
:on-success [:success-response method url]
:on-failure [:error-response method url]}
:db (assoc db :processing? true)}))
(reg-event-db
:success-response
(fn [db [_ method url result]]
(assoc db :foobar response
:processing? false)}))
(reg-event-db
:error
(fn [db [_ method url result]]
(assoc db :foobar nil
:last-error result
:processing? false)}))
Subscriptions:
(reg-sub
:processing
(fn [db _]
(:processing? db)))
View:
(defn button []
(let [processing? #(rf/subscribe [:processing])]
[:button {:class (when processing? "processing")
:on-click #(dispatch [:store-value 123]))}])))
Hint: You could reuse this code with all your requests.

Rollup function is not working in riemman when clj-time.core is added

I am using riemann to trigger an email alert which should get triggered only during 3AM to 8PM daily. When I have added clj-time.core function rollup function is not working.
Don't know where I am going wrong. Any help is appreciated.
(tcp-server {:host "127.0.0.1" :port 5555})
(let [userindex1 (default :ttl 300 (update-index (index)))])
(let [email (mailer {....email configuration})]
(streams
(where (service "log")
(smap
(fn [events]
(let [count-of-transaction (count (filter #(= "error" (:type %)) events))]
(event
{
:status "Failure"
:metric count-of-failures
:total-fail (< count-of-failures 2)})))
(where (let [now (clj-time.core/now)]
(and (<= 3 (clj-time.core/hour now) 20)
(= (:status event) "Failure")
(:total-fail event)))
(rollup 1 200
(email "xxx#xx.com")
))prn))))
Thanks in advance

Clojure:riemann.streams$smap$stream IllegalArgumentException: Key must be integer

I have a clojure code(riemann) to send an email if certain condition was met. I am facing some issue while passing the event to riemann server.
Riemann code
(let [email (mailer {"......"})]
(streams
(where (service "system_log")
(by :RefNo
(smap
(fn [events]
(let [count-of-failures (count (filter #(= "Failed" (:Status %)) events))]
(assoc (first events)
:status "Failure"
:metric count-of-failures
:total-fail (>= count-of-failures 2))))
(where (and (= (:status event) "Failure")
(:total-fail event))
(email "XXXXX#gmail.com"))prn)))))
O/P in riemann server
WARN [2015-11-18 05:24:49,596] defaultEventExecutorGroup-2-2 - riemann.streams - riemann.streams$smap$stream__3695#7addde9e threw
java.lang.IllegalArgumentException: Key must be integer
at clojure.lang.APersistentVector.assoc(APersistentVector.java:335)
at clojure.lang.APersistentVector.assoc(APersistentVector.java:18)
Update 2:
I simply changed the smap to sreduce. How I should update, since I am newbie to this I am little bit confused about altering the code as per your suggestion
(let [email (mailer {"......"})]
(streams
(where (service "system_log")
(by :RefNo
(sreduce
(fn [events]
(let [count-of-failures (count (filter #(= "Failed" (:Status %)) events))]
(assoc (first events)
:status "Failure"
:metric count-of-failures
:total-fail (>= count-of-failures 2))))
(where (and (= (:status event) "Failure")
(:total-fail event))
(email "XXXXX#gmail.com"))prn)))))
Update 3:
I have updated my code using coalesce and smap has its child. Now its not showing any error but email didn't get triggered. I am getting count-of-failures as 0. I guess count function is not working.
(let [email (mailer {"......"})]
(streams
(where (service "system_log")
(by :RefNo
(coalesce
(smap
(fn [events]
(let [count-of-failures (count (filter #(= "Failed" (:status %)) events))]
(assoc (first events)
:status "Failure"
:metric count-of-failures
:total-fail (>= count-of-failures 2))))
(where (and (= (:status event) "Failure")
(:total-fail event))
(email "XXXXX#gmail.com"))))prn))))
Off the top of my hat, by accepts a vector not a symbol:
(by [:Refno] ...
As a side note, I recommend using REPL (e.g. https://github.com/aphyr/riemann/wiki/playing-with-the-REPL) so you can build your stream processing gradually while testing functions in the REPL. It worked great for me.
Update: I'm also not sure if you shouldn't nest the where inside smap because you're assigning "Failure" but the where runs in parallel to smap so unless I'm missing something, I think it won't see it.
Update 2: I've ran it through the REPL connected to Riemann like this:
(require '[riemann.streams :refer :all])
(def f (stream
(where (service "system_log")
(by :RefNo
(smap
(fn [events]
(let [count-of-failures (count (filter #(= "Failed" (:Status %)) events))]
(prn events)
(assoc (first events)
:status "Failure"
:metric count-of-failures
:total-fail (>= count-of-failures 2))))
#_(where (and (= (:status event) "Failure")
(:total-fail event)))
prn)))))
(f {:RefNo 4444 :service "system_log" :status "Failed"})
It produces the same error that you've got. The error is there because you're assuming that the function passed to smap receives a list of events. It doesn't, it receives a single event (see the prn there). Calling first on a hashmap produces a vector, then trying to assoc using a symbol as a key gives you the error because vectors support only integers.
You cannot count failures this way just like you wouldn't use a regular map in Clojure for this purpose because you need past events.
Here's what I think might be compatible with your smap example.
Either:
Use coalesce http://riemann.io/api/riemann.streams.html#var-coalesce and smap as its child; I think smap will receive a list of events just like you wanted originally. I haven't tried it but there's no reason it shouldn't work.
You can control the time window you need (let's say max 2 failures per hour) by sending events with 1 hour TTL and querying the index within the stream. Here's a complete example: http://riemann.io/howto.html#query-the-index-from-within-a-stream
Apart from the that, I believe :Status should be in lower case. I hope it helps.

Riemann - Build a stream dynamically from a map

I have the following function which gets a map with service name and threshold. It checks if the service crossed a defined threshold and then calls multiple downstream children on the event.
(defn tc
[s & children]
(where
(and (service (:service_name s)) (not (expired? event)))
(by [:host :service]
(where (> metric (:threshold s)
(with :state "critical"
(apply sdo children)))))))
I would like to build a stream dynamically using a vector of maps:
(def services [{:service "cpu/usage" :threshold 90}
{:service "memory/usage" :threshold 90}])
When trying to run it in a stream i'm getting the following warning:
(streams
(doseq [s services] (tc s prn)))
WARN [2015-01-05 14:27:07,187] Thread-15 - riemann.core - instrumentation service caught
java.lang.NullPointerException
at riemann.core$stream_BANG_$fn__11140.invoke(core.clj:19)
at riemann.core$stream_BANG_.invoke(core.clj:18)
at riemann.core$instrumentation_service$measure__11149.invoke(core.clj:57)
at riemann.service.ThreadService$thread_service_runner__8782$fn__8783.invoke(service.clj:66)
at riemann.service.ThreadService$thread_service_runner__8782.invoke(service.clj:65)
at clojure.lang.AFn.run(AFn.java:22)
at java.lang.Thread.run(Thread.java:701)
It works, if i run the streams function inside the doseq.
This one works and gives the following output:
(doseq [s services]
(streams (tc s prn)))
#riemann.codec.Event{:host "testhost", :service "memory/usage", :state "critical", :description nil, :metric 91.0, :tags nil, :time 1420460856, :ttl 60.0}
It seems to blow up if your events don't have all the required fields, here's a sample from a similar project where I build an event from a sequence of events (reducing) It's not exactly what you are doing though I'm generating events in the same way:
{:service (:service (first events))
:metric (->> events count)
:host "All-counts"
:state "OK"
:time (:time (last events))
:ttl default-interval}
I got NPE specifically when time was missing. If you can't inherit it form somewhere, just make it up (use now for instance) without a reasonable value here, event expiration will not work and you'll run out of RAM