در باب OpenTelemetry

مقدمه
حتماً اسم OpenTelemetry (یا همون OTel) رو حول و حوش موضوعات مانیتورینگ و لاگینگ، شنیدین. هرچقدر تنوع کامپوننت‌ها، سرویس‌ها، اپلیکیشن‌ها بیشتر بیشتر بشه؛ یا به زبون ساده سیستم توزیع بشه، لزوم استفاده از یک استاندارد یا مکانیزم فراگیر، اهمیتش بیشتر می‌شه. فکر کنید لاگ رو با یه فرمتی که مختص دات‌نت باشه بنویسیم، یا اندازه‌گیری پرفرمنس سرویس دات‌نتی‌مون، مستقل از بخش فرانت که با ری‌اکت یا… نوشتیم باشه، ربط دادن و تحلیل داده‌های جمع شده چقدر دشوار می‌شه؟؟ ولی وقتی استانداردی داشته باشیم که هر بخش از نرم‌افزار با اون فرمت داده‌هاش رو به جایی ارسال کنه. و در نقطه‌ی تجمیع داده‌ها هم دستمون باز باشه تا با هر ابزاری که اون فرمت استاندارد رو می‌تونست بفهمه؛ داده‌ها رو جمع‌آوری، پردازش، نمایش و گزارش‌گیری کنیم؛ چقدر ایرادیابی و اشراف به شرایط برامون راحت‌تر می‌شه!
هدفم از این مطلب اینه که درک کنیم چرا OTel یه استاندارد مهم شده و چطوری از وابستگی به یه ابزار یا سرویس خاص برای جمع‌آوری داده‌های تل‌متری (Telemetry) نجاتمون می‌ده.


چرا OpenTelemetry به‌وجود اومد؟

تو سیستم‌های بزرگ، مخصوصاً وقتی سرویس‌های متعددی(.Distributed System, SOA, Microservices, etc) داریم، مانیتور کردن جامع و فراگیر خیلی مهمه. یه زمانی اگه کسی می‌خواست لاگ‌ها، متریک‌ها و تریس‌هاش (که بعدتر هر سه رو توضیح می‌دم) رو جمع کنه، باید ده‌جور کتابخونه و Agent می‌ذاشت که هر کدوم، داده‌هاش رو یه جور مختلف تحویل می‌داد. خلاصه آشفته می‌شد!

اینجا بود که پروژه‌های مختلفی مثل OpenTracing و OpenCensus سعی کردن این مشکل رو حل کنن اما هردوشون تا حدی موازی پیش می‌رفتن و هنوز یه استاندارد یکی نبود. Cloud Native Computing Foundation (CNCF) اومد گفت بیا این دوتا رو باهم یکی کنیم که یه استاندارد درست و حسابی و واحد درست بشه. از ترکیب این دوتا، محصول جدیدی به اسم OpenTelemetry متولد شد. حالا دیگه این استاندارد از طرف کلی شرکت و ابزار تجاری و اوپن‌سورس ساپورت می‌شه.


OpenTelemetry دقیقاً چیه؟

به زبان خیلی ساده، OpenTelemetry ابزار (یا بهتره بگیم یه مجموعه ابزار) و چهارچوبیه که کمک می‌کنه بتونیم داده‌های مربوط به لاگ‌ها، متریک‌ها و تریس‌ها رو به شکل استانداردی جمع کنیم و بفرستیم هر جایی که می‌خوایم. مثلاً می‌تونیم بفرستیم به Elastic, Prometheus, Jaeger, Datadog و… بدون اینکه مجبور بشیم توی کدمون کلی تغییرات عجیب و غریب بدیم. نکته باحال اینه که OTel خودش بک‌اند مانیتورینگ یا دیتابیس نگهداری داده‌های ما نیست؛ فقط کارش تولید، جمع‌آوری و ارسال این داده‌ها به سرویس مد نظره.

فایده‌اش؟

  • استاندارد کردن فرمت داده‌ها (یعنی هر کی و هر سرویسی گفت لاگ، تریس، متریک؛ دیگه مشخصه چی رو چه شکلی می‌فرسته، حالا می خواد یه نرم‌افزار جاوایی باشه، یا یه کد دات‌نت یا نرم‌افزار IoT).
  • جداکردن ابزار جمع‌آوری داده از محل ذخیره و تحلیل داده. یعنی می‌تونید خیلی راحت ابزار بک‌اندتون رو عوض کنید (ابزار دریافت و ذخیره‌سازی و تحلیل داده‌های دریافت شده با استاندارد OpenTelemetry).
  • پشتیبانی توسط اکثر زبان‌ها و فریم‌ورک‌ها؛ پس اگه یه گوشه پروژه‌تون با Go نوشته شده و یه گوشه دیگه با دات‌نت، باز همه‌اش می‌تونه داده‌ها رو یکپارچه بده بیرون.

