مدیریت متمرکز لاگ چیست و چرا SSH و grep دیگر کافی نیست

مدیریت متمرکز لاگ چیست و چرا SSH و grep دیگر کافی نیست
در این مقاله می‌خوانید
  1. grep کجا می‌شکند؟
  2. مدیریت متمرکز لاگ دقیقاً یعنی چه؟
  3. مسیر لاگ: مستقیم از برنامه یا با agent؟
  4. ارسال مستقیم از برنامه
  5. agent یا collector
  6. چند تصمیم که از ابزار مهم‌ترند
  7. نام‌های مشترک برای فیلدهای مشترک
  8. زمان، همیشه UTC و همیشه با منطقه
  9. یک شناسه برای کل درخواست
  10. ذخیره و جستجو: دو فلسفهٔ متفاوت
  11. نگه‌داری، حجم و هزینه
  12. دسترسی و امنیت: لاگ متمرکز، هدف متمرکز
  13. ساختن یا خریدن؟
  14. از کجا شروع کنیم؟
  15. منابع و مطالعهٔ بیشتر

صحنه‌ای که بارها در آن بوده‌ام: کاربری گزارش داده که پرداختش «حدود ساعت چهار» خطا داده. سیستم سه سرور وب پشت load balancer دارد، یک سرویس پرداخت جدا و یک worker که صف را پردازش می‌کند. کسی با SSH به سرور اول می‌رود، grep می‌زند، چیزی پیدا نمی‌کند. سرور دوم. فایل امروز چرخیده و نیمی از لاگ‌ها در فایل فشردهٔ دیروز است. سرور سوم ساعتش به وقت UTC است و بقیه به وقت تهران. چهل دقیقه بعد، خطا پیدا می‌شود — در سرویس پرداخت، که اصلاً کسی سراغش نرفته بود.

هیچ‌کدام از این آدم‌ها کم‌کار یا کم‌دانش نبودند. مشکل در خود روش بود: وقتی لاگ‌ها روی ماشین‌هایی پخش‌اند که هر کدام قاعدهٔ خودشان را دارند، پیدا کردن یک رخداد کار کارآگاهی می‌شود. این مقاله دربارهٔ راه دیگر است: مدیریت متمرکز لاگ، اینکه دقیقاً از چه بخش‌هایی ساخته می‌شود، چه تصمیم‌هایی باید گرفت، و از کجا شروع کرد.

grep کجا می‌شکند؟

اول منصف باشم: برای یک سرور و یک برنامه، grep و tail -f و less ابزارهای فوق‌العاده‌ای‌اند و من هنوز هر روز از آن‌ها استفاده می‌کنم. مشکل وقتی شروع می‌شود که یکی از این شرایط پیش بیاید:

  • بیش از یک ماشین. load balancer درخواست‌ها را پخش می‌کند و هیچ تضمینی نیست که درخواست‌های یک کاربر به یک سرور برود. باید همه‌جا را بگردید.
  • بیش از یک سرویس. یک درخواست کاربر از gateway، سرویس سفارش، سرویس پرداخت و یک صف می‌گذرد. خطای اصلی معمولاً جایی است که نشانه‌اش نیست.
  • ماشین‌های گذرا. در کانتینر و autoscaling، ماشینی که خطا داده ممکن است دیگر وجود نداشته باشد. لاگ‌هایش هم با آن رفته‌اند.
  • دسترسی. برای دیدن لاگ باید دسترسی SSH به سرور پروداکشن داشت. یعنی یا همهٔ توسعه‌دهنده‌ها این دسترسی را دارند (که از نظر امنیتی بد است) یا فقط یکی دو نفر، که گلوگاه می‌شوند.
  • سؤال‌های تجمیعی. «این خطا از کِی شروع شد؟»، «چند کاربر به آن خوردند؟»، «فقط روی نسخهٔ جدید است؟» — grep | wc -l روی چند سرور و چند فایل چرخیده، جواب قابل‌اعتمادی نمی‌دهد.
  • قالب‌های ناهمسان. هر سرویس و هر کتابخانه شکل خودش را برای زمان و سطح دارد و regex شما برای یکی کار می‌کند و برای دیگری نه.

