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

Dashboard กับทัวร์ Datadog UI

ทุก view ใน Datadog — Infrastructure, Metrics Explorer, Logs Explorer, APM, Dashboards — ถูก filter และ scope ด้วย tag syntax เดียวกัน ดังนั้นพอรู้จัก env/service/version จากบทที่แล้ว คุณก็รู้วิธี navigate ทุกตัวไปแล้ว ตัว UI เองเรียนรู้ได้ดีที่สุดด้วยการใช้จริง (บทนี้เน้น docs มากกว่าการพิสูจน์หนัก ๆ แบบบทอื่น) แต่ JSON ของ dashboard/Notebook ข้างใต้มันคือ API shape จริง ปัจจุบัน พิสูจน์ด้านล่างกับ model datadog_api_client จริง

ห้า view นี้ครอบคลุมเกือบทุกอย่างที่ต้องทำในแต่ละวัน

  • Infrastructure List / Host Map — host หรือ container ทุกตัวที่ Datadog รู้จัก เป็น list ที่ sort ได้ หรือ grid สีสัน (Host Map) ที่ระบายสีตาม metric ไหนก็ได้ (CPU, memory) และ filter ด้วย tag ได้
  • Metrics Explorer — สร้าง graph แบบ ad-hoc จาก metric ไหนก็ได้ แบ่งตาม tag โดยไม่ต้อง commit เป็น dashboard ที่ save ไว้ — นี่คือที่ที่ไปตอบคำถาม “ตัวเลขนี้หน้าตาเป็นยังไงตอนนี้จริง ๆ”
  • Logs Explorer — ค้นหาและ filter log event ที่ถูก index สร้าง facet จาก log attribute และ pivot ตรงจาก log line ไปยัง trace ของตัวเองได้ถ้าตั้งค่า trace injection ไว้แล้ว
  • APM Traces / Services — list Services โชว์ทุกค่า tag service ที่ Datadog เห็น trace มา พร้อม latency/error/throughput แบบเห็นภาพรวมทันที ลงลึกเข้าไปใน service ตัวหนึ่งจะเห็น trace ของตัวเองและตำแหน่งของตัวเองใน service map
  • Dashboards — ชุด widget ที่คัดสรรและ save ไว้ สร้างครั้งเดียวแล้วทั้งทีมใช้ซ้ำได้
flowchart TD
    Filter["Tag filter: env:prod service:orders-api"] --> Infra["Infrastructure List / Host Map"]
    Filter --> Metrics["Metrics Explorer"]
    Filter --> Logs["Logs Explorer"]
    Filter --> APM["APM Traces / Services"]
    Filter --> Dash["Dashboards"]
tag filter เดียว ห้า view

Datadog Dashboard มี layout สองแบบ และเลือกให้ถูกตั้งแต่แรกช่วยประหยัดงานทำซ้ำทีหลังได้เยอะ

  • Ordered — widget stack ต่อกันอัตโนมัติตามลำดับที่เพิ่มเข้าไป จากบนลงล่าง คุณไม่ต้องจัดการตำแหน่ง pixel คุณจัดการแค่ list นี่คือ default ที่ถูกต้องสำหรับ dashboard ส่วนใหญ่ — สร้างเร็ว, จัดลำดับใหม่ง่าย และ reflow ได้สมเหตุสมผลบนทุกขนาดหน้าจอ
  • Free — คุณวาง widget แต่ละตัวมือด้วยพิกัด x, y, width และ height ที่ชัดเจน ทำให้ได้ layout ที่ fix และตั้งใจวางไว้แบบนั้น — มีประโยชน์กับ dashboard สไตล์ status-board ที่ตั้งใจให้ดูบนจอใหญ่ ที่การจัดกลุ่มด้วยสายตาสำคัญกว่าความสะดวกในการแก้ไข
