🔍 وقتی یک تراکنش وسط مسیر Fail میشود، واقعاً از کجا شروع به Debug میکنید؟فرض کنید یک کاربر روی دکمه پرداخت کلیک میکند. چند ثانیه بعد...
❌ پرداخت ناموفق میشود.
حالا سؤال اینجاست:
مشکل دقیقاً کجا رخ داده است؟
آیا API Gateway درخواست را دریافت نکرده؟
آیا Validation در Command Handler شکست خورده؟
آیا داده داخل Database ذخیره نشده؟
آیا Outbox پردازش نشده؟
آیا پیام وارد RabbitMQ نشده؟
یا Consumer پیام را دریافت کرده ولی پردازش آن Fail شده؟
اگر هر سرویس فقط لاگ خودش را داشته باشد...
عملاً وارد یک کابوس خواهید شد.
😵 سناریوی رایج
فرض کنید سیستم شما از این بخشها تشکیل شده است:
Client
│
▼
API Gateway (Ocelot)
│
▼
Payment Service
│
▼
Command Handler
│
▼
Database + Outbox
│
▼
Background Job
│
▼
RabbitMQ
│
▼
Notification Service
حالا برای پیدا کردن علت خطا باید:
🔹 لاگ Gateway را باز کنید.
🔹 بعد لاگ Payment Service را بررسی کنید.
🔹 بعد لاگ Background Service را.
🔹 بعد لاگ RabbitMQ Consumer را.
🔹 بعد لاگ Notification Service را.
هر کدام روی یک Instance جدا... با Timestampهای مختلف... و فقط امیدوار باشید که ساعت همه سرورها Sync باشد.
📌راهحل؟🎯 تجربه واقعی در Hub پرداخت Dr. Link
ءCentralized Logging + Correlation ID + OpenTelemetry
در هاب پرداخت Dr. Link تعداد زیادی سرویس مستقل وجود دارد.
پرداخت فقط یک درخواست HTTP نیست.
بعد از ثبت درخواست، چندین اتفاق پشت سر هم رخ میدهد:
✅ ذخیره اطلاعات
✅ اجرای Business Ruleها
✅ ثبت Domain Event
✅ ذخیره در Outbox
✅ ءPublish شدن پیام توسط MassTransit
✅ پردازش توسط سرویسهای دیگر
اگر Correlation نداشته باشیم...
ردیابی مسیر تقریباً غیرممکن میشود.
🆔 همه چیز از Correlation ID شروع میشود
به محض اینکه درخواست وارد Ocelot API Gateway میشود...
یک Correlation ID تولید میشود.
مثلاً:
a17d59d8-78d2-4b1f-bd89-40d3f52b44f3
این شناسه همراه درخواست به تمام سرویسها ارسال میشود.
X-Correlation-Id:
a17d59d8-78d2-4b1f-bd89-40d3f52b44f3
از این لحظه...
تمام لاگهای مربوط به این درخواست همین شناسه را خواهند داشت.
🌐 اما داستان فقط HTTP نیست...
قسمت سخت ماجرا زمانی شروع میشود که سیستم وارد دنیای Asynchronous Messaging میشود.
در همین نقطه رد درخواست گم میشود.
📨 ءCorrelation ID داخل پیام هم قرار میگیرد
هنگام Publish کردن پیام توسط MassTransit همان Correlation ID داخل Envelope پیام ذخیره میشود.
یعنی پیام چیزی شبیه این خواهد داشت:
CorrelationId
a17d59d8-78d2-4b1f-bd89-40d3f52b44f3
در نتیجه...
وقتی Consumer پیام را دریافت میکند...
همان شناسه هنوز همراه پیام است.
هیچ بخشی از مسیر گم نمیشود.
📝 ءSerilog هم به صورت خودکار این شناسه را وارد Logها میکند
به کمک یک Serilog Enricher
ءCorrelation ID مستقیماً از:
✅ HTTP Header
یا
✅ MassTransit Message
خوانده میشود.
در نتیجه دیگر لازم نیست بنویسیم:
Log.Information(
"Payment {CorrelationId}",
correlationId);
تمام Logها به صورت خودکار این مقدار را خواهند داشت.
⚙️ نقش MediatR Pipeline Behavior
قبلاً برای Cross-Cutting Concernها از Pipeline Behavior استفاده کرده بودیم.
حالا همان Pipeline یک مسئولیت جدید هم دارد.
در ابتدای اجرای هر Command:
یک Logging Scope باز میکند.
BeginScope()
CorrelationId
از این لحظه...
هر لاگی که داخل:
Repository
Domain Service
Application Service
EF Core
یا هر بخش دیگری ثبت شود...
به صورت خودکار همین Correlation ID را خواهد داشت.
بدون اینکه حتی یک بار آن را دستی پاس بدهیم.