یک نشانهٔ دیگر هم هست که کمتر به آن توجه می‌شود: وقتی تیم دیگر سراغ لاگ نمی‌رود. اگر اولین واکنش به هر گزارش خطا «بگذار در محیط توسعه بازسازی‌اش کنم» است، نه «بگذار لاگش را ببینم»، معمولاً یعنی دیدن لاگ آن‌قدر سخت شده که کسی به آن امید ندارد. این هزینه در هیچ گزارشی ثبت نمی‌شود، ولی هر هفته ساعت‌ها وقت می‌گیرد و باگ‌هایی که فقط در پروداکشن رخ می‌دهند، اصلاً حل نمی‌شوند.

از جایی که دو مورد از این فهرست برایتان صدق می‌کند، روش دستی دیگر نه فقط کند، که غیرقابل‌اعتماد است: نتیجه نگرفتن از جستجو دیگر به معنای «خطایی نبوده» نیست.

مدیریت متمرکز لاگ دقیقاً یعنی چه؟

هستهٔ ایده ساده است: همهٔ لاگ‌های همهٔ سرویس‌ها، در لحظه یا نزدیک به لحظه، به یک جا فرستاده می‌شوند که می‌شود در آن جستجو کرد. ولی «یک جا» از چند بخش ساخته شده و هر بخش تصمیم‌های خودش را دارد:

  1. تولید: برنامه لاگ را با قالب و فیلدهای درست می‌نویسد. هیچ ابزار متمرکزی لاگ بد را خوب نمی‌کند.
  2. جمع‌آوری و ارسال: لاگ از برنامه یا از فایل یا stdout برداشته و به شبکه سپرده می‌شود — مستقیم از کتابخانهٔ لاگ، یا با یک agent کنار برنامه.
  3. پردازش: تجزیهٔ قالب، افزودن فیلد (نام سرویس، محیط)، حذف دادهٔ حساس، دسته‌بندی.
  4. ذخیره و نمایه: لاگ‌ها طوری ذخیره می‌شوند که جستجو روی میلیون‌ها خط در چند ثانیه جواب بدهد.
  5. جستجو و نمایش: رابطی که در آن فیلتر می‌کنید، روند زمانی را می‌بینید و یک لاگ را باز می‌کنید.
  6. نگه‌داری و دسترسی: چه مدت، با چه هزینه‌ای، و چه کسی چه چیزی را می‌بیند.

ابزارهای مختلف این بخش‌ها را به شکل‌های متفاوتی بسته‌بندی می‌کنند. در پشتهٔ ELK، Logstash یا Beats جمع‌آوری و پردازش را انجام می‌دهند، Elasticsearch ذخیره و نمایه را، و Kibana نمایش را. در Grafana Loki، یک agent (مثل Grafana Alloy) جمع می‌کند، Loki ذخیره می‌کند و Grafana نمایش می‌دهد. سرویس‌های ابری و SaaS همه را در یک محصول می‌دهند. ولی فرقی نمی‌کند کدام را انتخاب کنید؛ همین شش تصمیم را باید بگیرید.

مسیر لاگ: مستقیم از برنامه یا با agent؟

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

ارسال مستقیم از برنامه

کتابخانهٔ لاگ (یک sink در Serilog، یک exporter در OpenTelemetry، یک transport در pino) لاگ‌ها را در حافظه دسته می‌کند و با HTTP به مقصد می‌فرستد. ساده‌ترین راه است: هیچ نرم‌افزار اضافه‌ای روی سرور لازم نیست. برای برنامه‌های ⁦.NET⁩ روی سرور ویندوزی، که نصب agent همیشه آسان یا مجاز نیست، معمولاً بهترین گزینه همین است. ضعفش این است که اگر برنامه ناگهان بمیرد، لاگ‌های داخل بافر هم ممکن است با آن بروند — دقیقاً همان لاگ‌هایی که دلیل مرگ را می‌گویند.

agent یا collector

برنامه روی stdout یا فایل می‌نویسد و یک فرایند جدا آن را می‌خواند و می‌فرستد. ابزارهای رایجش Fluent Bit، Vector، Grafana Alloy و OpenTelemetry Collector‌اند. agent مستقل از برنامه است، پس مرگ برنامه لاگ‌هایش را نمی‌برد؛ می‌تواند لاگ برنامه‌هایی را هم جمع کند که کدشان در اختیار شما نیست (nginx، دیتابیس، سیستم‌عامل)؛ و پردازش و حذف دادهٔ حساس را یک‌جا انجام می‌دهد. هزینه‌اش یک قطعهٔ دیگر است که باید نصب، پیکربندی، پایش و به‌روز شود.

