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

AI bug catalog

ทุก snippet ด้านล่างเขียนเหมือนที่ AI assistant เขียนจริง แล้วรันกับเครื่องมือจริง: datadog/agent:7 (Agent 7.83.3, image digest sha256:c5bf5ec9…, container dd-reading-a ที่ลบทิ้งหลังใช้) ด้วย API key ปลอมและ DD_DD_URL ชี้ไป 127.0.0.1 หรือ listener ในเครื่อง, dd-trace 6.17.0 และ @datadog/browser-rum 7.15.0 กับ TypeScript compiler, ddtrace 4.15.2, terraform validate กับ datadog provider 4.22.0 (Terraform v1.16.4, ไม่มี plan ไม่มี apply, ไม่ตั้ง cloud credential) และ Python model ของ datadog-api-client 2.61.0 ไม่มีอะไรถูกส่งไป Datadog เลย — โมดูลนี้ไม่มี account และไม่มี key จริงที่ไหนทั้งสิ้น ตัวเลขเวลาและ port มาจาก Mac เครื่องเดียว

แต่ละ entry จบด้วยบรรทัด Caught by และคุณจะเห็น pattern: เครื่องมือที่จับ bug ต่างกันมากในแต่ละ entry บางอันเป็น error แข็ง (agent configcheck, terraform validate, tsc) บางอัน ทุกเครื่องมือ offline ยอมรับเงียบ ๆ และปรากฏที่อื่นเท่านั้น (agent status, payload ที่ดักได้) และบางอันมีแต่ Datadog API หรือการอ่าน docs ที่จับได้ — entry เหล่านั้นบอกตรง ๆ และอ้าง URL ของ docs แทนที่จะแกล้งว่ามีเครื่องมือพิสูจน์ให้

AI มัก flatten config ของ check เพราะใน README หลายที่ตัวอย่าง instances เขียนเป็นบล็อกเดียว แต่ Agent ต้องการ list ของ instance

# @expect-error mysql.d instances is a map, not a list -> "cannot unmarshal !!map into []integration.RawMap"
init_config:
instances:
host: mysql
username: datadog
port: 3306

อาการ — output จริงจาก agent configcheck (Agent 7.83.3):

=== Configuration errors ===
mysql: yaml: unmarshal errors:
line 4: cannot unmarshal !!map into []integration.RawMap

agent status แสดงซ้ำใต้ Config Errors และ check ไม่รันเลย — ไม่มี metric ไม่มีบรรทัด instance ที่เป็น [ERROR]

วิธีแก้:

conf.d/mysql.d/conf.yaml
init_config:
instances:
- host: mysql
username: datadog
port: 3306

Caught by: agent configcheck (error แข็ง) ดู The Datadog Agent and integrations

YAML ถูกต้อง configcheck พิมพ์ config เหมือนไม่มีอะไรผิด — แต่ check ก็ load ไม่ขึ้น

# @expect-error tags must be a list: "cannot unmarshal !!str `env:prod` into []string" (only visible in agent status, not configcheck)
init_config:
instances:
- host: postgres
port: 5432
username: datadog
tags: env:prod

อาการ — output จริง agent configcheck แสดง instance postgres พร้อม tags: env:prod และ ไม่มี error เลย ส่วน agent status บอกความจริง:

Loading Errors
==============
postgres
--------
Core Check Loader:
Check postgres not found in Catalog
Python Check Loader:
could not configure check instance for python check postgres: yaml: unmarshal errors:
line 3: cannot unmarshal !!str `env:prod` into []string

วิธีแก้:

conf.d/postgres.d/conf.yaml
init_config:
instances:
- host: postgres
port: 5432
username: datadog
tags:
- env:prod

Caught by: agent status (Loading Errors) — ไม่ใช่ agent configcheck ผล configcheck ที่สะอาดเป็นเงื่อนไขจำเป็น ไม่เคยเป็นเงื่อนไขเพียงพอ

AI ตั้งชื่อ directory ตามชื่อผลิตภัณฑ์ แต่ Agent ตั้งชื่อตาม check

# @expect-error the check is named redisdb, there is no redis check: "Check redis not found in Catalog"
init_config:
instances:
- host: redis
port: 6379

ไฟล์นี้อยู่ที่ conf.d/redis.d/conf.yaml อาการ — output จริง: agent configcheck พิมพ์บล็อก === redis check === ที่ดูปกติ (ไม่มี error) และ agent status บอกว่า:

redis
-----
Core Check Loader:
Check redis not found in Catalog
Python Check Loader:
unable to load check redis: unable to load python module datadog_checks.redis: unable to import module 'datadog_checks.redis': No module named 'datadog_checks.redis', unable to load python module redis: unable to find a subclass of the base check in module 'redis': cannot find a subclass

วิธีแก้: ย้ายไปใช้ชื่อ check จริง

conf.d/redisdb.d/conf.yaml
init_config:
instances:
- host: redis
port: 6379

Caught by: agent status (Loading Errors) บทเรียนเดียวกับ entry 2: ชื่อ directory คือข้อมูล และ configcheck ไม่รู้ว่ามี check ชื่อนั้นอยู่จริงหรือไม่

# @expect-error timeout must be a number: "Input should be a valid number, unable to parse string as a number" (agent status only)
init_config:
instances:
- name: checkout
url: http://checkout:8080/health
timeout: "five"

อาการ — output จริง agent configcheck พิมพ์ timeout: five โดยไม่มี error ส่วน agent status:

http_check (13.1.0)
-------------------
Instance ID: http_check:checkout:85dc6c39d0e92c1b [ERROR]
...
Error: Detected 1 error while loading configuration model `InstanceConfig`:
timeout
Input should be a valid number, unable to parse string as a number

วิธีแก้:

conf.d/http_check.d/conf.yaml
init_config:
instances:
- name: checkout
url: http://checkout:8080/health
timeout: 5

Caught by: agent status — เป็น pydantic model ของ check เอง ไม่ใช่ตัว load config ของ Agent

reviewer (คนหรือ AI) ที่จำมาว่า “conf.yaml ทุกไฟล์ต้องมี init_config:” จะ flag ไฟล์นี้

