2a58bcf5f1
history: Frontend generated node charts with graphite backend Resolution for bower conflict Let the user configure the metrics in config.json Fix grunt task for deploying c3 css Fix tooltip layout issue
256 lines
6.9 KiB
JavaScript
256 lines
6.9 KiB
JavaScript
define(["c3", "d3"], function (c3, d3) {
|
|
|
|
var charts = function (node, config) {
|
|
this.node = node
|
|
|
|
this.chartConfig = config
|
|
this.chart = null
|
|
|
|
this.zoomConfig = {
|
|
"levels": [
|
|
{"label": "8h", "from": 8 * 3600, "interval": 15 * 60}
|
|
/*{"label": "24h", "from": "26h", "interval": "1h"},
|
|
{"label": "1m", "from": "1mon", "interval": "1d"},
|
|
{"label": "1y", "from": 31536000, "interval": 2628000}*/
|
|
]
|
|
}
|
|
this.zoomLevel = 0
|
|
|
|
this.c3Config = {
|
|
"size": {
|
|
"height": 240
|
|
},
|
|
padding: {
|
|
bottom: 30
|
|
},
|
|
"legend": {
|
|
"item": {
|
|
"onclick": function (id) {
|
|
this.api.hide()
|
|
this.api.show(id)
|
|
}
|
|
}
|
|
},
|
|
"tooltip": {
|
|
"format": {
|
|
"value": this.c3FormatToolTip.bind(this)
|
|
}
|
|
},
|
|
"axis": {
|
|
"x": {
|
|
"type": "timeseries",
|
|
"tick": {
|
|
"format": this.c3FormatXAxis.bind(this),
|
|
"rotate": -45
|
|
}
|
|
},
|
|
"y": {
|
|
"min": 0,
|
|
"padding": {
|
|
"bottom": 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.cache = []
|
|
|
|
this.init()
|
|
}
|
|
|
|
charts.prototype = {
|
|
|
|
init: function () {
|
|
// Workaround for endless loop bug
|
|
if (this.c3Config.axis.x.tick && this.c3Config.axis.x.tick.format && typeof this.c3Config.axis.x.tick.format === "function") {
|
|
if (this.c3Config.axis.x.tick.format && !this.c3Config.axis.x.tick._format)
|
|
this.c3Config.axis.x.tick._format = this.c3Config.axis.x.tick.format
|
|
this.c3Config.axis.x.tick.format = function (val) {
|
|
return this.c3Config.axis.x.tick._format(val)
|
|
}.bind(this)
|
|
}
|
|
|
|
// Configure metrics
|
|
this.c3Config.data = {
|
|
"keys": {
|
|
"x": "time",
|
|
"value": this.chartConfig.metrics.map(function (metric) {
|
|
return metric.id
|
|
})
|
|
},
|
|
"colors": this.chartConfig.metrics.reduce(function (collector, metric) {
|
|
collector[metric.id] = metric.color
|
|
return collector
|
|
}, {}),
|
|
"names": this.chartConfig.metrics.reduce(function (collector, metric) {
|
|
collector[metric.id] = metric.label
|
|
return collector
|
|
}, {}),
|
|
"hide": this.chartConfig.metrics.map(function (metric) {
|
|
return metric.id
|
|
}).filter(function (id) {
|
|
return id !== this.chartConfig.defaultMetric
|
|
}.bind(this))
|
|
}
|
|
},
|
|
|
|
render: function () {
|
|
var div = document.createElement("div")
|
|
div.classList.add("chart")
|
|
var h4 = document.createElement("h4")
|
|
h4.textContent = this.chartConfig.name
|
|
div.appendChild(h4)
|
|
|
|
|
|
// Render chart
|
|
this.load(function (data) {
|
|
div.appendChild(this.renderChart(data))
|
|
|
|
// Render zoom controls
|
|
if (this.zoomConfig.levels.length > 0)
|
|
div.appendChild(this.renderZoomControls())
|
|
|
|
}.bind(this))
|
|
|
|
return div
|
|
},
|
|
|
|
renderChart: function (data) {
|
|
this.c3Config.data.json = data
|
|
this.chart = c3.generate(this.c3Config)
|
|
return this.chart.element
|
|
},
|
|
|
|
updateChart: function (data) {
|
|
this.c3Config.data.json = data
|
|
this.chart.load(this.c3Config.data)
|
|
},
|
|
|
|
renderZoomControls: function () {
|
|
// Draw zoom controls
|
|
var zoomDiv = document.createElement("div")
|
|
zoomDiv.classList.add("zoom-buttons")
|
|
|
|
var zoomButtons = []
|
|
this.zoomConfig.levels.forEach(function (v, level) {
|
|
var btn = document.createElement("button")
|
|
btn.classList.add("zoom-button")
|
|
btn.setAttribute("data-zoom-level", level)
|
|
|
|
if (level === this.zoomLevel)
|
|
btn.classList.add("active")
|
|
|
|
btn.onclick = function () {
|
|
if (level !== this.zoomLevel) {
|
|
zoomButtons.forEach(function (v, k) {
|
|
if (level !== k)
|
|
v.classList.remove("active")
|
|
else
|
|
v.classList.add("active")
|
|
})
|
|
this.setZoomLevel(level)
|
|
}
|
|
}.bind(this)
|
|
btn.textContent = v.label
|
|
zoomButtons[level] = btn
|
|
zoomDiv.appendChild(btn)
|
|
}.bind(this))
|
|
return zoomDiv
|
|
},
|
|
|
|
setZoomLevel: function (level) {
|
|
if (level !== this.zoomLevel) {
|
|
this.zoomLevel = level
|
|
this.load(this.updateChart.bind(this))
|
|
}
|
|
},
|
|
|
|
load: function (callback) {
|
|
if (this.cache[this.zoomLevel])
|
|
callback(this.cache[this.zoomLevel])
|
|
else {
|
|
var urls = []
|
|
var id = this.node.nodeinfo.node_id
|
|
var unixStamp = Math.floor(Date.now() / 1000)
|
|
var zoomConfig = this.zoomConfig.levels[this.zoomLevel]
|
|
|
|
this.chartConfig.metrics.forEach(function(metric) {
|
|
var parameters = [
|
|
"start=" + (unixStamp - zoomConfig.from),
|
|
"end=" + unixStamp,
|
|
"step=" + zoomConfig.interval,
|
|
"query=" + metric.query.replace("{{NODE_ID}}", id)
|
|
]
|
|
|
|
var url = this.chartConfig.url + "?" + parameters.join("&")
|
|
|
|
urls.push(url)
|
|
|
|
}.bind(this))
|
|
|
|
Promise.all(urls.map(getJSON)).then(function (data) {
|
|
this.cache[this.zoomLevel] = this.parse(data)
|
|
callback(this.cache[this.zoomLevel])
|
|
}.bind(this))
|
|
}
|
|
},
|
|
|
|
parse: function (results) {
|
|
var data = []
|
|
results[0].data.result[0].values.forEach(function (tp) {
|
|
var time = {"time": new Date(tp[0] * 1000)}
|
|
results.forEach(function(result) {
|
|
var metric = this.chartConfig.metrics[results.indexOf(result)]
|
|
var index = results[0].data.result[0].values.indexOf(tp)
|
|
time[metric.id] = this.formatValue(metric.id, result.data.result[0].values[index][1])
|
|
}.bind(this))
|
|
data.push(time)
|
|
}.bind(this))
|
|
return data
|
|
},
|
|
|
|
c3FormatToolTip: function (d, ratio, id) {
|
|
switch (id) {
|
|
case "uptime":
|
|
return d.toFixed(1) + " Tage"
|
|
default:
|
|
return d
|
|
}
|
|
},
|
|
|
|
c3FormatXAxis: function (d) {
|
|
var pad = function (number, pad) {
|
|
var N = Math.pow(10, pad)
|
|
return number < N ? ("" + (N + number)).slice(1) : "" + number
|
|
}
|
|
switch (this.zoomLevel) {
|
|
case 0: // 8h
|
|
case 1: // 24h
|
|
return pad(d.getHours(), 2) + ":" + pad(d.getMinutes(), 2)
|
|
case 2: // 1m
|
|
return pad(d.getDate(), 2) + "." + pad(d.getMonth() + 1, 2)
|
|
case 3: // 1y
|
|
return ["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"][d.getMonth()]
|
|
default:
|
|
break
|
|
}
|
|
},
|
|
|
|
formatValue: function (id, value) {
|
|
switch (id) {
|
|
case "loadavg":
|
|
return (d3.format(".2r")(value))
|
|
case "clientcount":
|
|
return (Math.ceil(value))
|
|
case "uptime":
|
|
return (value / 86400)
|
|
default:
|
|
return value
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return charts
|
|
})
|