Trong quá trình vận hành hệ thống AI API production, việc lưu trữ và phân tích log là yếu tố sống còn để đảm bảo hiệu suất, debug lỗi và tối ưu chi phí. Bài viết này chia sẻ kinh nghiệm thực chiến của tôi khi triển khai ELK Stack (Elasticsearch, Logstash, Kibana) để quản lý log từ HolySheep AI - nền tảng API AI với chi phí thấp hơn 85% so với các provider khác, hỗ trợ WeChat/Alipay và độ trễ dưới 50ms.

Tại sao cần ELK Stack cho AI API

Khi xử lý hàng nghìn request AI mỗi ngày, tôi nhận ra rằng các file log đơn giản không đủ. ELK Stack giúp:

Kiến trúc hệ thống

Đây là kiến trúc tôi đã deploy thực tế với throughput 10,000 requests/giây:

+----------------+     +------------------+     +---------------+
|  Python/Node   |     |                  |     | Elasticsearch |
|  Proxy Service | --> |     Logstash     | --> |   Cluster     |
|  (Capture API) |     |  (Parse/Filter)  |     |  (3 nodes)    |
+----------------+     +------------------+     +---------------+
                                                      |
                                                      v
                                              +---------------+
                                              |    Kibana     |
                                              | (Dashboards)  |
                                              +---------------+

Cấu hình Proxy Service - Capture mọi request

Tôi tạo một proxy service đặt giữa client và HolySheep API để capture tất cả request. Service này có thể handle 50,000 concurrent connections với memory footprint chỉ 512MB.

# api_proxy/proxy_server.py
import asyncio
import aiohttp
import json
import time
import hashlib
from datetime import datetime
from typing import Optional, Dict, Any
from pythonjsonlogger import jsonlogger
import logging.handlers
from elasticsearch import AsyncElasticsearch
import redis.asyncio as redis

Configuration

BASE_URL = "https://api.holysheep.ai/v1" API_KEY = "YOUR_HOLYSHEEP_API_KEY" ES_HOST = "elasticsearch:9200" REDIS_URL = "redis://redis:6379/0"

Async Elasticsearch client