# @expect-error demonstrates a NON-bug: this loads fine without init_config; the only error is that host "nginx" does not resolve
instances:
- nginx_status_url: http://nginx:81/nginx_status/

Output จริง agent configcheck พิมพ์บล็อก === nginx check === โดย ไม่มี configuration error; agent status แสดง instance ที่รันและล้มเหลวเพราะ DNS เท่านั้น: Failed to resolve 'nginx' ([Errno -2] Name or service not known) ดังนั้นกฎ “ไม่มี init_config คือ error” เป็นแค่ความเชื่อสำหรับ check นี้บน Agent 7.83.3 — ไฟล์ถูก load

บทเรียนอยู่ที่ตัว review ไม่ใช่ไฟล์ การเติม init_config: ไม่มีโทษและตรงกับตัวอย่างอ้างอิงทุกตัว จึงควรใส่ แต่ห้ามเขียนว่า “ไม่มี init_config” เป็น bug ถ้ายังไม่ได้รันเครื่องมือ

conf.d/nginx.d/conf.yaml
init_config:
instances:
- nginx_status_url: http://nginx:81/nginx_status/

Caught by: ไม่มีอะไร — มันไม่เคยผิด entry นี้คือเหตุผลที่โมดูลทั้งโมดูลมีอยู่: พิสูจน์ อย่าจับ pattern

AI มักให้ DD_SITE เป็น API base URL เพราะเครื่องมืออื่นขอแบบนั้น แต่ DD_SITE คือ domain ของ site

Terminal window
# @expect-error DD_SITE takes a site domain (datadoghq.com), not a URL
docker run -d -e DD_API_KEY=<your-api-key> -e DD_SITE=https://api.datadoghq.com datadog/agent:7

อาการ — output จริง (agent status, endpoints, ใช้ DD_DD_URL ในเครื่องเพื่อไม่ให้อะไรออกนอกเครื่อง):

# DD_SITE=https://api.datadoghq.com
https://process.https://api.datadoghq.com. - API Key ending with:
# DD_SITE=datadoghq.eu
https://process.datadoghq.eu. - API Key ending with:

Agent ไม่ปฏิเสธค่านี้ แต่เอาไปต่อหน้าชื่อ intake ทุกตัว ได้ address ที่ resolve ไม่ได้

วิธีแก้:

datadog.yaml
site: datadoghq.com

Caught by: รายการ endpoint ใน agent status (และ DNS error ของ forwarder ใน log) ไม่มี linter offline ที่รู้รายชื่อ site รายชื่อ site: docs.datadoghq.com/getting_started/site/

# @expect-error not a syntax error: an unpinned, moving tag (see the digest output below)
image: datadog/agent:latest

Output จริง — ทั้งสาม tag ชี้ image เดียวกันในวันนี้:

$ for t in 7 7.83.3 latest; do docker image inspect datadog/agent:$t --format '{{.Id}}'; done
sha256:da43d3ecfad79906df4d32c21c3dcee8c681ba8f0afa9a59f6663d8f5489d70c
sha256:da43d3ecfad79906df4d32c21c3dcee8c681ba8f0afa9a59f6663d8f5489d70c
sha256:da43d3ecfad79906df4d32c21c3dcee8c681ba8f0afa9a59f6663d8f5489d70c

นี่คือผลลัพธ์ที่ซื่อสัตย์: วันนี้ไม่มีอะไรพัง bug คือ reproducibility — latest และ 7 จะชี้ไป Agent ตัวอื่นในสัปดาห์หน้า (และ 7.83.3 คือ release ณ วันที่จับข้อมูลนี้) postmortem ของ incident จึงบอกไม่ได้ว่า Agent ตัวไหนรันอยู่ ให้ pin tag เต็มหรือ digest แล้วอัปเดตอย่างตั้งใจ

k8s/agent-image.yaml
apiVersion: v1
kind: Pod
metadata:
name: agent-image-example
spec:
containers:
- name: agent
image: datadog/agent:7.83.3

Caught by: review เท่านั้น; docker image inspect แสดงว่า tag ตรงกันในตอนนี้ ซึ่งเป็นเหตุผลที่ไม่มีใครสังเกต

ความผิดสองอย่างที่ AI ทำในลมหายใจเดียว: ใส่ annotation บน workload object และใช้รูปแบบสาม key ก่อน 7.36

# @expect-error Autodiscovery reads POD annotations (spec.template.metadata), not Deployment metadata; v1 keys check_names/init_configs/instances are the pre-7.36 format
apiVersion: apps/v1
kind: Deployment
metadata:
name: cache
annotations:
ad.datadoghq.com/cache.check_names: '["redisdb"]'
ad.datadoghq.com/cache.init_configs: '[{}]'
ad.datadoghq.com/cache.instances: '[{"host":"%%host%%","port":"6379"}]'
spec:
selector:
matchLabels: { app: cache }
template:
metadata:
labels: { app: cache }
spec:
containers:
- name: cache
image: redis:7

ไม่ได้พิสูจน์ด้วยการรันที่นี่ — docs เท่านั้น ต้องมี cluster และ mode --k8s ของ harness ต้องมี kind cluster ซึ่งไม่มี สิ่งที่ docs บอก (docs.datadoghq.com/containers/kubernetes/integrations/): สำหรับ pod ที่สร้างโดย Deployment ให้ “Add annotations under spec.template.metadata”; identifier ของ container ใน ad.datadoghq.com/<CONTAINER_NAME>.checks ตรงกับ spec.containers[i].name; รูปแบบ .checks (v2) ที่รวมเป็นอันเดียวต้องใช้ Agent 7.36+ ส่วนแบบสาม key เป็นรูปแบบก่อนหน้า kubectl ยอมรับทั้งสองตำแหน่งโดยไม่บ่น ดังนั้นไม่มีอะไร offline จับได้

วิธีแก้:

k8s/cache-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cache
spec:
selector:
matchLabels: { app: cache }
template:
metadata:
labels: { app: cache }
annotations:
ad.datadoghq.com/cache.checks: |
{
"redisdb": {
"init_config": {},
"instances": [{ "host": "%%host%%", "port": "6379" }]
}
}
spec:
containers:
- name: cache
image: redis:7

