⌛ در باب اهمیت Traceability
گاهی فکر میکنیم «سیستم ما که دو تا سرویس بیشتر نیست» یا «فعلا مونولیت هستیم، پس traceability فعلا اهمیتی نداره و هر وقت بزرگتر شد درستش میکنیم». ولی واقعیت اینه که زنجیرهی callها از همون روز اول وجود داره. حتی اگر فقط یک API به یک دیتابیس وصل باشه. مسئله این نیست که microservice داریم یا نه.
مسئله اینه که وقتی یک درخواست وارد سیستم میشه، آیا میتونیم مسیرش رو از ابتدا تا انتها ببینیم یا نه؟
حالا Traceability یعنی چه؟
یعنی بتونیم به این سوال ساده جواب بدهیم: این درخواست دقیقا از کجا اومده، از چه سرویسهایی عبور کرده، کجا کند شده، و چرا خطا داده؟
توی معماری microservice این موضوع حیاتیه، چون زنجیرهی callها طولانی و توزیعشده است. ولی حتی توی یک سیستم کوچک هم اگر این قابلیت رو نداشته باشیم، debugging زمانبر، و به حدس و گمان تبدیل میشه.
چرا حتی برای سیستمهای کوچک هم مهمه؟
برخی تصور میکنن Distributed Tracing مخصوص سیستمهای بزرگه. اما به تجربه:
- حتی یک سیستم سهلایه ساده هم بدون trace خوب، در زمان بروز مشکل، میتونه تبدیل به تاریکی مطلق بشه.
- وقتی یک feature ساده کند میشه، اگر trace نداشته باشی، شروع میکنی لاگها رو grep کردن و فرضیه ساختن.
- وقتی فرد جدیدی به تیم اضافه میشه، داشتن trace خوب، عملا بهترین مستند معماری زنده است.
پس Traceability باعث میشه:
- فرایند debugging از «حدس» به «مشاهده» تبدیل بشه
- به مرور blame culture توی تیم کمتر بشه، چون مسیر واقعی قابل دیدنه
- عملا bottleneckها خیلی سریعتر شناسایی بشن
- وابستگیها شفاف باشن
سه ستون اصلی Traceability
۱: اصل Context Propagation
یعنی باید context هر درخواست همراهش حرکت کنه. Trace ID و Span ID باید از سرویس A به B و C منتقل بشن. و اگر این زنجیره قطع بشه، عملا trace بیارزش میشه. استاندارد رایج امروز: W3C Trace Context. و این یعنی هدرهای استاندارد برای انتقال trace بین سرویسها.
۲: اصل Correlation بین Log و Trace
داشتن trace بدون لاگ بیفایده است. و داشتن لاگ بدون trace هم ناقصه. هر log مهم باید شامل:
trace_id
span_id
باشه تا بتونی از یک error log مستقیم بپری روی trace کامل اون درخواست. Structured logging اینجا حیاتیه (گرچه همه جا مهمه نه فقط توی traceability و متن آزاد و غیرساختیافته توی سیستمهای جدی قابل دفاع نیست.)
۳: طراحی درست Spanها
نام spanها باید پایدار و کمکاردینال باشه. به جای اینکه IDهای متغیر رو داخل نام span بگذاریم، اونها رو به صورت attribute باید ثبت کرد.
مثلا:
درست: HTTP POST /employee/{id}
غلط: HTTP POST /employee/1037685
اگر این موضوع رعایت نشه، هزینه و performance ابزارهای observability صدمه میبینه.
توی سیستمهای Async و Message-based
خیلی وقتها فکر میکنیم tracing فقط مربوط به HTTP است. ولی توی سیستمهای event-driven اگر context رو داخل header پیام منتقل نکنی، زنجیره عملا قطع میشه. برای publish و consume هم برای هر پیام باید span جداگانه وجود داشته باشه. وگرنه نمیفهمی این job واقعا نتیجهی کدوم درخواست بوده.
چند Best Practice عملی
- از یک استاندارد واحد استفاده کن. OpenTelemetry امروز انتخاب منطقیایه.
- سعی کنید sampling هوشمند داشته باشید. همه چیز رو صددرصد نگه داشتن، راهکار نیست.
- به هیچ وجه PII رو داخل trace و log نریزید. اگر لازمه، hash یا mask کنید (به صورت کلی complience رو جدی بگیرید).
- حتمن naming convention مشخص برای سرویسها و spanها داشته باشید.
- موضوع dependencyهای بیرونی مثل DB و external APIها رو حتما داخل trace بیارید.
- فقط errorها رو log نکنید، داخل span هم record کنید.
موضوع Traceability فقط ابزار نیست، فرهنگه! اگر تیم به این موضوع باور نداشته باشه، ابزار به تنهایی کمکی نمیکنه. باید در code reviewها به propagation دقت بشه. باید logging استاندارد enforce بشه. و باید observability contract بین تیمها تعریف بشه (توی سازمانهای بزرگتر وظیفه تیم اینتگریشن یا پلتفرم است)
جمعبندی: داستان Traceability چیزی نیست که «وقتی بزرگ شدیم» اضافه کنیم. این یکی از اون قابلیتهاییه که اگر از روز اول درست پیاده بشه:
- هزینهی debugging به شدت کاهش پیدا میکنه
- معماری شفافتر میشه
- رشد سیستم قابل کنترلتر میشه
- و در زمان بحران، تیم به جای سردرگمی، تصویر واضحی از واقعیت داره
سیستم بدون trace مثل شهریه که دوربین کنترل ترافیک نداره. شاید در روزهای آروم و تعطیلات مشکلی حس نشه. ولی در اولین روز پرکار و شلوغ، تازه میفهمی چی کم بوده.
💬 تجربه شما چی بوده؟