0

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?

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Mangoski
  • 2,058
  • 5
  • 25
  • 43
  • There are quite a few missing pieces on this snippet but regardless of that there are some obvious wrong expressions as `(and(:status "login failures") (:totalFail true))` (it will always produce nil). I suggest you to test line by line at the REPL and use a different thread to periodically check the counter value wrapped into an [atom](https://clojuredocs.org/clojure.core/atom) – Jaime Agudo Jul 07 '15 at 15:53

1 Answers1

0

There are a couple of issues here. I'll try to address some of them, then post a minimal working example:

  1. 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 ...)}))
      ...)
    
  2. 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.

  3. 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.

  4. 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"
    
  5. 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"))
    
  6. The :subject fn for mailer should be passed as part of a second map, as explained in the mailer documentation

  7. 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")))))))
nberger
  • 3,659
  • 17
  • 19
  • Thank you so much nberger. Your explanation was awesome which clearly taught me the functionality of the code. But still the email didn't get triggered as you said there was a bug in fixed-time-window. I removed the fixed-time-window.Still the last where condition in my code was not working. – Mangoski Jul 08 '15 at 05:59
  • Eventhough I removed the and condition from my code but still I am facing the same issue. I am posting my latest code for your review. – Mangoski Jul 08 '15 at 06:10
  • (let [email (mailer {...})] (streams (where (and (service "system_log") (not (expired? event))) (smap (fn [events] (let [count-of-failures (count (filter #(= "IE" (:description %)) events))] (events {:status "login failures" :metric count-of-failures :total-fail (> count-of-failures 5)}))) (where (= (:status event) "login failures") (email "123@gmail.com")))))) – Mangoski Jul 08 '15 at 06:14
  • Its working when I removed this line (not (expired? event))) form the (streams... I couldn't find out the logic happening here. – Mangoski Jul 08 '15 at 12:48
  • Maybe it's just that there are not enough events during the time window. To simplify things, you could use `rate` and `scale` to accomplish what you are doing with the `fixed-time-window` and `smap`. [See this gist](https://gist.github.com/nberger/fc636d8c2712b38a39f5) for a new version using `rate` and `scale`. If you are not sure if there are enough events, I left a couple `prn` so you can see if the rate is not enough, or if there's an issue with the mailer configuration. – nberger Jul 08 '15 at 14:10
  • If it's not clear what `scale` and `rate` do, [this answer](https://groups.google.com/d/msg/riemann-users/3yWmtAehqoA/7yDBebw5iYIJ) by @aphyr might be of help. There's also the [docs about rate](http://riemann.io/api/riemann.streams.html#var-rate). – nberger Jul 08 '15 at 14:16
  • I used rate and scale but it didn't works for me. May be I am using it in wrong way. – Mangoski Jul 10 '15 at 09:35
  • The above edited code is working by using fixed-time-window itseld but it triggering email for N number of condition satisfied with in 30 sec(for eg.If my condition was true for 10 times within that 30 sec means 10 emails got triggered). I want only one email at the end of every 30sec if the condition is satisfied. If no event matches my condition within 30sec interval means no email should trigger. – Mangoski Jul 10 '15 at 09:39