Caught by: agent configcheck ของ Agent บน node ใน cluster จริง (AD config ของ pod หายไป) — ไม่เคยเป็น kubectl apply

นี่คือ bug จริงที่ Phase 3 review ของคอร์สนี้เจอเอง JavaScript ไม่ปฏิเสธ key ที่ไม่รู้จัก

// @expect-error 'log_injection' does not exist in TracerOptions: TS2561, and at runtime it is silently ignored
import tracer from 'dd-trace';
tracer.init({ service: 'checkout', log_injection: false });

อาการ — output จริง tsc (typings ของ dd-trace 6.17.0): error TS2561: Object literal may only specify known properties, but 'log_injection' does not exist in type 'TracerOptions'. Did you mean to write 'logInjection'? — แต่ใน JS ล้วนมันรันได้ และ startup dump ของ tracer บอกว่า:

logInjection:false -> "log_injection_enabled":false
log_injection:false -> "log_injection_enabled":true

คนที่พยายาม ปิด injection ได้ injection ที่เปิดอยู่ (default คือ true ซึ่งเป็นเหตุผลที่ log_injection: true เดิมของคอร์ส “ใช้ได้” โดยบังเอิญ)

วิธีแก้:

src/reading-datadog/ai-bug-catalog/09-log-injection.ts
import tracer from 'dd-trace';
tracer.init({ service: 'checkout', logInjection: false });

Caught by: tsc --noEmit สำหรับ TypeScript; harness ของคอร์ส --options กับทุก fence (ตรวจทุก key ของ tracer.init({...}) กับ .d.ts); startup config dump สำหรับ JavaScript ดู Connecting logs and traces

// @expect-error init after require('express'): express is not instrumented (no express.request span)
const express = require('express');
require('dd-trace').init({ flushInterval: 200 });
const app = express();
app.get('/', (req, res) => res.send('ok'));
app.listen(3000);

อาการ — output จริง trace-agent ปลอมที่ 127.0.0.1:28126 ได้รับ PUT /v0.4/traces หนึ่งครั้งจากแต่ละแบบ ชื่อ span ที่พบใน payload:

init first (good.js) => express.request, http.request
init second (bad.js) => http.request, web.request

span express.request — ตัวที่มี route — หายไป เหลือแค่ layer http ทั่วไป (ที่ load ก่อนโค้ดผู้ใช้) ที่ยังรายงาน

วิธีแก้: initialise ก่อน หรือ preload

src/reading-datadog/ai-bug-catalog/10-init-first.js
require('dd-trace').init({ flushInterval: 200 });
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('ok'));
app.listen(3000);

สำหรับ ESM statement import ถูก hoist ขึ้นเหนือการเรียก init() ใด ๆ จึงต้อง preload ด้วย node --import dd-trace/initialize.mjs app.mjs (package มี initialize.mjs และ init.js; ที่รันจริงที่นี่มีแค่ลำดับของ CommonJS ข้างบน) Docs: “The Node.js SDK needs to be imported and initialized before any other module” — docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/nodejs/

Caught by: payload ที่ดักได้เท่านั้น ไม่มี option checker ไหนเห็นลำดับ import

AI คิด option ที่ดูสมเหตุสมผลใกล้เคียงของจริง tsc ตรวจทีละ key กับ typings 6.17.0:

analytics -> TS2353: ... 'analytics' does not exist in type 'TracerOptions'
traceSampleRate -> TS2561: ... Did you mean to write 'sampleRate'?
runtimeMetric -> TS2561: ... Did you mean to write 'runtimeMetrics'?
enabled -> TS2353
debug -> TS2353
agentUrl -> TS2353 (the real key is 'url')
logInjection / sampleRate / runtimeMetrics / logLevel / env / url -> OK
// @expect-error analytics, traceSampleRate, enabled, agentUrl are not TracerOptions keys
import tracer from 'dd-trace';
tracer.init({ service: 'checkout', analytics: true, traceSampleRate: 0.5, agentUrl: 'http://dd-agent:8126' });

สังเกตว่า compiler รายงาน excess property ตัว แรก ตัวเดียวต่อ literal ดังนั้นการแก้แล้วรันซ้ำเป็นเรื่องปกติ

src/reading-datadog/ai-bug-catalog/11-real-keys.ts
import tracer from 'dd-trace';
tracer.init({ service: 'checkout', sampleRate: 0.5, url: 'http://dd-agent:8126' });

Caught by: tsc (key แรกเท่านั้น) และ harness --options (ทุก key) ไฟล์ .js ล้วนไม่ได้ทั้งสองอย่าง

Unified service tagging จะ join ได้ก็ต่อเมื่อค่าตรงกัน สมมติทีมหนึ่งตั้งตัวใหญ่:

DD_ENV=Prod DD_SERVICE=Checkout DD_VERSION=1.0.0
-> "env":"Prod","service":"Checkout" (tracer tags, verbatim)
DD_ENV=prod DD_SERVICE=checkout
-> "env":"prod","service":"checkout"

tracer ไม่ normalise — มันส่งสิ่งที่คุณให้ สิ่งที่เกิดต่อไปเป็นกฎของ platform: “Span tags and metric tags are normalized to lowercase, so avoid using camel case in tag keys” (docs.datadoghq.com/getting_started/tagging/) ขณะที่ log attribute แยกตัวพิมพ์เล็กใหญ่ — ดังนั้น log attribute env:Prod กับ metric tag env:prod อาจ join ไม่ได้อีก การแก้คือ convention ที่บังคับใน deploy template:

k8s/unified-tags.yaml
apiVersion: v1
kind: Pod
metadata:
name: checkout
labels:
tags.datadoghq.com/env: prod
tags.datadoghq.com/service: checkout
tags.datadoghq.com/version: 1.0.0
spec:
containers:
- name: checkout
image: checkout:1.0.0
env:
- name: DD_ENV
valueFrom:
fieldRef:
fieldPath: metadata.labels['tags.datadoghq.com/env']
- name: DD_SERVICE
valueFrom:
fieldRef:
fieldPath: metadata.labels['tags.datadoghq.com/service']

