ข้ามไปยังเนื้อหา

สร้าง Dashboard และ Notebook

Datadog dashboard คือ grid ของ widget แต่ละตัวสร้างจาก named query หนึ่งตัวหรือมากกว่ารวมกันผ่าน formula ทำให้ interactive ด้วย template variable และ — พิสูจน์ด้านล่าง — validate แบบเดียวกันไม่ว่าจะเป็น JSON เขียนมือหรือจัดการผ่าน Terraform resource datadog_dashboard_json กายวิภาคพื้นฐานของ widget/JSON (layout_type, timeseries/query_value/toplist) พิสูจน์ไว้แล้วใน Dashboards and the Datadog UI Tour บทนี้ลงลึกกว่าเรื่อง formula and function, SLO widget, preset และเส้นทางที่จัดการด้วย Terraform — ไม่ซ้ำพื้นฐานของบทนั้น

widget สามแบบนี้ครอบคลุม dashboard ส่วนใหญ่ที่จะสร้าง บวกอีกหนึ่งแบบที่บทนี้พิสูจน์เพิ่ม:

  • timeseries — กราฟเส้นหรือแท่งของ metric ตามเวลา ตัวเลือก default สำหรับ “ค่านี้ trend ยังไง”
  • query_value — ตัวเลขใหญ่ตัวเดียว ปกติเป็นค่าล่าสุดหรือ aggregation ในช่วงเวลาหนึ่ง ใช้กับ KPI แบบดูปราดเดียวรู้เรื่อง
  • group — widget แบบ container ที่เก็บ widget อื่นไว้ข้างใน และ scope ได้ด้วย tag ทำให้ repeat ชุด graph เดียวกันได้ครั้งละหนึ่งค่าของ tag นั้น
  • slo — SLO status widget พิสูจน์ด้านล่างด้วย definition จริงที่ validate แล้ว โชว์สถานะ/error-budget ของ Service Level Objective ตัวหนึ่งตรงบน dashboard

query ของ widget ทุกตัวเริ่มจากตั้งชื่อ query — ตามธรรมเนียมคือ query1, query2 และต่อไป — ซึ่งจริง ๆ ก็คือ “metric นี้ filter ด้วย tag พวกนี้ aggregate แบบนี้” (shape queries/formulas เดียวกับที่พิสูจน์กับ model Dashboard จริงในบท UI-tour ของ foundations) field formula คือ expression เล็ก ๆ ที่ reference named query พวกนั้นด้วยชื่อ ทำให้คำนวณเลขระหว่างกันได้ (query1 / query2 * 100 สำหรับ error rate) นอกจากคำนวณเลขแล้ว มี function ที่มีชื่อสองตัวที่มัก wrap หรือ chain เข้ากับ query — และวิธีเรียกของมันต่างกันจริง ยืนยันตรงจาก docs.datadoghq.com/dashboards/functions/:

  • rollup() ถูก dot-chain ต่อท้าย query string: .rollup(<AGGREGATOR>, <INTERVAL>) เช่น sum:myservice.requests.count{*}.as_count().rollup(sum, 300) บังคับ 5-minute rolling sum ตัวอย่างจาก docs จริง โชว์ dot-chain pattern เดียวกันด้วย weekly rollup: sum:requests.count{*}.as_count().rollup(sum, weekly, monday, 'America/New_York')
  • timeshift() ตรงข้าม เป็น wrapping call ครอบทั้ง query: timeshift(<QUERY>, -<SECONDS>) ตัวอย่างจาก docs จริง: timeshift(avg:system.load.1{*}, -1209600) — เทียบ load ปัจจุบันกับ load เมื่อสองสัปดาห์ก่อนเป๊ะ ๆ (1,209,600 วินาที)

template variable — สมมติชื่อ env default เป็น tag prefix อย่าง env — คือ filter control ระดับ dashboard ที่ถูกแทนค่าเข้าไปใน query ของทุก widget พร้อมกัน field ที่เกี่ยวข้องแต่แยกกัน validate กับ model Dashboard จริง — template_variable_presets — ให้ save combination เฉพาะของค่า variable เป็น preset ที่ตั้งชื่อไว้ กดครั้งเดียวเสร็จ (เช่น “Production incident view” = env:prod):

