Source

clojurewise / metrics / src / metrics.clj

(ns metrics ^{ 
  :doc "Main server files, provide REST interface to metrics and collects them"} 
  (:gen-class)
  (:use compojure.core 
        ring.adapter.jetty
        [ring.middleware reload stacktrace params cookies]
        ring.util.response
        clojure.contrib.command-line
        [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)])
  (:require 
    db
    [compojure.route :as route]
    [clojure.contrib.logging :as log])
  (:import java.awt.Desktop
           java.net.URI))

; 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"
  [value]
  (re-find #"^\d+$" value))

(defn get-metrics 
  "Get metrics for url, return a sequence of [name value]. Ignore non numeric"
  [url]
  (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]
  (try
    (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"
  [url]
  (try
    (let [metrics (get-metrics url)]
      (db/insert url metrics)
      (future (updater-loop url))
      nil)
    (catch Exception e (str e))))

(def *title* "Metrics")
(def *jquery* 
  "http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js")
(def *jquery-ui* 
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js")
(def *jquery-ui-css* 
  "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/base/jquery-ui.css")

(defn page 
  "Page skeleton"
  [& content]
  (html
    (doctype :html4)
    [:head 
     [:title *title*]
     (include-css *jquery-ui-css*)
     (include-css "/style.css")]
    [:body 
     [: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 []
  (page))

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

(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-params)
    (wrap-stacktrace)
    (wrap-reload '[metrics])))

(defn get-int 
  "Get integer from string, return nil if not a number"
  [value]
  (try
    (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)]
        (do
          (db/initialize)
          (dorun (map #(future (updater-loop %)) (db/urls)))
          (future (open-browser port-num))
          (run-jetty #'app {:port port-num}))
        (do
          (println "error: bad port - " port)
          (System/exit 1)))))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.