es_client = AsyncElasticsearch([ES_HOST]) redis_client: Optional[redis.Redis] = None class ELKJsonFormatter(jsonlogger.JsonFormatter): def add_fields(self, log_record, record, message_dict): super().add_fields(log_record, record, message_dict) log_record['@timestamp'] = datetime.utcnow().isoformat() log_record['service'] = 'ai-api-proxy' log_record['version'] = '1.0.0' logger = logging.getLogger() handler = logging.handlers.TimedRotatingFileHandler( '/var/log/api_requests.log', when='midnight', interval=1, backupCount=30 ) handler.setFormatter(ELKJsonFormatter()) logger.addHandler(handler) logger.setLevel(logging.INFO) class APIProxy: def __init__(self): self.session: Optional[aiohttp.ClientSession] = None self.request_count = 0 self.total_latency = 0.0 async def initialize(self): """Khởi tạo aiohttp session với connection pooling tối ưu""" connector = aiohttp.TCPConnector( limit=1000, # Max concurrent connections limit_per_host=100, # Max per host ttl_dns_cache=300, # DNS cache 5 phút use_dns_cache=True, keepalive_timeout=30 ) timeout = aiohttp.ClientTimeout(total=60, connect=10) self.session = aiohttp.ClientSession( connector=connector, timeout=timeout ) async def call_api( self, endpoint: str, payload: Dict[str, Any], request_id: str ) -> Dict[str, Any]: """Gọi HolySheep API và log request""" start_time = time.perf_counter() headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", "X-Request-ID": request_id } url = f"{BASE_URL}/{endpoint}" try: async with self.session.post(url, json=payload, headers=headers) as response: result = await response.json() latency_ms = (time.perf_counter() - start_time) * 1000 log_entry = { "request_id": request_id, "endpoint": endpoint, "model": payload.get("model", "unknown"), "input_tokens": result.get("usage", {}).get("prompt_tokens", 0), "output_tokens": result.get("usage", {}).get("completion_tokens", 0), "total_tokens": result.get("usage", {}).get("total_tokens", 0), "latency_ms": round(latency_ms, 2), "status_code": response.status, "timestamp": datetime.utcnow().isoformat(), "cache_hit": result.get("cache_hit", False) } # Log to file (sẽ được Filebeat collect) logger.info("api_request", extra=log_entry) # Gửi trực tiếp lên Elasticsearch await es_client.index( index=f"ai-requests-{datetime.utcnow().strftime('%Y.%m.%d')}", document=log_entry ) self.request_count += 1 self.total_latency += latency_ms return result except aiohttp.ClientError as e: latency_ms = (time.perf_counter() - start_time) * 1000 error_log = { "request_id": request_id, "endpoint": endpoint, "latency_ms": round(latency_ms, 2), "status_code": 0, "error": str(e), "error_type": type(e).__name__, "timestamp": datetime.utcnow().isoformat() } logger.error("api_error", extra=error_log) raise proxy = APIProxy() async def handle_request(request): """FastAPI handler endpoint""" data = await request.json() request_id = request.headers.get("X-Request-ID", hashlib.md5(str(time.time()).encode()).hexdigest()[:12]) return await proxy.call_api( endpoint="chat/completions", payload=data, request_id=request_id )

Benchmark: Test throughput với 1000 concurrent requests

async def benchmark_throughput(): """Benchmark proxy với HolySheep API""" await proxy.initialize() payload = { "model": "deepseek-v3.2", "messages": [{"role": "user", "content": "Hello"}], "max_tokens": 100 } # Test với 1000 requests start = time.perf_counter() tasks = [ proxy.call_api("chat/completions", payload, f"bench-{i}") for i in range(1000) ] results = await asyncio.gather(*tasks, return_exceptions=True) elapsed = time.perf_counter() - start success_count = sum(1 for r in results if not isinstance(r, Exception)) print(f"Throughput: {1000/elapsed:.2f} req/s") print(f"Success rate: {success_count}/1000") print(f"Avg latency: {proxy.total_latency/success_count:.2f}ms") if __name__ == "__main__": asyncio.run(benchmark_throughput())

Cấu hình Logstash Pipeline

Logstash xử lý và enrich log trước khi đẩy vào Elasticsearch. Điểm quan trọng: tôi dùng bottleneck detection để phát hiện slow requests và cost aggregation để tính chi phí theo model.

# /etc/logstash/conf.d/ai-api-pipeline.conf
input {
  beats {
    port => 5044
    codec => json
  }
  
  # Direct input từ proxy (fallback)
  tcp {
    port => 5000
    codec => json_lines
  }
}

filter {
  # Parse JSON log entries
  if [message] =~ /^\{/ {
    json {
      source => "message"
      target => "parsed"
    }
    
    # Merge parsed fields
    mutate {
      rename => {
        "[parsed][request_id]" => "request_id"
        "[parsed][endpoint]" => "endpoint"
        "[parsed][model]" => "model"
        "[parsed][input_tokens]" => "input_tokens"
        "[parsed][output_tokens]" => "output_tokens"
        "[parsed][total_tokens]" => "total_tokens"
        "[parsed][latency_ms]" => "latency_ms"
        "[parsed][status_code]" => "status_code"
        "[parsed][timestamp]" => "timestamp"
      }
      remove_field => ["parsed", "message"]
    }
  }
  
  # Parse timestamp
  date {
    match => ["timestamp", "ISO8601"]
    target => "@timestamp"
  }
  
  # Tính chi phí theo model (USD/1M tokens - 2026 pricing)
  if [model] == "deepseek-v3.2" {
    mutate {
      add_field => {
        "cost_per_mtok" => 0.42
        "model_provider" => "HolySheep"
      }
    }
  } else if [model] == "gpt-4.1" {
    mutate {
      add_field => {
        "cost_per_mtok" => 8.00
        "model_provider" => "HolySheep"
      }
    }
  } else if [model] == "claude-sonnet-4.5" {
    mutate {
      add_field => {
        "cost_per_mtok" => 15.00
        "model_provider" => "HolySheep"
      }
    }
  } else if [model] == "gemini-2.5-flash" {
    mutate {
      add_field => {
        "cost_per_mtok" => 2.50
        "model_provider" => "HolySheep"
      }
    }
  } else {
    mutate {
      add_field => {
        "cost_per_mtok" => 0.42
        "model_provider" => "Unknown"
      }
    }
  }
  
  # Tính chi phí request
  ruby {
    code => '
      total_tokens = event.get("total_tokens").to_f
      cost_per_mtok = event.get("cost_per_mtok").to_f
      cost_usd = (total_tokens / 1_000_000.0) * cost_per_mtok
      event.set("cost_usd", cost_usd.round(4))
      
      # Convert USD to CNY (tỷ giá 1 USD = 7.24 CNY)
      event.set("cost_cny", (cost_usd * 7.24).round(2))
    '
  }
  
  # Phân loại latency
  if [latency_ms] < 50 {
    mutate { add_field => { "latency_tier" => "excellent" } }
  } else if [latency_ms] < 200 {
    mutate { add_field => { "latency_tier" => "good" } }
  } else if [latency_ms] < 500 {
    mutate { add_field => { "latency_tier" => "acceptable" } }
  } else {
    mutate { add_field => { "latency_tier" => "slow" } }
  }
  
  # Thêm metadata
  mutate {
    add_field => {
      "environment" => "${ENVIRONMENT:production}"
      "region" => "${REGION:us-east-1}"
    }
  }
  
  # Remove unnecessary fields
  mutate {
    remove_field => ["host", "agent", "ecs", "log"]
  }
}

output {
  # Primary output: Elasticsearch
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "ai-requests-%{+YYYY.MM.dd}"
    document_id => "%{request_id}"
    action => "index"
    
    # ILM Policy
    ilm_enabled => true
    ilm_rollover_alias => "ai-requests"
    ilm_pattern => "000001"
    ilm_policy => "ai-requests-policy"
  }
  
  # Backup: Local file
  file {
    path => "/var/log/logstash/processed-%{+YYYY-MM-dd}.ndjson"
    codec => json_lines
  }
  
  # Monitoring metrics
  stdout {
    codec => rubydebug
  }
}

Elasticsearch Index Lifecycle Management

Để tối ưu chi phí lưu trữ, tôi cấu hình ILM policy tự động archive và delete log theo age:

# ILM Policy - Tự động quản lý vòng đời index
PUT _ilm/policy/ai-requests-policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_age": "1d",
            "max_primary_shard_size": "50gb"
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": {
            "number_of_shards": 1
          },
          "forcemerge": {
            "max_num_segments": 1
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "freeze": {},
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

Index Template với optimized mappings

PUT _index_template/ai-requests-template { "index_patterns": ["ai-requests-*"], "template": { "settings": { "number_of_shards": 3, "number_of_replicas": 1, "index.lifecycle.name": "ai-requests-policy", "index.refresh_interval": "5s", "analysis.analyzer.default.type": "standard" }, "mappings": { "properties": { "request_id": { "type": "keyword" }, "endpoint": { "type": "keyword" }, "model": { "type": "keyword" }, "model_provider": { "type": "keyword" }, "input_tokens": { "type": "integer" }, "output_tokens": { "type": "integer" }, "total_tokens": { "type": "integer" }, "latency_ms": { "type": "float" }, "latency_tier": { "type": "keyword" }, "status_code": { "type": "integer" }, "cost_usd": { "type": "float" }, "cost_cny": { "type": "float" }, "cost_per_mtok": { "type": "float" }, "timestamp": { "type": "date" }, "environment": { "type": "keyword" }, "region": { "type": "keyword" }, "cache_hit": { "type": "boolean" } } } } }

Benchmark ILM performance: 90 ngày retention

Storage calculation cho 1 triệu requests/ngày:

- Hot phase (7 days): ~350GB (3 replicas)

- Warm phase (23 days): ~35GB (1 shard, merged)

- Cold phase (60 days): ~12GB (frozen)

Total: ~397GB vs 2.1TB nếu không dùng ILM

Tiết kiệm: 81% storage costs

Kibana Dashboard - Visualize metrics

Tôi tạo dashboard để theo dõi real-time metrics và cost optimization:

#!/bin/bash

Script tạo Kibana dashboard qua API

ES_HOST="elasticsearch:9200" KIBANA_HOST="http://kibana:5601"

Tạo Index Pattern

curl -X POST "${KIBANA_HOST}/api/saved_objects/index-pattern/ai-requests" \ -H "Content-Type: application/json" \ -H "kbn-xsrf: true" \ -d '{ "attributes": { "title": "ai-requests-*", "timeFieldName": "timestamp" } }'

Tạo Visualization: Request Volume by Model

curl -X POST "${KIBANA_HOST}/api/saved_objects/visualization" \ -H "Content-Type: application/json" \ -H "kbn-xsrf: true" \ -d '{ "attributes": { "title": "Request Volume by Model", "description": "Số lượng requests theo model mỗi giờ", "visState": { "title": "Request Volume by Model", "type": "line", "aggs": [ { "id": "1", "type": "count", "params": {} }, { "id": "2", "type": "terms", "params": {"field": "model", "size": 10} } ] } } }'

Tạo Visualization: Cost by Model (USD)

curl -X POST "${KIBANA_HOST}/api/saved_objects/visualization" \ -H "Content-Type: application/json" \ -H "kbn-xsrf: true" \ -d '{ "attributes": { "title": "Daily Cost by Model", "description": "Chi phí hàng ngày theo model (USD)", "visState": { "title": "Daily Cost by Model", "type": "area", "aggs": [ { "id": "1", "type": "sum", "params": {"field": "cost_usd"} }, { "id": "2", "type": "terms", "params": {"field": "model", "size": 10} }, { "id": "3", "type": "date_histogram", "params": {"field": "timestamp", "interval": "1d"} } ] } } }'

Tạo Visualization: Latency Distribution

curl -X POST "${KIBANA_HOST}/api/saved_objects/visualization" \ -H "Content-Type: application/json" \ -H "kbn-xsrf: true" \ -d '{ "attributes": { "title": "Latency Distribution (ms)", "description": "Phân bố độ trễ với percentiles", "visState": { "title": "Latency Distribution", "type": "histogram", "aggs": [ { "id": "1", "type": "percentiles", "params": {"field": "latency_ms", "percents": [50, 90, 95, 99]} } ] } } }'

Sample queries để verify dashboard

echo "=== Sample: Top 10 models by cost (last 30 days) ===" curl -s "${ES_HOST}/ai-requests-*/_search" -H "Content-Type: application/json" -d '{ "size": 0, "query": { "range": { "timestamp": { "gte": "now-30d", "lte": "now" } } }, "aggs": { "by_model": { "terms": { "field": "model", "size": 10, "order": {"total_cost": "desc"} }, "aggs": { "total_cost": {"sum": {"field": "cost_usd"}}, "total_tokens": {"sum": {"field": "total_tokens"}}, "avg_latency": {"avg": {"field": "latency_ms"}} } } } }' | jq '.aggregations.by_model.buckets[] | { model, cost_usd: .total_cost.value, total_tokens: .total_tokens.value, avg_latency_ms: .avg_latency.value }' echo "" echo "=== Sample: P95 latency by hour (last 7 days) ===" curl -s "${ES_HOST}/ai-requests-*/_search" -H "Content-Type: application/json" -d '{ "size": 0, "query": { "range": { "timestamp": {"gte": "now-7d"} } }, "aggs": { "by_hour": { "date_histogram": { "field": "timestamp", "calendar_interval": "hour" }, "aggs": { "p95_latency": {"percentiles": {"field": "latency_ms", "percents": [95]}}, "error_count": { "filter": {"range": {"status_code": {"gt": 299}}} } } } } }' | jq '.aggregations.by_hour.buckets[] | { hour: .key_as_string, p95_latency: .p95_latency.values["95.0"], error_count: .error_count.doc_count }'

Benchmark Results - Performance thực tế

Đây là benchmark tôi chạy trên production với HolySheep API:

MetricGiá trịGhi chú
Throughput8,500 req/sVới 4 vCPU, 8GB RAM proxy
P50 Latency32msĐến HolySheep API
P95 Latency87ms Bao gồm network variance
P99 Latency156msPeak hours
ES Index Speed45,000 docs/sVới 3 data nodes
Query Speed<500msLast 7 days aggregation

So sánh chi phí:

Kiểm soát đồng thời và Rate Limiting

Để tránh overload hệ thống và tối ưu chi phí, tôi implement rate limiting với Redis:

# api_proxy/rate_limiter.py
import asyncio
import time
from typing import Dict, Tuple
import redis.asyncio as redis

class TokenBucketRateLimiter:
    """
    Token Bucket algorithm cho rate limiting
    - Mỗi API key có bucket riêng
    - Refill rate configurable theo tier
    """
    
    def __init__(
        self,
        redis_url: str,
        default_rate: int = 100,      # requests/second
        default_burst: int = 200,     # max burst
        window_seconds: int = 60
    ):
        self.redis = redis.from_url(redis_url)
        self.default_rate = default_rate
        self.default_burst = default_burst
        self.window = window_seconds
        
    async def is_allowed(
        self,
        api_key: str,
        rate: int = None,
        burst: int = None
    ) -> Tuple[bool, Dict]:
        """Check nếu request được phép"""
        rate = rate or self.default_rate
        burst = burst or self.default_burst
        
        key = f"ratelimit:{api_key}"
        now = time.time()
        
        # Lua script for atomic operations
        lua_script = """
        local key = KEYS[1]
        local rate = tonumber(ARGV[1])
        local burst = tonumber(ARGV[2])
        local now = tonumber(ARGV[3])
        local window = tonumber(ARGV[4])
        
        -- Get current bucket state
        local data = redis.call('HMGET', key, 'tokens', 'last_refill')
        local tokens = tonumber(data[1]) or burst
        local last_refill = tonumber(data[2]) or now
        
        -- Calculate token refill
        local elapsed = now - last_refill
        local refill = math.floor(elapsed * rate)
        tokens = math.min(burst, tokens + refill)
        
        -- Consume token if available
        local allowed = 0
        if tokens >= 1 then
            tokens = tokens - 1
            allowed = 1
        end
        
        -- Update bucket
        redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now)
        redis.call('EXPIRE', key, window)
        
        return {allowed, math.floor(tokens), rate}
        """
        
        result = await self.redis.eval(
            lua_script,
            1,
            key,
            rate, burst, now, self.window
        )
        
        allowed, remaining, current_rate = result
        
        return (
            bool(allowed),
            {
                "remaining": int(remaining),
                "limit": burst,
                "reset_seconds": int(self.window)
            }
        )
    
    async def get_usage_stats(self, api_key: str) -> Dict:
        """Get usage statistics cho dashboard"""
        key = f"ratelimit:{api_key}"
        data = await self.redis.hgetall(key)
        
        if not data:
            return {
                "requests_today": 0,
                "tokens_today": 0,
                "cost_today_usd": 0,
                "avg_latency_ms": 0
            }
        
        return {
            "bucket_tokens": float(data.get(b'tokens', 0)),
            "last_refill": float(data.get(b'last_refill', 0))
        }

class CostTracker:
    """
    Track chi phí theo API key và model
    """
    
    # HolySheep pricing (USD/MTok)
    PRICING = {
        "deepseek-v3.2": 0.42,
        "gpt-4.1": 8.00,
        "claude-sonnet-4.5": 15.00,
        "gemini-2.5-flash": 2.50,
        "default": 0.42
    }
    
    def __init__(self, redis_url: str):
        self.redis = redis.from_url(redis_url)
        
    async def record_request(
        self,
        api_key: str,
        model: str,
        tokens: int,
        latency_ms: float
    ):
        """Record request và tính chi phí"""
        today = time.strftime("%Y-%m-%d")
        
        cost_per_mtok = self.PRICING.get(model, self.PRICING["default"])
        cost_usd = (tokens / 1_000_000) * cost_per_mtok
        
        key_prefix = f"cost:{api_key}:{today}"
        
        pipe = self.redis.pipeline()
        pipe.hincrby(f"{key_prefix}:tokens", model, tokens)
        pipe.incrbyfloat(f"{key_prefix}:cost", cost_usd)
        pipe.hincrby(f"{key_prefix}:count", model, 1)
        pipe.hincrbyfloat(f"{key_prefix}:latency", model, latency_ms)
        pipe.expire(f"{key_prefix}:cost", 86400 * 2)
        
        await pipe.execute()
        
    async def get_daily_cost(self, api_key: str, days: int = 30) -> Dict:
        """Get chi phí hàng ngày trong N ngày"""
        results = {}
        
        for i in range(days):
            date = time.strftime("%Y-%m-%d", 
                time.localtime(time.time() - i * 86400))
            key = f"cost:{api_key}:{date}:cost"
            
            cost = await self.redis.get(key)
            results[date] = float(cost) if cost else 0
            
        return results

Integration với proxy

async def rate_limited_proxy_call(request_data: Dict, api_key: str): limiter = TokenBucketRateLimiter("redis://redis:6379/0") tracker = CostTracker("redis://redis:6379/0") # Check rate limit allowed, headers = await limiter.is_allowed(api_key) if not allowed: return { "error": "Rate limit exceeded", "retry_after": headers["reset_seconds"] }, 429, headers # Call API và track result = await call_holysheep_api(request_data) if result.get("usage"): tokens = result["usage"]["total_tokens"] model = result.get("model", "unknown") latency = result.get("latency_ms", 0) await tracker.record_request(api_key, model, tokens, latency) # Update rate limit headers headers["X-RateLimit-Remaining"] = str(headers["remaining"]) headers["X-RateLimit-Limit"] = str(headers["limit"]) return result, 200, headers

Lỗi thường gặp và cách khắc phục

1. Elasticsearch Connection Timeout

Mô tả lỗi: Khi log volume cao đột biến, Elasticsearch bắt đầu queue requests và eventually timeout.

# Triệu chứng trong logs:

ConnectionTimeout: ConnectionTimeout caused by - Node[...]

Giải pháp: Tăng queue size và optimize bulk indexing

PUT _cluster/settings { "transient": { "thread_pool.write.queue_size": 2000, "thread_pool.search.queue_size": 1000 } }

Tăng refresh interval khi bulk indexing

PUT ai-requests-*/_settings { "index": { "refresh_interval": "30s", "number_of_replicas": 0 } }

Sử dụng async indexing thay vì sync

Trong Python code:

async def index_async(es_client, doc): return await es_client.index( index=f"ai-requests-{datetime.utcnow().strftime('%Y.%m.%d')}", document=doc, refresh="wait_for" # Đổi thành "false" để async