dashboards/orders-api-presets.json
{
"title": "orders-api overview with SLO + presets",
"layout_type": "ordered",
"template_variables": [
{ "name": "env", "prefix": "env", "available_values": ["prod", "staging"], "default": "prod" }
],
"template_variable_presets": [
{
"name": "Production incident view",
"template_variables": [{ "name": "env", "value": "prod" }]
}
],
"widgets": [
{
"definition": {
"type": "slo",
"title": "orders-api availability SLO",
"slo_id": "00000000000000000000000000000000",
"view_type": "detail",
"view_mode": "overall",
"time_windows": ["7d", "30d"],
"show_error_budget": true
}
}
]
}
$ python3 -c "
from datadog_api_client.v1.model.dashboard import Dashboard
Dashboard(**data, _check_type=True)
print('OK: valid')
"
OK: valid

ข้อเท็จจริงเรื่องชื่อที่แม่นยำที่ยืนยันได้ระหว่างเขียน proof นี้: class คือ SLOWidgetDefinition (SLO ตัวพิมพ์ใหญ่ทั้งหมด) ไม่ใช่ SloWidgetDefinition ที่ Pythonic กว่า — import ชื่อหลังทำให้เกิด ImportError จริงพร้อมคำแนะนำ “Did you mean…?” เป็นกับดักเล็กแต่จริงสำหรับใครก็ตามที่ generate โค้ดกับ client ตัวนี้จาก naming-convention ทั่วไปของ Python สอดคล้องกับข้อค้นพบเรื่อง widget definition ที่ type แบบหลวม ๆ จาก บท UI-tour (dashboard ที่ขาด layout_type ถูก reject แต่ widget ที่ขาด field เฉพาะ type ของตัวเองไม่ถูก) การทดสอบจริงที่ลบ slo_id ออกทั้งหมดจาก SLO widget นี้ก็ยัง validate ผ่านเรียบร้อย — model เช็ค field required ระดับ envelope อย่างเข้มงวด แต่ไม่ enforce field required เฉพาะต่อ widget type แบบที่ Datadog UI หรือ API จริงจะทำตอน save

นอกจาก resource HCL แบบ structured datadog_dashboard (ที่ shape HCL ของ widget-query ถูกครอบไว้ พร้อม bug fix จริง ใน Datadog as Code with Terraform — ไม่ซ้ำที่นี่) provider datadog ของ Terraform ยังมี datadog_dashboard_json — resource ที่ต่างออกไปจริง รับ JSON string ดิบแทนที่จะเป็น nested HCL block และเป็นคำแนะนำที่ Datadog เองบันทึกไว้สำหรับ widget type (อย่าง Wildcard widget) ที่ resource แบบ structured ยังไม่รองรับเต็มที่ HCL จริง validate แล้ว:

terraform/dashboard-json.tf
resource "datadog_dashboard_json" "orders_overview" {
dashboard = jsonencode({
title = "orders-api overview (Terraform JSON)"
layout_type = "ordered"
widgets = [
{
definition = {
type = "timeseries"
title = "orders-api error rate"
requests = [
{
response_format = "timeseries"
queries = [
{
data_source = "metrics"
name = "query1"
query = "sum:trace.http.request.errors{env:prod,service:orders-api}.as_count()"
}
]
formulas = [{ formula = "query1" }]
display_type = "line"
}
]
}
}
]
})
}
$ terraform validate -json
{"format_version":"1.0","valid":true,"error_count":0,"warning_count":0,"diagnostics":[]}

attribute dashboard คือ shape JSON queries/formulas เดียวกันเป๊ะที่คอร์สนี้ validate ตรงกับ model Python Dashboard — jsonencode(...) ตรงนี้เป็นแค่วิธีของ HCL ในการสร้าง JSON string นั้นแบบ inline ดังนั้นทุกอย่างที่พิสูจน์ด้านบนเรื่อง widget-query shape ปัจจุบันใช้ได้เหมือนเดิมข้างใน Terraform ด้วย

ครอบไว้เชิงลึกแล้ว (model NotebookCreateRequest จริง ยืนยันว่ามีแค่ v1 ไม่มี v2 เทียบเท่า payload cell markdown + timeseries จริง) ใน Dashboards and the Datadog UI Tour — ไม่ซ้ำที่นี่ สรุปสั้น ๆ: Notebook แลก grid ตายตัวกับ document เชิงบรรยายที่เรียงเส้นตรง ใช้ shape definition ของ widget เดียวกันเป๊ะสำหรับ cell กราฟของมัน

