雖然經由前面的章節已經可以形成一個完整的 Panel 或 Dashboard,但很多時候我們真正使用的資料數據經過一系列的 query filter 還不夠,需要更近一步地運算處理。或是一個圖表中希望疊加不同的資料圖層,顯示更顯眼的特定資料標示。因此 Grafana 也提供了兩組非常實用的 API - SceneDataTransformer 及 SceneDataLayerSet,讓使用者在進行資料呈現時可以有更多元化的自定義。
在第三章【Grafana 原始資料到可視化的層層遞進】中有介紹到關於 Data source 的 Transform 行為,同樣的我們也可以在 Grafana Scenes 中以程式碼的方式轉換 Data。在 Grafana Scenes 中以 SceneDataTransformer 進行資料轉換,包括篩選、聚合、重組等各種操作,最終輸出可以直接用於圖表渲染的資料格式。
由於 Grafana Scenes 中的各個實例皆繼承了 SceneObject 的屬性,其中包括了 $data 的屬性,因此如果已經在 SceneFlexLayout 或 SceneFlexItem 層設定了 SceneQueryRunner,也可以在 Panel 的 setData 或 $data 中以 SceneDataTransformer 所轉換的資料重新賦值。以下範例:
new SceneFlexItem({
$data: new SceneQueryRunner({...}),
body: PanelBuilders.stat()
.setTitle('Transformed data')
.setData(new SceneDataTransformer({...}))
.build(),
}),
或者可以在各個 Panel 中,又或是整個 Scene 皆需使用轉換的資料時, 直接在 $data 以 SceneDataTransformer 中同時設定 $data 和 transformations:
new SceneFlexItem({
$data: new SceneDataTransformer({
$data: new SceneQueryRunner({...}),
transformations: [...],
});,
body: PanelBuilders.stat()
.setTitle('Transformed data')
.build(),
}),
Transform 的實現原理是在圖表渲染之前,將 SceneQueryRunner 所返回的數據資料進行各種處理,再將轉換完成的資料送給圖表渲染。可以處理的轉換包括:
而使用的方式是在 transformations 屬性中為多種的轉換工具做設定,而每個 transformation 皆需要設定 id 和 options 屬性:
transformations: [
{
id: "concatenate",
options: {
frameNameMode: "label",
frameNameLabel: "frame"
}
},
{
id: "calculateField",
options: {
mode: "binary",
reduce: {
reducer: "sum"
},
binary: {
left: "Time",
right: "web_server_02"
},
replaceFields: true
}
}
],
💡NOTICE:
每一種 transformation 都有自己獨特的 id,但可選的功能眾多,如果記不起來名稱可以使用 grafana data 中的DataTransformerID
enum 做提示選擇。
以下提供一些簡單的 transform 使用範例,以及眾多的功能中皆無法滿足轉換條件時,Grafana 也提供了客製化的方法,讓使用者自行定義變化的方法:
這個功能可以將欄位經過計算後產生新的值,其中提供了幾種模式(mode):reduceRow(計算一行中所有數值的平均值、總和或最大值等)、cumulativeFunctions(用於計算累積值)、windowFunctions(計算移動平均、相對排名等)、binary(將兩個欄位相加、相減或相乘等)、unary(取絕對值、四捨五入等)、index(用於生成序列號或基於某些條件的索引值)。
使用 calculateField 時需要在 options 中選擇一個上述的 mode,並為該模式進行設定:
以下設定為以 binary 模式對 A-series 欄位的值做總和後再將結果 * 2。
transformations: [
{
id: 'calculateField',
options: {
mode: 'binary',
binary: {
left: 'A-series',
reducer: 'sum',
operator: '*',
right: '2',
},
// replaceFields: true, // 原始的欄位將被替代
alias: 'Transformed field', // 新被轉後換的圖表名稱
},
},
],
organize 可以用於重組或管理資料欄位,例如重新命名欄位、重新排列欄位、篩選欄位及修改欄位的相關屬性,提供 excludeByName、includeByName、indexByName、renameByName 四個屬性做設定。而設定的方法是將原始欄位放在 key 的位置,並在 value 進行設置:
{
id: 'organize',
options: {
excludeByName: {
Time: true // 排除 'Time' 欄位
},
indexByName: {
'Temp(°C)': 0, // 'Temp(°C)' 欄位放在第一位
Humidity: 1 // 'Humidity' 欄位放在第二位
Location: 2, // 'Location' 欄位放在第二位
},
renameByName: {
Temperature: 'Temp(°C)' // 將 'Temperature' 重命名為 'Temp(°C)'
}
}
}
Time | Temperature | Humidity | Location |
---|---|---|---|
2023-09-01 10:00:00 | 25.5 | 60 | Office A |
2023-09-01 10:05:00 | 26.0 | 58 | Office B |
Temp(°C) | Humidity | Location |
---|---|---|
25.5 | 60 | Office A |
26.0 | 58 | Office B |
Grafana 的自定義轉換(Custom Transformations)可以讓使用者建立自己的資料轉換邏輯,此功能一樣藉由 SceneDataTransformer 實現,並接受 CustomTransformOperator 作為 transformations 陣列的元素。
CustomTransformOperator 是一個返回 RxJS Operator 的函數,用於轉換 DataFrame 數組。這種設計使得開發者能夠利用 RxJS 的功能來處理資料流,實現複雜的數據操作和轉換。自定義轉換可以用於資料清理、進階計算、數據結構重組等。
💡DataFrame
DataFrame 的資料格式包含了 name、fields、length、refId 以及 meta 屬性,資料中所呈現的內容主要存於 fields 中,有多少欄位即有多少的 field,length 則是代表有多少組資料。而每一個 field 有 name、type、config、values 等屬性,在自定義的函數中所要調整的即為 values 的值。
下面範例為將溫度的值由攝氏轉為華氏:
const celsiusToFahrenheit: CustomTransformOperator = () => (source: Observable<DataFrame[]>) => {
return source.pipe(
map((data: DataFrame[]) => {
return data.map((frame: DataFrame) => {
return {
...frame,
fields: frame.fields.map((field) => {
if (field.name === 'Temperature') {
return {
...field,
values: field.values.map((celsius: number) => (celsius * 9) / 5 + 32),
};
}
return field;
}),
};
});
})
);
};
SceneDataLayerSet 用於在 time series 的視覺化圖表中添加註釋層。開發者可以在原本已有資料的 Panel 上標記重要事件、時間段或關鍵數據點。在 Grafana Scenes 中使用 SceneDataLayerSet,而每一層 layer 以 dataLayers.AnnotationsDataLayer 的資料格式自定義註釋的外觀,包括顏色、圖標和標籤,並定義互動的 tooltip 以顯示詳細資料,另外也可以設定 switch 依需求顯示或隱藏圖層。此功能在 Grafana 中稱為 Annotations,Grafana Annotations 的常見用途包括:
支援 data layer 的資料型態為可以轉換為 time series 的 data source,而可以呈現 data layer 資料的 Panel 就有所縮限,必須為以下的類型:
在 Grafana Scenes 中若要為一個 Panel 設置自定義的 Annotations,首先需要在兩種資料型態,一個是底圖的資料,可以使用 SceneQueryRunner 或 SceneDataTransformer 定義,而顯示註釋的 data layer 則是疊加在底圖上,即為使用 SceneDataLayerSet。一個 Panel 可以疊加多個圖層,設置在 Scenes 應用程式不同層級,其底下的 Panel 皆會取得該 Annotations:
在 SceneFlexLayout 或 SceneFlexItem 層以 SceneDataLayerSet 設置 dataLayers.AnnotationsDataLayer 作為 Annotations 的資料源。以下為各屬性的詳細介紹:
name:這個 Annotation 的名稱,通常會顯示於圖層開關的 label 上。
isHidden:預設為 false,如果設為 true,則會隱藏圖層開關的操作元件,即無法手動關閉 Annotation 圖層。
query:設定所取用的 data source、標籤的樣式屬性,以及標籤 tooltip 的內容細節。
tagKeys:為 tooltip 中顯示的 tag,會因不同的 data source 可以取用不同的屬性,範例中的 'namespace,pod'
,表示會取用 namespace
和 pod
的屬性,須以逗號分隔。但其他類型 data source 可能需要在 query 設定 mapping 屬性進行設定,例如:
mappings: {
text: {
source: AnnotationEventFieldSource.Text,
value: 'Independent annotation',
},
tags: {
source: AnnotationEventFieldSource.Field,
value: 'text',
},
},
💡NOTICE
query 中的各種屬性設定會依據不同的 data source 而有非常大的差異,尤其是 query 可取用的變數,這會影響到 tooltip 的 text、tags 以及 title 的顯示。例如下圖的 loki 類型資料,可使用的變數為所有欄位的 label,且格式化中需使用雙括號({{namespace}}
)取用,可以查看各種 data source 的 Grafana 文件進行設定,或是參考 dashboard 的 Panel JSON 所呈現的格式(如下圖)。
new SceneFlexLayout({
$data: new SceneDataLayerSet({
layers: [
new dataLayers.AnnotationsDataLayer({
name: 'Annotations from logs',
isHidden: false,
query: {
datasource: { uid: 'gdev-loki' },
enable: true,
iconColor: 'blue',
name: 'New annotation',
expr: '{service_name="apache", cluster="eu-west-1", level="warn"} |= `POST`',
textFormat: 'namespace: {{namespace}} in pod: {{pod}}',
tagKeys: 'namespace,pod',
titleFormat: '{{service_name}} - {{namespace}}',
},
}),
],
}),
children: [...],
}),
將底層的資料來源設置在可以設置 $data 的最底 SceneObject,例如 Panel 中:
new SceneFlexLayout({
$data: new SceneDataLayerSet({
layers: [...],
}),
children: [
new SceneFlexItem({
body: new VizPanel({
$data: new SceneQueryRunner({
datasource: { uid: 'gdev-prometheus' },
queries: [
{
refId: 'X',
expr: 'rate(prometheus_http_request_duration_seconds_count[5m])',
legendFormat: '__auto',
maxDataPoints: 500,
range: true,
},
],
}),
title: 'Annotations from logs',
pluginId: 'timeseries',
}),
}),
],
}),
設置可以操控圖層開關的元件。如果 dataLayers.AnnotationsDataLayer 中的 isHidden 設定為 false 則有一個 switch 元件可進行操控,此元件為 Scenes 提供的 SceneDataLayerControls 元件:
new SceneFlexItem({
body: new VizPanel({
...,
headerActions: [new SceneDataLayerControls()],
}),
}),
讀者語錄
這兩個功能讓圖表可以呈現的資料更有無限的可能,尤其使用 SceneDataTransformer 及 SceneDataLayerSet 兩個實例讓原本在 GUI 上無法取得的屬性也能設定。雖然屬性眾多,無法一一牢記,且官方文件提供的資料也不多,但每一個實例的屬性都有 enum 類型提供參考,從中下手可以得到不少幫助的!
https://grafana.com/developers/scenes/transformations#add-custom-transformations