سه پایه‌ی اصلی: لاگ‌ها، متریک‌ها و تریس‌ها

  1. لاگ‌ها (Logs):
    یه جورایی ساده‌ترین نوع داده. وقتی یه چیزی تو سیستم اتفاق می‌افته، ما میایم می‌نویسیمش. مثلاً «کاربر فلانی لاگین کرد»، «خطای ۵۰۰ پیش اومد»، «سرویس ایمیل فلان پیام را ارسال کرد» و … به کمک لاگ‌ها می‌فهمیم که چه وقایعی در چه زمانی رخ داده. (دو جور لاگ هم داریم، لاگ ساختارمند و غیرساختاری -> Structured Log, Unstructured Log)
  2. متریک‌ها (Metrics):
    عدد و رقم‌های زمان‌بندی‌شده. مثلاً نرخ درخواست در ثانیه (RPS) چقدره؟ CPU یا مموری مصرفی چقدره؟ پینگ یک سرویس دیگه چقدره؟ با متریک‌ها می‌تونیم وضعیت عملکرد سیستم رو تو بازه‌های زمانی مختلف مانیتور کنیم. حتی داده‌های بیزنسی: تعداد خریدهای موفق در یک ساعت، تعداد خروجی از انبار در یک دقیقه، مرخصی‌های ثبت شده در یک هفته و…) که باهاشون میشه BAM (Business Activity Monitor) و Business Dashboard ساخت.
  3. تریس‌ها (Traces):
    ردگیری جریانِ درخواست‌ها، توی سرویس‌های مختلف. مخصوصاً وقتی داریم تو یه معماری مایکروسرویسی کار می‌کنیم، هر درخواست ممکنه از ده‌ها سرویس رد بشه. با تریس‌ها راحت می‌فهمیم که این درخواست کجا چرخیده و اگه مشکلی پیش اومده، کجا کندی یا خطا داشته. توی سیستم‌های توزیع‌شده این ماجرا حیاتیه وگرنه به کابوس شبانه سلام کنید!

هر سه تای این‌ها «سیگنال»های مهمی هستن برای اینکه سیستم رو Observable کنیم. یعنی بتونیم از بیرون بفهمیم داخل چه خبره.


ساختار کلی OpenTelemetry

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

  • API: این بخش در زبان‌های مختلف ارائه شده؛ در دات‌نت هم APIهایی داریم که اجازه می‌ده کدمون رو برای جمع‌آوری تریس و متریک و لاگ تجهیز کنیم. (OpenTelemetry.Api)
  • SDK: پیاده‌سازی همون API و مسئولیت مدیریت و ارسال این داده‌ها رو داره.
  • Instrumentation: کتابخونه‌های آماده‌ای که برای کار با ASP.NET Core, HttpClient, SqlClient و… ارائه شدن تا بدون دردسر اضافه، بشه درخواست‌ها و متریک‌ها رو جمع کرد.
  • Collector: یه سرویس مجزاست که داده‌هایی که OTel تولید می‌کنه رو دریافت می‌کنه، پردازش می‌کنه و می‌فرسته به هر بک‌اندی که دوست داشته باشید. این وسط می‌تونین داده‌ها رو فیلتر، غنی‌سازی (داده اضافه کنین، enrich کنین) یا یه جای دیگه بفرستین.

مثال کد در .NET برای تریس و متریک

فرض کنیم یه پروژه ASP.NET Core داریم و می‌خوایم از OTel برای تریس‌ها و متریک‌ها استفاده کنیم. اول از همه باید پکیج‌های لازم رو نصب کنیم:

dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Exporter.Console

بعد مثلاً میایم تو فایل Program.cs (یا Startup.cs) یه چیزی شبیه این می‌نویسیم:

using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Metrics;

var builder = WebApplication.CreateBuilder(args);

// اضافه کردن سرویس‌های OTel
builder.Services.AddOpenTelemetry()
    .WithTracing(traceBuilder =>
    {
        traceBuilder
            .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyDotNetService"))
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            // می‌تونیم اینجا مثلاً SqlClient رو هم اضافه کنیم
            //.AddSqlClientInstrumentation() 
            .AddConsoleExporter();
    })
    .WithMetrics(metricBuilder =>
    {
        metricBuilder
            .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyDotNetService"))
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddConsoleExporter();
    });

