iT邦幫忙

2024 iThome 鐵人賽

DAY 12
2

https://ithelp.ithome.com.tw/upload/images/20240926/20152073CxGlLyGwGL.png

前言

在 Grafana 中,Dashboard 可能包含了多種查詢結果的 Panel。這些查詢的屬性通常非常複雜,且其可能的值無法一一列舉。在許多情況下,當屬性改變時,可能需要執行多個操作,例如重新請求資料、更新網址內容、改變 Panel 屬性名稱或動態更新內容等。在前端中以 React 為例,遇到這樣的情況時,不會在每個值 Hardcode 的賦值,而是使用 useState 等狀態管理工具將這些值轉為變數。而 Grafana Scenes 中的狀態管理就是靠著 Variables 來實現。

Dashboard 的行為

在了解 Variables 的邏輯和設置方法之前,我們可以先了解 Grafana Dashboard 中的操作行為,雖然前一章節中當我們設定完 $timeRange 和 $data 後即可取得資料讓圖表有數值可以呈現,但大部分的使用場景不是只有單看同一個查詢條件的資料,例如可以查看不同服務的相同條件、調整自動重新整理的頻率或查詢多種條件等。而在 Dashboard 修改這些屬性的方法,為了操作方便會使用下拉選單的方式(如下圖),且 Variables 的實例也是繼承了 SceneObject 的屬性,因此實例中的 Component 屬性皆為一個下拉選單的元件。

https://ithelp.ithome.com.tw/upload/images/20240926/20152073mSh3KLQd2h.png

Template

Grafana 中的變數值稱為 Template,可以通過特定的語法(如 $variableName)插入查詢、標題和描述中,從而根據使用者的選擇動態更新顯示的數據。如同 JavaScript 中的模板字串:${defaults.url}/search

而 Grafana 依據使用的情境提供兩種 syntax 語法:

  1. $variableName :易於閱讀,但無法在單詞中間使用變數。例如:apps.frontend.$server.requests.count
  2. ${var_name}${var_name:<format>}:當需要在表達式中間插入變數時,或更精確地控制 Grafana 變數值時使用。例如:keyValue: ${keyValue:text} (text value)

Variables 的總類

這個段落中要開始介紹 Grafana Scenes 各種類型的 Variables,從裝載及管理所有變數的 SceneVariableSet,以及 Scenes 提供在 Dashboard 中經常性會使用到的變數,並封裝成與功能、操作 UI 元素結合的實例,包括 DataSourceVariable、QueryVariable、ConstantVariable 等,涵蓋了從資料來源選擇到自定義查詢、常數設定、分組依據等多種功能。

SceneVariableSet

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({})],
});

https://ithelp.ithome.com.tw/upload/images/20240926/20152073gdoGN97OTN.png

基本屬性

  • type: 所使用的變數類型,如果使用 Grafana 提供的實例,已先預設綁定好。

  • name: 變數的名稱,例如:name: server ,則可使用 $server 作為模板。

  • label?: 操作 UI 元件的 label 名稱,如果沒有設定會以 name 的值替代。

  • hide?: 有 dontHide、hideLabel、hideVariable 三個選項。

  • skipUrlSync?: 如果變數的更動不需要與 URL 同步可設為 true

  • loading?: 代表變數是否在加載資料。

  • error?: 用於存取加載過程中發生的錯誤。

  • description?: 變數的描述文字,在元件上會以 tooltip 的方式顯示。

    https://ithelp.ithome.com.tw/upload/images/20240926/201520734xrivEIFVW.png

💡NOTICE
如果實例的 Component 有支援多選的下拉選單元件,可添加 isMulti 屬性來切換。

QueryVariable

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 值不存在),則會選擇正確值的第一個選項。

DataSourceVariable

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 的第一筆選項。

ConstantVariable

new ConstantVariable({
  name: VAR_METRIC,
  value: metric,
  hide: VariableHide.hideVariable,
}),

主要作為一個常數值的變數,其值通常不會改變,預設會通過設置 hide 屬性為 VariableHide.hideVariable,可以隱藏操作元件,讓使用者無法直接修改,因此不會觸發 URL 的更新。

CustomVariable

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 屬性中以逗號分割數值作為列表的選項,規則模式有以下多種類型:

  1. 'A,B,C’:選項的 label 和 value 分別皆為 A、B、C。

  2. 'label-1 : value-1,label-2 : value-2, label-3 : value-3’:label 和 value 以冒號做相互對應。

  3. 'label\,1 : value\,1’:值本身需要有逗號顯示時的表現方式,{ label: 'label,1', value: 'value,1' }

  4. 'a, b, c, d : e’:結合第一種及第二種模式的選項列表。

    [
      { label: 'a', value: 'a' },
      { label: 'b', value: 'b' },
      { label: 'c', value: 'c' },
      { label: 'd', value: 'e' },
    ]
    

LocalValueVariable

new LocalValueVariable({
  name: variable.state.name,
  value: variableValues[index],
  text: String(variableTexts[index]),
}),

對於重複的面板,作者創建一個包含常數變數(ConstantVariable)的本地 SceneVariableSet 來設定變數的範圍,但當時間範圍改變時,查詢會立即執行,因為 SceneQueryRunner 在檢查 hasVariableDependencyInLoadingState 時返回了 false(由於 ConstantVariable 在本地 SceneVariableSet 中沒有處於加載狀態)。這就是這個問題試圖解決的原因。


TextBoxVariable

new TextBoxVariable({
  name: LABELS_FILTER,
  label: 'search',
})

在實例的 Component 為 VariableValueInput 的元件,會顯示一個自由文字輸入欄位,可選擇設置預設值。在輸入後的 onBlur 行為會進行數據資料的重新請求。

https://ithelp.ithome.com.tw/upload/images/20240926/20152073JLye6yqgbe.png

IntervalVariable

new IntervalVariable({ name: 'interval', value: '1m', intervals: ['1m', '2m'] })

作為設定 Dashboard 自動重新整理的時間間隔操作,選項列表以 intervals 作為設定並且支援單選選取。另外頁面 refresh 的重新請求的設定是根據 TimeRange 改變時才執行。

https://ithelp.ithome.com.tw/upload/images/20240926/20152073OYtYTT3kbu.png


AdHocFilterSet 與 AdHocFiltersVariable

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 屬性:

  1. filters:定義初始或預設的可篩選資料集,在 Dashboard 初始加載的時候就會顯示在篩選區,使用者可以再進行其他值的選擇或刪除。
  2. baseFilters:定義一組基本的篩選器,Dashboard 初始化顯示的資料會先以這個條件過濾,但 UI 上不會出現此篩選條件的元件。

https://ithelp.ithome.com.tw/upload/images/20240926/20152073mXunMZiAee.png

在預設模式下篩選器在選完 key-value 會自動進行資料的重新請求並更新 Panel 的資料,但也可以使用 applyMode: 'manual’ 的設定,手動模式下值的更動不會應用到 query 中,需要在其他函式中使用這些過濾值,或是在其他的 query 條件下引用這個值。

自訂義 tag

預設情況下,實例會依據 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://ithelp.ithome.com.tw/upload/images/20240926/20152073OEVPz1AkN8.png

參考資料

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


上一篇
靠 Grafana 吃飯的第十一天 - Grafana Scenes 的資料取得與管理
下一篇
靠 Grafana 吃飯的第十三天 - Grafana 的視覺化圖書館 - Visualizations
系列文
論前端工程師如何靠 Grafana 吃飯:從 Grafana App 到前端可觀測性30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言