Logging, Monitoring ও Load Testing
Sentry, metrics, Locust
Production এ কিছু ভাঙলে সাধারণত প্রথম খবর আসে user এর কাছ থেকে: "app কাজ করছে না"। তখন তোমার হাতে কিছুই নেই, শুধু অনুমান। এই অধ্যায়ে এমন ব্যবস্থা বানাবো যাতে সমস্যা user এর আগে তুমি জানো, আর জানার পর মিনিটের মধ্যে কারণ খুঁজে পাও। সাথে load test করে আগেই জানবো system কতটা চাপ নিতে পারে।
তিনটা স্তম্ভ (System Design এর Observability অধ্যায়ে বিস্তারিত তত্ত্ব):
| প্রশ্ন | উদাহরণ | |
|---|---|---|
| Log | ঠিক কী ঘটেছিল? | "order 42 এর payment ব্যর্থ, gateway timeout" |
| Metric | সংখ্যায় কেমন চলছে? | প্রতি সেকেন্ডে ৩০০ request, ১% error, p95 latency ২৮০ ms |
| Trace | একটা request কোথায় কতক্ষণ কাটালো? | API ১০ ms → DB ১৫০ ms → payment API ১.২ সেকেন্ড |
#Structured logging
সাধারণ লেখার log মানুষ পড়তে পারে, কিন্তু খোঁজা আর গোনা কঠিন। Structured log (JSON) এ প্রতিটা তথ্য আলাদা field:
uv add structlog# core/logging.py
import logging
import structlog
def setup_logging(json_logs: bool) -> None:
logging.basicConfig(level=logging.INFO, format="%(message)s")
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars, # request_id ইত্যাদি নিজে যোগ হবে
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso", utc=True),
structlog.processors.format_exc_info,
structlog.processors.JSONRenderer() if json_logs else structlog.dev.ConsoleRenderer(),
],
)
log = structlog.get_logger()# middleware: প্রতিটা request এ id আর সময়
import time
import uuid
import structlog
@app.middleware("http")
async def request_context(request, call_next):
rid = request.headers.get("X-Request-ID") or uuid.uuid4().hex[:16]
structlog.contextvars.clear_contextvars()
structlog.contextvars.bind_contextvars(request_id=rid, path=request.url.path, method=request.method)
start = time.perf_counter()
try:
response = await call_next(request)
except Exception:
log.exception("request_failed")
raise
ms = round((time.perf_counter() - start) * 1000, 1)
log.info("request_done", status=response.status_code, duration_ms=ms)
response.headers["X-Request-ID"] = rid
return responselog.info("order_created", order_id=order.id, total=order.total, restaurant_id=order.restaurant_id){"event": "order_created", "order_id": 1042, "total": 55000, "restaurant_id": 15,
"request_id": "a8f3c19e0b2d4f11", "path": "/v1/orders", "level": "info", "timestamp": "2026-10-29T06:15:02Z"}এখন log system এ খোঁজা যায় order_id=1042 বা request_id=a8f3..., আর গোনা যায় "গত ১ ঘণ্টায় কয়টা payment_failed"।
#Error tracking: Sentry
Exception হলে সাথে সাথে খবর, পুরো traceback, কোন user, কোন request, কোন release সহ:
uv add "sentry-sdk[fastapi]"import sentry_sdk
sentry_sdk.init(
dsn=settings.sentry_dsn,
environment=settings.environment,
release=settings.git_sha, # কোন deploy এ bug এলো
traces_sample_rate=0.1, # ১০% request এর performance trace
send_default_pii=False, # ব্যক্তিগত তথ্য পাঠাবে না
)Sentry একই error কে group করে ("এই error ৩৪২ বার, ৮১ জন user এর"), নতুন release এ নতুন error এলে জানায়। Flutter এর জন্যও sentry_flutter আছে, তাই app আর backend এর error এক জায়গায়, একই trace এ যুক্ত। (Firebase Crashlytics ও app এর দিকে ভালো বিকল্প।)
#Metric: Prometheus আর Grafana
কয়েকটা সংখ্যা সারাক্ষণ মাপো। Web service এর জন্য সবচেয়ে কাজের চারটা (golden signals):
- Latency: কত সময় লাগছে (গড় না, p95/p99)
- Traffic: প্রতি সেকেন্ডে কত request
- Errors: কত শতাংশ ব্যর্থ (5xx)
- Saturation: কতটা ভরা (CPU, memory, database connection pool, queue এর দৈর্ঘ্য)
uv add prometheus-fastapi-instrumentatorfrom prometheus_fastapi_instrumentator import Instrumentator
Instrumentator().instrument(app).expose(app, endpoint="/metrics", include_in_schema=False)নিজের business metric ও:
from prometheus_client import Counter, Histogram
ORDERS = Counter("orders_created_total", "মোট order", ["city"])
PAYMENT_LATENCY = Histogram("payment_gateway_seconds", "Payment gateway এর সময়")
ORDERS.labels(city="dhaka").inc()
with PAYMENT_LATENCY.time():
await gateway.charge(...)Prometheus প্রতি ১৫ সেকেন্ডে /metrics পড়ে জমা রাখে, Grafana তে graph আর dashboard। Managed বিকল্প: Grafana Cloud, Datadog, New Relic; ছোট project এ platform এর নিজের metric (Railway/Render) দিয়েও শুরু করা যায়।
#Alert: কখন জাগাবে
সব কিছুতে alert দিলে কেউ আর alert দেখে না। Alert দাও user এর সমস্যা তে:
- 5xx error rate ৫ মিনিট ধরে ২% এর বেশি
- p95 latency ১০ মিনিট ধরে ১ সেকেন্ডের বেশি
/health/readyব্যর্থ- Queue তে ১০,০০০ এর বেশি job জমে আছে
- Disk ৮৫% ভরা
CPU ৮০% হলেই alert না, যদি user এর অভিজ্ঞতা ঠিক থাকে।
#Load testing: Locust
Production এ যাওয়ার আগে জানো system কতটা নিতে পারে। Locust দিয়ে Python এ নকল user লেখা যায়:
uv add --dev locust# locustfile.py
import random
import uuid
from locust import HttpUser, task, between
class NoteUser(HttpUser):
wait_time = between(1, 3) # প্রতিটা কাজের মাঝে ১-৩ সেকেন্ড
def on_start(self):
email = f"load-{uuid.uuid4().hex[:8]}@test.com"
self.client.post("/v1/auth/register", json={"email": email, "name": "Load", "password": "secret123"})
r = self.client.post("/v1/auth/login", json={"email": email, "password": "secret123"})
self.client.headers["Authorization"] = f"Bearer {r.json()['access_token']}"
@task(5) # সবচেয়ে বেশি: list দেখা
def list_notes(self):
self.client.get("/v1/notes?limit=20", name="/v1/notes")
@task(2)
def create_note(self):
self.client.post("/v1/notes", json={"title": f"নোট {random.randint(1, 9999)}"},
headers={"Idempotency-Key": uuid.uuid4().hex})
@task(1)
def search(self):
self.client.get("/v1/notes?search=নোট", name="/v1/notes?search")uv run locust -H http://localhost:8000
# browser এ http://localhost:8089, user সংখ্যা ধীরে ধীরে বাড়াওযা দেখবে:
- কত user/RPS পর্যন্ত p95 latency গ্রহণযোগ্য থাকে
- কোথায় error শুরু হয় (প্রায়ই database connection pool শেষ)
- সেই সময় CPU, memory, database এর কী অবস্থা
- Bottleneck কোথায়: তারপর ঠিক করো (index, cache, pool size, worker সংখ্যা), আবার মাপো
#অনুশীলন
#নিজেকে যাচাই করো
Structured (JSON) log এর সুবিধা কী?
প্রতিটা তথ্য আলাদা field, তাই log system এ নির্দিষ্ট field দিয়ে খোঁজা (order_id, request_id), গোনা আর filter করা যায়। সাধারণ লেখা থেকে এগুলো বের করা কঠিন।
Latency এর গড় না দেখে p95/p99 কেন দেখবে?
গড় অল্প কিছু খুব ধীর request কে লুকিয়ে ফেলে। p95/p99 দেখায় ধীরতম ৫% বা ১% user এর অভিজ্ঞতা, যারা আসলে সমস্যায় আছে।
চারটা golden signal কী?
Latency (কত সময়), Traffic (কত request), Errors (কত ব্যর্থ), Saturation (resource কতটা ভরা)।
Load test এর আসল উদ্দেশ্য কী?
System কতটা চাপ নিতে পারে আর প্রথম কোথায় ভাঙে (bottleneck) সেটা আগেভাগে জানা, যাতে সেটা ঠিক করা যায় আর বাস্তব চাপের জন্য পরিকল্পনা করা যায়।
চিহ্ন দিলে sidebar আর রোডম্যাপে এই অধ্যায়ের station ভরে যাবে।