# grafana api

本文旨在帮助了解 grafana api 调用方式，封装的代码不一定都是正确的，但可以覆盖绝大多数情况，后续需要慢慢完善。

## grafana 示例

![folder 示例](../../../../../docs/images/g_folder_img.png)
![dashbord 示例](../../../../../docs/images/g_dash_img.png)
![datasources 示例](../../../../../docs/images/g_datasources_img.png)
![panel 示例](../../../../../docs/images/g_panel_img.png)
![vars 示例](../../../../../docs/images/g_var_img.png)

## 相关链接

[Prometheus 官方文档](https://prometheus.io/docs/introduction/overview/)

[Prometheus 中文文档](https://prometheus.fuckcloudnative.io/di-san-zhang-prometheus/di-4-jie-cha-xun/basics)

[grafana 官方文档](https://grafana.com/docs/grafana/latest/whatsnew/)

## api 调用说明

### 前置说明

1. 文件夹名称: 只有一个，一个项目一个，名称存在全局
2. data sources: 为接口获取的数据源,调用接口时需要，一般 vars 配置在项目本地（从 grafana 中复制来的，再组合下格式），一般 panel json 中可以找到其使用了哪一个数据源
3. dashboard: 一个页面一个，名称存在具体的页面
4. panel: 一个 dashboard 中包含多个 panel, 一个页面存一份 panel json 配置（根据 dashboard 的 uuid 调接口获取），panel 需要关联页面的展示就需要把这些 id 写死在本地，一般会有一个 json 文件（panel_json_id.json），以页面（dashboard）来分组，用来存整个项目的 panel json id， 可以从 panel json 中获取其 id
5. Prometheus 与 MySql 获取 panel 数据的接口不一样，接口响应的数据格式也不一样
6. 开发时需要进入具体的 panel 编辑页或者视图页查看其调用了什么接口
7. panel json 中有些查询中包含了一些变量值，需要使用 vars 来进行替换

### 接口调用顺序

在封装的代码里可以找到这些接口，接口使用，所有接口使用的是 Basic Token

1. 获取文件夹
2. 获取数据源
3. 获取当前页面（dashboard）的所有 panel json 配置
4. 获取查询需要的变量数据（不一定有，看 dashboard 有没有查询条件）
5. 根据对应 panel 配置的获取其数据（有些需要传时间范围，为 query_range,有些只要传截止时间，为 query）

## 使用心得

1. 有些传时间范围的接口会出现参数都传了，grafana 有数据，但是我们调用的 api 却没有数据（这些接口大多发生在曲线图），此时大概率就是时间区间与步长不统一造成的，如：step 为 600（10min），获取 24h 内的数据，当前是 2022/1/24 19:31:30 ,31:30 代表在末尾存在一个 1 分 30 秒它不足 10min,应该要舍弃掉，舍弃掉后的结束时间在减 24h 来获取开始时间（这个时间获取的方法在封装里面有，可以直接用）
2. step 的值只能是 60 的整数倍或者 60 是其整数倍，60 指一分钟（1min）

## 以下演示基本用法：

参考分支：feature/y-console-dev1.0

封装在 common-utils/grafana , 在 y-console 中有基本使用
如：src/models/grafana.js src/common/grafana-utils

大致是以 dcp-storage 项目，分支为 feature/storage-v1.1.0 来封装的，实际使用建议去参考下（文件夹以 monitor 起始命名的，如：monitorAlarmDeal）。

```javascript
import { chartsQuery, varToString } from 'gac-common-utils/src/grafana/utils'
import { computeTimeInterval, accordingStepGetTimeRange } from 'gac-common-utils/src/grafana/time-utils'

// chartsQuery 方法目的是将panel json拿到，然后将里面的变量替换成实际的值,值只能是字符串，如果是数组就要用 | 连接。
// varToString 方法是数组转字符 [].jion('|'),
```

### 数据源为 prometheus 时：

```javascript
// 变量
dispatch({
  setType: setClusterArr, // 变量一般直接保存在页面中，使用 useState 接收就行
  type: 'grafana/getVar', // 表明是要获取变量
  payload: {
    ...qcomputTimeRange, // { start: '', end: '' } 这个传的是时间，注意传的是毫秒（时间戳）
  },
  // q (query)就是把处理好的panel json整个传进去
  q: chartsQuery(
    grafanaVars.$cluster, // 变量的查询json配置需要在项目中写死的，当然表达式还是来源grafana
  ),
})
// 变量本地的配置示例
export default {
  // $cluster 变量配置
  $cluster: {
    targets: [{ expr: 'ceph_health_status{job=~"ceph-exporter"}' }], // 变量的查询式，（来源grafana中dashboard：右上角的小齿轮 -> Variables ）
    type: 'var', // 表明查询的类型为变量
    flag: 1, // 该查询为激活状态（使用中）
    datasource: 'promxy', // 使用的数据源的名称 (切记要换啊)
    valKey: 'group', // 取这个key得数据 （举例：label_values(ceph_pool_objects_total, group)，这个表示 查询式为 ceph_pool_objects_total , valKey 为 group）
  },
}

// 数据 - query_range 接口
dispatch({
  setType: 'monitorClusterStatus/setCapacityData', // 需要将接口返回结果传给谁，可以在接收的方法进行数据格式的变化来适应页面展示，或者使用 useMemo来处理
  type: 'grafana/getMetric', // 表明是要获取数据
  isRange: true, // 指明是 query_range
  payload: {
    ...generateTimeRange, // { start: '', end: '' } 这个传的是时间，注意传的是毫秒（时间戳）
    ...timeRangeStep, // { step: 60 } 步长，就是点与点之间的时间，单位：秒
  },
  q: chartsQuery(
    pagePanels[PanelConfig.capacity], // 从接口获取到的 panel json，通过本地保存的id，取到它
    { $cluster: _cluster, $pool: _pool }, // 填充panle json中查询的变量值
  ),
})

// 数据 - query 接口
dispatch({
  setType: 'monitorSystemLook/setNodeStatus_pingAll', // 需要将接口返回结果传给谁，可以在接收的方法进行数据格式的变化来适应页面展示，或者使用 useMemo来处理
  type: 'grafana/getMetric',
  transformAttr: 'ip', // 就是将响应体中的数据按哪一个字段来组装到一起，响应体中有一个result字段（数组），数组的每一项又分为 metric（对象，这里面会有很多属性值）和 value（数组，时间序列的值）
  payload: {
    time: qcomputTimeRange.end, // 这个传的是时间，注意传的是毫秒
  },
  q: chartsQuery(
    pagePanels[PanelConfig.nodeStatus_ping], // 从接口获取到的 panel json，通过本地保存的id，取到它
    { $cluster: clusterAll }, // 填充panle json中查询的变量值
  ),
})
```

### 问答

```
Q: 我怎么知道查询时应该使用 query 还是 query_range ？
A：这位同学问得好，打开grafana页面，打开浏览器控制台，然后点击查看某一个panel，进入到具体的panel页面后，刷新页面，很容易就知道该使用哪一个了。

Q: 没有接口文档不好调试，能不能给一份接口文档？
A：这位同学问的也不错，从封装的api来看也没几个，关键在于panel json是灵活的，就造成它的响应体也是可变的，调试时一定要关注grafana页面的接口响应
```

### 数据源为 mysql 时

```javascript
// grafana 查询语句推荐 in (${var}) ,['a', 'b', 'c']需要拼接为 in ('a', 'b', 'c')，一样使用 varToString 来进行数组拼接字符串，因为 chartsQuery会将"a|b|c"转为 "'a','b','c'"

// 变量
dispatch({
  setType: setAlarmType, // 变量一般直接保存在页面中，使用 useState 接收就行
  type: 'grafana/getVarMySql', // 表明是要获取变量
  payload: {
    ...computTimeRange, // { from: '', to: '' } 这个传的是时间，注意传的是秒 (时间戳 / 1000)
  },
  // q (query)就是把处理好的panel json整个传进去
  q: chartsQuery(
    mysqlVars.$alertType, // 变量的查询json配置需要在项目中写死的，当然表达式还是来源grafana
    {}, // 一般变量的查询式中不会包含其他变量
    [],
    'rawSql', // 指明查询的key
    true, // 指明 mysql
  ),
})

// 变量本地的配置示例
export default {
  // $alertStatus 变量配置
  $alertStatus: {
    targets: [
      {
        rawSql: 'SELECT DISTINCT(alerttype) FROM alert_claim_history', // 变量的查询sql（来源grafana中dashboard：右上角的小齿轮 -> Variables ）
        refId: 'alertType', // 这个是响应体中 results 节点包含的key, 基本就是sql中 DISTINCT 中的字段
        format: 'table', // 就这样写
      },
    ],
    type: 'var-table', // 这个其实是自定义的，目的是告诉后面接口处理这个是table类的变量
    flag: 1, // 该查询为激活状态（使用中）
  },
}

// 数据
dispatch({
  setType: 'monitorAlarmDeal/setDataSourceList', // 需要将接口返回结果传给谁，可以在接收的方法进行数据格式的变化来适应页面展示，或者使用 useMemo来处理
  type: 'grafana/getMetricMySql', // 表明需要从mysql中获取数据
  payload: {
    ...computTimeRangeVars, // { from: '', to: '' } 这个传的是时间，注意传的是秒 (时间戳 / 1000)
    // 以下两个如果不知道怎么传，就参考 dcp-storage/src/common/grafana-utils/time.js，表格不是很关注这两个，但是必传
    intervalMs: rangeIntervalMsVars.intervalMs, // 步长， 单位：毫秒
    maxDataPoints: rangeIntervalMsVars.maxDataPoints, // 最大数据点个数
  },
  // q (query)就是把处理好的panel json整个传进去
  q: chartsQuery(
    pagePanels[PanelConfig.list], // 从接口获取到的 panel json，通过本地保存的id，取到它
    // 以下对象就是填充panle json中查询的变量值
    {
      $alertType: vars._alerttype,
      $claimstatus: vars._claimstatus,
      $keywords: vars._keywords,
    },
    [], // 这个固定这样传吧，因为一些历史原因，本意该条查询想保留某一些被排除的变量
    'rawSql', // 这个是panel json中查询所对应的字段，mysql 为 "rawSql"
    true, // 指明为mysql
  ),
})
```
