用ECharts绘制Prometheus图表,实现类似Grafana的自定义Dashboard
大家一般都是用Grafana自定义Dashboard来监控Prometheus数据的,作者这次尝试用ECharts来绘制Prometheus数据图表,一方面可以减少依赖,另一方面可以将监控界面灵活的集成进应用系统。至于如何在被监测机器上安装NodeExporter以及如何部署Prometheus作者就不描述了,园子里有很多文章介绍。
一、数据查询及转换
Prometheus提供了Http Api来执行promql查询,但需要将返回的数据格式转换为ECharts的格式,好在EChars的xAxis.type可以设置为'time'类型,与Prometheus返回的格式接近。作者写了个简单的服务来执行查询及转换数据,详见以下代码:
public class MetricService
{
private static readonly HttpClient http = new HttpClient()
{
//请修改指向Prometheus地址
BaseAddress = new Uri("http://10.211.55.2:9090/api/v1/"),
Timeout = TimeSpan.FromSeconds(2)
};
public async Task<object> GetCpuUsages(string node, DateTime start, DateTime end)
{
var promql = $"100-irate(node_cpu{{instance='{node}:9100',mode='idle'}}[5m])*100";
return await QueryRange(promql, start, end, 20, 2);
}
public async Task<object> GetMemUsages(string node, DateTime start, DateTime end)
{
var promql = $"(1-(node_memory_MemAvailable{{instance='{node}:9100'}}/(node_memory_MemTotal{{instance='{node}:9100'}})))*100";
return await QueryRange(promql, start, end, 20, 2);
}
public async Task<object> GetNetTraffic(string node, DateTime start, DateTime end)
{
var downql = $"irate(node_network_receive_bytes{{instance='{node}:9100',device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}}[5m])";
var ls = await QueryRange(downql, start, end, 15/*4*/, 0);
var upql = $"irate(node_network_transmit_bytes{{instance='{node}:9100',device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}}[5m])";
ls.Add(await QueryRange(upql, start, end, 15/*4*/, 0));
return ls;
}
public async Task<object> GetDiskIO(string node, DateTime start, DateTime end)
{
var readql = $"irate(node_disk_bytes_read{{instance='{node}:9100'}}[1m])";
var ls = await QueryRange(readql, start, end, 15/*10*/, 0);
var writeql = $"irate(node_disk_bytes_written{{instance='{node}:9100'}}[1m])";
ls.Add(await QueryRange(writeql, start, end, 15/*10*/, 0));
return ls;
}
#region ====Parse PromQL====
private static async Task<List<object>> QueryRange(string promql, DateTime start, DateTime end, int step, int round)
{
if (start >= end) throw new ArgumentOutOfRangeException();
var ts1 = (int)(start.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds;
var ts2 = (int)(end.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds;
var res = await http.GetAsync($"query_range?query={promql}&start={ts1}&end={ts2}&step={step}s");
var stream = await res.Content.ReadAsStreamAsync();
using (var sr = new System.IO.StreamReader(stream))
using (var jr = new JsonTextReader(sr))
{
return ParseToSeries(jr, round);
}
}
private static List<object> ParseToSeries(JsonTextReader jr, int round)
{
if (!jr.Read() || jr.TokenType != JsonToken.StartObject) throw new Exception();
if (!jr.Read() || jr.TokenType != JsonToken.PropertyName || (string)jr.Value != "status")
throw new Exception();
var status = jr.ReadAsString();
if (status != "success") throw new Exception();
if (!jr.Read() || jr.TokenType != JsonToken.PropertyName || (string)jr.Value != "data")
throw new Exception();
if (!jr.Read() || jr.TokenType != JsonToken.StartObject) throw new Exception();
if (!jr.Read() || jr.TokenType != JsonToken.PropertyName || (string)jr.Value != "resultType")
throw new Exception();
var resultType = jr.ReadAsString();
if (!jr.Read() || jr.TokenType != JsonToken.PropertyName || (string)jr.Value != "result")
throw new Exception();
return ReadResultArray(jr, round);
//No need read others
}
private static List<object> ReadResultArray(JsonTextReader jr, int round)
{
if (!jr.Read() || jr.TokenType != JsonToken.StartArray) throw new Exception();
var ls = new List<object>();
do
{
if (!jr.Read()) throw new Exception();
if (jr.TokenType == JsonToken.EndArray) break;
if (jr.TokenType != JsonToken.StartObject) throw new Exception();
ls.Add(ReadResultItem(jr, round));
} while (true);
return ls;
}
private static List<double[]> ReadResultItem(JsonTextReader jr, int round)
{
//已读取StartObject标记
if (!jr.Read() || jr.TokenType != JsonToken.PropertyName || (string)jr.Value != "metric")
throw new Exception();
ReadMetric(jr);
if (!jr.Read() || jr.TokenType != JsonToken.PropertyName || (string)jr.Value != "values")
throw new Exception();
var values = ReadValues(jr, round);
if (!jr.Read() || jr.TokenType != JsonToken.EndObject) throw new Exception();
return values;
}
private static void ReadMetric(JsonTextReader jr)
{
if (!jr.Read() || jr.TokenType != JsonToken.StartObject) throw new Exception();
do
{
//PropertyName or EndObject
if (!jr.Read()) throw new Exception();
if (jr.TokenType == JsonToken.EndObject) return;
//PropertyValue
jr.Read();
} while (true);
}
private static List<double[]> ReadValues(JsonTextReader jr, int round)
{
if (!jr.Read() || jr.TokenType != JsonToken.StartArray) throw new Exception();
var ls = new List<double[]>();
do
{
if (!jr.Read()) throw new Exception();
if (jr.TokenType == JsonToken.EndArray) break;
if (jr.TokenType != JsonToken.StartArray) throw new Exception();
var ts = jr.ReadAsDouble().Value * 1000; //PromQL时间*1000
var value = Math.Round(double.Parse(jr.ReadAsString()), round, MidpointRounding.ToEven); //PromQL值为字符串
ls.Add(new double[] { ts, value });
if (!jr.Read() || jr.TokenType != JsonToken.EndArray) throw new Exception();
} while (true);
return ls;
}
#endregion
}
Tip: promql的写法可参考grafana网站相关Dashboard。
二、单指标Vue组件
作者使用Vue-ECharts作为ECharts的包装,以CPU使用率Vue组件为例:
<v-chart theme="dark" autoresize :options="chartOptions" style="height:250px">
</v-chart>
@Component
export default class CpuUsages extends Vue {
/** 目标实例IP */
@Prop({ type: String, default: '10.211.55.3' }) node
/** 开始时间 */
@Prop({ type: Date, default: () => { var now = new Date(); return new Date(now.getFullYear(), now.getMonth(), now.getDate()) } }) start
/** 结束时间 */
@Prop({ type: Date, default: () => { return new Date() } }) end
chartOptions = {
title: { text: 'Cpu Usages', x: 'center' },
tooltip: { trigger: 'axis' },
xAxis: { type: 'time' },
yAxis: { min: 0, max: 100 },
series: []
}
refresh() {
sys.Services.MetricService.GetCpuUsages(this.node, this.start, this.end).then(res => {
this.chartOptions.series.splice(0)
for (var i = 0; i < res.length; ++i) {
var seria = { type: 'line', name: 'cpu' + i, data: res[i], showSymbol: false }
this.chartOptions.series.push(seria)
}
}).catch(err => {
this.$message(err)
})
}
mounted() {
this.refresh()
}
}
三、组合多个组件形成Dashboard
根据需要可以灵活组合多个指标组件,形成相应的Dashboard界面(如下图所示)。

四、小结
感谢Vue、ECharts、Vue-ECharts、Prometheus等项目,使得开发并集成监控Dashboard如此简单。另码文不易,码技术文更不易,所以请您多多推荐!
用ECharts绘制Prometheus图表,实现类似Grafana的自定义Dashboard的更多相关文章
- 如何快速使用ECharts绘制可视化图表
1.在ECharts官网,下载ECharts的源码和示例文件. 2.解压缩下载下来的Echars压缩包,找到doc\example\www\echartsjs目录,将里面的js文件全部取出来,放到项目 ...
- echarts生成的图表大小怎么随屏幕的大小改变自适应
最近在做图表,记录一下用到的知识点,当做自己的日记吧,会不断添加新内容 1,echarts生成的图表大小怎么随屏幕的大小改变自适应? this.chart.setOption(this.options ...
- 使用echarts绘制漂亮的渐变键盘仪表盘
echarts官方示例和默认样式都比较难看,经过一顿捣鼓实现比较漂亮的渐变仪表盘. 第一步:设置轴线 将图表轴线.label.分割线.隐藏,只保留刻度,然后修改刻度样式达到最终效果.不过要注意的是ax ...
- 使用echarts绘制条形图和扇形图
使用echarts绘制条形图和扇形图 简单举例说明下echarts如何绘制条形图和扇形图 代码示例 <!doctype html> <html lang="en" ...
- 前端 | 使用 ECharts 绘制关系图
0 需求 做的项目需要画一个关系图,主要需求如下: 需要展示6种对象之间的关系:数据机构 数据 合约 模型 计算机构 应用 支持突出显示6种对象中的某一种的所有对象 支持Top x子图功能.top x ...
- 使用highcharts 绘制Web图表
问题描述: 使用highcharts 绘制Web图表 Highcharts说明: 问题解决: (1)安装Highcharts 在这些图表中,数据源是一个典型的JavaScrip ...
- Python使用plotly绘制数据图表的方法
转载:http://www.jb51.net/article/118936.htm 本篇文章主要介绍了Python使用plotly绘制数据图表的方法,实例分析了plotly绘制的技巧. 导语:使用 p ...
- 利用ichart绘制网页图表
首先,最好的教程在这里:ichartjs 有了这个网站,要绘制网页图表简直方便愉快! 接下来说一下使用方法~~~ 进入网站,点击在线设计器 在线设计器的使用方法就不说了,摸索一下就会了!关键在于两个地 ...
- [k8s]容器化node-expolore(9100)+cadvisor(8080)+prometheus(9090) metric搜集,grafana展示
Prometheus 的核心,多维数据模型 传统监控工具统计数据方式 指标多 - 需求1,统计app1-3,的(总)内存,则定义3个指标 container.memory_usage_bytes.we ...
随机推荐
- 深入浅出Ajax
原文(我的GitHub):https://github.com/liangfengbo/frontend-ability/issues/1 学习大纲 理解Ajax的工作原理 Ajax核心-XMLHtt ...
- 使用 Cake 推送 NuGet 包到 AzureDevops 的 Artifacts 上
前言 大家好,我最近在想如何提交代码的时候自动的打包 NuGet 然后发布到 AzureDevOps 中的 Artifacts,在这个过程中踩了很多坑,也走了很多弯路,所以这次篇文章就是将我探索的结果 ...
- URL收集
window下 php5.5 安装pthread扩展:http://blog.csdn.net/aoyoo111/article/details/19020161
- Python 爬虫从入门到进阶之路(八)
在之前的文章中我们介绍了一下 requests 模块,今天我们再来看一下 Python 爬虫中的正则表达的使用和 re 模块. 实际上爬虫一共就四个主要步骤: 明确目标 (要知道你准备在哪个范围或者网 ...
- AIX/Linux/HP-UX查看CPU/内存/磁盘/存储命令
1.1 硬件环境验证方式 硬件环境主要包括CPU.内存.磁盘/存储.网络设备(如F5等).系统特有设备(如密押设备等)等,其中网络设备和系统特有设备由网络管理员或项目组提供为准,本节主要关注CP ...
- 关于AI本质的思考
前言 最近几天和一位朋友探讨了一下现阶段的人工智能以及未来发展,并且仔细重读了尤瓦尔赫拉利的“简史三部曲”,产生了一些关于AI的新想法,觉得有必要整理出来. 程序.AI的本质 现代的计算机都是基于图灵 ...
- 【Mysql】细节补充,约束、索引等
约束: 显示建表语句:show create table 表名 查询表中的约束:SELECT * FROM information_schema.`TABLE_CONSTRAINTS` where ...
- HDU 6053:TrickGCD(莫比乌斯反演)
题目链接 题意 给出n个数,问在这n个数里面,有多少组bi(1<=bi<=ai)可以使得任意两个bi不互质. 思路 想法就是枚举2~min(ai),然后去对于每个ai都去除以这些质数,然后 ...
- python数据库-MySQL与python的交互
一.python3中安装PyMySQL模块 命令安装: sudo apt-get install python-mysql 或者 pip install pymysql 2.使用在pyCharm中安装 ...
- 6.秋招复习简单整理之请你谈谈JDBC的反射,以及它的作用?
通过反射com.mysql.jdbc.Driver类,实例化该类时会调用该类的静态代码块,该代码块会去java的DriverManager类中注册自己,DriverManager管理所有已注册的驱动类 ...