یک الگوی میانی که برایم بسیار خوب کار کرده: برنامه‌ها با OTLP به یک OpenTelemetry Collector در همان شبکه می‌فرستند و collector به مقصد نهایی. برنامه‌ها فقط یک نشانی محلی می‌شناسند و عوض کردن مقصد، یا فرستادن هم‌زمان به دو مقصد در دوران مهاجرت، فقط تغییر پیکربندی collector است:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:

exporters:
  otlphttp:
    endpoint: https://otel.example.com
    headers:
      x-api-key: ${env:LOG_API_KEY}

service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp]

exporter otlphttp خودش /v1/logs را به نشانی اضافه می‌کند و processor batch لاگ‌ها را دسته‌ای می‌فرستد تا تعداد درخواست‌ها کم شود. کلید را از متغیر محیطی بخوانید، نه از متن فایل.

در هر دو الگو یک چیز را از اول تصمیم بگیرید: وقتی مقصد در دسترس نیست چه می‌شود؟ بافر در حافظه تا چه سقفی؟ روی دیسک؟ دور ریختن قدیمی‌ترها؟ جواب درستی برای همه وجود ندارد، ولی جوابی که ندانید، روز قطعی شبکه خودش را نشان می‌دهد.

چند تصمیم که از ابزار مهم‌ترند

نام‌های مشترک برای فیلدهای مشترک

فایدهٔ اصلی تمرکز، پرسیدن یک سؤال از همهٔ سرویس‌ها با هم است. اگر یک سرویس service می‌نویسد و دیگری app و سومی ApplicationName، این فایده از بین می‌رود. دست‌کم این چهار فیلد را در همهٔ سرویس‌ها یکی کنید: نام سرویس، محیط، میزبان و trace_id. قراردادهای معنایی OpenTelemetry (service.name، deployment.environment.name، host.name) نقطهٔ شروع خوبی‌اند، حتی اگر از OpenTelemetry استفاده نکنید، چون اختراع دوبارهٔ نام‌ها هیچ سودی ندارد.

زمان، همیشه UTC و همیشه با منطقه

همهٔ برنامه‌ها زمان را در UTC و با قالب ISO 8601 بنویسند (مثل 2026-09-10T10:35:12.481Z) و نمایش به وقت محلی را به رابط کاربری بسپارید. مخلوط کردن وقت محلی و UTC، و به‌خصوص زمان بدون منطقه، یکی از رایج‌ترین دلایلی است که دو رخداد مرتبط در جستجو کنار هم نمی‌آیند. ساعت سرورها را هم با NTP همگام نگه دارید.

یک شناسه برای کل درخواست

بدون trace_id، لاگ متمرکز فقط grep سریع‌تر است. با آن، می‌توانید از یک خطا در سرویس پرداخت به همهٔ خطوط همان درخواست در همهٔ سرویس‌ها بروید و ماجرا را به ترتیب بخوانید. استاندارد W3C Trace Context این شناسه را بین سرویس‌ها منتقل می‌کند؛ کافی است همهٔ سرویس‌ها هدر traceparent را بپذیرند و ادامه دهند.

ذخیره و جستجو: دو فلسفهٔ متفاوت

اینجا جایی است که ابزارها واقعاً با هم فرق دارند و این فرق روی هزینه و تجربهٔ جستجو اثر مستقیم دارد.

نمایهٔ کامل متن (مثل Elasticsearch و OpenSearch): هر کلمهٔ هر لاگ نمایه می‌شود. جستجوی متن آزاد بسیار سریع و انعطاف‌پذیر است و برای تحلیل‌های پیچیده قدرت زیادی دارد. هزینه‌اش فضای دیسک و حافظهٔ زیاد است و نگه‌داری یک خوشهٔ بزرگ Elasticsearch، خودش یک مهارت و یک شغل است.

نمایهٔ فقط برچسب‌ها (مثل Grafana Loki): فقط چند برچسب (سرویس، محیط، میزبان) نمایه می‌شوند و متن لاگ فشرده و ارزان ذخیره می‌شود. ذخیره‌سازی بسیار ارزان‌تر است و با object storage خوب کار می‌کند. در عوض، جستجوی متن آزاد روی بازهٔ بزرگ کندتر است چون باید داده خوانده شود، و انتخاب برچسب‌ها (و پرهیز از برچسب‌های با تنوع زیاد مثل user_id) را باید جدی گرفت.