Powerpack คือ ตาม glossary ของ Datadog เอง: “templated groups of widgets that scale graphing expertise as reusable dashboard building blocks.” มีสองแบบ: Powerpack แบบ preset (Datadog สร้างเอง ผูกกับ product หรือ integration เฉพาะ ใช้ได้กับทุกลูกค้า) และแบบ custom (user สร้างเองในองค์กรของตัวเอง) คุณค่าเชิงปฏิบัติ ตาม docs.datadoghq.com/dashboards/guide/powerpacks-best-practices/: การ update Powerpack แบบ custom กระจายไปยังทุก dashboard ที่ embed มันอัตโนมัติ ดังนั้นความรู้ของทีมแบบ “ห้ากราฟที่ถูกต้องสำหรับ Postgres host” ถูกเก็บไว้ครั้งเดียวและทันสมัยอยู่เสมอทุกที่ที่ใช้ แทนที่จะ copy-paste แล้ว drift ไม่ตรงกันไปตาม dashboard ต่าง ๆ คอร์สนี้ไม่ได้ตั้ง Powerpack จริงขึ้นมาใช้ (ไม่มี account ตามที่ตั้งใจ) — กลไกนี้มาจาก docs อ้างไว้ด้านบนแทนที่จะสาธิตจริง

flowchart LR
  A[Named query query1: errors] --> C["Formula: query1 / query2 * 100"]
  B[Named query query2: requests] --> C
  C --> D["Widget: timeseries / query_value / group / slo"]
  E["Template variable env"] -->|filters| D
  F["Preset: env=prod"] -->|one click| E
  D --> G[Dashboard JSON]
  G --> H["datadog_dashboard_json Terraform resource"]
  D --> I[Notebook: graphs + narrative text]
  J[Powerpack: reusable widget group] -->|embedded in| G
จาก named query ไปสู่ dashboard ที่ filter และ save แล้ว

🐛 หา bug ในโค้ดนี้

# AI comment: "5-minute rolling sum of error count, using rollup"
sum:myservice.errors.count{env:prod}.as_count().rollup(sum, 300)
# AI comment: "Same idea, applied via a wrapping function like timeshift"
rollup(sum:myservice.errors.count{env:prod}.as_count(), sum, 300)
เฉลย

บรรทัดแรกคือ syntax จริงที่ถูกต้อง — rollup dot-chain ต่อท้าย query ตรงกับตัวอย่าง docs จริงที่พิสูจน์ด้านบน บรรทัดที่สองคือ bug: มันใช้ argument ของ rollup ด้วย calling convention แบบ wrapping-call ของ timeshift แทน ความผิดพลาดที่เกิดได้เองธรรมชาติมาก พอเรียนรู้ถูกต้องแล้วว่า function query ของ Datadog บางตัว wrap รอบ query (พิสูจน์ด้านบน: timeshift ทำแบบนั้นจริง) ยืนยันตรงจาก docs.datadoghq.com/dashboards/functions/rollup/ rollup ไม่เคยถูกเขียนเป็น rollup(<query>, ...) — shape นั้นเป็นของ timeshift และ function อื่นอีกไม่กี่ตัว ไม่ใช่ของ rollup เพราะมันอยู่ข้างใน string ธรรมดา (พิสูจน์ด้านบน ทึบต่อทั้ง Terraform และ JSON-model validation) ไม่มีอะไรใน harness ของคอร์สนี้จับบรรทัดที่สองว่าผิดได้เลย — มีแค่ parser query จริงของ Datadog (หรือการรู้ docs จริง ตามที่พิสูจน์ด้านบน) เท่านั้นที่จะ reject มัน