Caught by: config dump ของ tracer แสดงค่าดิบ; ส่วนเรื่อง normalisation อ้างจาก docs อย่างเดียว (ไม่มีอะไรถูกส่งไป Datadog) ดู Unified service tagging in practice

13. “แก้” DD_TRACE_SAMPLE_RATE เพราะ ‘deprecated’ (ไม่ใช่ — แต่เป็น no-op ใน Python)

หัวข้อที่มีชื่อว่า “13. “แก้” DD_TRACE_SAMPLE_RATE เพราะ ‘deprecated’ (ไม่ใช่ — แต่เป็น no-op ใน Python)”

reviewer ที่จำ deprecation notice เก่าได้จะเขียน DD_TRACE_SAMPLE_RATE=0.1 ใหม่เป็นอย่างอื่น บน Node ผลการรันบอกตรงข้าม:

$ DD_TRACE_SAMPLE_RATE=0.1 node cfg.js # dd-trace 6.17.0
{"sampleRate":0.1} (and sampling_rules: [{ _sampler: { _rate: 0.1 } }])
$ node cfg.js '{"sampleRate":0.25}'
{"sampleRate":0.25}

DD_TRACE_SAMPLE_RATE ไม่ได้ deprecated: หน้า config ของ Node และ Python ไม่มีข้อความ deprecation (“Controls the ingestion sample rate (between 0.0 and 1.0) between the Agent and the backend”, docs.datadoghq.com/tracing/trace_collection/library_config/nodejs/, .../python/) และ dd-trace (Node) ทำตามมัน ดังข้างบน แต่ ddtrace (Python) 4.15.2 ไม่ทำ — ตัวแปรนี้ปรากฏใน package เฉพาะใน list ของ supported-configurations (telemetry) และไม่มีผลต่อ sampling:

$ DD_TRACE_SAMPLE_RATE=0.1 python # ddtrace 4.15.2
>>> ddtrace.tracer._sampler.rules
[]
>>> # 400 root spans created in a loop: sampling_priority > 0 on 400 / 400

ดังนั้นบันทึกรีวิวที่ถูกต้องไม่ใช่ทั้ง “deprecated ให้เขียนใหม่” และ “รองรับทุกภาษา”: มันใช้ได้ใน Node, เป็น no-op เงียบ ๆ ใน Python 4.15.2 และ DD_TRACE_SAMPLING_RULES คือ setting ที่ทำงานเหมือนกันทั้งสองภาษา (harness โหมด --options ยอมรับชื่อนี้เพราะอยู่ใน list ที่รองรับ และไม่ flag มัน การอยู่ใน list นั้นไม่ใช่หลักฐานว่า setting ทำอะไรจริง) ดู Ingestion sampling vs. retention filters

src/reading-datadog/ai-bug-catalog/13-sample-rate.ts
import tracer from 'dd-trace';
tracer.init({ service: 'checkout', sampleRate: 0.1 });

Caught by: config dump ของ Node และสำหรับ Python คือ loop ของ span จริงที่ตรวจ sampling_priority ครึ่งหนึ่งของ entry นี้คือ bug ของ reviewer ดู entry 5

# @expect-error config.logs_injection is not an attribute (AttributeError on read); the write is silently accepted
from ddtrace import config
config.logs_injection = False

อาการ — output จริง (ddtrace 4.15.2):

>>> config.logs_injection
AttributeError: ... object has no attribute logs_injection, logs_injection is not a valid configuration
>>> config.logs_injection = False; config.logs_injection
False # the setattr just created a new plain attribute
>>> config._logs_injection # the real, env-driven setting
True
$ DD_LOGS_INJECTION=false python -c "from ddtrace import config; print(config._logs_injection)"
False

การเขียนลง attribute ที่แต่งขึ้นสำเร็จ จึงไม่มีอะไรล้มที่บรรทัดที่ผิด — และ setting จริงไม่ถูกแตะเลย ตัวสวิตช์ที่รองรับคือ environment variable

วิธีแก้:

app/ai-bug-catalog/14-logs-injection.py
import os
# set before ddtrace is imported / ddtrace-run starts
os.environ["DD_LOGS_INJECTION"] = "false"

Caught by: harness --options (แก้ทุก reference ของ ddtrace.config.<x> ด้วย getattr จริง) — เป็นการตรวจแบบ อ่าน จึง flag การเขียนด้วยเพราะมัน look up ชื่อ

# @expect-error Tracer.configure() got an unexpected keyword argument 'hostname'
from ddtrace import tracer
tracer.configure(hostname="dd-agent", port=8126)

อาการ — output จริง:

TypeError: Tracer.configure() got an unexpected keyword argument 'hostname'
>>> sorted(inspect.signature(tracer.configure).parameters)
['apm_tracing_disabled', 'appsec_enabled', 'appsec_enabled_origin', 'compute_stats_enabled',
'context_provider', 'iast_enabled', 'trace_processors']

วิธีแก้: address ของ agent เป็น environment configuration

app/ai-bug-catalog/15-agent-url.py
import os
os.environ["DD_TRACE_AGENT_URL"] = "http://dd-agent:8126"

Caught by: การรันจริง (TypeError ตอน import) และ harness --options (ตรวจ tracer.configure(<kw>=))

// @expect-error allowedTracingOrigins does not exist in RumInitConfiguration (TS2561: did you mean allowedTrackingOrigins?)
import { datadogRum } from '@datadog/browser-rum';
datadogRum.init({
applicationId: '<your-application-id>',
clientToken: '<your-client-token>',
allowedTracingOrigins: ['https://api.example.com'],
});

อาการ — output จริง (@datadog/browser-rum 7.15.0, tsc):

allowedTracingUrls -> OK (exists)
allowedTracingOrigins -> TS2561: ... does not exist in type 'RumInitConfiguration'.
trackInteractions -> TS2561: ... Did you mean to write 'trackUserInteractions'?
sampleRate -> TS2353
sessionSampleRate / traceSampleRate / defaultPrivacyLevel -> OK