// A widget on a free dashboard carries explicit layout coordinates
{
"definition": { "type": "timeseries", "title": "orders-api error rate" },
"layout": { "x": 0, "y": 0, "width": 6, "height": 3 }
}
// The same widget on an ordered dashboard has no layout block —
// its position is just its position in the widgets array
{
"definition": { "type": "timeseries", "title": "orders-api error rate" }
}

ถ้าไม่แน่ใจว่าต้องใช้แบบไหน เริ่มจาก ordered ก่อน — คุณขยาย widget หรือแยกเป็นสองตัวได้เสมอ และไม่ต้องมานั่งปรับพิกัดมือแค่เพื่อเพิ่ม graph อีกตัว

dashboard คือ JSON document ที่มี top-level field แบบแบนบวก array widgets — แต่ละ widget มี definition (widget ชนิดไหน query อะไร) และมี layout เฉพาะบน free dashboard เท่านั้น ตัวอย่าง shape จริงปัจจุบัน — widget สามชนิด, template variable, layout_type — validate ด้วย datadog_api_client.v1.model.dashboard.Dashboard(**data, _check_type=True):

dashboards/orders-api-overview.json
{
"title": "orders-api overview",
"description": "Error rate, request volume, and top endpoints for orders-api",
"layout_type": "ordered",
"template_variables": [
{ "name": "env", "prefix": "env", "available_values": ["prod", "staging"], "default": "prod" },
{ "name": "service", "prefix": "service", "available_values": ["orders-api"], "default": "orders-api" }
],
"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,$service}.as_count()"
}
],
"formulas": [{ "formula": "query1" }],
"display_type": "line"
}
]
}
},
{
"definition": {
"type": "query_value",
"title": "orders-api request volume (5m)",
"requests": [
{
"response_format": "scalar",
"queries": [
{
"data_source": "metrics",
"name": "query1",
"query": "sum:trace.http.request.hits{$env,$service}.as_count()"
}
],
"formulas": [{ "formula": "query1" }]
}
]
}
},
{
"definition": {
"type": "toplist",
"title": "Top endpoints by error count",
"requests": [
{
"response_format": "scalar",
"queries": [
{
"data_source": "metrics",
"name": "query1",
"query": "sum:trace.http.request.errors{$env,$service} by {resource_name}.as_count()"
}
],
"formulas": [{ "formula": "query1" }]
}
]
}
}
]
}
$ python3 -c "
import json
from datadog_api_client.v1.model.dashboard import Dashboard
data = json.load(open('orders-api-overview.json'))
Dashboard(**data, _check_type=True)
print('OK: valid')
"
OK: valid

สองเรื่องที่ควรพูดถึงเรื่อง query shape ยืนยันด้วยการ introspect model จริงที่ติดตั้งอยู่ ไม่ใช่การเดา: array queries ของ widget request เก็บ object data_source/name/query ต่อ data source ("data_source": "metrics" สำหรับ metric query) และ formulas เป็น array แยกที่ reference name ของ query เหล่านั้น ({"formula": "query1"}) — นี่คือ shape formulas-and-functions ปัจจุบัน shape เดียวกับที่ review เจอว่าผิดใน Terraform resource datadog_dashboard ของคอร์สนี้ (fix นั้นอยู่ที่ datadog-as-code-with-terraform ไม่ใช่ที่นี่ แต่ API shape ข้างใต้เหมือนกันเป๊ะไม่ว่าจะเขียนเป็น Terraform HCL หรือ JSON เขียนมือ) model ยัง รับ field แบบเก่า "q": "sum:trace...{...}" string เปล่า ๆ บน request แทน queries/formulas ได้ด้วย — ทั้งคู่เป็น field จริง valid บน model เดียวกัน — แต่ queries/formulas คือ shape ปัจจุบันที่แนะนำ และเป็นตัวที่บทนี้ใช้ตลอด

layout_type ไม่ใช่ optional — มันคือ constructor argument ที่ required จริงบน model จริง และ dashboard JSON ที่ copy มาจากตัวอย่างที่โชว์แค่ array widgets (การละเว้นแบบที่ AI-generated code มีโอกาสสูง) จะ fail ทันทีและชัดเจน:

// @expect-error dashboards/orders-api-overview-broken.json -- missing required layout_type
{
"title": "orders-api overview",
"widgets": []
}
$ python3 -c "
from datadog_api_client.v1.model.dashboard import Dashboard
Dashboard(title='orders-api overview', widgets=[], _check_type=True)
"
TypeError: Dashboard.__init__() missing 1 required positional argument: 'layout_type'

Notebooks คือทางเลือกที่เบากว่า Dashboard สำหรับงานคนละแบบ — เล่าเรื่อง ไม่ใช่ดูแล status board แบบ live Notebook ผสม graph, log query และ markdown ข้อความอิสระไว้ในเอกสารเดียวที่ scroll ได้ — เป็นที่ธรรมชาติสำหรับเขียน timeline ของ incident (“นี่คือ graph latency ตอนที่พุ่ง, นี่คือ deploy marker, นี่คือสิ่งที่สรุปได้”), postmortem หรือการสืบสวนแบบครั้งเดียวที่อยากส่งต่อให้เพื่อนร่วมทีม ต่างจาก Dashboard ตรงที่ Notebook ตั้งใจให้อ่านจากบนลงล่างครั้งเดียว ไม่ใช่เหลือบดูซ้ำ ๆ

payload สำหรับสร้าง Notebook คือ envelope ที่ nested: {"data": {"type": "notebooks", "attributes": {"name", "time", "cells": [...]}}} ข้อเท็จจริงจริงที่ควรพูดถึงตรง ๆ ยืนยันด้วยการค้นใน package datadog_api_client ที่ติดตั้งจริง ไม่ใช่การสมมติ: Notebooks มีอยู่แค่ใน module v1 เท่านั้น — ไม่มี datadog_api_client.v2.model.notebook* เลย ต่างจาก Dashboard/Monitor/SLO ที่คอร์สนี้ validate ด้วย ถ้าคุณเขียนโค้ดเรื่อง Notebooks v1 ไม่ใช่ fallback แบบเก่าตรงนี้ มันคือทางเลือกเดียว model ระดับบนสุดคือ NotebookCreateRequest validate แบบเดียวกับตัวอย่าง Dashboard ด้านบน:

// notebooks/orders-api-incident.json -- not a harness-collected directory
// (dashboards/monitors/slos/service-catalog/logs-pipelines only); validated
// by hand against datadog_api_client.v1.model.notebook_create_request below.
{
"data": {
"type": "notebooks",
"attributes": {
"name": "orders-api incident 2026-09-29",
"time": { "live_span": "4h" },
"cells": [
{
"type": "notebook_cells",
"attributes": {
"definition": {
"type": "markdown",
"text": "## orders-api latency spike\n\nTimeline of the incident and what we concluded."
}
}
},
{
"type": "notebook_cells",
"attributes": {
"definition": {
"type": "timeseries",
"title": "orders-api error rate",
"requests": [
{
"response_format": "timeseries",
"queries": [
{
"data_source": "metrics",
"name": "query1",
"query": "sum:trace.http.request.errors{service:orders-api}.as_count()"
}
],
"formulas": [{ "formula": "query1" }],
"display_type": "line"
}
]
},
"time": { "live_span": "4h" }
}
}
]
}
}
}
$ python3 -c "
import json
from datadog_api_client.v1.model.notebook_create_request import NotebookCreateRequest
data = json.load(open('orders-api-incident.json'))
NotebookCreateRequest(**data, _check_type=True)
print('OK: valid')
"
OK: valid

cell ของ Notebook ใช้ shape definition ของ widget เดียวกันเป๊ะกับ widget ของ Dashboard (cell timeseries ด้านบนมี structure requests/queries/formulas เหมือนกับตัวอย่าง Dashboard เป๊ะ) — cell markdown คือส่วนเสริมเฉพาะของ Notebook เพราะการเล่าเรื่องต้องมี prose คั่นระหว่าง graph