ลองนึกภาพชั้น query ของ dashboard เป็น ครัวที่มีเครื่องมือสองชิ้นที่รูปร่างต่างกันมาก rollup คือช้อนที่คุณคน เข้าไปใน จานที่กำลังทำอยู่แล้ว — มันแนบท้ายสิ่งที่กำลังปรุงอยู่ dot-chain กัน timeshift คือถาดเสิร์ฟที่คุณวาง จานที่เสร็จแล้วทั้งจาน ลงไปแล้วยกไปที่อื่น — มัน wrap ทั้ง query จากข้างนอก ทั้งคู่เป็นเครื่องมือครัวที่มีประโยชน์จริง แต่การหยิบจับผิดตัว (พยายาม “wrap” ด้วยช้อน หรือ “แนบ” ด้วยถาด) ไม่ fail เสียงดัง — การ์ดสูตรอาหาร (string ธรรมดา ทึบต่อทุก automated check ในคอร์สนี้) แค่พูดบางอย่างที่ไม่เคยจริงอย่างเงียบ ๆ

  • datadog_dashboard (HCL แบบ structured) เทียบกับ datadog_dashboard_json (JSON string ดิบ) — HCL แบบ structured ได้ diff แบบ Terraform-native บน field ของแต่ละ widget และเครื่องมือของ HCL (variable, for_each บน list ของ widget); datadog_dashboard_json (พิสูจน์ด้านบน จริงและ valid) แลกความละเอียดนั้นกับการรับประกันว่าตรงกับสิ่งที่ Datadog UI export ออกมาจริง และเป็นคำแนะนำของ Datadog เองสำหรับ widget type ที่ resource แบบ structured ยังโมเดลไม่ครบ
  • Template variable เทียบกับ preset — template variable เปล่า ๆ ยืดหยุ่นที่สุด (เลือกค่าไหนก็ได้ตอนดู) แต่ต้องเลือกมือทุกครั้ง; preset (พิสูจน์ด้านบน validate บน model จริง) แลกความยืดหยุ่นนั้นกับการทำซ้ำได้แบบกดครั้งเดียวของ combination ที่ตั้งชื่อไว้เฉพาะที่คุ้มค่าจะกลับมาใช้บ่อย ๆ (อย่าง “production incident view”)
  • Powerpack แบบ custom เทียบกับ copy-paste กลุ่ม widget — Powerpack รวมศูนย์การ update ให้ทุก dashboard ที่ embed รับไปอัตโนมัติ (ตาม docs อ้างไว้ด้านบน); copy-paste ไม่ต้อง setup อะไรเลย แต่รับประกันว่า drift ทันทีที่ใครสักคน update copy หนึ่งแต่ไม่ update อีกตัว
  • “rollup กับ timeshift เรียกแบบเดียวกัน เพราะทั้งคู่เป็น dashboard query function” ผิด — พิสูจน์ด้านบนจาก docs จริง: rollup dot-chain (<query>.rollup(...)), timeshift wrap (timeshift(<query>, ...)) — convention ต่างกันจริง และ draft ก่อนหน้าของคอร์สนี้เองเขียน rollup ผิดโดยสมมติว่ามันเหมือนกัน
  • “Terraform (หรือ model JSON Dashboard) validate ว่า function query ของฉันใช้ถูก” ผิด — พิสูจน์ด้านบน: ทั้งคู่อยู่ข้างใน query string ธรรมดา ทึบ; การสลับ rollup/timeshift ผิดแบบตรงข้ามกันเป๊ะยังผ่าน terraform validate และ Dashboard(**data, _check_type=True) สะอาดหมดจด
  • “datadog_dashboard_json เป็น fallback ตอนที่ Terraform resource จริงพัง” ไม่เชิง — ตาม docs ของ Datadog เอง (อ้างด้านบน) มันคือเส้นทางที่แนะนำอย่างจริงจังสำหรับ widget type เฉพาะ (อย่าง Wildcard) ที่ resource แบบ structured ยังไม่รองรับเต็มที่ ไม่ใช่แค่ทางหนีที
  • “Powerpack ก็แค่ dashboard template ที่ save ไว้ให้ copy” ผิด — ตาม docs ที่ยกมาด้านบน จุดเด่นหลักคือการ update Powerpack แบบ custom กระจายไปยังทุก dashboard ที่ embed มันอัตโนมัติ; dashboard ที่ copy มาตรงกันข้าม เริ่ม drift ออกจากต้นฉบับทันที
ควรใช้ widget type ไหนสำหรับตัวเลขปราดเดียวรู้เรื่องอย่าง "active connection ตอนนี้"
พิสูจน์ด้านบนตรงจาก docs.datadoghq.com/dashboards/functions/ calling convention จริงของ rollup() คืออะไร และต่างจาก timeshift() ยังไง
พิสูจน์ด้านบนด้วย terraform validate จริง ทำไม query string ที่สลับ rollup/timeshift ผิดตรงข้ามกันเป๊ะยัง validate ผ่านสะอาด
พิสูจน์ด้านบนด้วยการพยายาม import SloWidgetDefinition จาก package datadog_api_client จริง เกิดอะไรขึ้น
พิสูจน์ด้านบนด้วย terraform validate จริงที่ผ่าน datadog_dashboard_json คืออะไร และ Datadog เองแนะนำให้ใช้เมื่อไหร่แทนที่ resource แบบ structured datadog_dashboard
ตาม glossary ของ Datadog เอง ที่ยกมาด้านบน อะไรทำให้ Powerpack แบบ custom ต่างจากการแค่ copy-paste กลุ่ม widget เดียวกันไปหลาย dashboard