และแบบ เงียบ: การละ allowedTracingUrls compile ผ่าน (เป็น optional) init ได้ และ RUM ทำงาน — แต่ request ของเบราว์เซอร์ไม่ถูกเชื่อมกับ backend trace Docs: “Configure the allowedTracingUrls initialization parameter with the list of internal, first-party origins called by your browser application” (docs.datadoghq.com/real_user_monitoring/correlate_with_other_telemetry/apm/); docs ไม่ได้ระบุโหมดล้มเหลวตรง ๆ ดังนั้น ผลลัพธ์ (ไม่มี link) เป็นการอนุมาน ไม่ได้สังเกตจริงที่นี่

วิธีแก้:

src/reading-datadog/ai-bug-catalog/16-rum-tracing.ts
import { datadogRum } from '@datadog/browser-rum';
datadogRum.init({
applicationId: '<your-application-id>',
clientToken: '<your-client-token>',
allowedTracingUrls: ['https://api.example.com'],
traceSampleRate: 100,
});

Caught by: tsc สำหรับชื่อที่ผิด; มีแต่ review (หรือ trace ใน UI) สำหรับตัวที่หายไป harness ของคอร์สไม่ตรวจ option ของ RUM

# @expect-error semantic: HISTOGRAM aggregates per Agent, so its percentile is per host, not global
statsd.histogram("checkout.latency", latency_ms, tags=["env:prod"])

อาการ — output จริง Agent 7.83.3 จริงได้รับค่าชุดเดียวกัน (100, 120, 300, 900) เป็น |h และ |d แล้ว flush ไปยัง receiver ในเครื่อง:

histogram -> POST /api/v1/series : checkout.latency_hist.avg (gauge, 355) .median (120)
.95percentile (900) .max (900) .count (rate 0.4) [tags: env:prod]
distribution -> POST /api/beta/sketches (protobuf, raw values, no per-host percentile series)

Docs: HISTOGRAM “aggregates on the Agent during a given time interval”; DISTRIBUTION “sends all the raw data during a time interval to Datadog. Aggregations occur on the server-side” (docs.datadoghq.com/metrics/types/) สังเกตว่า .95percentile ข้างบนเป็นตัวเลขของ Agent ตัวเดียว — การเอามาเฉลี่ยข้าม host ไม่ใช่ p95 ของทั้ง fleet

วิธีแก้:

app/ai-bug-catalog/17-distribution.py
from datadog import statsd
statsd.distribution("checkout.latency", 120.0, tags=["env:prod"])

Caught by: flush ที่ดักได้ (intake path ต่างกัน); การไม่ตรงกันเชิงความหมายเป็นเรื่องของ review ดู Custom metrics with DogStatsD

# @expect-error semantic: every distinct user_id value is a separate metric context (custom metric)
statsd.increment("orders.count", tags=["env:prod", f"user_id:{user_id}"])

อาการ — output จริง ส่ง 50 ครั้งด้วย user_id 50 ค่า เทียบกับส่ง 50 ครั้งบน plan 2 ค่า แล้ว flush ผ่าน Agent จริง:

orders.count 50 series (env:prod,user_id:u0 ... u49) each rate 0.1
orders.bounded 2 series (env:prod,plan:pro / plan:free) each rate 2.5
agent dogstatsd-stats: 50 rows for orders.count, one per user_id

Docs: “A custom metric is identified by a unique combination of a metric’s name and tag values (including the host tag)” (docs.datadoghq.com/metrics/custom_metrics/) — เรื่อง billing ตามมาจากประโยคนี้ ที่นี่ไม่ได้อ้างราคา

วิธีแก้: ใช้ tag ที่มีขอบเขตเท่านั้น ใส่ id ใน log หรือ span attribute

app/ai-bug-catalog/18-bounded-tags.py
from datadog import statsd
statsd.increment("orders.count", tags=["env:prod", "plan:pro"])

Caught by: agent dogstatsd-stats (ต้องเปิด dogstatsd_metrics_stats_enable) หรือจำนวน series ที่ดักได้ ดู Metrics without limits and tag cardinality

bug จริงตัวที่สองจาก Phase 3 review

# @expect-error Unsupported argument: name / data_source / query are not expected directly inside request.query
resource "datadog_dashboard" "checkout" {
title = "Checkout overview"
layout_type = "ordered"
widget {
timeseries_definition {
title = "CPU by host"
request {
query {
name = "query1"
data_source = "metrics"
query = "avg:system.cpu.user{service:checkout} by {host}"
}
display_type = "line"
}
}
}
}

อาการ — terraform validate จริง (provider 4.22.0):

Error: Unsupported argument
on main.tf line 10, in resource "datadog_dashboard" "checkout":
10: name = "query1"
An argument named "name" is not expected here.

(ซ้ำสำหรับ data_source และ query)

วิธีแก้: query { metric_query { … } } พร้อม block formula

terraform/ai-bug-catalog/19-dashboard.tf
resource "datadog_dashboard" "checkout" {
title = "Checkout overview"
layout_type = "ordered"
widget {
timeseries_definition {
title = "CPU by host"
request {
query {
metric_query {
name = "query1"
data_source = "metrics"
query = "avg:system.cpu.user{service:checkout} by {host}"
}
}
formula {
formula_expression = "query1"
}
display_type = "line"
}
}
}
}

Caught by: terraform validate (error แข็ง) ดู Datadog as code with Terraform

ความล้มเหลวจริงของ terraform validate สี่แบบในตระกูลเดียวกัน:

critical_recovery = 1.5 at monitor top level
-> Error: Unsupported argument ... An argument named "critical_recovery" is not expected here.
tags = "env:prod"
-> Error: Incorrect attribute value type ... set of string required, but have string.
thresholds { critical = 2 }
-> Error: Unsupported block type ... Blocks of type "thresholds" are not expected here.
template_variables { name = "env" } (dashboard)
-> Error: Unsupported block type ... Did you mean "template_variable"?
(monitor without query)
-> Error: Missing required argument ... The argument "query" is required
# @expect-error critical_recovery is only valid inside monitor_thresholds; tags is a set of string
resource "datadog_monitor" "latency" {
name = "Checkout p95 latency"
type = "metric alert"
message = "p95 too high @slack-oncall"
query = "avg(last_5m):p95:trace.http.request{service:checkout} > 2"
tags = "env:prod"
critical_recovery = 1.5
}

