{"product_id":"observability-cho-claude-api-metrics-logging-va-alerting-production","title":"Observability cho Claude API — Metrics, Logging và Alerting production","description":"\n\u003cp\u003eKhi ứng dụng sử dụng Claude API chuyển từ giai đoạn prototype sang production, một câu hỏi quan trọng xuất hiện: làm sao bạn biết hệ thống đang hoạt động tốt? Làm sao phát hiện sớm khi latency tăng đột biến, error rate vượt ngưỡng, hoặc chi phí API vượt budget? Câu trả lời nằm ở observability — khả năng quan sát và hiểu trạng thái bên trong hệ thống thông qua dữ liệu nó tạo ra.\u003c\/p\u003e\n\n\u003cp\u003eBài viết này hướng dẫn bạn xây dựng observability stack hoàn chỉnh cho ứng dụng Claude API, từ việc xác định metrics quan trọng, thiết lập structured logging, tích hợp OpenTelemetry, cho đến xây dựng dashboard và cấu hình alert rules.\u003c\/p\u003e\n\n\u003ch2\u003eTại sao observability quan trọng với LLM API\u003c\/h2\u003e\n\u003cp\u003eKhác với API truyền thống, LLM API có những đặc điểm riêng khiến observability trở nên phức tạp hơn:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eLatency biến động lớn:\u003c\/strong\u003e Một request đơn giản có thể mất 500ms, trong khi request phức tạp với output dài mất 30 giây trở lên\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eChi phí theo token:\u003c\/strong\u003e Mỗi request có chi phí khác nhau tùy thuộc vào số lượng input và output tokens\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eStreaming response:\u003c\/strong\u003e Response được trả về theo chunks, khiến việc đo lường end-to-end latency phức tạp hơn\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRate limiting:\u003c\/strong\u003e Anthropic áp dụng rate limits theo tokens per minute và requests per minute, cần giám sát để tránh bị throttle\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eChất lượng output:\u003c\/strong\u003e Không chỉ quan tâm hệ thống có hoạt động không, mà còn chất lượng kết quả có đáp ứng yêu cầu không\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eCác metrics quan trọng cần thu thập\u003c\/h2\u003e\n\n\u003ch3\u003e1. Time to First Token (TTFT)\u003c\/h3\u003e\n\u003cp\u003eTTFT đo thời gian từ khi gửi request đến khi nhận được token đầu tiên trong response. Đây là metric quan trọng nhất với trải nghiệm người dùng vì nó quyết định thời gian người dùng phải chờ trước khi thấy kết quả bắt đầu xuất hiện.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport time\nimport anthropic\n\nclient = anthropic.Anthropic()\n\ndef call_claude_with_metrics(messages, model=\"claude-sonnet-4-20250514\"):\n    start_time = time.monotonic()\n    ttft = None\n    total_input_tokens = 0\n    total_output_tokens = 0\n    chunks_received = 0\n\n    with client.messages.stream(\n        model=model,\n        max_tokens=1024,\n        messages=messages\n    ) as stream:\n        for event in stream:\n            if ttft is None and hasattr(event, 'type') and event.type == 'content_block_delta':\n                ttft = time.monotonic() - start_time\n            chunks_received += 1\n\n        response = stream.get_final_message()\n        total_time = time.monotonic() - start_time\n        total_input_tokens = response.usage.input_tokens\n        total_output_tokens = response.usage.output_tokens\n\n    return {\n        \"ttft_seconds\": ttft,\n        \"total_time_seconds\": total_time,\n        \"input_tokens\": total_input_tokens,\n        \"output_tokens\": total_output_tokens,\n        \"tokens_per_second\": total_output_tokens \/ total_time if total_time \u0026gt; 0 else 0,\n        \"chunks_received\": chunks_received,\n    }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003e2. Tokens Per Second (TPS)\u003c\/h3\u003e\n\u003cp\u003eTPS đo tốc độ sinh token của model. Metric này giúp bạn phát hiện khi model đang chạy chậm hơn bình thường, có thể do tải cao phía Anthropic hoặc vấn đề mạng.\u003c\/p\u003e\n\u003cp\u003eCông thức: \u003cstrong\u003eTPS = output_tokens \/ (total_time - ttft)\u003c\/strong\u003e. Lưu ý trừ đi TTFT vì giai đoạn đầu là thời gian model xử lý prompt, không phải thời gian sinh token.\u003c\/p\u003e\n\n\u003ch3\u003e3. Error Rate\u003c\/h3\u003e\n\u003cp\u003ePhân loại error theo HTTP status code để có hành động phù hợp:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003e400 Bad Request:\u003c\/strong\u003e Lỗi từ phía ứng dụng — prompt quá dài, format sai\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e401\/403:\u003c\/strong\u003e Vấn đề authentication — API key hết hạn hoặc không hợp lệ\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e429 Rate Limited:\u003c\/strong\u003e Vượt quá giới hạn — cần điều chỉnh concurrency hoặc nâng tier\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e500\/529 Server Error:\u003c\/strong\u003e Vấn đề phía Anthropic — cần retry với backoff\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003e4. Cost Per Request\u003c\/h3\u003e\n\u003cp\u003eTính chi phí theo công thức của Anthropic: (input_tokens * input_price + output_tokens * output_price). Track chi phí theo user, feature, hoặc department để phân bổ ngân sách chính xác.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003ePRICING = {\n    \"claude-sonnet-4-20250514\": {\"input\": 3.0, \"output\": 15.0},\n    \"claude-haiku-4-20250414\": {\"input\": 0.80, \"output\": 4.0},\n    \"claude-opus-4-20250514\": {\"input\": 15.0, \"output\": 75.0},\n}\n\ndef calculate_cost(model, input_tokens, output_tokens):\n    prices = PRICING.get(model, PRICING[\"claude-sonnet-4-20250514\"])\n    input_cost = (input_tokens \/ 1_000_000) * prices[\"input\"]\n    output_cost = (output_tokens \/ 1_000_000) * prices[\"output\"]\n    return {\n        \"input_cost_usd\": input_cost,\n        \"output_cost_usd\": output_cost,\n        \"total_cost_usd\": input_cost + output_cost,\n    }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003e5. Rate Limit Utilization\u003c\/h3\u003e\n\u003cp\u003eAnthropic trả về rate limit headers trong mỗi response. Thu thập và giám sát chúng để biết bạn đang sử dụng bao nhiêu phần trăm quota:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003edef extract_rate_limit_headers(response_headers):\n    return {\n        \"requests_remaining\": int(response_headers.get(\"anthropic-ratelimit-requests-remaining\", 0)),\n        \"requests_limit\": int(response_headers.get(\"anthropic-ratelimit-requests-limit\", 0)),\n        \"tokens_remaining\": int(response_headers.get(\"anthropic-ratelimit-tokens-remaining\", 0)),\n        \"tokens_limit\": int(response_headers.get(\"anthropic-ratelimit-tokens-limit\", 0)),\n        \"requests_reset\": response_headers.get(\"anthropic-ratelimit-requests-reset\", \"\"),\n        \"tokens_reset\": response_headers.get(\"anthropic-ratelimit-tokens-reset\", \"\"),\n    }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eStructured Logging cho Claude API\u003c\/h2\u003e\n\u003cp\u003eLog không cấu trúc như \"Called Claude API successfully\" gần như vô dụng trong production. Structured logging sử dụng format JSON để mỗi log entry chứa đầy đủ context cần thiết cho việc debug và phân tích.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport logging\nimport json\nimport uuid\nfrom datetime import datetime\n\nclass ClaudeAPILogger:\n    def __init__(self):\n        self.logger = logging.getLogger(\"claude_api\")\n        handler = logging.StreamHandler()\n        handler.setFormatter(logging.Formatter('%(message)s'))\n        self.logger.addHandler(handler)\n        self.logger.setLevel(logging.INFO)\n\n    def log_request(self, request_id, model, messages, metadata=None):\n        log_entry = {\n            \"timestamp\": datetime.utcnow().isoformat(),\n            \"event\": \"claude_api_request\",\n            \"request_id\": request_id,\n            \"model\": model,\n            \"message_count\": len(messages),\n            \"estimated_input_tokens\": sum(len(m.get(\"content\", \"\")) \/\/ 4 for m in messages),\n            \"metadata\": metadata or {},\n        }\n        self.logger.info(json.dumps(log_entry))\n\n    def log_response(self, request_id, metrics, metadata=None):\n        log_entry = {\n            \"timestamp\": datetime.utcnow().isoformat(),\n            \"event\": \"claude_api_response\",\n            \"request_id\": request_id,\n            \"ttft_ms\": round(metrics[\"ttft_seconds\"] * 1000, 2),\n            \"total_time_ms\": round(metrics[\"total_time_seconds\"] * 1000, 2),\n            \"input_tokens\": metrics[\"input_tokens\"],\n            \"output_tokens\": metrics[\"output_tokens\"],\n            \"tps\": round(metrics[\"tokens_per_second\"], 2),\n            \"cost_usd\": metrics.get(\"cost_usd\", 0),\n            \"metadata\": metadata or {},\n        }\n        self.logger.info(json.dumps(log_entry))\n\n    def log_error(self, request_id, error, metadata=None):\n        log_entry = {\n            \"timestamp\": datetime.utcnow().isoformat(),\n            \"event\": \"claude_api_error\",\n            \"request_id\": request_id,\n            \"error_type\": type(error).__name__,\n            \"error_message\": str(error),\n            \"status_code\": getattr(error, 'status_code', None),\n            \"metadata\": metadata or {},\n        }\n        self.logger.error(json.dumps(log_entry))\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eNguyên tắc logging hiệu quả\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eLuôn gắn request_id:\u003c\/strong\u003e Cho phép trace toàn bộ lifecycle của một request từ khi nhận đến khi trả kết quả\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eKhông log nội dung prompt\/response:\u003c\/strong\u003e Tránh rủi ro bảo mật và tuân thủ GDPR\/privacy. Chỉ log metadata\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eLog ở mức phù hợp:\u003c\/strong\u003e INFO cho request\/response bình thường, WARNING cho retry, ERROR cho failures\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eThêm business context:\u003c\/strong\u003e user_id, feature_name, department giúp phân tích chi phí và usage pattern\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eTích hợp OpenTelemetry\u003c\/h2\u003e\n\u003cp\u003eOpenTelemetry (OTel) là tiêu chuẩn mở để thu thập telemetry data gồm traces, metrics và logs. Tích hợp OTel giúp bạn có cái nhìn end-to-end về performance và dễ dàng kết nối với nhiều backend khác nhau.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003efrom opentelemetry import trace, metrics\nfrom opentelemetry.sdk.trace import TracerProvider\nfrom opentelemetry.sdk.metrics import MeterProvider\nfrom opentelemetry.sdk.trace.export import BatchSpanExporter\nfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\nfrom opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter\nfrom opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader\n\ndef setup_telemetry(service_name=\"claude-api-service\", endpoint=\"localhost:4317\"):\n    # Traces\n    trace_provider = TracerProvider()\n    trace_provider.add_span_processor(\n        BatchSpanExporter(OTLPSpanExporter(endpoint=endpoint))\n    )\n    trace.set_tracer_provider(trace_provider)\n\n    # Metrics\n    metric_reader = PeriodicExportingMetricReader(\n        OTLPMetricExporter(endpoint=endpoint),\n        export_interval_millis=30000\n    )\n    meter_provider = MeterProvider(metric_readers=[metric_reader])\n    metrics.set_meter_provider(meter_provider)\n\n    return trace.get_tracer(service_name), metrics.get_meter(service_name)\n\ntracer, meter = setup_telemetry()\n\n# Tao cac metrics instruments\nrequest_counter = meter.create_counter(\n    \"claude_api.requests.total\",\n    description=\"Total number of Claude API requests\"\n)\nerror_counter = meter.create_counter(\n    \"claude_api.errors.total\",\n    description=\"Total number of Claude API errors\"\n)\nttft_histogram = meter.create_histogram(\n    \"claude_api.ttft.milliseconds\",\n    description=\"Time to first token in milliseconds\"\n)\ncost_counter = meter.create_counter(\n    \"claude_api.cost.usd\",\n    description=\"Total cost in USD\"\n)\ntoken_counter = meter.create_counter(\n    \"claude_api.tokens.total\",\n    description=\"Total tokens processed\"\n)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eInstrumented API Client\u003c\/h3\u003e\n\u003cp\u003eKết hợp tracing và metrics vào API client:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport anthropic\nimport time\n\nclass InstrumentedClaudeClient:\n    def __init__(self):\n        self.client = anthropic.Anthropic()\n\n    def create_message(self, messages, model=\"claude-sonnet-4-20250514\",\n                       max_tokens=1024, feature=\"default\", user_id=\"unknown\"):\n\n        attributes = {\n            \"claude.model\": model,\n            \"claude.feature\": feature,\n            \"claude.user_id\": user_id,\n        }\n\n        with tracer.start_as_current_span(\"claude_api_call\", attributes=attributes) as span:\n            start = time.monotonic()\n            request_counter.add(1, {\"model\": model, \"feature\": feature})\n\n            try:\n                response = self.client.messages.create(\n                    model=model,\n                    max_tokens=max_tokens,\n                    messages=messages\n                )\n\n                elapsed_ms = (time.monotonic() - start) * 1000\n                input_tokens = response.usage.input_tokens\n                output_tokens = response.usage.output_tokens\n                cost = calculate_cost(model, input_tokens, output_tokens)\n\n                ttft_histogram.record(elapsed_ms, {\"model\": model})\n                token_counter.add(input_tokens, {\"type\": \"input\", \"model\": model})\n                token_counter.add(output_tokens, {\"type\": \"output\", \"model\": model})\n                cost_counter.add(cost[\"total_cost_usd\"], {\"model\": model, \"feature\": feature})\n\n                span.set_attribute(\"claude.input_tokens\", input_tokens)\n                span.set_attribute(\"claude.output_tokens\", output_tokens)\n                span.set_attribute(\"claude.cost_usd\", cost[\"total_cost_usd\"])\n                span.set_attribute(\"claude.latency_ms\", elapsed_ms)\n\n                return response\n\n            except anthropic.APIError as e:\n                error_counter.add(1, {\"model\": model, \"status_code\": str(e.status_code)})\n                span.set_attribute(\"error\", True)\n                span.set_attribute(\"error.type\", type(e).__name__)\n                span.set_attribute(\"error.status_code\", e.status_code)\n                raise\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eThiết lập Grafana Dashboard\u003c\/h2\u003e\n\u003cp\u003eGrafana là lựa chọn phổ biến để visualize metrics từ nhiều nguồn. Dưới đây là các panel quan trọng bạn cần có trong dashboard giám sát Claude API.\u003c\/p\u003e\n\n\u003ch3\u003ePanel 1: Request Overview\u003c\/h3\u003e\n\u003cp\u003ePanel tổng quan hiển thị số lượng request, error rate, và latency trong khoảng thời gian chọn. Sử dụng Prometheus query:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Request rate (requests per second)\nrate(claude_api_requests_total[5m])\n\n# Error rate percentage\nrate(claude_api_errors_total[5m]) \/ rate(claude_api_requests_total[5m]) * 100\n\n# P50, P95, P99 latency\nhistogram_quantile(0.50, rate(claude_api_ttft_milliseconds_bucket[5m]))\nhistogram_quantile(0.95, rate(claude_api_ttft_milliseconds_bucket[5m]))\nhistogram_quantile(0.99, rate(claude_api_ttft_milliseconds_bucket[5m]))\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003ePanel 2: Cost Tracking\u003c\/h3\u003e\n\u003cp\u003eBiểu đồ chi phí theo thời gian, chia theo model và feature:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Cumulative cost today\nincrease(claude_api_cost_usd_total[1d])\n\n# Cost by model\nsum by (model) (increase(claude_api_cost_usd_total[1h]))\n\n# Cost by feature\nsum by (feature) (increase(claude_api_cost_usd_total[1h]))\n\n# Projected daily cost (based on current rate)\nrate(claude_api_cost_usd_total[1h]) * 24\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003ePanel 3: Token Usage\u003c\/h3\u003e\n\u003cp\u003eTheo dõi token consumption để phát hiện prompt quá dài hoặc response bất thường:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Average input tokens per request\nrate(claude_api_tokens_total{type=\"input\"}[5m]) \/ rate(claude_api_requests_total[5m])\n\n# Average output tokens per request\nrate(claude_api_tokens_total{type=\"output\"}[5m]) \/ rate(claude_api_requests_total[5m])\n\n# Token ratio (output\/input)\nrate(claude_api_tokens_total{type=\"output\"}[5m]) \/ rate(claude_api_tokens_total{type=\"input\"}[5m])\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003ePanel 4: Rate Limit Utilization\u003c\/h3\u003e\n\u003cp\u003eGauge hiển thị mức sử dụng rate limit hiện tại. Cảnh báo khi vượt 80% để có thời gian xử lý trước khi bị throttle.\u003c\/p\u003e\n\n\u003ch3\u003eSử dụng Datadog thay thế\u003c\/h3\u003e\n\u003cp\u003eNếu tổ chức bạn đã sử dụng Datadog, quy trình tương tự. Thay vì Prometheus exporter, sử dụng DogStatsD hoặc Datadog APM:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003efrom datadog import statsd\n\ndef report_to_datadog(metrics, model, feature):\n    tags = [f\"model:{model}\", f\"feature:{feature}\"]\n\n    statsd.increment(\"claude_api.requests\", tags=tags)\n    statsd.histogram(\"claude_api.ttft_ms\", metrics[\"ttft_seconds\"] * 1000, tags=tags)\n    statsd.histogram(\"claude_api.total_time_ms\", metrics[\"total_time_seconds\"] * 1000, tags=tags)\n    statsd.increment(\"claude_api.tokens.input\", metrics[\"input_tokens\"], tags=tags)\n    statsd.increment(\"claude_api.tokens.output\", metrics[\"output_tokens\"], tags=tags)\n    statsd.increment(\"claude_api.cost_usd\", metrics.get(\"cost_usd\", 0), tags=tags)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eThiết lập Alert Rules\u003c\/h2\u003e\n\u003cp\u003eDashboard chỉ hữu ích khi có người xem. Alert rules giúp phát hiện vấn đề tự động và thông báo cho team kịp thời.\u003c\/p\u003e\n\n\u003ch3\u003eAlert 1: Latency Spike\u003c\/h3\u003e\n\u003cp\u003eCảnh báo khi P95 TTFT vượt ngưỡng bình thường. Ngưỡng phụ thuộc vào use case — chatbot cần TTFT thấp hơn batch processing.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Grafana Alert Rule: Latency Spike\n# Condition: P95 TTFT \u0026gt; 5000ms for 5 minutes\n# Severity: warning\n\nhistogram_quantile(0.95, rate(claude_api_ttft_milliseconds_bucket[5m])) \u0026gt; 5000\n\n# Critical: P95 TTFT \u0026gt; 15000ms for 3 minutes\nhistogram_quantile(0.95, rate(claude_api_ttft_milliseconds_bucket[3m])) \u0026gt; 15000\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eAlert 2: Error Burst\u003c\/h3\u003e\n\u003cp\u003ePhát hiện khi error rate tăng đột biến, đặc biệt phân biệt giữa client errors (4xx) và server errors (5xx):\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Error rate exceeds 5% for 3 minutes\nrate(claude_api_errors_total[3m]) \/ rate(claude_api_requests_total[3m]) \u0026gt; 0.05\n\n# Specific: Rate limit errors spike (429s)\nrate(claude_api_errors_total{status_code=\"429\"}[5m]) \u0026gt; 0.1\n\n# Server errors (500\/529) - indicates Anthropic-side issues\nrate(claude_api_errors_total{status_code=~\"5..\"}[3m]) \u0026gt; 0\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eAlert 3: Cost Anomaly\u003c\/h3\u003e\n\u003cp\u003ePhát hiện chi phí bất thường — có thể do bug trong prompt construction gây token explosion hoặc sử dụng sai model:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Hourly cost exceeds budget threshold\nincrease(claude_api_cost_usd_total[1h]) \u0026gt; 50\n\n# Daily projected cost exceeds budget\nrate(claude_api_cost_usd_total[1h]) * 24 \u0026gt; 500\n\n# Average cost per request abnormally high\nincrease(claude_api_cost_usd_total[5m]) \/ increase(claude_api_requests_total[5m]) \u0026gt; 0.10\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eAlert 4: Rate Limit Approaching\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Rate limit utilization exceeds 80%\n(claude_api_ratelimit_tokens_limit - claude_api_ratelimit_tokens_remaining)\n  \/ claude_api_ratelimit_tokens_limit \u0026gt; 0.80\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eThiết lập notification channels\u003c\/h3\u003e\n\u003cp\u003eCấu hình alert gửi qua nhiều kênh tùy mức độ nghiêm trọng:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eWarning:\u003c\/strong\u003e Gửi qua Slack channel #claude-api-alerts\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCritical:\u003c\/strong\u003e Gửi qua Slack + PagerDuty on-call\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCost alerts:\u003c\/strong\u003e Gửi qua email cho engineering lead và finance team\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eĐịnh nghĩa SLO (Service Level Objectives)\u003c\/h2\u003e\n\u003cp\u003eSLO là cam kết về chất lượng dịch vụ mà team engineering đặt ra cho hệ thống. Với ứng dụng Claude API, bạn nên định nghĩa SLO cho các khía cạnh sau:\u003c\/p\u003e\n\n\u003ch3\u003eSLO 1: Availability\u003c\/h3\u003e\n\u003cp\u003eTỷ lệ request thành công trong khoảng thời gian. Lưu ý: availability ở đây bao gồm cả phía Anthropic, nên target không nên quá cao.\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTarget:\u003c\/strong\u003e 99.5% requests thành công trong 30 ngày (cho phép khoảng 3.6 giờ downtime\/tháng)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMeasurement:\u003c\/strong\u003e 1 - (error_requests \/ total_requests), tính trên rolling 30 ngày\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eExclusions:\u003c\/strong\u003e Không tính 400 errors (lỗi client-side)\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eSLO 2: Latency\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTarget:\u003c\/strong\u003e P95 TTFT dưới 3 giây cho interactive use cases\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTarget:\u003c\/strong\u003e P99 total response time dưới 30 giây\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eSLO 3: Cost Efficiency\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTarget:\u003c\/strong\u003e Chi phí trung bình mỗi request dưới 0.05 USD\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTarget:\u003c\/strong\u003e Chi phí hàng tháng không vượt 120% budget đã duyệt\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eError Budget\u003c\/h3\u003e\n\u003cp\u003eError budget là phần SLO còn lại cho phép fail. Với SLO 99.5% availability trong 30 ngày, error budget là 0.5% = khoảng 2,160 failed requests trên 432,000 total requests. Khi error budget sắp hết, team nên:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003eDừng deploy feature mới, tập trung fix reliability\u003c\/li\u003e\n  \u003cli\u003eTăng số lượng on-call engineers\u003c\/li\u003e\n  \u003cli\u003eReview và cải thiện retry logic, circuit breaker\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eSLO Burn Rate Dashboard\u003c\/h3\u003e\n\u003cp\u003eBurn rate cho biết tốc độ tiêu thụ error budget. Nếu burn rate = 1, bạn đang tiêu thụ error budget đúng theo kế hoạch. Nếu burn rate = 2, bạn đang tiêu thụ gấp đôi và sẽ hết error budget trong 15 ngày thay vì 30 ngày.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# SLO burn rate calculation\n# Window: 1 hour\n# Budget consumption rate\n(1 - (sum(rate(claude_api_requests_total[1h])) - sum(rate(claude_api_errors_total{status_code!~\"4..\"}[1h])))\n  \/ sum(rate(claude_api_requests_total[1h])))\n\/ (1 - 0.995)  # 0.995 = SLO target\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eThiết lập alert theo burn rate:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBurn rate \u0026gt; 2 trong 1 giờ:\u003c\/strong\u003e Warning — đang tiêu thụ error budget nhanh\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBurn rate \u0026gt; 6 trong 5 phút:\u003c\/strong\u003e Critical — có sự cố nghiêm trọng, cần xử lý ngay\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBurn rate \u0026gt; 10 trong 2 phút:\u003c\/strong\u003e Page on-call — hệ thống có thể đang down\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eProduction Checklist\u003c\/h2\u003e\n\u003cp\u003eTrước khi go-live, đảm bảo bạn đã thiết lập đầy đủ các thành phần sau:\u003c\/p\u003e\n\n\u003ch3\u003eMetrics Collection\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eTTFT, TPS, total latency được thu thập cho mọi request\u003c\/li\u003e\n  \u003cli\u003eError rate phân loại theo status code\u003c\/li\u003e\n  \u003cli\u003eToken usage (input và output) theo model, feature, user\u003c\/li\u003e\n  \u003cli\u003eCost per request được tính và lưu trữ\u003c\/li\u003e\n  \u003cli\u003eRate limit utilization được giám sát\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eLogging\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eStructured JSON logs với request_id, timestamp, model, tokens, latency\u003c\/li\u003e\n  \u003cli\u003eKhông log nội dung prompt\/response (bảo mật)\u003c\/li\u003e\n  \u003cli\u003eLog rotation và retention policy đã cấu hình\u003c\/li\u003e\n  \u003cli\u003eLog aggregation (ELK Stack, Loki, CloudWatch) đã thiết lập\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eAlerting\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eLatency spike alerts (warning + critical)\u003c\/li\u003e\n  \u003cli\u003eError burst alerts phân biệt 4xx và 5xx\u003c\/li\u003e\n  \u003cli\u003eCost anomaly alerts (hourly và daily)\u003c\/li\u003e\n  \u003cli\u003eRate limit approaching alerts\u003c\/li\u003e\n  \u003cli\u003eNotification channels đã cấu hình và test\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eDashboard\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eRequest overview panel\u003c\/li\u003e\n  \u003cli\u003eCost tracking panel\u003c\/li\u003e\n  \u003cli\u003eToken usage panel\u003c\/li\u003e\n  \u003cli\u003eRate limit utilization panel\u003c\/li\u003e\n  \u003cli\u003eSLO burn rate panel\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eTối ưu hóa dựa trên dữ liệu observability\u003c\/h2\u003e\n\u003cp\u003eSau khi có dữ liệu, bạn có thể đưa ra quyết định tối ưu chính xác. Đây là lúc observability chuyển từ \"biết chuyện gì đang xảy ra\" sang \"biết cần làm gì\".\u003c\/p\u003e\n\n\u003ch3\u003eModel Selection theo use case\u003c\/h3\u003e\n\u003cp\u003ePhân tích cost và quality theo feature để chọn model phù hợp. Ví dụ: feature tóm tắt văn bản đơn giản có thể dùng Haiku thay vì Sonnet, giảm chi phí 75% mà chất lượng vẫn đáp ứng. Dashboard cho thấy feature nào đang dùng model đắt mà không cần thiết, giúp bạn tối ưu chi phí mà không ảnh hưởng đến trải nghiệm người dùng.\u003c\/p\u003e\n\n\u003ch3\u003ePrompt Optimization\u003c\/h3\u003e\n\u003cp\u003eMetrics cho thấy average input tokens cao bất thường ở một feature cụ thể? Có thể prompt đang bao gồm quá nhiều context không cần thiết. Tối ưu prompt dựa trên dữ liệu thực tế, không phải phỏng đoán. So sánh input token count trước và sau khi tối ưu để đo lường hiệu quả.\u003c\/p\u003e\n\n\u003ch3\u003eCaching Strategy\u003c\/h3\u003e\n\u003cp\u003ePhân tích request patterns để xác định đâu là ứng viên tốt cho prompt caching. Nếu một system prompt dài được sử dụng lặp lại, bật prompt caching có thể giảm chi phí và latency đáng kể. Anthropic hỗ trợ prompt caching cho các prefix prompt giống nhau — metrics sẽ cho thấy bao nhiêu phần trăm request có thể hưởng lợi từ tính năng này.\u003c\/p\u003e\n\n\u003ch3\u003eCapacity Planning\u003c\/h3\u003e\n\u003cp\u003eDựa trên trend request volume và token usage, dự đoán khi nào cần nâng rate limit tier với Anthropic. Chuẩn bị trước thay vì chờ đến khi bị throttle. Một cách tiếp cận hiệu quả là xây dựng forecast dựa trên dữ liệu sử dụng 30 ngày gần nhất, ngoại suy thêm 30-60 ngày để lên kế hoạch.\u003c\/p\u003e\n\n\u003ch3\u003eAnomaly Detection\u003c\/h3\u003e\n\u003cp\u003eNgoài các alert cố định, thiết lập anomaly detection để phát hiện các pattern bất thường mà alert thông thường không bắt được. Ví dụ: một user cụ thể tăng sử dụng gấp 10 lần trong 1 ngày có thể là dấu hiệu của automation loop không mong muốn hoặc là dấu hiệu abuse. Kết hợp dữ liệu observability với business context để phân biệt giữa tăng trưởng tự nhiên và vấn đề cần xử lý.\u003c\/p\u003e\n\n\u003ch2\u003eBước tiếp theo\u003c\/h2\u003e\n\u003cp\u003eObservability là nền tảng cho mọi hệ thống production đáng tin cậy. Bắt đầu với những metrics cơ bản nhất (latency, error rate, cost), sau đó mở rộng dần khi hiểu rõ hơn về patterns sử dụng của ứng dụng. Tham khảo thêm các hướng dẫn về xây dựng ứng dụng Claude API quy mô lớn tại \u003ca href=\"\/collections\/nang-cao\"\u003eThư viện Nâng cao\u003c\/a\u003e.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47730157813972,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/observability-cho-claude-api-metrics-logging-va-alerting-production.jpg?v=1774715929","url":"https:\/\/claude.vn\/products\/observability-cho-claude-api-metrics-logging-va-alerting-production","provider":"CLAUDE.VN","version":"1.0","type":"link"}