স্বাগতম! আমরা আপনার কোর্স Outline অনুযায়ী Section 20 (Filters)-এর অত্যন্ত চমৎকার এবং রিয়েল-ওয়ার্ল্ড ইন্ডাস্ট্রিতে বহুল ব্যবহৃত একটি টপিকে চলে এসেছি: Serilog Structured Logging। আগের লেকচারগুলোতে আমরা দেখেছি Action Filter-এর মাধ্যমে কীভাবে কাজ করতে হয়। আজ আমরা শিখব অ্যাপ্লিকেশনের লগিং সিস্টেমকে কীভাবে আরও স্মার্ট, সার্চেবল এবং প্রফেশনাল করা যায়। সাধারণ টেক্সট লগের চেয়ে Structured Logging কেন হাজার গুণ বেশি পাওয়ারফুল, আজ আপনি সেটাই অনুভব করবেন। চলুন শুরু করি!


📝 Quick Revision Summary

ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য আজকের লেকচারের মূল পয়েন্টগুলো নিচে দেওয়া হলো:

  • Static Logs পরিহার করুন: লগ মেসেজে সরাসরি হার্ডকোড করা স্ট্রিং (যেমন: "Error in PersonsListActionFilter") লেখা থেকে বিরত থাকুন।
  • Structured Logging কী: লগ মেসেজের ভেতর ডাইনামিক ডেটা বা ভেরিয়েবলগুলোকে প্লেসহোল্ডারের মাধ্যমে প্যারামিটার হিসেবে পাস করার পদ্ধতিই হলো Structured Logging।
  • nameof() অপারেটরের ব্যবহার: ক্লাস বা মেথডের নাম সরাসরি স্ট্রিং হিসেবে না লিখে nameof(ClassName) ব্যবহার করুন। এতে কোড রিফ্যাক্টর করলে লগের নামও অটোমেটিক আপডেট হয়ে যায়।
  • Log Context: Structured Logging-এ পাঠানো প্যারামিটারগুলো (যেমন {FilterName}) সাধারণ টেক্সট হিসেবে থাকে না, বরং এগুলো Log Context-এ আলাদা প্রপার্টি (Key-Value) হিসেবে সেভ হয়।
  • অ্যাডভান্সড Searchability: Seq বা অন্য যেকোনো লগিং ড্যাশবোর্ডে নির্দিষ্ট প্যারামিটারের নাম ধরে (যেমন FilterName == "PersonsListActionFilter") খুব সহজেই ফিল্টার বা সার্চ করা যায়।

🔍 Comprehensive Breakdown

১. Static Logging vs Structured Logging [Priority: 10/10]

The “Why”: আমরা যখন কোনো লগ লিখি, তখন আমাদের প্রধান উদ্দেশ্য থাকে অ্যাপ্লিকেশন প্রোডাকশনে চলার সময় কোনো সমস্যা হলে সেটি যেন সহজে খুঁজে বের করা যায়।

Static Logging (ভুল পদ্ধতি):

_logger.LogInformation("PersonsListActionFilter.OnActionExecuted executed.");
 

এখানে পুরো কথাটি একটি সাধারণ স্ট্রিং বা টেক্সট। প্রোডাকশনে যখন দিনে লক্ষ লক্ষ লগ তৈরি হবে, তখন এই বিশাল টেক্সট ফাইল থেকে নির্দিষ্ট ফিল্টারের লগ খুঁজে বের করা প্রায় অসম্ভব হয়ে দাঁড়ায়।

Structured Logging (সঠিক পদ্ধতি): Structured Logging-এ আমরা ভেরিয়েবল বা সার্চেবল কি-ওয়ার্ডগুলোকে প্যারামিটার হিসেবে পাস করি।

_logger.LogInformation("{FilterName}.{MethodName} executed.", "PersonsListActionFilter", "OnActionExecuted");
 

এখানে {FilterName} এবং {MethodName} হলো প্লেসহোল্ডার। আপনি আপনার ইচ্ছামতো যেকোনো নাম দিতে পারেন (এগুলো user-defined)। ডানপাশের আর্গুমেন্টগুলো ক্রমানুসারে (first value to first parameter, second value to second parameter) এই প্লেসহোল্ডারগুলোতে বসে যাবে।

২. nameof() অপারেটরের স্মার্ট ব্যবহার [Priority: 9/10]

লেকচারার অত্যন্ত সুন্দর একটি টেকনিক দেখিয়েছেন। ক্লাস বা মেথডের নাম হার্ডকোড (যেমন "PersonsListActionFilter") করাটা মোটেও ভালো প্র্যাকটিস নয়। কারণ ভবিষ্যতে আপনি যদি ক্লাসের নাম পরিবর্তন করে PersonsActionFilter রাখেন, তখন আপনার লগ মেসেজটি পুরানো নামই দেখাতে থাকবে।

