Steve Losh avatar Steve Losh committed caf4e68

Initial commit.

Comments (0)

Files changed (13)

+pom.xml
+*jar
+/lib/
+/classes/
+.lein-failures
+.lein-deps-sum
+syntax: glob
+pom.xml
+*jar
+/lib/
+/classes/
+.lein-failures
+.lein-deps-sum
+MIT/X11 License
+===============
+
+Copyright (c) 2011 Steve Losh and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+metrics-clojure
+===============
+
+`metrics-clojure` is a Clojure façade around Coda Hale's [metrics][] library.
+
+[metrics]: http://github.com/codahale/metrics/
+
+Installation
+------------
+
+Add this to your `project.clj`'s dependencies:
+
+    [metrics-clojure "0.1.0"]
+
+That's it.
+
+Usage
+-----
+
+`metrics-clojure` is a thin wrapper around `metrics`, so if you don't know what
+any of these words mean you should probably read the [metrics documentation][]
+and/or watch the [talk][]:
+
+
+[metrics documentation]: https://github.com/codahale/metrics/tree/development/docs
+[talk]: http://pivotallabs.com/talks/139-metrics-metrics-everywhere
+
+### Creating Metrics
+
+`metrics-clojure` wraps up the `metrics` classes to make them more Clojurey.
+
+#### Gauges
+
+Create your gauge:
+
+    (use '[metrics.gauges :only (gauge)])
+
+    (def files-open
+      (gauge "files-open"
+             (return-number-of-files-open ...)))
+
+That's it.  Pretty simple.
+
+`gauge` is a macro.  If you need a function instead you can use `gauge-fn`, but
+you have to pass it a function, not just a body:
+
+    (use '[metrics.gauges :only (gauge-fn)])
+
+    (def files-open
+      (gauge-fn "files-open"
+             #(return-number-of-files-open ...)))
+
+#### Counters
+
+Create your counter:
+
+    (use '[metrics.counters :only (counter)])
+
+    (def users-connected (counter "users-connected"))
+
+Now increment and decrement it:
+
+    (use '[metrics.counters :only (inc! dec!)])
+
+    (inc! users-connected)
+    (inc! users-connected 2)
+
+    (dec! users-connected)
+    (dec! users-connected 2)
+
+#### Meters
+
+Create your meter:
+
+    (use '[metrics.meters :only (meter)])
+
+    (def files-served (meter "files-served" "files"))
+
+The second argument to `meter` is a string describing the "units" for the meter.
+In this example it's "files", as in "18732 files".
+
+Mark the meter every time the event happens:
+
+    (use '[metrics.meters :only (mark!)])
+
+    (mark! files-served)
+
+#### Histograms
+
+Create your histogram:
+
+    (use '[metrics.histograms :only (histogram)])
+
+    (def search-results-returned
+      (meter "search-results-returned"))
+
+You can create a biased histogram by passing an extra boolean argument:
+
+    (def search-results-returned-biased
+      (meter "search-results-returned-biased" true))
+
+Update the histogram when you have a new value to record:
+
+    (use '[metrics.histograms :only (update!)])
+
+    (update! search-results-returned 10)
+
+#### Timers
+
+Create your timer:
+
+    (use '[metrics.timers :only (timer)])
+
+    (def image-processing-time (timer "image-processing-time"))
+
+Now time something:
+
+    (use '[metrics.timers :only (time!)])
+
+    (time! image-processing-time
+           (process-image-part-1 ...)
+           (process-image-part-2 ...))
+
+`time!` is a macro.  If you need a function instead, you can use `time-fn!`, but
+you'll need to pass it a function instead of just a body:
+
+    (use '[metrics.timers :only (time-fn!)])
+
+    (time-fn! image-processing-time
+              (fn []
+                (process-image-part-1 ...)
+                (process-image-part-2 ...)))
+
+### Metric Names
+
+In all these examples we've used a string to name the metric.  The `metrics`
+library actually names metrics with three-part names.  In Java and Scala those
+names are usually the package and class where the timer is being used.
+
+In Clojure you usually won't have a meaningful class name to record, and the
+package name would be a pain to find, so `metrics-clojure` uses "default" and
+"default" for those parts.  This results in a name like
+"default.default.my-metric".
+
+If you want to specify something other than "default" you can pass a collection
+of three strings instead of a single string:
+
+    (use '[metrics.timers :only (timer)])
+
+    (def response-time
+      (timer ["webservice" "views" "response-time"]))
+
+This will result in a name like "webservice.views.response-time".
+
+### Removing Metrics
+
+You can remove metrics as long as you know their names:
+
+    (use '[metrics.core :only (remove-metric)])
+
+    (remove-metric "files-served")
+
+You can use the sequence form of metric names here too, of course:
+
+    (remove-metric ["webservice" "views" "response-time"])
+
+### Reporting Metrics
+
+Currently `metrics-clojure` only supports reporting metrics through the console
+(on standard error):
+
+    (use '[metrics.core :only (report-to-console)])
+
+    (report-to-console 10)
+
+This will tell `metrics` to print all metrics to the console every ten seconds.
+
+More reporting methods will be added in the future.  Pull requests are welcome.
+
+### Side Effects and Agents
+
+Pretty much everything `metrics-clojure` does causes side effects.  If you're
+recording metrics inside of a `dosync` they may be recorded multiple times if
+the transaction is restarted.
+
+This may or may not be what you want.  If you're recording how long it takes to
+do something, and you do it twice, then it makes sense to record it each time.
+If you're recording how many responses get sent to a user, then you probably
+don't want to overcount them.
+
+`metrics-clojure` doesn't try to decide for you.  It leaves it up to you to
+handle the issue.
+
+If you don't want to record something multiple times, an agent may be a good way
+to handle things:
+
+    (def thing-given-to-user (agent (counter "thing-given-to-user")))
+
+    (dosync
+       (send inc! thing-given-to-user)
+       (make-thing ...))
+
+This works because the recording functions for counters, meters and histograms
+return the metric object.
+
+**This will *not* work with timers!**  `time!` returns the result of the work,
+so the agent's value will be replaced by that and it won't work more than once.
+
+In practice this shouldn't be a problem though, because if you're timing work
+you'll probably want to time it every time it happens, right?
+
+More Information
+----------------
+
+* Source (Mercurial): <http://bitbucket.org/sjl/metrics-clojure/>
+* Source (Git): <http://github.com/sjl/metrics-clojure/>
+* License: MIT/X11
+* Issues: <http://github.com/sjl/metrics-clojure/issues/>
+
+(defproject metrics-clojure "0.1.0"
+  :description "A Clojure façade for Coda Hale's metrics library."
+  :dependencies [[org.clojure/clojure "1.2.1"]
+                 [com.yammer.metrics/metrics-core "2.0.0-BETA17"]]
+  :repositories {"repo.codahale.com" "http://repo.codahale.com"})

src/metrics/core.clj

+(ns metrics.core
+  (use [metrics.utils :only (metric-name)])
+  (import (com.yammer.metrics Metrics))
+  (import (com.yammer.metrics.reporting ConsoleReporter))
+  (import (java.util.concurrent TimeUnit)))
+
+
+(defn remove-metric [title]
+  (Metrics/removeMetric (metric-name title)))
+
+
+(defn report-to-console [seconds]
+  (ConsoleReporter/enable seconds TimeUnit/SECONDS))

src/metrics/counters.clj

+(ns metrics.counters
+  (use [metrics.utils :only (metric-name)])
+  (import (com.yammer.metrics Metrics)))
+
+(defn counter [title]
+  (Metrics/newCounter (metric-name title)))
+
+(defn inc!
+  ([c] (inc! c 1))
+  ([c n]
+   (.inc c n)
+   c))
+
+(defn dec!
+  ([c] (dec! c 1))
+  ([c n]
+   (.dec c n)
+   n))
+

src/metrics/gauges.clj

+(ns metrics.gauges
+  (use [metrics.utils :only (metric-name)])
+  (import (com.yammer.metrics Metrics))
+  (import (com.yammer.metrics.core GaugeMetric)))
+
+
+(defn gauge [title & body]
+  `(Metrics/newGauge (metric-name title)
+                     (proxy [GaugeMetric] []
+                       (value [] (do ~@body)))))
+
+(defn gauge-fn [title f]
+  (Metrics/newGauge (metric-name title)
+                    (proxy [GaugeMetric] []
+                      (value [] (f)))))
+

src/metrics/histograms.clj

+(ns metrics.histograms
+  (use [metrics.utils :only (metric-name)])
+  (import (com.yammer.metrics Metrics)))
+
+
+(defn histogram
+  ([title] (histogram title false))
+  ([title biased]
+   (Metrics/newHistogram (metric-name title) biased)))
+
+(defn update!
+  ([h] (update! h 1))
+  ([h n]
+   (.update h n)
+   h))

src/metrics/meters.clj

+(ns metrics.meters
+  (use [metrics.utils :only (metric-name)])
+  (import (com.yammer.metrics Metrics))
+  (import (java.util.concurrent TimeUnit)))
+
+
+(defn meter [title event-type]
+  (Metrics/newMeter (metric-name title)
+                    event-type
+                    TimeUnit/SECONDS))
+
+(defn mark! [m]
+  (.mark m)
+  m)
+

src/metrics/timers.clj

+(ns metrics.timers
+  (use [metrics.utils :only (metric-name)])
+  (import (com.yammer.metrics Metrics))
+  (import (java.util.concurrent TimeUnit)))
+
+
+(defn timer [title]
+  (Metrics/newTimer (metric-name title)
+                    TimeUnit/MILLISECONDS
+                    TimeUnit/SECONDS))
+
+(defmacro time! [t & body]
+  `(.time ~t
+         (proxy [Callable] []
+           (call [] (do ~@body)))))
+
+(defn time-fn! [t f]
+  (.time t
+         (proxy [Callable] []
+           (call [] (f)))))
+

src/metrics/utils.clj

+(ns metrics.utils
+  (import (com.yammer.metrics.core MetricName)))
+
+
+(defn metric-name [title]
+  (if (string? title)
+    (new MetricName "default" "default" title)
+    (new MetricName (first title) (second title) (last title))))

test/metrics/test/core.clj

+(ns metrics-clojure.test.core
+  (:use [metrics-clojure.core])
+  (:use [clojure.test]))
+
+(deftest replace-me ;; FIXME: write
+  (is false "No tests have been written."))
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.