Observability در Backend چیست؟ چگونه علت کندی سرور را پیدا کنیم؟

Observability در Backend چیست؟ چگونه علت کندی سرور را پیدا کنیم؟

توسعه‌دهنده بک‌اند

Monitoring به شما دیدن وضعیت سیستم را می‌دهد؛ Observability کمک می‌کند از روی شواهد، علت رفتار سیستم را پیدا کنید.

Observability در Backend چیست؟ چگونه علت کندی سرور را پیدا کنیم؟

صارم توکلی

۱۰ دقیقه مطالعه

۲ نفر

۱۴۰۵/۶/۳۰

Observability در Backend چیست؟ وقتی سرور کند شده از کجا بفهمیم چرا؟

مقدمه؛ وقتی سرور کند شده، مشکل دقیقاً کجاست؟

فرض کنید کاربران گزارش می‌دهند که سایت یا اپلیکیشن شما کند شده است.

بررسی اولیه را انجام می‌دهید:

Server: Running ✓
Database: Running ✓
API: Running ✓
CPU: 45%
Memory: 60%

همه‌چیز ظاهراً سالم است.

اما کاربران همچنان با این وضعیت مواجه‌اند:

GET /api/products
→ 1800ms

در حالی که همین API چند ساعت قبل:

→ 120ms

زمان پاسخ داشته است.

حالا سؤال اصلی این است:

مشکل از کجاست؟

آیا Database کند شده؟

آیا یک Query خاص مشکل دارد؟

آیا یک API خارجی پاسخ نمی‌دهد؟

آیا Connection Pool پر شده؟

آیا CPU درگیر یک پردازش سنگین است؟

یا اصلاً مشکل از Network است؟

اینجاست که مفهوم Observability اهمیت پیدا می‌کند.

Observability به ما کمک می‌کند از روی رفتار و داده‌های سیستم بفهمیم داخل سیستم چه اتفاقی در حال رخ دادن است.


Observability چیست؟

Observability یا «قابلیت مشاهده‌پذیری» به توانایی یک سیستم برای ارائه اطلاعات کافی درباره وضعیت داخلی خود گفته می‌شود؛ به شکلی که بتوانیم رفتار، خطاها و مشکلات آن را از بیرون بررسی و علت آن‌ها را پیدا کنیم.

به زبان ساده:

Monitoring به شما می‌گوید چه چیزی خراب شده؛ Observability کمک می‌کند بفهمید چرا خراب شده است.

فرض کنید:

API Latency ↑

Monitoring می‌تواند نشان دهد:

API Response Time = 2.1s

اما Observability کمک می‌کند مسیر Request را بررسی کنیم:

Request
   ↓
Authentication      15ms
   ↓
Database Query      80ms
   ↓
External API       1700ms
   ↓
Serialization        20ms

حالا مشخص است که بخش عمده زمان در External API مصرف شده است.


Monitoring و Observability چه تفاوتی دارند؟

این دو مفهوم بسیار نزدیک‌اند، اما یکسان نیستند.

Monitoring

Monitoring بیشتر برای مشاهده وضعیت شناخته‌شده سیستم استفاده می‌شود.

مثلاً:

CPU > 80%
Memory > 90%
Error Rate > 5%
Latency > 500ms

در این حالت سیستم می‌تواند Alert ارسال کند.

Observability

Observability سؤال‌های عمیق‌تری را پاسخ می‌دهد:

چرا Latency افزایش پیدا کرده؟
کدام Endpoint مشکل دارد؟
کدام Database Query کند شده؟
کدام سرویس خارجی پاسخ نمی‌دهد؟
کدام Requestها بیشتر خطا می‌دهند؟
مشکل فقط در یک Region اتفاق افتاده؟

پس می‌توان ساده گفت:

Monitoring
    ↓
What happened?

Observability
    ↓
Why did it happen?

البته این تفکیک یک ساده‌سازی مفهومی است؛ Monitoring و Observability در عمل هم‌پوشانی زیادی دارند.


سه ستون اصلی Observability

معمولاً Observability را با سه نوع داده اصلی توضیح می‌دهند:

        Observability
             │
     ┌───────┼───────┐
     ↓       ↓       ↓
   Logs   Metrics   Traces

این سه مورد عبارت‌اند از:

  1. Logs

  2. Metrics

  3. Traces

هرکدام دید متفاوتی از سیستم به ما می‌دهند.


۱. Logs؛ چه اتفاقی افتاد؟