پایگاه‌های ستونی (مثل ClickHouse، که زیرساخت چند ابزار لاگ امروزی است): لاگ‌ها مثل جدول با ستون ذخیره می‌شوند، فشرده‌سازی بسیار خوبی دارند و پرسش‌های تجمیعی («تعداد خطا به تفکیک سرویس در هر ساعت») را بسیار سریع جواب می‌دهند. جستجوی متن آزاد در آن‌ها با نمایه‌های کمکی انجام می‌شود.

هیچ‌کدام برندهٔ مطلق نیست. برای تیم کوچکی که بیشتر با فیلتر سرویس و سطح و trace_id جستجو می‌کند، تفاوت‌ها کم‌رنگ‌اند و سادگی عملیات مهم‌تر است. برای تیم امنیتی که روی لاگ‌های یک سال تحلیل پیچیده می‌کند، Splunk یا Elastic دلایل واقعی برای محبوبیتشان دارند. مقایسهٔ دقیق‌تر با نقاط قوت و ضعف هر ابزار را در مقایسهٔ ابزارهای مدیریت لاگ نوشته‌ام.

نگه‌داری، حجم و هزینه

اولین ماهی که لاگ‌ها متمرکز می‌شوند، تقریباً همیشه یک غافلگیری دارد: حجم. تا وقتی لاگ‌ها روی دیسک سرورها بودند، کسی آن‌ها را نمی‌شمرد. حالا یک عدد روشن روی صفحه است و معمولاً معلوم می‌شود بخش بزرگی از آن از یکی دو منبع پرحرف می‌آید: لاگ هر کوئری ORM، health check که هر چند ثانیه یک خط می‌نویسد، یا کتابخانه‌ای که در سطح Debug مانده.

چند سؤال که پیش از انتخاب مدت نگه‌داری باید جواب بدهید:

  • معمولاً چند روز طول می‌کشد تا یک مشکل گزارش شود؟ اگر کاربران گاهی دو هفته بعد خبر می‌دهند، نگه‌داری هفت‌روزه کافی نیست.
  • آیا الزام قانونی یا قراردادی برای نگه‌داری لاگ‌های امنیتی و ممیزی دارید؟ این‌ها معمولاً مدت طولانی‌تری لازم دارند، ولی حجمشان کم است.
  • آیا همهٔ لاگ‌ها ارزش یکسانی دارند؟ معمولاً نه. لاگ Debug یک هفته، لاگ عادی یک ماه، لاگ ممیزی یک سال، منطقی‌تر از یک عدد برای همه است.

بهترین راه کم کردن هزینه، تقریباً همیشه کم کردن حجم در منبع است، نه کوتاه کردن نگه‌داری. این بحث را با جزئیات بیشتر، ازجمله برآورد حجم، در لاگ را چند روز نگه داریم؟ باز کرده‌ام.

دسترسی و امنیت: لاگ متمرکز، هدف متمرکز

جمع کردن همهٔ لاگ‌ها در یک جا یک روی دیگر هم دارد: حالا یک جا هست که اگر نشت کند، همه‌چیز نشت کرده است. لاگ‌ها اگر با دقت نوشته نشده باشند، شامل ایمیل، شمارهٔ موبایل، توکن نشست و گاهی بدتر از این‌ها هستند. سه کار را از روز اول انجام دهید:

  • دادهٔ حساس را پیش از ذخیره حذف کنید — در خود برنامه، در agent، یا در مقصد؛ ترجیحاً در بیش از یک لایه. اینکه چه چیزی نباید در لاگ بیاید را در این مقاله فهرست کرده‌ام.
  • دسترسی را محدود و قابل‌پیگیری کنید. مزیت بزرگ تمرکز این است که دیگر لازم نیست به همه SSH پروداکشن بدهید؛ این مزیت را با دادن دسترسی مدیریتی به همه در ابزار لاگ هدر ندهید.
  • کلید ارسال را با کلید خواندن یکی نکنید و برای هر سرور یا سرویس کلید جدا بگذارید تا اگر یکی لو رفت، فقط همان باطل شود.

