Skip to content

Instantly share code, notes, and snippets.

@eric
Created June 1, 2012 04:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eric/2848810 to your computer and use it in GitHub Desktop.
Save eric/2848810 to your computer and use it in GitHub Desktop.
cubism.js source for Papertrail
/*
* Cubism source for Papertrail
* https://papertrailapp.com/
*/
cubism.context.prototype.papertrail = function(token) {
var source = {};
source.metric = function(expression, title) {
var lookup = {};
return context.metric(function(start, stop, step, callback) {
papertrailCount(expression, start, stop, step, callback);
}, title || expression)
}
function papertrailCount(expression, start, stop, step, callback) {
var lookup = {},
dateFormat = d3.time.format.iso,
min_time = epochTime(start),
max_time = (epochTime(stop) + (step / 1000 * 2))
t0 = new Date();
function map(result) {
for (var i = 0; i < result.events.length; i++) {
var event = result.events[i];
var time = +dateFormat.parse(event.received_at);
time -= time % step;
if (lookup[time]) {
lookup[time] += 1;
} else {
lookup[time] = 1;
}
}
}
function reduce() {
var values = [],
cur = +start,
startAt = +start,
stopAt = +stop;
while (cur < stopAt) {
values.push(lookup[cur] || 0);
cur += step;
}
if (isNaN(values[values.length - 1]) || isNaN(values[0])) {
console.log(start, stop, +start, +stop, values, lookup);
}
callback(null, values);
}
function epochTime(time) {
return Math.floor(time / 1000);
}
function papertrailSearch(expression, options, callback) {
var url = "https://papertrailapp.com/api/v1/events/search" +
"?q=" + encodeURIComponent(expression);
for (var key in options) {
url += "&" + key + "=" + encodeURIComponent(options[key]);
}
var req = new XMLHttpRequest;
req.open("GET", url, true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("X-Papertrail-Token", token);
req.onreadystatechange = function() {
if (req.readyState === 4) {
var s = req.status;
callback(s >= 200 && s < 300 || s === 304 ? JSON.parse(req.responseText) : null);
}
};
req.send(null);
}
function handleResponse(result) {
if (result) {
map(result);
if (result.reached_record_limit) {
papertrailSearch(expression, { max_id: result.min_id, min_time: min_time },
handleResponse)
} else {
console.log("Ran '" + expression + "' in " + (new Date() - t0) + "ms");
reduce();
}
} else {
console.log("Returning error");
callback(new Error("unable to load data"));
}
}
papertrailSearch(expression, {
min_time: min_time,
max_time: max_time
}, handleResponse);
}
return source;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Visualizing Papertrail</title>
<style>
@import url(http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:400,700);
@import url(style.css);
#chart1 { min-height: 155px; }
</style>
<script src="http://square.github.com/cubism/d3.v2.js"></script>
<script src="http://square.github.com/cubism/cubism.v1.js"></script>
<script src="cubism.papertrail.js"></script>
</head>
<body>
<div id="body">
<h1>Visualizing Papertrail</h1>
<h2>with cubism.js</h2>
<div id="chart1"></div>
<script>
var context = cubism.context()
.serverDelay(2 * 1000)
.clientDelay(7 * 1000)
.step(2000)
.size(960);
var papertrail = context.papertrail("<your-api-key>");
var optimize_events1 = papertrail.metric("metric:optimize.time events1 -passenger.log",
"optimize events1");
var optimize_events2 = papertrail.metric("metric:optimize.time events2 -passenger.log",
"optimize events2");
var metrics = [
papertrail.metric("search1", "search1 title"),
papertrail.metric("search2", "search2 title"),
];
d3.select("#chart1").call(function(div) {
div.append("div")
.attr("class", "axis")
.call(context.axis().orient("top"));
div.selectAll(".horizon")
.data(metrics)
.enter().append("div")
.attr("class", "horizon")
.call(context.horizon());
div.append("div")
.attr("class", "rule")
.call(context.rule());
});
// On mousemove, reposition the chart values to match the rule.
context.on("focus", function(i) {
d3.selectAll(".value").style("right", i == null ? null : context.size() - i + "px");
});
</script>
</div>
</body>
</html>
html {
min-width: 1040px;
}
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
margin: auto;
margin-top: 40px;
margin-bottom: 4em;
width: 960px;
}
#body {
position: relative;
}
footer {
font-size: small;
margin-top: 8em;
}
aside {
font-size: small;
left: 780px;
position: absolute;
width: 180px;
}
#body > p, li > p {
line-height: 1.5em;
}
#body > p {
width: 720px;
}
#body > blockquote {
width: 640px;
}
li {
width: 680px;
}
a {
color: steelblue;
}
a:not(:hover) {
text-decoration: none;
}
pre, code, textarea {
font-family: "Menlo", monospace;
}
code {
line-height: 1em;
}
textarea {
font-size: 100%;
}
#body > pre {
border-left: solid 2px #ccc;
padding-left: 18px;
margin: 2em 0 2em -20px;
}
.html .value,
.javascript .string,
.javascript .regexp {
color: #756bb1;
}
.html .tag,
.css .tag,
.javascript .keyword {
color: #3182bd;
}
.comment {
color: #636363;
}
.html .doctype,
.javascript .number {
color: #31a354;
}
.html .attribute,
.css .attribute,
.javascript .class,
.javascript .special {
color: #e6550d;
}
svg {
font: 10px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
sup, sub {
line-height: 0;
}
q:before,
blockquote:before {
content: "“";
}
q:after,
blockquote:after {
content: "”";
}
blockquote:before {
position: absolute;
left: 2em;
}
blockquote:after {
position: absolute;
}
h1 {
font-size: 96px;
margin-top: .3em;
margin-bottom: 0;
}
h1 + h2 {
margin-top: 0;
}
h2 {
font-weight: 400;
font-size: 28px;
}
h1, h2 {
font-family: "Yanone Kaffeesatz";
text-rendering: optimizeLegibility;
}
#logo {
width: 122px;
height: 31px;
}
.axis {
font: 10px sans-serif;
}
.axis text {
-webkit-transition: fill-opacity 250ms linear;
}
.axis path {
display: none;
}
.axis line {
stroke: #000;
shape-rendering: crispEdges;
}
.horizon {
border-bottom: solid 1px #000;
overflow: hidden;
position: relative;
}
.horizon {
border-top: solid 1px #000;
border-bottom: solid 1px #000;
}
.horizon + .horizon {
border-top: none;
}
.horizon canvas {
display: block;
}
.horizon .title,
.horizon .value {
bottom: 0;
line-height: 30px;
margin: 0 6px;
position: absolute;
text-shadow: 0 1px 0 rgba(255,255,255,.5);
white-space: nowrap;
}
.horizon .title {
left: 0;
}
.horizon .value {
right: 0;
}
.line {
background: #000;
opacity: .2;
z-index: 2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment