راه‌اندازی Observability با LGTM و Pyroscope، بخش ۴: لاگ‌ها با Loki

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

Loki چگونه کار می‌کند؟

Loki یک سامانه‌ی ذخیره‌ی لاگ از خانواده‌ی Grafana است. نکته‌ی کلیدی طراحی آن این است که برخلاف بسیاری از سامانه‌های دیگر، کل متن لاگ‌ها را ایندکس نمی‌کند؛ فقط مجموعه‌ای کوچک از برچسب‌ها (labels) مانند namespace، pod و container را ایندکس می‌کند و خود متن را فشرده ذخیره می‌کند. نتیجه، هزینه و منابع بسیار کمتر است و مدل کار آن هم شبیه Prometheus می‌ماند.

جمع‌آوری لاگ با Alloy

خبر خوب این است که به جمع‌کننده‌ی جدیدی نیاز نداریم؛ همان Grafana Alloy که در بخش قبل متریک‌ها را جمع می‌کرد، می‌تواند لاگ pod ها را هم بخواند. کافی است دو جزء به پیکربندی اضافه کنیم: یکی لاگ‌ها را از Kubernetes می‌خواند و دیگری آن‌ها را به Loki می‌فرستد:

loki.source.kubernetes "pods" {
  targets    = discovery.relabel.pod_logs.output
  forward_to = [loki.write.default.receiver]
}

loki.write "default" {
  endpoint {
    url = "http://loki.observability.svc:3100/loki/api/v1/push"
  }
}

هر خط لاگ به‌همراه برچسب‌هایی مانند namespace، pod، container و app به Loki می‌رسد. همین برچسب‌ها بعداً امکان فیلترکردن دقیق را می‌دهند.

نمایش لاگ در Grafana

Loki را هم به‌عنوان یک datasource به Grafana معرفی می‌کنیم و مانند بخش قبل، یک داشبورد لاگ به‌شکل کد می‌سازیم: یک نمودار میله‌ای از حجم لاگ در طول زمان و یک پنل که خطوط لاگ زنده را نشان می‌دهد. با تولید ترافیک روی اپلیکیشن (شامل درخواست‌های موفق و درخواست‌هایی به مسیرهای ناموجود)، لاگ‌های واقعی نمایش داده می‌شوند:

داشبورد لاگ در Grafana با داده‌ی واقعی از Loki
داشبورد لاگ: نمودار حجم لاگ در بالا و خطوط لاگ زنده در پایین. لاگ‌های دسترسی اپلیکیشن، هم پاسخ‌های موفق (200) و هم درخواست‌های ناموجود (404) را نشان می‌دهند.

فیلترکردن با LogQL

Loki زبان پرس‌وجوی خودش را دارد به نام LogQL که ساختاری شبیه PromQL دارد. ابتدا با برچسب‌ها مجموعه‌ی لاگ را محدود می‌کنیم و سپس با عملگرهای متنی، خطوط موردنظر را فیلتر می‌کنیم؛ برای نمونه فقط درخواست‌هایی که با خطای 404 پاسخ گرفته‌اند:

{namespace="guestbook"}                 # همه‌ی لاگ‌های اپلیکیشن
{namespace="guestbook"} |= "404"        # فقط درخواست‌های ناموفق
فیلترکردن لاگ‌های 404 با LogQL از Loki
با یک فیلتر LogQL، از میان همه‌ی لاگ‌ها تنها درخواست‌های ۴۰۴ را بیرون می‌کشیم. همین مدل فیلتر در Grafana هم به‌شکل بصری در دسترس است.

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

جمع‌بندی و بخش بعد

در این بخش زنجیره‌ی لاگ را ساختیم: خواندن لاگ pod ها با Alloy، ذخیره در Loki و بررسی و فیلتر با LogQL در Grafana. حالا دو سیگنال از سه سیگنال اصلی را داریم: متریک‌ها می‌گویند مشکلی هست و لاگ‌ها می‌گویند چرا.

در بخش بعد سیگنال سوم را اضافه می‌کنیم: با Tempo مسیر یک درخواست را از ابتدا تا انتها ردیابی می‌کنیم تا ببینیم زمان دقیقاً کجا صرف می‌شود.

بخش‌های این مجموعه

← بازگشت به وبلاگ