1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
|
# `trapperkeeper-metrics` Documentation
## What This Library Does
The main purpose of this library is to provide some wrapper code around the
[Dropwizard Metrics Java Library](https://dropwizard.github.io/metrics/3.1.0/),
but there is some other functionality provided as well. Here are the major features
available in `trapperkeeper-metrics`:
* A `Trapperkeeper` service that handles life cycle management for objects from
the Dropwizard Metrics library
* Support for reading basic metrics configuration info from a `Trapperkeeper`
config file, so that the metrics configuration syntax is consistent across
all of your `Trapperkeeper` apps
* Other utility functions for creating and interacting with metrics in your
application
For more detail on these features, read on.
### `MetricRegistry` Life Cycle
The main entry point into the Dropwizard Metrics API is a class called
[`MetricRegistry`](https://dropwizard.github.io/metrics/3.1.0/apidocs/com/codahale/metrics/MetricRegistry.html).
This class requires some basic initialization, so `trapperkeeper-metrics`
provides a Trapperkeeper service (`MetricsService`) that manages the life cycle
of a `MetricRegistry`. The service includes a function, `get-metrics-registry`,
so that all of your other Trapperkeeper services can access the registry and
register new metrics with it.
For example:
```clj
(defservice my-service
[[:MetricsService get-metrics-registry]]
(init [this context]
(let [metrics-registry (get-metrics-registry)
my-metric-name (metrics/host-metric-name "localhost" "my-metric")
my-timer (.timer metrics-registry my-metric-name)]
(metrics/time! my-timer (do-some-work)))
context))
```
See the [source code for the sample app](https://github.com/puppetlabs/trapperkeeper-comidi-metrics/blob/master/dev/example/comidi_metrics_web_app.clj)
for a working example. See the utility functions in the `trapperkeeper-metrics`
[`puppetlabs.metrics`](../src/puppetlabs/metrics.clj) namespace for some helpers
for constructing other kinds of metrics besides just `Timer`. See the
[Dropwizard Metrics docs](https://dropwizard.github.io/metrics/3.1.0/) for more
info about all of the available metrics types and their features.
The `get-metrics-registry` also allows you to specify two additional fields,
`registry-key` and `domain` which allow you to create other registries (besides
the default given by `(get-metrics-registry)` and allow you to configure the
namespace of the reporter for that registry.
```clj
(defservice my-service
[[:MetricsService get-metrics-registry]]
(init [this context]
(let [default-metrics-registry (get-metrics-registry)
my-metrics-registry (get-metrics-registry "my.metrics.domain")
;; This will create the metric
;; `my.metrics.domain:name=puppetlabs.localhost.my-metric`
my-metric-name (metrics/host-metric-name "localhost" "my-metric")
my-timer (.timer metrics-registry my-metric-name)]
(assert (not= default-metrics-registry my-metrics-registry))
(metrics/time! my-timer (do-some-work)))
context)
(start [this context]
;; We can retrieve the same metrics-registry later.
(let [my-metrics-registry (get-metrics-registry "my.metrics.domain")]
(do-some-other-work my-metrics-registry))
context))
```
For more specific details on the service API, see the [`Metrics
Service` API docs](./api.md).
### Configuration & Reporters
The `MetricsService` also provides a configuration syntax that users can use to
configure the metrics for a running TK app. This means that all TK services can
provide a consistent interface for interacting with metrics.
For more specific details, see the
[`MetricsService` configuration docs](../documentation/configuration.md).
### Utility Functions
The [`puppetlabs.metrics`](../src/puppetlabs/metrics.clj) namespace contains some
utility functions for working with metrics. See the source code and docstrings
for more detail. Here are a few bits of basic info:
`time!` is a macro that can be used to time some Clojure forms against an existing
`Timer`. e.g.:
```clj
(let [my-timer (.timer (get-metrics-registry) "my.metric.name")]
(time! my-timer
(do-some-work!)
(do-some-more-work!)))
```
`host-metric-name` can be used to provide a qualified, namespaced metric name.
For best results, it is advisable to use this function to create a name for each
of your application's metrics; this will ensure that the metrics are namespaced
consistently across services. It will also ensure that metrics are namespaced
by hostname, which is critical when consolidating metrics data from multiple
hosts/services. You can use the
`server-id` value from the `MetricsService` configuration to get the appropriate
hostname for your metric.
(TODO: this part of the API needs to be fleshed out a bit further. We might want
to have a more dynamic way to get the hostname/server-id rather than having to
put it into the config file. We may want to tweak some other things as well.
*In the interim*, it's probably a good idea to
*make it clear in your application/service documentation* that the specific
metric namespaces should not be considered part of a formal API and may change
in subsequent releases.)
`register` can be used to add a metric to an existing `MetricRegistry`.
`ratio`, `metered-ratio`, and `gauge` can be used to construct other types of
Metrics.
### Low-level HTTP API
To enable the HTTP API for accessing individual metrics add the service to your
`bootstrap.cfg` and configure the `web-router-service` accordingly:
```
web-router-service {
"puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-webservice" : "/metrics"
}
```
#### Listing available metrics
##### Request format
To get a list of all available metric names:
* Request `/metrics/v1/mbeans`.
* Use a `GET` request.
##### Response format
Responses return a JSON object mapping a string to a string:
* The key is the name of a valid MBean.
* The value is a URI to use for requesting that MBean's attributes.
#### Retrieving multiple metrics
##### Request format
To get a the attributes for multiple metrics at the same time:
* Request `/metrics/v1/mbeans`.
* Use a `POST` request.
* Use a request body which is either a JSON object whose values are metric
names, JSON array of metric names or a JSON string of a metric name.
##### Response format
The response format, though always JSON, depends on the request format:
* Requests with a JSON object will return the a JSON object where the values of
the original object have been transformed into the Mbeans' attributes for the
metric names.
* Requests with a JSON array will return the a JSON array where the items of the
original array have been transformed into the Mbeans' attributes for the
metric names.
* Requests with a JSON string will return the a JSON object of the Mbean's
attributes for the given metric name.
#### Retrieving an specific metric
##### Request format
To get the attributes of a particular metric:
* Request `/metrics/v1/mbeans/<name>`, where `<name>` is something that was
returned in the list of available metrics specified above.
* Use a `GET` request.
##### Response format
Responses return a JSON object mapping strings to (strings/numbers/Booleans).
For example, using `curl` from localhost:
curl 'http://localhost:8080/metrics/v1/mbeans/java.lang:type=Memory'
{
"ObjectPendingFinalizationCount" : 0,
"HeapMemoryUsage" : {
"committed" : 807403520,
"init" : 268435456,
"max" : 3817865216,
"used" : 129257096
},
"NonHeapMemoryUsage" : {
"committed" : 85590016,
"init" : 24576000,
"max" : 184549376,
"used" : 85364904
},
"Verbose" : false,
"ObjectName" : "java.lang:type=Memory"
}
#### Alternatives
Since we support sending the metrics data to JMX, there are several existing
tools and approaches that can be used to read the data for individual metrics
via JMX. JVisualVM is one example; see
[pe-puppetserver-jruby-jmx-client](https://github.com/puppetlabs/pe-puppetserver-jruby-jmx-client)
for an example of how to do this from a JRuby script.
## What This Library Does *Not* Do
## Notes for Developers
For best results, use this library in combination with the
[`comidi`](https://github.com/puppetlabs/comidi) library,
and then take advantage of the `wrap-with-request-metrics` Ring middleware
to track metrics about all HTTP requests made to your application. See
the `comidi` docs for more info.
## In The Future There Will Be Robots
Some ideas for things we might want to add/change in the future:
### metrics-clojure
There is an existing clojure library that wraps Dropwizard Metrics:
[`metrics-clojure`](https://github.com/sjl/metrics-clojure). At the time
when we originally wrote our metrics code, this library was very out-of-date
and didn't support some of the features we needed. It also seemed like a pretty
thin facade around the Java library, and we decided that it wasn't worth
adding an extra dependency.
Since then, it's been updated and should be much more compatible with the
more recent versions of Dropwizard Metrics. At a glance, it seems like it
would be possible for other TK services to use `metrics-clojure` in
combination with `trapperkeeper-metrics` as-is; it mostly just provides utility
functions that should work fine with the `MetricRegistry` object surfaced
by `trapperkeeper-metrics`. So, if you feel like the abstractions it
provides over the Java library are worthwhile, try it out and let us know if
something doesn't work properly.
At some point in the future we may go ahead and add it as a direct dependency
and refactor things in `trapperkeeper-metrics` to use it, but so far there
hasn't been a hugely compelling reason to do so.
### Additional Facilities for Exposing Metrics
There are a few other ideas floating around for how to make it easier for
apps/services that are using `trapperkeeper-metrics` to expose the metrics info
to end users. More utility functions for easier integration with status service,
other tools for facilitating consumption / visualization / etc. Stay tuned.
|