最近在使用Grafana的時候,發現Grafana功能比想象中要強大,除了配合Prometheus使用之外,他自身都可以做很多事情,視覺化和監控平臺,還可以直接根據使用者自定義的告警規則完成告警和進行各種通知。於是在深入學習了一段時間之後,整理成此博文。溫馨提示,本文約1.3w字,幾十張範例圖片並且含描述。
Grafana是一個開源的資料視覺化和監控平臺,它可以幫助使用者通過建立儀表盤和圖表來實時監控和分析資料。Grafana支援多種資料來源,包括Prometheus、Graphite、InfluxDB、Elasticsearch等,使用者可以通過Grafana將這些資料來源的資料整合在一起,進行統一的視覺化展示和分析。
Grafana提供了豐富的圖表型別和外掛,使用者可以根據自己的需求客製化各種圖表和儀表盤,以便更直觀地瞭解資料的趨勢和變化。此外,Grafana還支援告警功能,使用者可以設定告警規則並及時收到通知,以便及時處理問題。
總的來說,Grafana是一個功能強大的資料視覺化和監控平臺,適用於各種場景,包括IT運維、應用效能監控、工業物聯網等領域。它的開源特性也使得使用者可以根據自己的需求進行客製化和擴充套件。
儀表盤建立有如下三個核心設定點:
Grafana結合Prometheus和Alertmanager在我之前寫的文章中 已經講解過了,這裡就主要寫對接MySql直接實現儀表盤建立和告警推播。
Prometheus+Grafana+Alertmanager實現告警推播教學 ----- 圖文詳解
點選data sources -> add new source->搜尋mysql->填寫mysql的連結資訊即可。
選擇首頁-》儀表盤-》新建-》填寫對應資訊即可。
我們可以在上面的建立的面板中,建立對應的儀表盤,點選新增,即可建立。
對應的主要功能的說明在下列圖例彙總,這裡就不過多描述了。
這裡下列為了方便演示,統一使用如下sql資料進行。
建立的sql和範例資料語句:
-- 建立學生表
CREATE TABLE student (
id INT AUTO_INCREMENT PRIMARY KEY,
birthday_time DATETIME,
NAME VARCHAR(50),
classId INT,
source INT
);
-- 插入範例資料
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-01-01', '張三', 1, 80);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-02-02', '李四', 2, 75);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-03-03', '王五', 1, 90);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-04-04', '趙六', 3, 85);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-05-05', '小明', 2, 78);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-06-06', '小紅', 1, 92);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-07-07', '小剛', 3, 87);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-08-08', '小美', 2, 79);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-09-09', '小麗', 1, 88);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-10-10', '小強', 3, 82);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-11-11', '小雨', 2, 91);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-12-12', '小霞', 1, 76);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-01-13', '小風', 3, 83);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-02-14', '小雪', 2, 89);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-03-15', '小玲', 1, 84);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-04-16', '小華', 3, 77);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-05-17', '小龍', 2, 86);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-06-18', '小虎', 1, 93);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-07-19', '小燕', 3, 81);
INSERT INTO student (birthday_time, NAME, classId, source) VALUES ('2015-08-20', '小菲', 2, 74);
這裡為了方便演示,下列所有的我都是用code模式,也就是直接貼上sql語句。將寫好的sql貼上去之後,點選run query,然後選擇對應的圖表即可顯示。
查詢的sql
select * from student;
顯示的圖表
這裡我們也是用一個分詞簡單易懂的查詢來展示吧。
查詢的sql
SELECT
UNIX_TIMESTAMP(birthday_time) AS time_sec,
sum(source) AS VALUE,
CONCAT(classId,"班級") AS metric
FROM student
GROUP BY classId
ORDER BY birthday_time ASC
顯示的效果
點選suggestions可以切換各種推薦圖表
這裡我們就統計一下學生的月份生日數量。這裡我們需要用$__timeGroup
和$__timeFilter
函數,它們是Grafana的函數,他們的用法如下:
$__timeGroup
函數用於將時間範圍分組成特定的時間間隔,例如每分鐘、每小時、每天等。這個函數通常用於建立時間序列圖表,幫助使用者將資料按照時間間隔進行聚合和展示。$__timeFilter
函數用於過濾特定的時間範圍,使用者可以使用該函數來限制資料的時間範圍,例如只顯示最近一小時的資料或者只顯示某個特定時間段的資料。除了 $__timeGroup
和 $__timeFilter
,Grafana 還提供了其他與時間相關的內建函數,例如:
$__timeFrom()
:用於指定起始時間,可以指定相對時間(例如「now-1h」表示當前時間的一小時前)或絕對時間(例如「2023-11-14T00:00:00」表示具體的時間點)。$__timeTo()
:用於指定結束時間,同樣可以指定相對時間或絕對時間。$__timeFilterGroup()
:用於根據時間範圍過濾資料,並且可以結合其他過濾條件使用。查詢的Sql
SELECT
$__timeGroup(birthday_time, '1M') AS time_sec,
COUNT(id) AS '數量'
FROM student
WHERE $__timeFilter(birthday_time)
GROUP BY time_sec
對應的範例圖:
這裡我們就根據統計上述的統計在加一條線,比如我這邊想看1班的情況。
查詢的sql
SELECT
$__timeGroup(birthday_time, '1M') AS time_sec,
COUNT(id) AS '數量',
SUM(classId =1 ) as '1班'
FROM student
WHERE $__timeFilter(birthday_time)
GROUP BY time_sec
範例圖:
根據上述的過程中,我們發現一點問題,上述的查詢sql是寫死的,那麼我們有辦法增加一個變數去統計呢,比如上述的資料中我們想查單獨看1班的資料或2班的資料。那麼這裡我們就要用到Grafana的一個變數,他可以自定義變數,還可以設定變數。整體操作如下範例圖,相關注釋已寫。
這裡我們還是根據上述的統計來進行改造,這裡我們設定一個變數,支援按照班級統計。
對應的查詢SQL
SELECT
$__timeGroup(birthday_time, '1M') AS time_sec,
COUNT(id) AS '數量'
FROM student
WHERE $__timeFilter(birthday_time)
and classId = $class_name
GROUP BY time_sec
上述範例中,我們新增了單條篩選,這裡我們來新增多條篩選,以及新增自定義的常數資料來進行
篩選,常數資料設定如下範例圖:
這裡在順便說下右邊的兩個小圖示的含義:
查詢的SQL如下:
SELECT
$__timeGroup(birthday_time, '1M') AS time_sec,
COUNT(id) AS '數量'
FROM student
WHERE $__timeFilter(birthday_time)
and classId in ($class_names)
GROUP BY time_sec
上述範例中,我們已經完成基本的簡單的建立,可以最基本的完成我們想要的圖表。
如果我們想要完成更復雜一點的,比如將每個Y軸指標通過頁面篩選呈現多條狀態資料,並可以根據頁面篩選進行資料圖表呈現。也就是我根據上述的多選條件,來生成對應的則線圖,選擇幾個就生成幾條。
這裡的設定會稍微複雜一些。
查詢的SQL:
SELECT
$__timeGroup(birthday_time, '1M') AS time_sec,
CAST(classId AS CHAR) as '班級',
COUNT(classId) AS '數量'
FROM student
WHERE $__timeFilter(birthday_time)
and classId in ($class_names)
GROUP BY classId,time_sec
注: 如果統計的欄位型別是整型,需要進行轉換,否則會不生效!
這裡順便在說下樣式之類的調整,在右邊的選項中往下拉,可以對圖表進行樣式調整,相關說明都在範例圖中了。
我們要實現上述的功能,需要用到Transform功能,範例圖如下:
最終演示的效果圖圖如下:
上述的面板的json檔案下載地址: https://files.cnblogs.com/files/xuwujing/測試面板-1699947693208.json?t=1699952258&download=true
上述的圖例中,我們完成了基本的圖例建立,這裡我們就稍微玩點高階一點點圖表,根據下拉多選的資料,自動建立多個面板。
查詢sql
SELECT
$__timeGroup(birthday_time, '1M') AS time_sec,
CAST(classId AS CHAR) as '班級',
COUNT(classId) AS '數量'
FROM student
WHERE $__timeFilter(birthday_time)
and classId in ($class_names)
GROUP BY classId,time_sec
設定基於選擇的值進行重複查詢並針對變數每個成員應用到新面板.
最終效果圖如下:
Grafana的告警核心設定:
首先需要明確使用什麼方式進行聯絡,目前使用比較多的是企微、釘釘和飛書,其中除了飛書稍微麻煩一點外,需要一箇中轉服務,企微和釘釘不需要直接對接即可。
對接釘釘和企微畢竟簡單,下拉選擇釘釘和微信,然後填寫對應資訊即可。
對接飛書比較麻煩一點,Grafana並不支援接入飛書,所以需要通過一箇中轉服務來對Grafana的資料進行轉換成飛書需要的資料格式,然後進行告警訊息推播。
這裡選擇webhook,然後填寫中轉服務的地址,中轉服務只需要提供一個對應的介面地址即可,然後次http介面裡面需要將接收Grafana請求的資料傳輸到飛書。
飛書對應的檔案連結:
https://www.feishu.cn/hc/zh-CN/articles/807992406756-webhook-觸發器
https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
中轉服務程式碼範例如下:
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/**
* @Author pancm
* @Description 飛書告警中轉服務
* @Date 2023/9/11
* @Param
* @return
**/
@RestController
@RequestMapping("alert")
@Slf4j
public class AlertController {
@Value("${feishu.webhook-url:https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxx}")
private String webHookUrl;
@Autowired
private RestTemplate restTemplate;
private String msg = "告警狀態: %s\n告警標題:%s\n告警描述:%s\n面板URL:%s\n發生時間:%s\n負責人: %s ";
@RequestMapping("/webhook/receive")
public void receive(@RequestBody String message) {
log.info("AlertController.receive 獲取的Grafana的告警資料:{}", message);
Map<String, Object> m = createMessage(getText(message));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
log.info("AlertController.receive 傳送飛書的告警內容:{}", m);
restTemplate.postForEntity(webHookUrl, new HttpEntity<>(m, headers), Void.class);
}
/**
* 構建通知報文
*
* @param content
* @return
*/
protected Map<String, Object> createMessage(String content) {
Map<String, Object> messageJson = new HashMap<>();
messageJson.put("msg_type", "text");
Map<String, Object> text = new HashMap<>();
text.put("text", content);
messageJson.put("content", text);
Long timestamp = System.currentTimeMillis() / 1000;
messageJson.put("timestamp", timestamp);
return messageJson;
}
/**
* 構建文字內容
*
* @param body
* @return
*/
private String getText(String body) {
JSONObject json = JSON.parseObject(body);
JSONObject alertObject = json.getJSONArray("alerts").getJSONObject(0);
String senMsg = String.format(msg,
getStatusMsg(json.getString("status")),
json.getString("title"),
json.getJSONObject("commonAnnotations").getString("description"),
alertObject.getString("panelURL"),
DateUtil.now(),
json.getJSONObject("commonAnnotations").getString("handler")
);
return senMsg;
}
private String getStatusMsg(String msg) {
if ("firing".equals(msg)) {
return "告警產生";
}
return "告警恢復";
}
}
告警的規則建立有兩種方式
直接在對應的儀表盤建立
在告警規則裡面建立
主要是將告警聯絡點和告警規則進行關聯匹配。
靜默規則設定
注:靜默時間的設定,存在時區情況,具體情況請看Grafana設定的時區。
這裡我們就隨便寫一個告警sql,讓他滿足觸發告警,比如主要有學生成績低於90分就進行告警
告警SQL如下
SELECT
COUNT(1) AS c
FROM student
WHERE
source < 90
設定範例圖如下:
最終在飛書群裡傳送的訊息如下:
https://grafana.com/docs/grafana/latest/datasources/mysql/
https://grafana.com/grafana/plugins/
https://grafana.com/docs/grafana/latest/developers
https://grafana.com/docs/grafana/latest/developers/http_api/dashboard/
https://blog.csdn.net/u014756339/article/details/107816038
https://blog.csdn.net/weixuan_/article/details/131848725
https://zhuanlan.zhihu.com/p/580145725
https://blog.csdn.net/qq_23598037/article/details/99850396
https://docs.aws.amazon.com/zh_cn/grafana/latest/userguide/v9-alerting-explore-labels-matching.html
一首很帶感的動漫鋼琴曲~
原創不易,如果感覺不錯,希望給個推薦!您的支援是我寫作的最大動力!
版權宣告:
作者:虛無境
部落格園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm
個人部落格出處:https://xuwujing.github.io/