یک ملاحظهٔ دیگر برای تیم‌های ایرانی هم هست: لاگ‌ها، به‌خصوص با دادهٔ مشتری، ممکن است مشمول الزامات نگه‌داری داده در داخل کشور باشند، و دسترسی پایدار به سرویس‌های خارجی هم همیشه تضمین‌شده نیست. این‌ها را پیش از انتخاب ابزار بسنجید، نه بعد از آن.

ساختن یا خریدن؟

راه‌اندازی یک پشتهٔ متن‌باز (ELK، OpenSearch، Loki با Grafana) کاملاً شدنی است و تیم‌های زیادی این کار را به‌خوبی انجام می‌دهند. کنترل کامل دارید، هزینهٔ مجوز ندارید و داده از سرورهای خودتان بیرون نمی‌رود. ولی هزینهٔ واقعی‌اش در سخت‌افزار و در زمان آدم‌هاست: ارتقا، پشتیبان‌گیری، تنظیم نگه‌داری، پایش خود سامانهٔ لاگ، و رسیدگی وقتی دیسکش پر می‌شود — که معمولاً دقیقاً وسط همان حادثه‌ای است که برایش لاگ لازم دارید.

سرویس آماده این بار را برمی‌دارد و در ازایش هزینهٔ ماهانه و وابستگی به یک فروشنده دارد. معیار من برای تصمیم ساده است: اگر تیم شما کسی را دارد که نگه‌داری زیرساخت لاگ بخشی از کار اصلی‌اش باشد، ساختن گزینهٔ جدی‌ای است. اگر نه، وقت تیم صرف محصول خودتان شود بهتر است.

ما LogMug را برای همین موقعیت ساختیم: تیم‌هایی که چند سرویس بک‌اند دارند و می‌خواهند لاگ‌ها را با OpenTelemetry، Serilog یا JSON ساده بفرستند، در یک جا با فیلتر سطح و سرویس و محیط جستجو کنند و با یک کلیک همهٔ خطوط یک درخواست را بر اساس trace_id ببینند — با داده‌هایی که در مرکز داده‌ای داخل ایران نگه‌داری می‌شوند. ولی اصولی که در این مقاله گفتم، برای هر ابزاری که انتخاب کنید یکسان است.

از کجا شروع کنیم؟

لازم نیست همه‌چیز را یک‌باره درست کنید. ترتیبی که توصیه می‌کنم:

  1. یک سرویس، همان پرمشکل‌ترین. لاگ‌هایش را ساخت‌یافته کنید و به مقصد مرکزی بفرستید. یک هفته با آن کار کنید.
  2. فیلدهای مشترک را تعریف کنید: سرویس، محیط، میزبان، trace_id، و زمان UTC. یک صفحه بنویسید و در اختیار همه بگذارید.
  3. بقیهٔ سرویس‌ها را یکی‌یکی وصل کنید، با همان فیلدها. برنامه‌های قدیمی را هم فراموش نکنید؛ معمولاً همان‌ها بیشترین خطا را دارند.
  4. حجم را ببینید و منابع پرحرف را خاموش کنید. پس از یک هفته، معلوم می‌شود چه چیزی ارزش نگه‌داری دارد.
  5. حادثهٔ بعدی را با ابزار جدید حل کنید و ببینید کجا هنوز چیزی کم است. روش گام‌به‌گام این کار را در عیب‌یابی خطا در پروداکشن با لاگ نوشته‌ام.

اگر می‌خواهید همین امروز اولین لاگ را در یک انبار متمرکز ببینید، شروع سریع: از ثبت‌نام تا اولین لاگ چند دقیقه بیشتر وقت نمی‌گیرد. و اگر هنوز اصول لاگ‌نویسی در خود برنامه‌ها را مرور نکرده‌اید، راهنمای جامع لاگ‌نویسی در بک‌اند را پیش از هر چیز بخوانید؛ لاگ متمرکز فقط به‌اندازهٔ لاگ‌هایی که به آن می‌رسند ارزش دارد.

منابع و مطالعهٔ بیشتر

لاگ همهٔ سرویس‌هایتان را در یک جا جستجو کنید

C#، Java یا هر زبان دیگر — با چند خط پیکربندی وصل می‌شود. پلن رایگان کارت بانکی نمی‌خواهد.

شروع رایگان