在 Grafana 中,Dashboard 可能包含了多種查詢結果的 Panel。這些查詢的屬性通常非常複雜,且其可能的值無法一一列舉。在許多情況下,當屬性改變時,可能需要執行多個操作,例如重新請求資料、更新網址內容、改變 Panel 屬性名稱或動態更新內容等。在前端中以 React 為例,遇到這樣的情況時,不會在每個值 Hardcode 的賦值,而是使用 useState 等狀態管理工具將這些值轉為變數。而 Grafana Scenes 中的狀態管理就是靠著 Variables 來實現。
在了解 Variables 的邏輯和設置方法之前,我們可以先了解 Grafana Dashboard 中的操作行為,雖然前一章節中當我們設定完 $timeRange 和 $data 後即可取得資料讓圖表有數值可以呈現,但大部分的使用場景不是只有單看同一個查詢條件的資料,例如可以查看不同服務的相同條件、調整自動重新整理的頻率或查詢多種條件等。而在 Dashboard 修改這些屬性的方法,為了操作方便會使用下拉選單的方式(如下圖),且 Variables 的實例也是繼承了 SceneObject 的屬性,因此實例中的 Component 屬性皆為一個下拉選單的元件。
Grafana 中的變數值稱為 Template,可以通過特定的語法(如 $variableName
)插入查詢、標題和描述中,從而根據使用者的選擇動態更新顯示的數據。如同 JavaScript 中的模板字串:${defaults.url}/search
。
而 Grafana 依據使用的情境提供兩種 syntax 語法:
$variableName
:易於閱讀,但無法在單詞中間使用變數。例如:apps.frontend.$server.requests.count
。${var_name}
、${var_name:<format>}
:當需要在表達式中間插入變數時,或更精確地控制 Grafana 變數值時使用。例如:keyValue: ${keyValue:text} (text value)這個段落中要開始介紹 Grafana Scenes 各種類型的 Variables,從裝載及管理所有變數的 SceneVariableSet,以及 Scenes 提供在 Dashboard 中經常性會使用到的變數,並封裝成與功能、操作 UI 元素結合的實例,包括 DataSourceVariable、QueryVariable、ConstantVariable 等,涵蓋了從資料來源選擇到自定義查詢、常數設定、分組依據等多種功能。
SceneVariableSet 像是一個變數收集器,用於管理和更新 Scenes 中的所有變數。它一樣繼承自 SceneObjectBase 類型,當變數的值發生變化時,SceneVariableSet 會自動觸發更新流程,確保所有依賴於該變數的 SceneObject 能夠及時獲取最新資料。此外,還負責管理變數的生命周期,確保在 Scenes 在 activate 和 Deactivate 時正確處理變數的更新和記錄。
而操作變數的行為則是使用 VariableValueSelectors 元件,在 controls 屬性中設置後,會依據每個變數實例所定義的 Component 元件渲染出相對應的 UI 元素。
const scene = new EmbeddedScene({
$variables: new SceneVariableSet({
variables: [handlers],
}),
body: new SceneFlexLayout({
children: [],
}),
controls: [new VariableValueSelectors({})],
});
type: 所使用的變數類型,如果使用 Grafana 提供的實例,已先預設綁定好。
name: 變數的名稱,例如:name: server
,則可使用 $server
作為模板。
label?: 操作 UI 元件的 label 名稱,如果沒有設定會以 name 的值替代。
hide?: 有 dontHide、hideLabel、hideVariable 三個選項。
skipUrlSync?: 如果變數的更動不需要與 URL 同步可設為 true
。
loading?: 代表變數是否在加載資料。
error?: 用於存取加載過程中發生的錯誤。
description?: 變數的描述文字,在元件上會以 tooltip 的方式顯示。
💡NOTICE
如果實例的 Component 有支援多選的下拉選單元件,可添加isMulti
屬性來切換。
const handlers = new QueryVariable({
name: 'handler',
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
query: {
query: 'label_values(prometheus_http_requests_total,handler)',
},
});
new QueryVariable({ name: 'pods', value: ['pA', 'pB'], text: ['podA', 'podB'], query: { refId: 'A' } })
當你指定一個 Query 時,QueryVariable 會從 Data source 中獲取該 query 所對應的所有可用選項或 metric 列表。例如,如果 query 是針對某個 metric 的,則返回的選項將是該 metric 的所有實例或相關數據。而實例預設是在變數改變時就進行資料數據的重新請求。
QueryVariable 也可以在內部指定下 query 的 Date source。另外也提供設置固定選項的功能,而不是通過 query 動態生成選項,例如範例中的 value: ['pA', 'pB'], text: ['podA', 'podB']
,將是含有一個選項的下拉選單:顯示為 ['pA', 'pB'],值為 ['podA', 'podB']。
如果當前的值都無效(即 value 值不存在),則會選擇正確值的第一個選項。
new DataSourceVariable({
name: VAR_LOGS_DATASOURCE,
label: 'Logs data source',
value: initialDS,
pluginId: 'loki',
regex: 'slow.*',
}),
快速更改整個 Dashboard 的資料來源,主要是透過 pluginId 來判斷,pluginId 代表 Data source 的類型,而不是特定的 Data source 實例,例如範例中,使用 pluginId: 'loki'
則會返回所有應用程式有安裝的 loki data source。
另外也可以設定 regex 做更精細的篩選,例如範例的設置會篩選出所有以 slow
開頭的 loki data source,如果沒有符合的選項則會返回 loki 的第一筆選項。
new ConstantVariable({
name: VAR_METRIC,
value: metric,
hide: VariableHide.hideVariable,
}),
主要作為一個常數值的變數,其值通常不會改變,預設會通過設置 hide 屬性為 VariableHide.hideVariable
,可以隱藏操作元件,讓使用者無法直接修改,因此不會觸發 URL 的更新。
new CustomVariable({
name: 'keyValue',
description: 'CustomVariable with key value pairs',
value: '',
isMulti: true,
label: 'End state:',
hide: VariableHide.dontHide,
text: '',
query: 'A : 1, B : 2, C : 3',
}),
在 query 屬性中以逗號分割數值作為列表的選項,規則模式有以下多種類型:
'A,B,C’:選項的 label 和 value 分別皆為 A、B、C。
'label-1 : value-1,label-2 : value-2, label-3 : value-3’:label 和 value 以冒號做相互對應。
'label\,1 : value\,1’:值本身需要有逗號顯示時的表現方式,{ label: 'label,1', value: 'value,1' }
。
'a, b, c, d : e’:結合第一種及第二種模式的選項列表。
[
{ label: 'a', value: 'a' },
{ label: 'b', value: 'b' },
{ label: 'c', value: 'c' },
{ label: 'd', value: 'e' },
]
new LocalValueVariable({
name: variable.state.name,
value: variableValues[index],
text: String(variableTexts[index]),
}),
對於重複的面板,作者創建一個包含常數變數(ConstantVariable
)的本地 SceneVariableSet
來設定變數的範圍,但當時間範圍改變時,查詢會立即執行,因為 SceneQueryRunner
在檢查 hasVariableDependencyInLoadingState
時返回了 false
(由於 ConstantVariable
在本地 SceneVariableSet
中沒有處於加載狀態)。這就是這個問題試圖解決的原因。
new TextBoxVariable({
name: LABELS_FILTER,
label: 'search',
})
在實例的 Component 為 VariableValueInput 的元件,會顯示一個自由文字輸入欄位,可選擇設置預設值。在輸入後的 onBlur 行為會進行數據資料的重新請求。
new IntervalVariable({ name: 'interval', value: '1m', intervals: ['1m', '2m'] })
作為設定 Dashboard 自動重新整理的時間間隔操作,選項列表以 intervals 作為設定並且支援單選選取。另外頁面 refresh 的重新請求的設定是根據 TimeRange 改變時才執行。
new AdHocFiltersVariable({
applyMode: 'manual',
datasource: { uid: 'gdev-prometheus' },
filters: [{ key: 'job', operator: '=', value: 'grafana', condition: '' }],
baseFilters: [{ key: 'env', operator: '=', value: 'production' }]
});
有了上述由 Grafana 提供的變數實例已經非常的方便,但依舊有許多屬性無法一一列出,於是 Grafana 也提供了功能更強大的篩選器 - AdHocFiltersVariable,與其他控制預先定義的 tag 或維度的變數不同,使用者可以藉由 AdHoc 篩選器自行選擇要過濾的屬性( key )。最簡單的應是在 AdHocFiltersVariable 中設置 datasource,會自動找出所有這個資料源可以進行篩選的 key。
除此之外,有提供 filters 和 baseFilters 屬性:
在預設模式下篩選器在選完 key-value 會自動進行資料的重新請求並更新 Panel 的資料,但也可以使用 applyMode: 'manual’
的設定,手動模式下值的更動不會應用到 query 中,需要在其他函式中使用這些過濾值,或是在其他的 query 條件下引用這個值。
預設情況下,實例會依據 datasource 實現的 getTagKeys
以及 getTagValues
方法取得所有可篩選的屬性與值,但 Grafana 也提供 getTagKeysProvider 和 getTagValuesProvider 自訂義屬性於值的功能,讓使用者可以更自由的設定下拉選單的內容:
new AdHocFiltersVariable({
name: 'filters',
hide: VariableHide.hideLabel,
layout: 'vertical',
applyMode: 'manual',
filters: [{ key: 'job', operator: '=', value: 'grafana', condition: '' }],
getTagKeysProvider: async (variable: AdHocFiltersVariable, currentKey: string | null) => {
await new Promise((resolve)=>setTimeout(resolve, 200));
return {
replace: true,
values: [{text: 'job'}, {text: 'instance'}],
};
},
getTagValuesProvider: async (variable: AdHocFiltersVariable, filter: AdHocVariableFilter) => {
await new Promise((resolve)=>setTimeout(resolve, 400));
return {
replace: true,
values: ['A', 'B', 'C', 'D', 'E', 'F', 'grafana'].map((v)=>({text: v})),
};
},
}),
https://grafana.com/docs/grafana/latest/dashboards/variables/
https://grafana.com/developers/scenes/variables
https://github.com/grafana/grafana/blob/40157111336615a85440f990dd584c93c42600b6/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx#L185