var app = builder.Build();

app.MapGet("/", () => "Hello, OpenTelemetry World!");
app.Run();
  • با AddAspNetCoreInstrumentation()، می‌گیم هر ریکوئست ورودی به سایت ما مانیتور بشه.
  • با AddHttpClientInstrumentation()، هر جایی که از HttpClient استفاده می‌کنیم تریس یا متریک براش ثبت می‌شه.
  • با AddConsoleExporter()، داده‌ها رو می‌فرستیم تو کنسول (برای تست خیلی خوبه). توی محیط واقعی ممکنه بخوایم بفرستیمش به Jaeger, Zipkin, OTLP Collector یا هر سرویس دیگه‌ای.

این کد یه شروع ساده‌س؛ می‌تونیم بعداً جنبه‌های دیگه (متریک‌های دلخواه، لاگ‌ها و…) رو هم اضافه کنیم.


استقلال از ابزار تجمیع

با OTel، اگه امروز داده‌هاتون رو به Jaeger می‌فرستین و فردا تصمیم گرفتین برین Elastic یا Datadog، برای بخش جمع‌آوری نیازی نیست کلی کد رو بریزین دور. instrumentation‌تون سرجاشه؛ فقط Exporterتون عوض می‌شه. مثلاً جایی که قبلاً AddConsoleExporter() داشتین، حالا AddOtlpExporter() یا AddJaegerExporter() یا هر چی لازم دارین جایگزین می‌کنین. اینجوری به راحتی می‌تونیم از Vendor Lock-In فرار کنیم.

این داستان رو خیلی دقیق‌تر در باب Aspire خواهید دید، مثلا توی Aspire در محیط توسعه این داده‌ها توی ابزار وبی خودش که فقط نمایش می‌ده و ذخیره نمی‌کنه نشون داده می‌شه، ولی می‌تونیم با همون زیرساخت کاری کنیم که وقتی توی محیط acceptance یا production دپلوی کردیم مستقیم بره توی Datadog یا Elastic یا Azure Application Insights 😉


نکاتی در مورد داده‌های خروجی

فرمت لاگ: اگه از OTel برای لاگ استفاده می‌کنید، می‌تونید فرمت JSON یا هر فرمت استاندارد دیگه‌ای رو انتخاب کنید. ایده اینه که همه چیز با Schemaای که توسط OTel پیشنهاد شده (Semantic Conventions) همخونی داشته باشه.

متریک‌ها: معمولاً یه سری متریک مثل تعداد درخواست موفق، تعداد درخواست ناموفق، مدت پاسخ (Latency) و… به صورت پیش‌فرض جمع می‌شه. اگه خواستین متریک سفارشی داشته باشین (مثلاً تعداد سبدهای خرید تسویه‌شده)، از طریق Meter و Counterها در .NET می‌شه خیلی راحت بسازین.

تریس‌ها: هر تریس مجموعه‌ای از Span هاست که نشون می‌ده یه درخواست چطوری بین سرویس‌ها حرکت کرده. می‌تونیم برای هر Span تگ‌ها و Annotationهای دلخواه بفرستیم تا تو سرویس‌های تریسینگ بتونیم ریزجزئیات رو ببینیم.


خلاصه: چرا مهمه؟

  1. یکی شدن فرمت: دیگه هزار شکل مختلف داده نداریم. همه چیز رو با یه استاندارد می‌گیریم، بعد هر جا دلمون بخواد می‌فرستیم.
  2. آینده‌نگری: سریع‌تر می‌تونیم بک‌اندهای مختلف رو تغییر بدیم (یا حتی چندتا همزمان استفاده کنیم) بدون اینکه دردسر زیاد داشته باشیم.
  3. اجازه به تیم‌ها برای انتخاب ابزار: هر تیم می‌تونه ابزار مورد علاقه‌اش (مثلاً Prometheus یا Elastic) رو استفاده کنه و در عین حال از همان OTel data استفاده کنه.
  4. ساده شدن عملیات: کد instrumentation رو یه‌بار می‌نویسیم (یا حتی از Auto-instrumentation استفاده می‌کنیم). بعدش ارسال به هر جا رو با تنظیمات ساده کنترل می‌کنیم.

نتیجه‌گیری

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

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


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

دیدگاهتان را بنویسید