স্বাগতম! আজকে আমরা অত্যন্ত গুরুত্বপূর্ণ একটি প্র্যাকটিক্যাল সেশন নিয়ে আলোচনা করতে যাচ্ছি—ILogger in Controller, Service, and Repository।
আপনার Outline অনুযায়ী, আমরা এখন Section 20: Logging-এর পঞ্চম লেকচার “270. ILogger in Controller”-এ আছি। এতক্ষণ আমরা Program.cs ফাইলে লগের বেসিক এবং কনফিগারেশন নিয়ে শিখেছি। কিন্তু রিয়েল-ওয়ার্ল্ড প্রজেক্টে তো আর সব কোড Program.cs-এ থাকে না! আপনার কোড Controller, Service এবং Repository—এই তিনটি লেয়ারে ভাগ করা থাকে। আজকের লেকচারে আমরা শিখব কীভাবে Dependency Injection (DI) ব্যবহার করে এই প্রতিটি লেয়ারে সঠিকভাবে লগিং করা যায়।
চলুন শুরু করা যাক!
📝 Short Summary for Quick Revision
- The Goal: Application-এর প্রতিটি লেয়ার (Controller -> Service -> Repository) ঠিকমতো কাজ করছে কি না, তা ট্র্যাক করার জন্য সব জায়গায় Log message রাখা।
- Dependency Injection (DI): যেকোনো ক্লাসে লগ ব্যবহার করার জন্য প্রথমে Constructor-এর মাধ্যমে
ILogger<T>ইনজেক্ট করতে হয়। - Generic Parameter
<T>:ILogger-এর জেনেরিক প্যারামিটার হিসেবে ওই ক্লাসের নাম দিতে হয় (যেমন:ILogger<PersonsController>), যাতে লগে বোঝা যায় মেসেজটি কোথা থেকে এসেছে। - Information Log: কোনো মেথড কল হয়েছে কি না, তা বোঝানোর জন্য
LogInformationব্যবহার করা হয়। - Debug Log: মেথডের প্যারামিটারের ভ্যালু কী এসেছে, তা চেক করার জন্য
LogDebugব্যবহার করা হয়। - Entity Framework Logs: আমরা নিজেদের লগ লেখার পাশাপাশি, EF Core-এর জেনারেট করা SQL Query-এর লগগুলোও কনসোলে দেখতে পাই।
🧠 Comprehensive Breakdown
এখানে আমরা লেকচারের প্রতিটি বিষয় বিস্তারিতভাবে আলোচনা করব এবং সাথে Code Implementation দেখব।
১. ILogger Inject করা (Priority: 10/10)
যেকোনো ক্লাসে (হোক সেটা Controller, Service বা Repository) লগিং করার প্রথম শর্ত হলো ILogger সার্ভিসটিকে ইনজেক্ট করা। ASP.NET Core-এর IoC (Inversion of Control) কন্টেইনার স্বয়ংক্রিয়ভাবে রানটাইমে এর অবজেক্ট তৈরি করে দেয়।
Why use Generic Type (<T>)?
ILogger<PersonsController> লেখার কারণ হলো, যখন কনসোল বা ইভেন্ট ভিউয়ারে লগটি প্রিন্ট হবে, তখন সেখানে PersonsController নামটি লেখা থাকবে। এতে ডেভেলপার খুব সহজেই বুঝতে পারবেন যে কোন ফাইল বা ক্লাস থেকে এই লগটি জেনারেট হয়েছে।
Code Implementation (Controller Layer):
using Microsoft.Extensions.Logging;
public class PersonsController : Controller
{
private readonly ILogger<PersonsController> _logger;
private readonly IPersonsService _personsService;
// Constructor Dependency Injection
public PersonsController(ILogger<PersonsController> logger, IPersonsService personsService)
{
_logger = logger;
_personsService = personsService;
}
public IActionResult Index(string searchBy, string searchString, string sortBy, string sortOrder)
{
// 1. Information Log: মেথডটি যে কল হয়েছে তা ট্র্যাক করা
_logger.LogInformation("Index action method of PersonsController reached.");
// 2. Debug Log: প্যারামিটারগুলোর ভ্যালু ঠিকঠাক এসেছে কি না তা ট্র্যাক করা
_logger.LogDebug($"searchBy: {searchBy}, searchString: {searchString}, sortBy: {sortBy}, sortOrder: {sortOrder}");
// Service মেথড কল করা...
var persons = _personsService.GetFilteredPersons(searchBy, searchString);
return View(persons);
}
}
২. Service Layer-এ Logging (Priority: 9/10)
Controller থেকে রিকোয়েস্ট Service-এ যায়। তাই Service-এর মেথডগুলো ঠিকমতো কল হচ্ছে কি না, তা নিশ্চিত করার জন্য এখানেও লগ বসাতে হবে।
Code Implementation (Service Layer):
public class PersonsService : IPersonsService
{
private readonly ILogger<PersonsService> _logger;
private readonly IPersonsRepository _personsRepository;
public PersonsService(ILogger<PersonsService> logger, IPersonsRepository personsRepository)
{
_logger = logger;
_personsRepository = personsRepository;
}
public List<Person> GetFilteredPersons(string searchBy, string searchString)
{
// Service-এর লগ
_logger.LogInformation("GetFilteredPersons method of PersonsService reached.");
// এখানে আমরা আলাদা করে Debug লগ লিখছি না, কারণ Controller-এই প্যারামিটারগুলো প্রিন্ট করা হয়েছে।
return _personsRepository.GetFilteredPersons(searchBy, searchString);
}
}
৩. Repository Layer-এ Logging (Priority: 9/10)
Service থেকে রিকোয়েস্ট Repository-তে যায় ডেটাবেস অপারেশনের জন্য। এখানেও লগ রাখা জরুরি।
Code Implementation (Repository Layer):
public class PersonsRepository : IPersonsRepository
{
private readonly ILogger<PersonsRepository> _logger;
public PersonsRepository(ILogger<PersonsRepository> logger)
{
_logger = logger;
}
public List<Person> GetAllPersons()
{
_logger.LogInformation("GetAllPersons method of PersonsRepository reached.");
// EF Core Data Fetching logic...
return new List<Person>();
}
public List<Person> GetFilteredPersons(string searchBy, string searchString)
{
_logger.LogInformation("GetFilteredPersons method of PersonsRepository reached.");
// Filtering logic...
return new List<Person>();
}
}
৪. Execution Sequence Tracking (The Real Benefit) (Priority: 10/10)
উপরের মতো তিন লেয়ারে লগ লেখার পর যখন আপনি অ্যাপ্লিকেশন রান করবেন এবং Index পেইজে যাবেন, তখন Kestrel কনসোলে ক্রমানুসারে নিচের লগগুলো দেখতে পাবেন:
info: PersonsController... Index action method...dbug: searchBy: null, searchString: null...(যেহেতু কোনো সার্চ দেওয়া হয়নি)info: PersonsService... GetFilteredPersons...info: PersonsRepository... GetAllPersons...(ট্রেইনার প্রমাণ করেছেন যে, সার্চ স্ট্রিং null থাকলে কন্ডিশন অনুযায়ী GetFilteredPersons কল না হয়ে GetAllPersons কল হয়। লগের কারণেই এই ফ্লো-টা ক্লিয়ারলি বোঝা গেছে!)
এর পাশাপাশি, আপনি কনসোলে Entity Framework-এর জেনারেট করা SELECT কোয়েরিগুলোও দেখতে পাবেন, যা EF Core নিজে থেকেই Information লেভেলে লগ করে।
🚀 Best Practices & .NET Modern Updates
Best Practices for Multi-Layer Logging:
- Don’t Over-log: Controller-এ যদি আপনি ভ্যারিয়েবলের ভ্যালু
LogDebugকরে ফেলেন, তবে Service বা Repository-তে গিয়ে আবার একই ভ্যালু লগ করার দরকার নেই। এটি লগ ফাইলে নয়েজ (Noise) তৈরি করে। - Use Structured Logging: ট্রেইনার লেকচারে স্ট্রিং ইন্টারপোলেশন (
$"") ব্যবহার করেছেন। কিন্তু বেস্ট প্র্যাকটিস হলো স্ট্রাকচার্ড লগিং ব্যবহার করা, যাতে ভ্যারিয়েবলগুলো প্রপার্টি হিসেবে সেভ হয়। - Bad (String Interpolation):
_logger.LogDebug($"searchBy: {searchBy}"); - Good (Structured Logging):
_logger.LogDebug("searchBy: {SearchBy}", searchBy);
Modern .NET Updates (.NET 10 & Source Generators):
বর্তমান .NET 10-এ হাই-পারফরম্যান্স অ্যাপ্লিকেশনের জন্য ILogger-এর সাধারণ ইনজেকশন ছাড়াও Source Generator (LoggerMessage Attribute) খুব জনপ্রিয়। এটি প্রতিবার স্ট্রিং পার্স করার ওভারহেড কমিয়ে দেয়।
.NET 10 Code Implementation (High Performance Logger):
public partial class PersonsController : Controller
{
private readonly ILogger<PersonsController> _logger;
public PersonsController(ILogger<PersonsController> logger)
{
_logger = logger;
}
// .NET 8/10 Source Generated Logging
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Index action method reached.")]
public partial void LogIndexMethodReached();
[LoggerMessage(EventId = 2, Level = LogLevel.Debug, Message = "Search details - By: {SearchBy}, String: {SearchString}")]
public partial void LogSearchParameters(string? searchBy, string? searchString);
public IActionResult Index(string searchBy, string searchString)
{
// কল করার সময় সাধারণ মেথডের মতো কল হবে, যা অনেক ফাস্ট
LogIndexMethodReached();
LogSearchParameters(searchBy, searchString);
return View();
}
}
এটি মেমরি অ্যালোকেশন কমায় এবং বড় স্কেলের প্রোজেক্টে অসাধারণ পারফরম্যান্স দেয়।
আশা করি লেয়ার বাই লেয়ার লগিংয়ের কনসেপ্টটি এখন আপনার কাছে একদম ক্লিয়ার! সামনের লেকচারে “HTTP Logging” নিয়ে আরও ইন্টারেস্টিং কিছু অপেক্ষা করছে।