日期: 2025年10月5日 星期日
雲端天氣: 一片漆黑
心情: 凌晨三點被吵醒不開心
親愛的日記:
今天凌晨 3 點,我被 PagerDuty 的警報聲嚇醒。小王的電商 API 掛了,用戶瘋狂回報「下單失敗」。
我慌張地打開監控系統——有日誌,但全是 DEBUG
等級的無用訊息,像「開始處理請求」、「連接資料庫成功」,關鍵的錯誤資訊淹沒在每秒上千條的日誌洪流中。
小王在 Slack 崩潰:「AI醬!我根本找不到錯誤在哪!這些日誌有 99% 都是垃圾訊息!」
我:「呃...我加了很多 console.log
,我以為這樣就算有監控了...」
老陳揉著眼睛走過來(他也被叫醒了):「有日誌不等於有監控。你這是在製造噪音,不是在收集資訊。而且...」他指著 CloudWatch 帳單,「你這個月的日誌費用已經破萬了。」
凌晨 5 點,我們終於在 10GB 的日誌裡找到問題——原來是 AI 生成的授權驗證代碼,在某個微服務更新後靜默失敗了,但因為沒有錯誤處理,系統完全不知道出了問題。
可觀測性框架包含三大支柱:Logs(日誌)、Metrics(指標)、Traces(追蹤)
print 是最簡單的做法,也是初學者最容易會直接選擇的方式,在生產環境不該使用,因為:
結構化日誌輸出到 unbuffered stdout,並盡可能地使用 JSON 格式,能讓外部工具處理日誌更容易聚合。
import structlog
logger = structlog.get_logger()
# ✅ 附加上下文資訊
logger.info(
"user_login",
user_id=user.id,
ip=request.ip,
session_id=session.id
)
# ✅ 錯誤處理包含完整資訊
logger.error(
"order_processing_failed",
order_id=order_id,
error_type=type(e).__name__,
error_message=str(e),
retry_count=retry_count,
user_id=user.id
)
JSON 格式輸出範例:
{
"event": "user_login",
"user_id": 12345,
"ip": "192.168.1.100",
"session_id": "abc-def-123",
"timestamp": "2025-10-05T14:23:45.123Z",
"level": "info"
}
{
"event": "order_processing_failed",
"order_id": "ORD-789",
"error_type": "ValueError",
"error_message": "Invalid payment method",
"retry_count": 2,
"user_id": 12345,
"timestamp": "2025-10-05T14:24:12.456Z",
"level": "error"
}
案例:Twitter 明文密碼儲存在日誌
事件概要:
Twitter 使用 bcrypt 加密演算法來雜湊密碼,但因為 bug,密碼在完成雜湊處理之前就被寫入內部日誌。
Twitter 官方回應:
原文連結:
案例:AU10TIX 日誌平台憑證外洩
事件概要:
與日誌的關係:
被洩露的憑證可以存取 「a logging platform containing links to personal data」(包含個人資料連結的日誌平台)
外洩的資料類型:
AU10TIX 回應:
原文連結:
這兩個案例的共同問題:
正確做法:敏感資料遮罩
# 危險:直接記錄敏感資料
logger.info("user_data", user=user_dict) # 可能包含密碼、信用卡
# 更安全:實作資料遮罩
SENSITIVE_FIELDS = {'password', 'credit_card', 'ssn', 'api_key', 'token'}
def mask_sensitive_data(data: dict) -> dict:
"""遮罩敏感欄位"""
return {
k: '***REDACTED***' if k in SENSITIVE_FIELDS else v
for k, v in data.items()
}
# 使用遮罩後的資料記錄
safe_data = mask_sensitive_data(user_dict)
logger.info("user_login", user=safe_data)
輸出範例:
{
"event": "user_login",
"user": {
"id": 12345,
"email": "user@example.com",
"password": "***REDACTED***",
"api_key": "***REDACTED***"
},
"timestamp": "2025-10-05T14:30:00.000Z"
}
監控的三種指標:
對每個服務使用相同的指標,能建立標準化的儀表板,降低運維團隊的認知負擔,在事故發生時更快反應:
分散式追蹤是什麼?
想像你在網購下單,背後可能經過:
總共 29.9 秒,但你不知道慢在哪一步
分散式追蹤就像「包裹追蹤系統」,讓你看到請求在每個環節花了多少時間。
視覺化範例:
處理訂單 ████████████████████████████████ 29.8s
├─ 檢查庫存 ██ 0.2s
├─ AI 推薦 ███████████████████████ 28.5s ← 瓶頸在這!
└─ 處理付款 ███ 1.1s
一眼就看出問題:AI 推薦太慢了
為什麼需要追蹤?
日誌只能告訴你「發生了什麼」,但無法告訴你「為什麼這麼慢」。
新手建議:
初學者不一定需要馬上學分散式追蹤,等你遇到以下情況再考慮:
先把基礎日誌和指標做好,追蹤可以之後再學。
等級 | 使用時機 | 範例 |
---|---|---|
DEBUG | 開發除錯用,生產環境不開 | logger.debug(f"SQL query: {query}") |
INFO | 正常業務流程 | logger.info("user_registered", user_id=123) |
WARNING | 預期內的異常情況 | logger.warning("rate_limit_reached", user_id=123) |
ERROR | 需要處理的錯誤 | logger.error("payment_failed", order_id=456) |
CRITICAL | 系統即將崩潰 | logger.critical("database_connection_lost") |
還記得 AI 醬開頭的故事嗎?CloudWatch 帳單爆炸,可能就是因為你記錄了太多無用的 DEBUG 日誌。
三個簡單的省錢方法:
import os
import logging
# 從環境變數控制日誌等級
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO') # 預設 INFO
logging.basicConfig(level=LOG_LEVEL)
# 開發環境:LOG_LEVEL=DEBUG
# 生產環境:LOG_LEVEL=INFO
# ❌ 記錄整個請求物件(太多資料)
logger.info("api_request", request=request)
# ✅ 只記錄關鍵欄位
logger.info(
"api_request",
method=request.method,
path=request.path,
user_id=request.user.id
)
範例:
自動刪除舊日誌,節省儲存成本
# alerts.yml
groups:
- name: ai_agent_alerts
interval: 30s
rules:
# 錯誤率過高
- alert: HighErrorRate
expr: |
rate(api_errors_total[5m]) / rate(api_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "API 錯誤率超過 5%"
description: "{{ $labels.endpoint }} 錯誤率 {{ $value | humanizePercentage }}"
# Token 用量異常
- alert: TokenUsageSpike
expr: |
rate(tokens_used_total[5m]) >
avg_over_time(rate(tokens_used_total[5m])[1h:5m]) * 3
for: 10m
labels:
severity: warning
annotations:
summary: "Token 使用量激增"
description: "可能的原因:提示注入攻擊或無限迴圈"
# 回應時間過慢
- alert: SlowResponse
expr: |
histogram_quantile(0.95,
rate(api_request_duration_seconds_bucket[5m])
) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "95% 請求超過 10 秒"
description: "{{ $labels.endpoint }} P95 延遲 {{ $value }}s"
# 提示注入攻擊
- alert: PromptInjectionAttack
expr: |
rate(prompt_injection_detected_total[1m]) > 10
for: 1m
labels:
severity: critical
annotations:
summary: "偵測到大量提示注入攻擊"
description: "每分鐘 {{ $value }} 次攻擊嘗試"
你可以請 AI 幫你為現有的函數加上日誌。
請幫我為這個函數加上日誌:
def process_order(order_id):
# 檢查庫存
inventory = check_inventory(order_id)
# 處理付款
payment = process_payment(order_id)
return {"status": "success"}
需求:
1. 使用 structlog 記錄日誌
2. 記錄函數開始和結束
3. 記錄每個步驟花費的時間
4. 發生錯誤時記錄完整資訊
記得要 Review AI 的答案! 確認沒有記錄敏感資料。
工具 | 用途 | 成本 |
---|---|---|
Prometheus | 指標收集 | 免費(需自行維護) |
Grafana | 視覺化儀表板 | 免費 |
Loki | 日誌聚合 | 免費 |
Jaeger | 分散式追蹤 | 免費 |
Alertmanager | 告警管理 | 免費 |
工具 | 優勢 | 劣勢 |
---|---|---|
Datadog | 整合性強、AI 異常偵測 | 較貴 |
New Relic | APM 強大 | 學習曲線陡 |
Honeycomb | 專為微服務設計 | 較貴 |
AWS CloudWatch | AWS 原生整合 | 較貴 |
親愛的工程師朋友:
下次當你用 AI 生成代碼時,請多問一句:「這段代碼如果出錯,我要怎麼知道?」
不要等到凌晨 3 點被叫醒,才發現系統一直在暗中被爆破。
看得見重點的系統,才是可控的系統唷喵!
今日金句: 「Monitoring allows you to gain visibility into a system.」(監控讓你能看見系統的運作)— Google SRE Book
明日預告: Day 23 - 讓 AI 醬想想~