เหตุผลที่ทุกอย่างนี้เชื่อมกันได้ — Infrastructure, Metrics Explorer, Logs Explorer, APM และ Dashboards ทั้งหมดรับ tag filter syntax เดียวกันเป๊ะ — env:prod, service:orders-api, version:1.4.2 รวมกับ AND/OR ตามต้องการ ตั้งค่า unified service tagging ให้ถูกต้อง (บทที่แล้ว) แล้ว filter สามค่าเดียวกันนั้นจะ narrow ทุก view เหล่านี้ลงเหลือ “deployable unit ตัวนี้ environment นี้ release นี้” — นั่นคือเหตุผลทั้งหมดที่คอร์สนี้ใช้เวลาทั้งบทสอนเรื่อง tagging ก่อนจะแตะ dashboard เลยด้วยซ้ำ

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

// AI comment: "Dashboard JSON for the orders-api overview"
{
"title": "orders-api overview",
"description": "Error rate and request volume for orders-api",
"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{service:orders-api}.as_count()" }
],
"formulas": [{ "formula": "query1" }]
}
]
}
}
]
}
เฉลย

ตัว widget เองคือ shape จริง ปัจจุบัน — queries/formulas, ชื่อ field ถูกต้อง, metric query syntax valid bug ที่พิสูจน์ด้วย model Dashboard จริงด้านบน: JSON นี้ขาด layout_type และมันไม่ใช่ field optional ที่มี default สมเหตุสมผล — Python client จริงโยน TypeError: Dashboard.__init__() missing 1 required positional argument: 'layout_type' ทันที ตัวอย่าง dashboard JSON จำนวนมากที่ลอยอยู่ทั่วไป (รวมถึงตัวที่ training data ของ AI ถูกสร้างมาจาก) โชว์แค่ array widgets แยกเดี่ยว ๆ ในฐานะ “ส่วนที่น่าสนใจ” ซึ่งเป็นเหตุผลตรง ๆ ว่าทำไมการละเว้นนี้ถึงหลุดรอดไปได้ วิธีแก้คือบรรทัดเดียว "layout_type": "ordered" (หรือ "free" พร้อม block layout ที่ตรงกันบนทุก widget) แต่ถ้าไม่มีมัน payload จะถูก reject ก่อนที่ Datadog จะดู widgets ด้วยซ้ำ