এর সমাধান হলো nameof() অপারেটর ব্যবহার করা।

public void OnActionExecuting(ActionExecutingContext context)
{
    _logger.LogInformation("{FilterName}.{MethodName}", 
        nameof(PersonsListActionFilter), 
        nameof(OnActionExecuting));
}
 
public void OnActionExecuted(ActionExecutedContext context)
{
    _logger.LogInformation("{FilterName}.{MethodName}", 
        nameof(PersonsListActionFilter), 
        nameof(OnActionExecuted));
}
 

উপকারিতা: nameof() রানটাইমে ক্লাসের নামটিকে স্ট্রিংয়ে কনভার্ট করে নেয়। ফলে আপনি যদি ক্লাসের নাম চেঞ্জ করেন, কোড অটোমেটিক্যালি আপডেট হয়ে যাবে।

৩. Log Context এবং Seq-এ Searchability [Priority: 10/10]

Structured Logging-এর আসল ম্যাজিক দেখা যায় যখন আমরা Seq (বা Kibana, Splunk) এর মতো লগ সার্ভার ব্যবহার করি।

আপনি যখন {FilterName} প্যারামিটারটি পাস করেন, Serilog এই নামটিকে শুধুমাত্র স্ট্রিংয়ে রিপ্লেস করে না, বরং এটিকে Log Context-এ একটি আলাদা প্রপার্টি বা JSON ফিল্ড হিসেবে সেভ করে। এর ফলে Seq ড্যাশবোর্ডে গিয়ে আপনি লক্ষ লক্ষ লগের মাঝে শুধু নিচের কমান্ডটি লিখলেই কাঙ্ক্ষিত লগ পেয়ে যাবেন: FilterName == "PersonsListActionFilter"

💡 Note: Seq-এ সার্চ করার সময় Case Sensitivity-র দিকে খেয়াল রাখবেন। অর্থাৎ personslistactionfilter লিখলে কাজ নাও করতে পারে, ঠিক যেভাবে কোডে লেখা আছে সেভাবেই সার্চ করতে হবে।


🚀 Best Practices & Modern .NET Updates

Best Practices for Logging (ইন্ডাস্ট্রি স্ট্যান্ডার্ড):

  1. Never use String Interpolation ($"") in Loggers: এটি জুনিয়র ডেভেলপারদের সবচেয়ে কমন একটি ভুল।
  • ভুল পদ্ধতি: _logger.LogInformation($"User {userId} logged in."); (এটি Structured Logging নয়, এটি ব্যাকগ্রাউন্ডে Static String হয়ে যায়, ফলে userId দিয়ে আর সার্চ করা যায় না)।
  • সঠিক পদ্ধতি: _logger.LogInformation("User {UserId} logged in.", userId);
  1. PascalCase for Placeholders: প্যারামিটারের নামগুলো সবসময় PascalCase-এ (যেমন {UserId} বা {FilterName}) লেখা উচিত। এটি লগিং প্ল্যাটফর্মগুলোতে প্রপার্টির নামের স্ট্যান্ডার্ড কনভেনশন।

.NET 10 & Minimal APIs Context: যদিও এই লেকচারটি MVC এবং Action Filters-এর উপর ফোকাস করছে, .NET 10-এ Minimal API বা সাধারণ যেকোনো সার্ভিসের ক্ষেত্রেও Structured Logging হুবহু একইভাবে কাজ করে। তবে .NET 10-এ লগিং পারফরম্যান্স আরও বাড়ানোর জন্য LoggerMessage সোর্স জেনারেটর (Source Generator) ব্যবহার করা হয়, যা হাই-পারফরম্যান্স অ্যাপ্লিকেশনে (যেখানে প্রতি সেকেন্ডে হাজার হাজার লগ জেনারেট হয়) Structured Logging-কে আরও ফাস্ট করে।

Example in .NET 10 (High-Performance Logging Approach):

public partial class PersonService
{
    private readonly ILogger<PersonService> _logger;
 
    public PersonService(ILogger<PersonService> logger)
    {
        _logger = logger;
    }
 
    // Source Generator Approach for extreme performance
    [LoggerMessage(Level = LogLevel.Information, Message = "Executing method {MethodName} in service {ServiceName}")]
    public partial void LogMethodExecution(string MethodName, string ServiceName);
 
    public void DoWork()
    {
        // Instead of _logger.LogInformation(...), call the highly optimized method
        LogMethodExecution(nameof(DoWork), nameof(PersonService));
    }
}
 

এই নতুন সোর্স জেনারেটর অ্যাপ্রোচ মেমরি অ্যালোকেশন কমায় এবং অ্যাপ্লিকেশনকে আরও ফাস্ট করে। তবে কনসেপ্ট সেই একই—সবকিছু প্যারামিটারাইজড রাখতে হবে!