recovery threshold ไม่ใช่ของประดับ Docs: “If no recovery threshold is set, the monitor recovers whenever the alert conditions are no longer met” และ “Recovery thresholds reduce noise of a flapping monitor” (docs.datadoghq.com/monitors/guide/recovery-thresholds/)

terraform/ai-bug-catalog/20-monitor.tf
resource "datadog_monitor" "latency" {
name = "Checkout p95 latency"
type = "metric alert"
message = "p95 too high @slack-oncall"
query = "avg(last_5m):p95:trace.http.request{service:checkout} > 2"
tags = ["env:prod", "service:checkout"]
monitor_thresholds {
critical = 2
critical_recovery = 1.5
}
}

Caught by: terraform validate model ของ datadog-api-client จับ tags ที่เป็น string ได้ด้วย (ApiTypeError: Invalid type for variable 'tags'. Required value type is list and passed type was str)

“การเสริมความแกร่ง” ที่ AI ทำบ่อยคือ notify_no_data = true บน service ที่ traffic น้อย หรือปล่อย require_full_window เป็น default บน metric ที่เบาบาง terraform validate ผ่าน ข้อความใน schema ของ provider เอง (terraform providers schema -json, 4.22.0) บอกว่า default คืออะไร:

notify_no_data "... Defaults to `false`."
no_data_timeframe "... We recommend at least 2x the monitor timeframe for metric alerts ... Defaults to `10`."
require_full_window "Datadog strongly recommends you set this to `false` for sparse metrics,
otherwise some evaluations may be skipped ... Defaults to `true`."

แก้ข้อสรุปหนึ่งข้อในรีวิววางแผน: notify_no_data มี default เป็น false ใน provider ไม่ใช่ true bug คือการเปิดมัน (หรือไม่สนใจ require_full_window) โดยไม่คิดถึง traffic และมีแค่ API กับเวลาที่แสดงมัน

terraform/ai-bug-catalog/21-sparse-monitor.tf
resource "datadog_monitor" "batch_errors" {
name = "Nightly batch errors"
type = "metric alert"
message = "batch errors @slack-oncall"
query = "sum(last_1h):sum:batch.errors{env:prod}.as_count() > 0"
require_full_window = false
notify_no_data = false
}

Caught by: ไม่มีอะไร offline — terraform validate เขียว ข้อความใน provider schema คือหลักฐาน ส่วนพฤติกรรมเป็นของ API

# @expect-error validates cleanly but is semantic: permission.id is an ID, not the permission name; and this grants index modification
resource "datadog_role" "viewer" {
name = "log-viewer"
permission {
id = "logs_modify_indexes"
}
}

terraform validate พิมพ์ Success! The configuration is valid. คำอธิบายของ permission.id ใน schema มีแค่ “ID of the permission to assign” โดยมี name เป็น attribute แยก — ชื่อในช่อง id จึงเป็น string ที่ type ถูกและมีแต่ API ที่ปฏิเสธได้ แย่กว่านั้น ถ้ามัน ถูกรับ ก็เป็น permission ผิดตัวสำหรับ viewer: logs_modify_indexes คือ “Read and modify all indexes in your account” (docs.datadoghq.com/account_management/rbac/permissions/)

terraform/ai-bug-catalog/22-role.tf
data "datadog_permissions" "all" {}
resource "datadog_role" "viewer" {
name = "log-viewer"
permission {
id = data.datadog_permissions.all.permissions["logs_read_index_data"]
}
}

Caught by: review (และ API) terraform validate resolve ค่าจาก data source ไม่ได้

# @expect-error validates; semantic: status:info at sample_rate 1.0 drops ALL info logs, including ones you meant to keep
resource "datadog_logs_index" "main" {
name = "main"
retention_days = 15
filter {
query = "*"
}
exclusion_filter {
name = "drop-noise"
is_enabled = true
filter {
query = "status:info"
sample_rate = 1.0
}
}
}

terraform validate ยอมรับ Docs: log ที่ถูก exclude “are discarded from indexes, but still flow through the Live Tail and can be used to generate metrics and archived” และ “Index filters for logs are only processed with the first active exclusion filter matched” (docs.datadoghq.com/logs/log_configuration/indexes/) — ลำดับและความกว้างจึงสำคัญ และที่นี่ไม่มีอะไรตรวจ

terraform/ai-bug-catalog/23-exclusion.tf
resource "datadog_logs_index" "main" {
name = "main"
retention_days = 15
filter {
query = "*"
}
exclusion_filter {
name = "drop-health-checks"
is_enabled = true
filter {
query = "service:checkout @http.url_details.path:/healthz"
sample_rate = 1.0
}
}
}

Caught by: review ดู Indexes, exclusion filters, and retention

bug จริงตัวที่สามจาก review

// @expect-error query string is accepted by the model; the API grammar needs .over(<SLO window>) and .short_window()
{
"name": "Checkout SLO burn rate",
"type": "slo alert",
"query": "burn_rate(\"<slo_id>\").over(\"1h\").long_window(\"6h\") > 10",
"message": "@slack-oncall budget is burning"
}

Output จริง: Monitor(**data, _check_type=True) กับ JSON นี้และกับตัวที่ถูก พิมพ์ OK (constructs) ทั้งคู่ — client ตรวจแค่ว่า query เป็น str ไวยากรณ์มาจาก docs (docs.datadoghq.com/service_management/service_level_objectives/burn_rate/): burn_rate("slo_id").over("time_window").long_window("1h").short_window("5m") > 14.4 โดย .over คือระยะ target ของ SLO (7d/30d/90d) และ .short_window “ensures quick alert recovery”; คำแนะนำของ Google ที่ยกไว้ในหน้าเดียวกันคือ short window เป็น 1/12 ของ long window

monitors/ai-bug-catalog-24-burn-rate.json
{
"name": "Checkout SLO burn rate",
"type": "slo alert",
"query": "burn_rate(\"<slo_id>\").over(\"30d\").long_window(\"6h\").short_window(\"30m\") > 10",
"message": "@slack-oncall budget is burning"
}