Log گزارشی از یک رویداد در سیستم است.

مثلاً:

2026-09-21 20:31:10
GET /api/orders
status=200
duration=842ms

یا:

Database connection timeout

Logs برای فهمیدن جزئیات یک اتفاق بسیار مفید هستند.

مثلاً:

Request received
User authenticated
Order fetched
Payment API called
Payment failed

اما Log ساده می‌تواند خیلی سریع تبدیل به یک مشکل شود.

اگر در Production میلیون‌ها Log تولید کنید:

INFO
INFO
INFO
INFO
ERROR
INFO
INFO
INFO

پیدا کردن اطلاعات موردنظر سخت خواهد شد.

به همین دلیل Logging باید Structured باشد.


Structured Logging چیست؟

به جای اینکه Log را به صورت متن ساده بنویسیم:

User 42 requested order 123

می‌توانیم داده ساختاریافته تولید کنیم:

{
  "level": "info",
  "event": "order.request",
  "userId": 42,
  "orderId": 123,
  "duration": 184,
  "requestId": "abc123"
}

حالا ابزارهای Log Management می‌توانند بر اساس فیلدهای مختلف جستجو کنند.

مثلاً:

duration > 1000

یا:

event = "order.request"

یا:

requestId = "abc123"

این موضوع در سیستم‌های بزرگ اهمیت زیادی دارد.


۲. Metrics؛ سیستم در چه وضعیتی قرار دارد؟

Metrics داده‌های عددی هستند که وضعیت سیستم را در طول زمان نشان می‌دهند.

مثلاً:

CPU Usage
Memory Usage
Request Count
Error Rate
Request Duration
Database Connections
Queue Length

فرض کنید نمودار زیر را داریم:

API Latency

100ms ────────────────
150ms ────────────────
200ms ────────────╮
500ms             │
1s                │
2s                ╰────────

این نمودار به ما می‌گوید که در یک بازه مشخص، Latency افزایش پیدا کرده است.

اما هنوز نمی‌دانیم چرا.

برای پاسخ به این سؤال باید سراغ Logs و Traces برویم.


۳. Tracing؛ مسیر کامل یک Request

Tracing یکی از مهم‌ترین قسمت‌های Observability در معماری‌های مدرن است.

فرض کنید یک Request وارد Backend می‌شود:

Client
  ↓
API Gateway
  ↓
Auth Service
  ↓
Order Service
  ↓
Database
  ↓
Payment Service

اگر Response Time برابر با 2 ثانیه باشد، سؤال این است:

کدام بخش 2 ثانیه زمان برده است؟

Distributed Tracing می‌تواند مسیر Request را به شکل زیر نشان دهد:

Request              2000ms
│
├── API Gateway        20ms
│
├── Auth Service       30ms
│
├── Order Service     100ms
│
├── Database           50ms
│
└── Payment Service  1800ms

حالا Bottleneck تقریباً مشخص است.

مشکل اصلی:

Payment Service
≈ 1800ms

است.


Trace و Span چیست؟

در سیستم‌های Distributed معمولاً یک Request به یک Trace تبدیل می‌شود.

Trace مسیر کلی یک عملیات را نشان می‌دهد.

هر قسمت از این مسیر یک Span است.

مثلاً:

Trace
│
├── HTTP Request
│
├── Authentication Span
│
├── Database Span
│
├── External API Span
│
└── Response Span

در یک سیستم Microservices ممکن است یک Trace بین چند سرویس حرکت کند.

این قابلیت کمک می‌کند بفهمیم یک Request دقیقاً چه مسیری را طی کرده است.


Trace ID و Correlation ID چیست؟

فرض کنید یک Request وارد سیستم شده است:

Trace ID:
7f9c2a1b

همین شناسه می‌تواند در سرویس‌های مختلف همراه Request حرکت کند:

API Gateway
Trace ID: 7f9c2a1b

Order Service
Trace ID: 7f9c2a1b

Payment Service
Trace ID: 7f9c2a1b

Database Operation
Trace ID: 7f9c2a1b

حالا اگر مشکلی ایجاد شود، می‌توانیم تمام اطلاعات مرتبط با همان Request را پیدا کنیم.

این موضوع در سیستم‌هایی که چند سرویس دارند بسیار ارزشمند است.


وقتی API کند شده، از کجا شروع کنیم؟

فرض کنید:

GET /api/dashboard

Before:
120ms

Now:
2100ms

به جای تغییر تصادفی کد، یک فرآیند مشخص را دنبال می‌کنیم.

مرحله اول: بررسی Metrics

ابتدا بررسی می‌کنیم:

Latency
Error Rate
Request Rate
CPU
Memory
Database Connections

مثلاً متوجه می‌شویم:

CPU = 35%
Memory = 55%
Latency = 2.1s

پس احتمالاً مشکل CPU نیست.


مرحله دوم: بررسی Endpoint

ممکن است فقط یک Endpoint مشکل داشته باشد.

مثلاً:

/api/users      → 120ms
/api/products   → 140ms
/api/orders     → 150ms
/api/dashboard  → 2100ms

این اطلاعات دامنه جستجو را محدود می‌کند.


مرحله سوم: بررسی Trace

حالا Trace مربوط به /api/dashboard را باز می‌کنیم:

Dashboard API
│
├── Auth          15ms
├── User Query    40ms
├── Orders Query  80ms
├── Analytics    1900ms
└── Serialize      20ms

حالا مشکل تقریباً مشخص است:

Analytics
≈ 1900ms

مرحله چهارم: بررسی Database یا سرویس مربوطه

حالا می‌توانیم دقیق‌تر بررسی کنیم.

مثلاً Query:

SELECT *
FROM analytics
WHERE user_id = 42;

با بررسی Execution Plan متوجه می‌شویم Query روی حجم زیادی از داده‌ها Scan انجام می‌دهد.

ممکن است مشکل مربوط به:

Missing Index
Large Dataset
Expensive JOIN
Bad Query Plan

باشد.

در اینجا Observability ما را از:

"سرور کند شده"

به:

"این Query خاص باعث افزایش Latency شده"

می‌رساند.


p95 و p99؛ چرا Average کافی نیست؟

یکی از اشتباهات رایج این است که فقط Average Latency را بررسی کنیم.

فرض کنید:

Average = 150ms

به نظر خوب می‌رسد.

اما اگر داده‌ها این‌طور باشند:

p50 = 90ms
p95 = 400ms
p99 = 1800ms

می‌بینیم که بخشی از کاربران با Latency بسیار بیشتری مواجه‌اند.

p50

نقطه میانی داده‌ها را نشان می‌دهد.

p95

نشان می‌دهد حدود 95٪ داده‌ها در این مقدار یا کمتر قرار دارند.

p99

Tail Latency را بهتر نشان می‌دهد و برای پیدا کردن Requestهای بسیار کند اهمیت دارد.

به همین دلیل در سیستم‌های Production بهتر است به جای یک عدد، Distribution مربوط به Latency را بررسی کنیم.


Observability و Database

Database یکی از مهم‌ترین قسمت‌هایی است که باید در Observability تحت نظر باشد.

Metrics مهم می‌توانند شامل:

Query Duration
Connection Pool Usage
Active Connections
Slow Queries
Cache Hit Ratio
Transaction Duration
Lock Wait Time

باشند.

مثلاً:

API Latency
     ↑
     │
Database Query
     ↑
Connection Pool
     ↑
Connection Wait

گاهی خود Query سریع است، اما Request مدت زیادی منتظر Connection می‌ماند.

بدون Instrumentation مناسب ممکن است این تفاوت را متوجه نشویم.


Observability و External API

فرض کنید Backend شما به سرویس پرداخت متصل است.

Metrics:

Payment API Latency

ناگهان افزایش پیدا می‌کند:

200ms
↓
400ms
↓
900ms
↓
1800ms

همزمان API شما نیز کند می‌شود.

Tracing می‌تواند نشان دهد:

Your API       1900ms
      ↓
Payment API    1750ms

در این حالت مشکل لزوماً از کد Backend شما نیست.

این یکی از مزیت‌های مهم Observability است:

می‌توانیم مرز بین مشکل داخلی سیستم و وابستگی خارجی را بهتر تشخیص دهیم.


OpenTelemetry چیست؟

OpenTelemetry یکی از پروژه‌های مهم برای Instrumentation و جمع‌آوری داده‌های Observability است.

به کمک آن می‌توان داده‌هایی مانند:

Traces
Metrics
Logs

را در سیستم‌های مختلف جمع‌آوری و به Backendهای Observability ارسال کرد.

معماری مفهومی:

Application
     │
     ↓
OpenTelemetry
     │
     ├── Metrics
     ├── Logs
     └── Traces
            │
            ↓
Observability Backend

مزیت مهم OpenTelemetry این است که Instrumentation را از ابزار نهایی Observability تا حد زیادی جدا می‌کند.

یعنی می‌توانید داده‌ها را به زیرساخت مناسب خود ارسال کنید، بدون اینکه کل کد Application به یک Vendor خاص وابسته شود.


Observability فقط برای Microservices نیست

گاهی تصور می‌شود Observability فقط برای سیستم‌های بسیار بزرگ یا Microservices لازم است.

در حالی که یک Monolith هم می‌تواند از Observability بهره ببرد.

مثلاً:

Monolith
│
├── API
├── Database
├── Cache
├── External Services
└── Background Jobs

حتی در یک پروژه نسبتاً ساده نیز دانستن اینکه:

Request → 20ms
Database → 500ms
External API → 700ms

بسیار ارزشمند است.

بنابراین Observability بیشتر از اینکه به اندازه پروژه وابسته باشد، به پیچیدگی رفتار سیستم و اهمیت آن در Production وابسته است.


Alerting؛ قبل از کاربران متوجه مشکل شویم

Observability بدون Alerting می‌تواند ناقص باشد.

فرض کنید:

p95 Latency > 1s

برای مدت مشخصی ادامه داشته باشد.

سیستم می‌تواند Alert ایجاد کند:

⚠ API Latency increased
Endpoint: /api/orders
p95: 1.4s

یا:

⚠ Error Rate increased
Current: 8.2%
Normal: 1.1%

هدف این است که تیم قبل از تبدیل شدن یک مشکل کوچک به Incident بزرگ، از آن مطلع شود.

اما Alert زیاد نیز مشکل‌ساز است.

اگر برای هر اتفاق کوچک Alert ارسال شود:

Alert
Alert
Alert
Alert
Alert

تیم ممکن است دچار Alert Fatigue شود.

پس Alertها باید برای شرایطی طراحی شوند که واقعاً نیاز به بررسی یا اقدام دارند.


چه چیزهایی را در Backend اندازه‌گیری کنیم؟

یک چک‌لیست کاربردی:

HTTP

Request Count
Response Time
Status Code
Error Rate
Request Size
Response Size

Database

Query Duration
Active Connections
Pool Utilization
Slow Queries
Lock Wait
Transaction Duration

Infrastructure

CPU
Memory
Disk
Network
Container Restarts

Application

Queue Length
Job Duration
Cache Hit Rate
Background Job Failures
External API Latency

Distributed Systems

Trace Duration
Span Duration
Service Errors
Service Dependencies

یک سناریوی واقعی؛ API از 100ms به 2 ثانیه رسیده

فرض کنیم کاربران گزارش داده‌اند:

صفحه سفارش‌ها خیلی کند شده است.

ابتدا Metrics را بررسی می‌کنیم:

p95:
100ms → 2100ms

CPU:

42%

Memory:

58%

پس مشکل واضحی در CPU و Memory دیده نمی‌شود.

حالا Trace را بررسی می‌کنیم:

GET /api/orders
│
├── Authentication       12ms
├── Orders Query         70ms
├── Product Query        50ms
├── Shipping API       1850ms
└── Serialization        18ms

مشکل مشخص شد.

Shipping API
≈ 1850ms

حالا می‌توانیم راهکار مناسب را بررسی کنیم:

Timeout
Caching
Parallel Requests
Fallback
Queue
Circuit Breaker

راهکار نهایی به رفتار واقعی سیستم و نیاز Business بستگی دارد.

اما نکته مهم این است که بدون Observability ممکن بود ساعت‌ها Database یا CPU را بررسی کنیم، در حالی که مشکل در یک سرویس خارجی بود.


Observability چه مشکلاتی را حل می‌کند؟

Observability می‌تواند در پیدا کردن مشکلاتی مانند موارد زیر کمک کند:

  • افزایش ناگهانی API Latency

  • Slow Database Queries

  • Memory Leak

  • افزایش Error Rate

  • مشکلات Connection Pool

  • کندی سرویس‌های خارجی

  • Queueهای طولانی

  • خطاهای یک Microservice خاص

  • مشکلات مربوط به Deployment

  • افزایش Tail Latency

  • خطاهای وابسته به یک Region یا سرویس خاص

اما Observability به خودی خود مشکل را حل نمی‌کند.

این ابزار اطلاعات لازم برای تشخیص و تصمیم‌گیری فنی را فراهم می‌کند.


اشتباهات رایج در پیاده‌سازی Observability

۱. Log کردن همه چیز

