clojurewise / metrics / src / metrics.clj

Full commit
(ns metrics ^{ 
  :doc "Main server files, provide REST interface to metrics and collects them"} 
  (:use compojure.core 
        [ring.middleware reload stacktrace params cookies]
        [clojure.contrib.str-utils2 :only (split-lines split)]
        [hiccup core form-helpers page-helpers]
        [hiccup.middleware :only (wrap-base-url)]
        [ring.middleware reload stacktrace params]
        [clojure.contrib.json :only (json-str)])
    [compojure.route :as route]
    [clojure.contrib.logging :as log])
  (:import java.awt.Desktop

; Number of values to return
(def *num-values* 100)
; Update interval for each url
(def *update-interval* (* 10 1000))

(defn num? 
  "Check if value is a number"
  (re-find #"^\d+$" value))

(defn get-metrics 
  "Get metrics for url, return a sequence of [name value]. Ignore non numeric"
  (let [data (slurp url)]
    (for [[m v] (map #(split % #":\s+" 2) (split-lines data)) :when (num? v)]
      [m (Long/valueOf v)])))

(defn updater-loop [url]
    (db/insert url (get-metrics url))
    (catch Exception e
      (log/error (format "%s: %s" url e))))
  (Thread/sleep *update-interval*)
  (recur url))

(defn json-reply [value]
  {:status 200
   :headers {"Content-Type" "application/json"}
   :body (json-str value)})

(defn add-url
  "Add a URL to pull from, currently unused"
    (let [metrics (get-metrics url)]
      (db/insert url metrics)
      (future (updater-loop url))
    (catch Exception e (str e))))

(def *title* "AMG Metrics")
(def *jquery* 
(def *jquery-ui* 
(def *jquery-ui-css* 

(defn page 
  "Page skeleton"
  [& content]
    (doctype :html4)
     [:title *title*]
     (include-css *jquery-ui-css*)
     (include-css "/style.css")]
     [:div.header *title* 
      [:span.right [:span#home.ui-icon.ui-icon-image {:title "Home"}]
                   [:span#options.ui-icon.ui-icon-gear {:title "Options"}]]]
     [:div#content content]
     [:img#loading {:src "/ajax-loader.gif"}]]
     (include-js *jquery*)
     (include-js *jquery-ui*)
     (include-js "/jquery.cookie.js")
     (include-js "/jquery.json.js")
     (include-js "/jquery.flot.js")
     (include-js "/metrics.js")))

(defn home-page []

(defn config-page []
  (page [:span "URL: " [:select#urls nil]] [:span#add-url.ui-icon.ui-icon-plus {:title "Add URL"}]

(defn metrics-page []
  "Dummy metrics page"
  (let [metrics ["metric1" "metric2" "metric3"]]
    (apply str (map #(format "%s: %d\n" % (rand-int 100)) metrics))))

(defroutes app-routes
  (GET "/" [] (home-page))
  (GET "/config" [] (config-page))
  (POST "/url" {params :params} 
        (json-reply (add-url (params "url"))))
  (GET "/url" {params :params} 
       (json-reply (db/metrics (params "url"))))
  (GET "/metric" {params :params}
       (json-reply (db/fetch (params "url") (params "metric") *num-values*)))
  (GET "/urls" [] 
       (json-reply (db/urls)))
  (GET "/metrics" [] (metrics-page))
  (route/resources "/")
  (route/not-found "Sorry, can't find it."))

(def app 
  (-> #'app-routes
    (wrap-reload '[metrics])))

(defn get-int 
  "Get integer from string, return nil if not a number"
    (Integer/valueOf value)
    (catch Exception e nil)))

(defn open-browser [port]
  (let [desktop (Desktop/getDesktop)]
    (Thread/sleep 2) ; Let the server time to start
    (.browse desktop (URI. (format "http://localhost:%d" port)))))

(defn -main [& args]
  (with-command-line (cons "metrics" args)
    "metrics - Metrics web server"
    [[port p "Port to run on" 8001] extra-args]
      (if-let [port-num (get-int port)]
          (dorun (map #(future (updater-loop %)) (db/urls)))
          (future (open-browser port-num))
          (run-jetty #'app {:port port-num}))
          (println "error: bad port - " port)
          (System/exit 1)))))