Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 106 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Bucky
=====

Bucky is a client and server for sending performance data from the client into statsd+graphite, OpenTSDB, or any
Bucky is a client and server for sending performance data from the client into statsd+graphite, OpenTSDB, InfluxDB, or any
other stats aggregator of your choice.

It can automatically measure how long your pages take to load, how long AJAX requests take and how long
Bucky can measure how long your pages take to load, how long AJAX requests take, and how long
various functions take to run. Most importantly, it's taking the measurements on actual page loads,
so the data has the potential to be much more valuable than in vitro measurements.

If you already use statsd or OpenTSDB, you can get started in just a few minutes. If you're not
If you already use statsd, OpenTSDB or InfluxDB, you can get started in just a few minutes. If you're not
collecting stats, you [should start](http://github.hubspot.com/BuckyServer/getting_started/choosing_a_stats_service/)!
What gets measured gets managed.

Expand All @@ -21,20 +21,37 @@ You can play with Bucky just using the client, but if you'd like to start collec

#### From The Client

Include [bucky.js](https://raw.github.com/HubSpot/BuckyClient/v0.2.3/bucky.min.js) on your page, the only required config can be done right in the script tag:
Include [bucky.js](https://raw.github.com/HubSpot/BuckyClient/v0.3.0/bucky.min.js) on your page. All of the required configuration can be done right in the script tag:

```html
<script src="bucky.js" data-bucky-host="/bucky" data-bucky-page data-bucky-requests></script>
```

That config will automatically log the performance of your page and all the requests you make to a server running at
/bucky. It will automatically decide the right key name based on the url of the page. If you'd like, you can also specify it manually:
The example above sets up Bucky to report on page load performance and all XMLHttpRequests. The [data-bucky-host] attribute configures Bucky to send the reporting data to a server that's listening at /bucky. Since no key string was listed Bucky will automatically generate a key name based on the url of the page. If you'd like, you can also specify it manually:

```html
<script src="bucky.js" data-bucky-host="/bucky" data-bucky-page="index" data-bucky-requests="index"></script>
```

The `Bucky` object will be available globally, but there is nothing you need to call for basic usage.
Here are the full list of options available as attributes with example values:
- `data-bucky-host="/bucky"`: Where we can reach your [Bucky server](http://github.com/HubSpot/BuckyServer), including the
APP_ROOT.

The Bucky server has a very liberal CORS config, so we should be able to connect to it even if
it's on a different domain, but hosting it on the same domain and port will save you some preflight requests.

- `data-bucky-page="page"`: Turns on page performance logging, this example assigns the key "page".

- `data-bucky-requests="xhr"`: Turns on XMLHttpRequest monitoring, this example assigns the key "xhr".

- `data-bucky-json="true"`: Setting this flag to true changes the output to json. By default Bucky outputs using a line format similar to what's used by statsd.

- `data-bucky-influx-line-protocol="true"`: Change the output to use InfluxDB Line Protocol. The data-bucky-json attribute must setting must be set to true for this option.

- `data-bucky-query-string="escape|replace"`: This option only applies to InfluxDB Line Protocol. The two accepted values for this attribute are "escape" and "replace". When InfluxDB Line protocol is used the full URL will be attached as a tag. If the URL contains a query string, due to InfluxDB Line Protocol restrictions, either the = in the query string will need to be escaped to make the query string part of the URL, or the ? and & need to be replaced with commas to create additional tags.


This will create a `Bucky` object with the set attributes. This object will be available globally but there is nothing you need to call for basic usage.

Bucky can also be loaded with AMD or Browserify (see the methods below).

Expand All @@ -45,16 +62,16 @@ npm install bucky
```

```coffeescript
bucky = require('bucky')
Bucky = require 'bucky'
```

### Configuring

Before sending any data, call `setOptions` if you're not using the data- attribute based configuration:
Before sending any data you can call the `setOptions` to set any configuration options. Be aware that configuration set using `setOptions` will override any data attributes that were set in the script tag.

```javascript
Bucky.setOptions({
host: 'http://myweb.site:9999/bucky'
host: 'http://my.web.hostname:5999/bucky'
});
```

Expand All @@ -69,7 +86,9 @@ Some options you might be interested in:
- `active`: Should Bucky actually send data? Use this to disable Bucky during local dev for example.
- `sample`: What fraction of clients should actually send data? Use to subsample your clients if you have
too much data coming in.

- `json`: Boolean flag for sending data to the server as json instead of statsd line protocol. Default is false (to use statsd line protocol).
- `influxLineProtocol`: Boolean flag to change the format of the output to optimize for InfluxDB Line Protocol. Default is False. (influxLineProtocol requires the json flag to be set to true).
- `queryString`: This option only applies to InfluxDB Line Protocol. The two accepted values are "escape" and "replace". When InfluxDB Line protocol is used the full URL will be attached as a tag. If the URL contains a query string, due to InfluxDB Line Protocol restrictions, either the = in the query string will need to be escaped to make the query string part of the URL, or the ? and & need to be replaced with commas to create additional tags. The default is null.
Take a look at [the source](http://github.com/HubSpot/BuckyClient/blob/master/bucky.coffee#L34) for a
full list of options.

Expand All @@ -80,10 +99,10 @@ one go. It won't do anything on browsers which don't support the performance.ti
it will bind an event if the data isn't ready yet.

```coffeescript
Bucky.sendPagePerformance('where.the.data.should.go')
Bucky.sendPagePerformance 'key.for.the.data'
```

Setting `data-bucky-page` triggers this automatically.
This function gets called by default if the `data-bucky-page` attribute is set in the script tag.

The two most relevant stats provided are `responseEnd` which is the amount of time it took for the
original page to be loaded and `domInteractive` which is the amount of time before the page has
Expand All @@ -97,29 +116,45 @@ If you're using Backbone, it might be a good idea to send your data based on rou
```coffeescript
Backbone.history.on 'route', (router, route) ->
# Will only send on the initial page load:
Bucky.sendPagePerformance("some.location.page.#{ route }")
Bucky.sendPagePerformance "key.page.#{ route }"
```

This is how the data will be sent to the server after page has fully loaded:

With the default statsd line protocol style syntax:

```text
key.page.routeName.connectEnd:172.000|ms
key.page.routeName.connectStart:106.000|ms
key.page.routeName.domComplete:1029.000|ms
key.page.routeName.domContentLoadedEventEnd:1019.000|ms
key.page.routeName.domContentLoadedEventStart:980.000|ms
key.page.routeName.domInteractive:980.000|ms
```

The data collected will look something like this:
With JSON turned on:

```javascript
pages.contactDetail.timing.connectEnd: "172.000|ms"
pages.contactDetail.timing.connectStart: "106.000|ms"
pages.contactDetail.timing.domComplete: "1029.000|ms"
pages.contactDetail.timing.domContentLoadedEventEnd: "1019.000|ms"
pages.contactDetail.timing.domContentLoadedEventStart: "980.000|ms"
pages.contactDetail.timing.domInteractive: "980.000|ms"
pages.contactDetail.timing.domLoading: "254.000|ms"
pages.contactDetail.timing.domainLookupEnd: "106.000|ms"
pages.contactDetail.timing.domainLookupStart: "106.000|ms"
pages.contactDetail.timing.fetchStart: "103.000|ms"
pages.contactDetail.timing.loadEventEnd: "1030.000|ms"
pages.contactDetail.timing.loadEventStart: "1029.000|ms"
pages.contactDetail.timing.navigationStart: "0.000|ms"
pages.contactDetail.timing.requestStart: "173.000|ms"
pages.contactDetail.timing.responseEnd: "243.000|ms"
pages.contactDetail.timing.responseStart: "235.000|ms"
pages.contactDetail.timing.secureConnectionStart: "106.000|ms"
{
"key.page.routeName.domLoading": "254.000|ms",
"key.page.routeName.domainLookupEnd": "106.000|ms",
"key.page.routeName.domainLookupStart": "106.000|ms",
"key.page.routeName.fetchStart": "103.000|ms",
"key.page.routeName.loadEventEnd": "1030.000|ms",
"key.page.routeName.loadEventStart": "1029.000|ms"
}
```

And with JSON and Influx Line Protocol:

```javascript
{
"key.page.routeName,url=http://window.location.string,timing=navigationStart": "0.000|ms",
"key.page.routeName,url=http://window.location.string,timing=requestStart": "173.000|ms",
"key.page.routeName,url=http://window.location.string,timing=responseEnd": "243.000|ms",
"key.page.routeName,url=http://window.location.string,timing=responseStart": "235.000|ms",
"key.page.routeName,url=http://window.location.string,timing=secureConnectionStart": "106.000|ms"
}
```

A description of what each datapoint represents is included in [the spec](http://www.w3.org/TR/navigation-timing-2/#sec-PerformanceNavigationTiming).
Expand All @@ -130,40 +165,56 @@ Bucky can automatically log all ajax requests made by hooking into XMLHttpReques
on the url to try and create a graphite key from it. Enable it as early in your app's load as is possible:

```coffeescript
Bucky.requests.monitor('my.project.requests')
Bucky.requests.monitor 'my.project.requests'
```

Setting `data-bucky-requests` calls this automatically.
This function gets called by default if the `data-bucky-requests` attribute is set in the script tag.

The data collected will look something like this for a GET request to
This is how the data will be sent to the server for a GET request to
`api.hubapi.com/automation/v2/workflows`:

With the default statsd line protocol style syntax:

```text
my.project.requests.api.hubapi.automation.v2.workflows.get:656.794|ms
my.project.requests.api.hubapi.automation.v2.workflows.get.2xx:1|c
my.project.requests.api.hubapi.automation.v2.workflows.get.200:1|c
```

With JSON turned on:

```javascript
{
"my.project.requests.api.hubapi.automation.v2.workflows.get.headers": "436.737|ms",
"my.project.requests.api.hubapi.automation.v2.workflows.get.receiving": "0.182|ms"
}
```

And with JSON and Influx Line Protocol:

```javascript
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get: "656.794|ms"
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get.2xx: "1|c"
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get.200: "1|c"
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get.headers: "436.737|ms"
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get.receiving: "0.182|ms"
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get.sending: "0.059|ms"
contacts.web.prod.requests.api.hubapi.automation.v2.workflows.get.waiting: "206.035|ms"
{
"my.project.requests,url=http://window.location,endpoint=http://api.hubapi.com/automation/v2/workflows,method=get,status=sending": "0.059|ms",
"my.project.requests,url=http://window.location,endpoint=http://api.hubapi.com/automation/v2/workflows,method=get,status=waiting": "206.035|ms"
}
```

#### Prefixing

You can build a client which will prefix all of your datapoints by calling bucky as a function:

```coffeescript
myBucky = Bucky('awesome.app.view')
myBucky = Bucky 'awesome.app.view'

# You can then use all of the normal methods:
myBucky.send('data.point', 5)
myBucky.send 'data.point', 5
```

You can repeatedly call clients to add more prefixes:

```coffeescript
contactsBucky = bucky('contacts')
cwBucky = contactsBucky('web')
contactsBucky = bucky 'contacts'
cwBucky = contactsBucky 'web'

cwBucky.send('x', 1) # Data goes in contacts.web.x
```
Expand All @@ -174,16 +225,16 @@ By default `send` sends absolute values, this is rarely what you want when worki
a counter is usually more helpful:

```coffeescript
bucky.count('my.awesome.thing')
bucky.count('number.of.chips.eaten', 5)
bucky.count 'my.awesome.thing'
bucky.count 'number.of.chips.eaten', 5
```

#### Timing Things

You can manually send ms durations using `timer.send`:

```coffeescript
bucky.timer.send('timed.thing', 55)
bucky.timer.send 'timed.thing', 55
```

Bucky includes a method to time async functions:
Expand All @@ -207,10 +258,10 @@ You can time synchronous functions as well:

```coffeescript
bucky.timer.timeSync 'my.awesome.function', ->
Math.sqrt(100)
Math.sqrt 100
```

The `time` and `timeSync` functions also accept a context and arguments to pass to the
The `time` and `timeSync` functions also accept a context and arguments to pass to the
called function:

```coffeescript
Expand All @@ -220,7 +271,7 @@ bucky.timer.timeSync 'my.render.function', @render, @, arg1, arg2
You can wrap existing functions using `wrap`:

```coffeescript
func = bucky.timer.wrap('func.time', func)
func = bucky.timer.wrap 'func.time', func
```

It also supports a special syntax for methods:
Expand All @@ -236,15 +287,15 @@ Note that this wrapping does not play nice with CoffeeScript `super` calls.
Bucky also includes a function for measuring the time since the navigationStart event was fired (the beginning of the request):

```coffeescript
bucky.timer.mark('my.thing.happened')
bucky.timer.mark 'my.thing.happened'
```

It acts like a timer where the start is always navigation start.

The stopwatch method allows you to begin a timer which can be stopped multiple times:

```coffeescript
watch = bucky.stopwatch('some.prefix.if.you.want')
watch = bucky.stopwatch 'some.prefix.if.you.want'
```

You can then call `watch.mark('key')` to send the time since the stopwatch started, or
Expand All @@ -270,7 +321,7 @@ You can find your stats in the `stats` and `stats.timing` folders in graphite, o
Bucky will send your data in bulk from the client either five seconds after the last datapoint is added, or thirty seconds after
the last send, whichever comes first. If you log multiple datapoints within this send frequency, the points will
be averaged (and the appropriate frequency information will be sent to statsd) (with the exception of counters, they
are incremented). This means that the max and min numbers you get from statsd actually represent the max and min
are incremented). This means that the max and min numbers you get from statsd actually represent the max and min
5-30 second bucket. Note that this is per-client, not for the entire bucky process (it's generally only important
on the server where you might be pushing out many points with the same key).

Expand Down
1 change: 1 addition & 0 deletions bower.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "bucky",
"version": "0.3.0",
"description": "Collect performance data from the client",
"main": "bucky.js",
"homepage": "http://github.hubspot.com/bucky",
Expand Down
Loading