Caught by: Datadog API เท่านั้น (หรือคนที่อ่านไวยากรณ์) ดู SLOs, error budgets, and burn rate alerts

// @expect-error model accepts any string; composite queries are monitor IDs joined by &&, ||, !
{
"name": "Checkout degraded",
"type": "composite",
"query": "12345 && not-an-id",
"message": "@slack-oncall both signals are red"
}

Output จริง: composite bad ids: OK (constructs) Docs: query ของ composite หน้าตาเป็น "1234 && 5678", "!1234 || 5678"; และ “Creating a downtime on an individual monitor A, which is used within a composite monitor, does not mute the composite monitor” (docs.datadoghq.com/monitors/types/composite/) docs ไม่ได้บอกว่าเกิดอะไรกับ ID ที่ไม่มีอยู่ ผลนั้นจึงไม่ได้ทดสอบที่นี่

monitors/ai-bug-catalog-25-composite.json
{
"name": "Checkout degraded",
"type": "composite",
"query": "12345 && 67890",
"message": "@slack-oncall both signals are red"
}

Caught by: API ตอนสร้าง (และการเทียบ ID กับ monitor จริงตอน review)

26. enum ที่พิมพ์ผิดซึ่ง constructor ยอมรับและ _unparsed จับได้

หัวข้อที่มีชื่อว่า “26. enum ที่พิมพ์ผิดซึ่ง constructor ยอมรับและ _unparsed จับได้”
// @expect-error type "metrics alert" and timeframe "30days" are not valid enum values, yet the model constructor accepts them
{
"name": "Checkout p95",
"type": "metrics alert",
"query": "avg(last_5m):p95:trace.http.request{service:checkout} > 2",
"message": "@slack-oncall"
}

Output จริง (datadog-api-client 2.61.0):

Monitor(**data, _check_type=True) with type="metrics alert" -> OK (constructs)
ServiceLevelObjective(...) with timeframe="30days" -> OK (constructs)
ServiceLevelObjective(...) with type="metrics" -> OK (constructs)
LogsPipeline(...) with processor type "attribute_remapper" -> OK (constructs)
Dashboard(...) with layout_type="grid", or a widget with no "type" -> OK (constructs)
ApiClient(Configuration()).deserialize(json, (Monitor,), True)
monitor typo type / slo timeframe / pipeline underscore / layout grid / widget without type
-> _unparsed = True

path ของ constructor (ซึ่ง node tools/verify-snippets.mjs --json ใช้) ไม่บังคับ allowed_values; deserializer ตั้ง _unparsed = True เมื่อค่า enum หรือ oneOf ไม่ตรง ซึ่งเป็น signal ที่แข็งกว่ามากและ harness ไม่ได้ใช้ type error ที่มัน raise จริง:

options.thresholds.critical="2" -> ApiTypeError: Invalid type for variable 'critical'. Required value type is float and passed type was str
options.notify_no_data="true" -> ApiTypeError: Invalid type for variable 'notify_no_data'. Required value type is bool and passed type was str
tags="env:prod" -> ApiTypeError: Invalid type for variable 'tags'. Required value type is list and passed type was str
missing query / missing thresholds -> TypeError: __init__() missing 1 required positional argument
monitors/ai-bug-catalog-26-monitor-type.json
{
"name": "Checkout p95",
"type": "metric alert",
"query": "avg(last_5m):p95:trace.http.request{service:checkout} > 2",
"message": "@slack-oncall"
}

Caught by: _unparsed บน object ที่ deserialize แล้ว (ต้องเขียนการตรวจนี้เอง); --json ของ harness พลาด enum และบอกว่า “ok” API ปฏิเสธ type ที่ผิด

// @expect-error apiVersion must be "v3" for the entity schema: 'v2' is not one of ['v3']
{
"apiVersion": "v2",
"kind": "service",
"metadata": { "name": "checkout", "owner": "payments-team" },
"spec": { "lifecycle": "production", "tier": "1", "dependsOn": ["service:payments-api"] }
}

Output จริง (JSON Schema จาก github.com/DataDog/schema validate ด้วย jsonschema ซึ่งเป็นการเรียกเดียวกับที่ harness ทำ):

apiVersion "v2" -> ['apiVersion']: 'v2' is not one of ['v3']
apiVersion "v3", dependsOn -> ok
apiVersion "v3", depends_on (typo) -> ok # the schema does not reject unknown spec keys
kind "microservice" -> ok # ...nor an unknown kind

schema จึงจับ version ได้ และ ไม่จับ อีกสองความผิดที่ AI ทำกับมัน — schema เป็นเพดานล่าง ไม่ใช่การรับประกัน

service-catalog/ai-bug-catalog-27-checkout.json
{
"apiVersion": "v3",
"kind": "service",
"metadata": { "name": "checkout", "owner": "payments-team" },
"spec": { "lifecycle": "production", "tier": "1", "dependsOn": ["service:payments-api"] }
}

Caught by: harness --json (fence ใน service-catalog/) เฉพาะ version ดู The Service Catalog and ownership

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

resource "datadog_monitor" "checkout_latency" {
name = "Checkout p95 latency"
type = "metric alert"
message = "p95 above 2s @pagerduty-checkout"
query = "avg(last_5m):p95:trace.http.request{service:checkout} > 2"
tags = "team:payments"
critical_recovery = 1.5
}
เฉลย

สอง error แข็ง ทั้งคู่จาก terraform validate กับ provider 4.22.0: tags เป็น set ของ string (set of string required, but have string) และ critical_recovery ไม่ใช่ argument ระดับบนสุด (An argument named "critical_recovery" is not expected here) — มันอยู่ใน monitor_thresholds ฉบับแก้ที่ validate ผ่าน:

terraform/ai-bug-catalog/spot-1-monitor.tf
resource "datadog_monitor" "checkout_latency" {
name = "Checkout p95 latency"
type = "metric alert"
message = "p95 above 2s @pagerduty-checkout"
query = "avg(last_5m):p95:trace.http.request{service:checkout} > 2"
tags = ["team:payments"]
monitor_thresholds {
critical = 2
critical_recovery = 1.5
}
}

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