Log بیشتر همیشه به معنی Observability بهتر نیست.

Logهای زیاد می‌توانند:

Storage Cost ↑
Noise ↑
Search Difficulty ↑

را افزایش دهند.


۲. نداشتن Context

این Log:

Request failed

اطلاعات زیادی نمی‌دهد.

اما:

requestId
traceId
endpoint
user/session context
statusCode
duration
error

می‌تواند برای Debugging بسیار مفیدتر باشد؛ البته بدون ثبت اطلاعات حساس و شخصی غیرضروری.


۳. فقط بررسی CPU و Memory

ممکن است:

CPU = 30%
Memory = 50%

باشد ولی API همچنان 2 ثانیه طول بکشد.

چون مشکل می‌تواند در:

Database
Network
External API
Connection Pool
Queue
Lock

باشد.


۴. نداشتن Distributed Tracing در سیستم‌های چندسرویسی

در معماری Microservices اگر Trace بین سرویس‌ها منتقل نشود، پیدا کردن علت یک خطای زنجیره‌ای دشوارتر می‌شود.


۵. Alertهای بیش از حد

Alert زیاد می‌تواند باعث شود Alertهای مهم در میان هشدارهای کم‌اهمیت گم شوند.


معماری پیشنهادی ساده برای Observability

یک معماری مفهومی می‌تواند چنین باشد:

                  ┌──────────────┐
                  │   Client     │
                  └──────┬───────┘
                         ↓
                  ┌──────────────┐
                  │   Backend    │
                  └──────┬───────┘
                         │
             ┌───────────┼───────────┐
             ↓           ↓           ↓
           Logs       Metrics      Traces
             │           │           │
             └───────────┼───────────┘
                         ↓
               Observability Platform
                         ↓
                  Dashboards / Alerts

هدف این نیست که از روز اول ده‌ها ابزار اضافه کنیم.

هدف این است که بتوانیم به سؤال‌های مهم پاسخ دهیم:

چه چیزی خراب شده؟
کجا خراب شده؟
از چه زمانی؟
چند کاربر تحت تأثیر قرار گرفته‌اند؟
علت احتمالی چیست؟

چک‌لیست Observability برای یک Backend

اگر بخواهید یک Backend را از نظر Observability بررسی کنید، این موارد شروع خوبی هستند:

Logs

  • Structured Logging

  • Log Levels

  • Error Context

  • Request ID

  • Trace ID

Metrics

  • Request Rate

  • Error Rate

  • Latency

  • p50 / p95 / p99

  • CPU

  • Memory

  • Database Connections

Tracing

  • HTTP Requests

  • Database Queries

  • External APIs

  • Internal Service Calls

  • Background Jobs

Alerting

  • High Error Rate

  • High Latency

  • Service Down

  • Database Problems

  • Queue Backlog

Security

  • عدم ثبت Password

  • عدم ثبت Secret

  • عدم ثبت Tokenهای حساس

  • محدود کردن اطلاعات شخصی غیرضروری در Logs


جمع‌بندی

وقتی یک Backend کند می‌شود، مهم‌ترین سؤال این نیست که:

«کدام خط کد را تغییر دهیم؟»

سؤال بهتر این است:

«داده‌های سیستم دقیقاً چه چیزی درباره علت کندی به ما می‌گویند؟»

Observability این امکان را فراهم می‌کند که به جای حدس زدن، رفتار سیستم را از طریق سه منبع اصلی بررسی کنیم:

Logs
 ↓
جزئیات اتفاقات

Metrics
 ↓
وضعیت و روند سیستم

Traces
 ↓
مسیر و زمان مصرف‌شده در هر بخش

ترکیب این سه دیدگاه می‌تواند یک API مبهم و کند را به یک مسئله قابل اندازه‌گیری تبدیل کند.

مثلاً:

API = 2.1s
       ↓
Trace
       ↓
Database = 80ms
External API = 1.8s
       ↓
Bottleneck مشخص شد

و این دقیقاً ارزش Observability است.

در یک Backend حرفه‌ای، هدف فقط این نیست که وقتی سیستم خراب شد بفهمیم چه چیزی خراب شده است؛ بلکه باید بتوانیم با داده کافی بفهمیم کجا، چه زمانی و چرا این اتفاق افتاده است.

Monitoring به شما دیدن وضعیت سیستم را می‌دهد؛ Observability کمک می‌کند از روی شواهد، علت رفتار سیستم را پیدا کنید.