ลองนึกภาพ JSON ของ Dashboard เป็น ผังที่นั่งบวกสคริปต์สำหรับแต่ละที่นั่ง layout_type ตัดสินว่าสถานที่จะจัดที่นั่งเป็นแถวอัตโนมัติ (ordered) หรือคุณวางเก้าอี้แต่ละตัวมือด้วยพิกัด (free) — พิสูจน์ด้านบน สถานที่ปฏิเสธที่จะเปิดเลยถ้าไม่รู้ว่าแบบไหน definition ของแต่ละ widget คือสคริปต์ของที่นั่งนั้น — จะพูดอะไร (queries, data_source: metrics, query string จริง) และพูดยังไง (formulas รวม query ที่ตั้งชื่อไว้หนึ่งตัวหรือมากกว่าเป็นสิ่งที่แสดงจริง) cell ของ Notebook คือ format สคริปต์เดียวกัน แค่แทรกด้วยการบรรยายธรรมดา (cell markdown) แทนที่จะเป็น graph ทุกที่นั่ง

  • Layout แบบ ordered เทียบกับ free — ordered แทบไม่ต้องดูแลเลย (เพิ่ม widget มันก็โผล่ท้าย list) แต่จัดกลุ่มด้วยสายตาแบบตั้งใจนอกเหนือจากลำดับเรียงเส้นตรงไม่ได้; free ให้ควบคุม pixel เต็มที่สำหรับ layout แบบ status-board แลกกับการแก้ไขในอนาคตทุกครั้งต้องคำนวณพิกัด
  • q string เปล่า ๆ เทียบกับ queries/formulas — พิสูจน์ด้านบน ทั้งคู่เป็น field จริง valid บน widget-request model เดียวกัน; q เขียนมือสั้นกว่าสำหรับ query เดี่ยวง่าย ๆ, queries/formulas คือสิ่งที่ support การรวมหลาย query ด้วยคณิตศาสตร์ (จุดประสงค์ทั้งหมดของ “formulas and functions”) และเป็น shape ที่คอร์สนี้, docs API และตัว UI เองสร้างให้ในปัจจุบัน
  • Dashboard เทียบกับ Notebook สำหรับ widget ข้างใต้เดียวกัน — พิสูจน์ด้านบน cell timeseries ของ Notebook กับ widget timeseries ของ Dashboard ใช้ shape requests/queries/formulas เหมือนกันเป๊ะ ดังนั้นทักษะเขียน query ย้ายไปมาได้เต็มที่ ความต่างคือเชิง structure ล้วน ๆ (grid ที่ live ใช้ซ้ำได้ เทียบกับเอกสารเส้นตรงที่บรรยาย) ไม่ใช่ความต่างด้านความสามารถของ query
  • “dashboard JSON แค่ต้องมี title กับ array widgets” ผิด — พิสูจน์ด้านบน: layout_type คือ constructor argument ที่ required จริงบน model Dashboard จริง และการละเว้นมันคือ TypeError จริงทันที ไม่ใช่ค่าที่ default ให้เงียบ ๆ
  • “query format ของ widget ปัจจุบันคือ query string เปล่า ๆ เหมือนที่ bug ของ Terraform datadog_dashboard เก่าใช้” ผิด — พิสูจน์ด้านบนด้วยการ inspect model จริง: shape ปัจจุบันที่แนะนำคือ queries (array ของ query definition ที่ตั้งชื่อและมี data source) บวก formulas (reference ชื่อเหล่านั้น) q string เปล่า ๆ ยัง valid บน model แต่เป็น shape แบบเก่า ไม่ใช่ตัวที่บทนี้ (หรือบท Terraform ที่แก้แล้ว) ใช้
  • “Notebooks มี v2 API model แยกของตัวเอง เหมือน Dashboards กับ Monitors” ผิด — ยืนยันด้วยการค้นใน client ที่ติดตั้งจริง: Notebooks มีอยู่แค่ใน datadog_api_client.v1 ไม่มี v2 เทียบเท่าเลย
  • “cell graph ของ Notebook ใช้ query format คนละแบบกับ widget ของ Dashboard เลย” ผิด — พิสูจน์ด้านบน: definition ของ cell timeseries ใน Notebook มี structure เหมือนกันเป๊ะกับ definition ของ widget timeseries ใน Dashboard ลงไปถึง field queries/formulas เดียวกัน
ควรเปิด view ไหนเพื่อดูทุก service ที่ Datadog มี trace อยู่ พร้อม latency/error/throughput แบบเห็นภาพรวมทันที
ความต่างหลักระหว่าง ordered dashboard กับ free dashboard คืออะไร
พิสูจน์ด้านบนด้วยการสร้าง model Dashboard จริงที่มีแค่ title/widgets เกิดอะไรขึ้นเมื่อ layout_type ถูกละเว้น
พิสูจน์ด้านบนด้วยการ inspect model จริงที่ติดตั้งอยู่ของ datadog_api_client shape ปัจจุบันที่แนะนำสำหรับ metric query ของ widget คืออะไร ต่างจาก shape เก่ายังไง
พิสูจน์ด้านบนด้วยการค้นหาใน package datadog_api_client ที่ติดตั้งจริง ข้อไหนเกี่ยวกับ model API ของ Notebooks ถูกต้อง
ทำไมถึงใช้ filter env:prod AND service:orders-api ตัวเดียวกันได้ทั้งใน Logs Explorer และใน APM