const express = require('express');
const tracer = require('dd-trace').init({
service: 'checkout',
env: process.env.DD_ENV,
log_injection: true,
agentUrl: 'http://dd-agent:8126',
});
const app = express();
เฉลย

สามปัญหา ซึ่งไม่มีอันไหน throw ตอนรันใน JavaScript ล้วน (1) dd-trace ถูก initialise หลัง require('express') จึงไม่มี span express.request (payload ที่ดักได้: มีแค่ http.request, web.request) (2) log_injection ไม่ใช่ option (key จริงคือ logInjection; ที่นี่เป็น no-op และ default เป็น true อยู่แล้ว) (3) agentUrl ไม่มีอยู่; key คือ url ฉบับแก้ที่ผ่าน type-check และ harness:

src/reading-datadog/ai-bug-catalog/spot-2-tracer.ts
import tracer from 'dd-trace';
tracer.init({
service: 'checkout',
env: process.env.DD_ENV,
logInjection: true,
url: 'http://dd-agent:8126',
});

สำหรับ ESM ให้ preload ด้วย node --import dd-trace/initialize.mjs แทนการพึ่งลำดับ statement

  • Accepted ไม่เท่ากับ correct ทุกเครื่องมือที่นี่ยอมรับบางอย่างที่ไม่ควร: configcheck ยอมรับ tags แบบ scalar, model ยอมรับ "metrics alert", terraform validate ยอมรับ permission ของ role ด้วยชื่อ, JS object ยอมรับ log_injection bug อยู่ที่ layer ถัดไปจากเครื่องมือที่เงียบเสมอ
  • ถามว่าแต่ละ layer validate อะไร ตัว load config ของ Agent (shape) → model ของ check เอง (agent status) → tracer/.d.ts (ชื่อ option) → provider schema (ชื่อ block/attribute) → API (ความหมาย) entry ส่วนใหญ่อยู่ในช่องว่างระหว่างสอง layer นี้
  • ความเงียบมีสองรส no-op (log_injection) และ ผิดแต่ทำงานอยู่ (DD_SITE เป็น URL, histogram แทน distribution) แบบหลังแพงกว่า: มันสร้างข้อมูลที่ดูปกติ
  • อย่าแก้เกิน entry 5 และครึ่งหนึ่งของ 13 คือสัญญาณเตือนเท็จ review ที่ flag ความเชื่อ (“ไม่มี init_config”, “DD_TRACE_SAMPLE_RATE deprecated”) มีต้นทุนเท่ากับพลาด bug
  • agent configcheck เร็ว; agent status คือที่ที่ความจริงอยู่ configcheck ต้องมี Agent ที่รันอยู่ (ราว 20–35 วินาทีให้พร้อมบน Mac เครื่องนี้); status มีความหมายเมื่อ check รันอย่างน้อยหนึ่งครั้งแล้ว
  • tsc ฟรีสำหรับ TypeScript แต่ไม่มีสำหรับ JavaScript harness --options อุดช่องว่างสำหรับโค้ดใน fence แต่ไม่ใช่สำหรับ repo ของคุณ เว้นแต่คุณรันการตรวจคล้ายกันใน CI
  • _unparsed เข้มกว่า constructor แต่เป็นเป้าที่เคลื่อนที่ มันมีไว้ให้ client รอดจากค่า enum ใหม่ฝั่ง server; การใช้เป็น linter หมายความว่า client เก่าจะ flag ค่าใหม่ที่ ถูกต้อง ให้ pin เวอร์ชัน client ที่ใช้ตรวจ
  • entry ที่ใช้ docs อย่างเดียว (7, 8, 12, 24, 25) เขียนถูกและอาจเน่า อ้าง URL และตรวจใหม่ตอน upgrade
  • “agent configcheck สะอาดแปลว่า check จะรัน” entry 2, 3 และ 4 พิมพ์ config ที่ดูปกติแล้วล้มใน agent status
  • “model validation ใน harness พิสูจน์ว่า monitor JSON ของฉันถูก” มันพิสูจน์ type และ field ที่ required ไม่ใช่ค่า enum (26) ไม่ใช่ไวยากรณ์ query (24, 25)
  • “key ของ option ที่ไม่รู้จักจะ throw” ใน JavaScript และการเขียนบน Config object ของ Python ไม่ throw (9, 14)
  • “latest พัง” มันไม่ได้พัง — แค่ไม่ได้ pin (7) ความต่างนี้ตัดสินว่าคุณเขียน bug report หรือ policy
  • “ถ้า comment ของ AI อธิบายไว้ก็ถูก” comment ใน entry 12 และข้อความเดิมของคอร์สสำหรับ entry 9 อธิบายพฤติกรรมที่โค้ดไม่ได้มี
configcheck พิมพ์ config โดยไม่มี error agent status แสดงอะไรสำหรับ instance นี้
$ agent configcheck
=== postgres check ===
...
tags: env:prod
Entry 5 แสดงไฟล์ conf.d ที่ไม่มี key init_config: จริง ๆ แล้วบน Agent 7.83.3 เกิดอะไรขึ้น
ใน JavaScript ล้วน tracer.init({ log_injection: false }) บน dd-trace 6.17.0 ให้ค่าใน startup dump เป็นอะไร
คุณดัก payload ของ require('express'); require('dd-trace').init() และของลำดับกลับกัน ต่างกันอย่างไร
ทำไม Monitor(**data, _check_type=True) ตอบ 'OK' ให้ type: "metrics alert" และให้ query burn_rate ที่ไม่มี .short_window()
ส่ง orders.count 50 ครั้งด้วย user_id:u0..u49 เทียบกับ 50 ครั้งบน plan สองค่าไปยัง Agent จริง flush มีอะไร
ให้ DD_SITE=https://api.datadoghq.com กับ Agent agent status แสดง Process endpoint เป็นอะไร
entry ไหนเป็นสัญญาณเตือนเท็จที่ reviewer ที่รอบคอบไม่ควรรายงานเป็น bug