এই নিবন্ধে, আমরা কীভাবে লগিং সংক্রান্ত উদ্বেগ এবং কোডগুলিকে অবকাঠামো এবং ব্যবসায়িক কোড থেকে আলাদা এবং ডিকপল করা যায় তা পর্যালোচনা করে শেষ করব। পূর্ববর্তী নিবন্ধে , আমরা পরিকাঠামোগত ক্রিয়াকলাপের জন্য এটি অর্জন করতে কীভাবে ডায়াগনস্টিকসোর্স এবং ডায়াগনস্টিকলিস্টেনার ব্যবহার করতে হয় তা পর্যালোচনা করেছি।
এখন, আমরা অতিরিক্ত প্রসঙ্গ সহ লগ করা ডেটা কীভাবে সমৃদ্ধ করা যায় তা পর্যালোচনা করব।
মূলত, লগ করা ডেটা সমৃদ্ধ করার ধারণাটি অতিরিক্ত প্রসঙ্গ, বা ডেটা নিবন্ধনের চারপাশে ঘোরে, যা লগ বার্তাগুলির সাথে একত্রিত হওয়া উচিত। এই প্রেক্ষাপট যেকোনও হতে পারে - একটি সাধারণ স্ট্রিং থেকে একটি জটিল বস্তু পর্যন্ত, এবং এটি অবকাঠামো বা ব্যবসায়িক কোডের মধ্যে কিছু মুহুর্তে উত্পাদিত বা পরিচিত হয়, লগ বার্তাগুলি লেখার মুহূর্তে নয়।
সুতরাং, আমরা লগ মেসেজগুলিতে অন্য কোনও সম্পত্তি যোগ করতে পারি না, কারণ এই কোডটি কোনও লগিং আউটপুট তৈরি করে না, বা আমরা চাই যে প্রসঙ্গটি একাধিক লগ বার্তার সাথে সংযুক্ত করা হোক যা এক্সিকিউশন চেইন হতে পারে বা নাও হতে পারে।
এই প্রেক্ষাপটটি এমনকি শর্তসাপেক্ষও হতে পারে - যেমন শুধুমাত্র ত্রুটি লগ বার্তাগুলিতে নির্দিষ্ট ডেটা যোগ করা যখন অন্য সমস্ত বার্তাগুলির প্রয়োজন নেই৷
আমরা সেরিলগ এবং এর সমৃদ্ধকরণ বৈশিষ্ট্য ব্যবহার করব, কারণ সেরিলগ এটিকে খুব নমনীয় এবং শক্তিশালী করেছে। অন্যান্য সমাধানেরও, বিভিন্ন পরিপক্কতা স্তরে একই বৈশিষ্ট্য রয়েছে, এবং আমরা সেরিলগ-এর সমৃদ্ধকরণকে Microsoft.Extensions.Logging বাক্সের বাইরে যা প্রদান করে তার সাথে তুলনা করব।
সেরিলগ অনেক দরকারী সমৃদ্ধির সাথে প্যাক করে যা কিছু পরিস্থিতিতে খুব সহজ হতে পারে। আপনি এই পৃষ্ঠাটি দেখতে পারেন - https://github.com/serilog/serilog/wiki/Enrichment - সমৃদ্ধকারীদের সম্পূর্ণ তালিকা এবং তাদের বিবরণ দেখতে।
উদাহরণ স্বরূপ, LogContext এবং GlobalLogContext সমৃদ্ধকারীরা আছে যেগুলি লগ মেসেজগুলির সাথে লগ ইন করার জন্য অতিরিক্ত ডেটা পুশ করার অনুমতি দেয় যদি সেগুলি একটি ম্যাচিং স্কোপের মধ্যে লেখা হয়৷
LogContext enricher অনেকটা Microsoft.Extensions.Logging-এ লগিং স্কোপের ধারণার অনুরূপ। মূলত, তারা উভয়ই কিছু কাস্টম ডেটা পুশ করে এবং একটি আইডিসপোজেবল অবজেক্ট প্রদান করে যা লগ কনটেক্সট থেকে ডেটা অপসারণের জন্য নিষ্পত্তি করা উচিত।
অর্থাৎ, যতক্ষণ পর্যন্ত আইডিসপোজেবল অবজেক্টটি সুযোগের মধ্যে থাকবে, ততক্ষণ ডেটা সেই সুযোগের মধ্যে লেখা সমস্ত লগ বার্তার সাথে সংযুক্ত থাকবে। এটি নিষ্পত্তি হয়ে গেলে, ডেটা আর সংযুক্ত করা হবে না।
Serilog এবং Microsoft ডকুমেন্টেশন এই উদাহরণ প্রদান করে:
// For Serilog log.Information("No contextual properties"); using (LogContext.PushProperty("A", 1)) { log.Information("Carries property A = 1"); using (LogContext.PushProperty("A", 2)) using (LogContext.PushProperty("B", 1)) { log.Information("Carries A = 2 and B = 1"); } log.Information("Carries property A = 1, again"); } // For Microsoft.Extensions.Logging scopes using (logger.BeginScope(new List<KeyValuePair<string, object>> { new KeyValuePair<string, object>("TransactionId", transactionId), })) { _logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id); todoItem = await _context.TodoItems.FindAsync(id); if (todoItem == null) { _logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id); return NotFound(); } }
কিছু পরিস্থিতিতে উপযোগী হলেও, সেগুলি বেশ সীমিত। এই ধরনের লগ সমৃদ্ধকরণের জন্য সর্বোত্তম ব্যবহার হল মিডলওয়্যার বা ডেকোরেটর ধরনের বাস্তবায়নে, যেখানে সুযোগটি ভালভাবে সংজ্ঞায়িত করা হয়েছে, এবং স্কোপ তৈরির মুহুর্তে ডেটা জানা যায়, এবং আমরা নিশ্চিত যে ডেটার বাইরে কোন মূল্য নেই সুযোগ
উদাহরণস্বরূপ, আমরা একটি একক HTTP অনুরোধ প্রক্রিয়াকরণ সুযোগের মধ্যে সমস্ত লগ বার্তাগুলিতে একটি পারস্পরিক সম্পর্ক আইডি সংযুক্ত করতে এটি ব্যবহার করতে পারি।
GlobalLogContext সমৃদ্ধকরণ LogContext এর মতোই, কিন্তু এটি বিশ্বব্যাপী - এটি একটি অ্যাপ্লিকেশনের মধ্যে লেখা সমস্ত লগ বার্তাগুলিতে ডেটা পুশ করে। যাইহোক, এই ধরনের সমৃদ্ধকরণের জন্য প্রকৃত ব্যবহারের ক্ষেত্রে খুবই সীমিত।
প্রকৃতপক্ষে, Serilog-এর জন্য কাস্টম লগ সমৃদ্ধকরণ বাস্তবায়ন করা খুবই সহজ - শুধু ILogEventEnricher
ইন্টারফেস প্রয়োগ করুন, এবং লগার কনফিগারেশনের সাথে সমৃদ্ধিকে নিবন্ধন করুন। ইন্টারফেসের বাস্তবায়নের জন্য শুধুমাত্র একটি পদ্ধতি রয়েছে - Enrich
- যা লগ ইভেন্ট গ্রহণ করে এবং আপনার পছন্দসই ডেটা দিয়ে সমৃদ্ধ করে।
একটি কাস্টম লগ সমৃদ্ধ করার জন্য একটি নমুনা বাস্তবায়ন পর্যালোচনা করা যাক।
public sealed class CustomLogEnricher : ILogEventEnricher { private readonly IHttpContextAccessor contextAccessor; public CustomLogEnricher(IHttpContextAccessor contextAccessor) { this.contextAccessor = contextAccessor; } public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { var source = contextAccessor.HttpContext?.RequestServices.GetService<ICustomLogEnricherSource>(); if (source is null) { return; } var loggedProperties = source.GetCustomProperties(logEvent.Level); foreach (var item in loggedProperties) { var property = item.ProduceProperty(propertyFactory); logEvent.AddOrUpdateProperty(property); } } }
যেমনটি আমরা দেখছি, লগ ইভেন্টকে সমৃদ্ধ করার জন্য কাস্টম বৈশিষ্ট্য পেতে এই সমৃদ্ধকারী একটি ICustomLogEnricherSource
এর উপর নির্ভর করে। সাধারণত, লগ সমৃদ্ধকারীরা দীর্ঘজীবী উদাহরণ এবং এমনকি সিঙ্গেলটন প্যাটার্ন অনুসরণ করে - তারা অ্যাপ্লিকেশন স্টার্টআপে একবার নিবন্ধিত হয় এবং পুরো অ্যাপ্লিকেশন আজীবন বেঁচে থাকে।
সুতরাং, আমাদের কাস্টম বৈশিষ্ট্যের উত্স থেকে সমৃদ্ধকরণকে ডিকপল করতে হবে এবং উত্সটি একটি সীমিত জীবনকালের সাথে একটি স্কোপড উদাহরণ হতে পারে।
public sealed class CustomLogEnricherSource : ICustomLogEnricherSource { private readonly Dictionary<string, CustomLogEventProperty> properties = new(); public ICustomLogEnricherSource With<T>(string property, T? value, LogEventLevel logLevel) where T : class { if (value is null) { return this; } properties[property] = new CustomLogEventProperty(property, value, logLevel); return this; } public void Remove(string property) { properties.Remove(property); } public IEnumerable<CustomLogEventProperty> GetCustomProperties(LogEventLevel logLevel) { foreach (var item in properties.Values) { if (item.Level <= logLevel) { yield return item; } } } } // And CustomLogEventProperty is a simple struct (you can make it a class, too): public struct CustomLogEventProperty { private LogEventProperty? propertyValue; public CustomLogEventProperty(string property, object value, LogEventLevel level) { Name = property; Value = value; Level = level; } public string Name { get; } public object Value { get; } public LogEventLevel Level { get; } public LogEventProperty ProduceProperty(ILogEventPropertyFactory propertyFactory) { propertyValue ??= propertyFactory.CreateProperty(Name, Value, destructureObjects: true); return propertyValue; } }
এই বাস্তবায়নের কয়েকটি সহজ বিবরণ রয়েছে:
ICustomLogEnricherSource
দৃষ্টান্তগুলির একটি তালিকা ব্যবহার করে সমৃদ্ধ করা যেতে পারে, কারণ বিভিন্ন অ্যাপ্লিকেশন উপাদান বিভিন্ন কাস্টম বৈশিষ্ট্য তৈরি করতে পারে।
ICustomLogEnricherSource
) কাস্টম বৈশিষ্ট্য সহ লগ বার্তা সমৃদ্ধ করতে প্রয়োজন এমন কোনো উপাদানে ইনজেকশন করা যেতে পারে। আদর্শভাবে, এটি একটি স্কোপড উদাহরণ হওয়া উচিত, কারণ এটি কাস্টম বৈশিষ্ট্যের মেয়াদ শেষ করার একটি সুবিধাজনক উপায় প্রদান করে না।
LogEvent
বৈশিষ্ট্যের উপর নির্ভর করে, এমনকি অ্যাপ্লিকেশন অবস্থার উপরও।
CustomLogEventProperty
একই কাস্টম সম্পত্তির জন্য একাধিকবার তৈরি করা এড়াতে একটি উত্পাদিত LogEventProperty
উদাহরণ ক্যাশে করে, কারণ এটি একাধিক লগ বার্তার সাথে সংযুক্ত হতে পারে।
কোডের এই লাইনে আরেকটি গুরুত্বপূর্ণ বিশদ রয়েছে, destructureObjects
প্যারামিটার:
propertyFactory.CreateProperty(Name, Value, destructureObjects: true);
যখন আমরা জটিল বস্তু সংযুক্ত করি - ক্লাস, রেকর্ড, স্ট্রাকট, সংগ্রহ ইত্যাদি - এবং এই প্যারামিটারটি true
হিসাবে সেট করা হয় না, সেরিলগ অবজেক্টে ToString
কল করবে এবং লগ মেসেজের সাথে ফলাফল সংযুক্ত করবে। যদিও রেকর্ডগুলিতে একটি ToString
বাস্তবায়ন রয়েছে যা সমস্ত পাবলিক প্রপার্টি আউটপুট করবে এবং আমরা আমাদের ক্লাস এবং স্ট্রাকটের জন্য ToString
ওভাররাইড করতে পারি, এটি সবসময় হয় না।
এছাড়াও, এই ধরনের আউটপুট কাঠামোবদ্ধ লগিংয়ের জন্য ভাল কাজ করে না, কারণ এটি একটি সাধারণ স্ট্রিং হবে, এবং আমরা সংযুক্ত বস্তুর বৈশিষ্ট্যগুলির উপর ভিত্তি করে লগ বার্তাগুলি অনুসন্ধান এবং ফিল্টার করতে সক্ষম হব না। সুতরাং, আমরা এখানে এই প্যারামিটারটিকে true
হিসাবে সেট করি। সাধারণ প্রকারগুলি (মান প্রকার এবং স্ট্রিং) এই প্যারামিটারের যেকোনো একটি মানের সাথে ঠিক কাজ করবে।
এই বাস্তবায়নের আরেকটি সুবিধা হল যে একবার একটি কাস্টম সম্পত্তি উৎসে নিবন্ধিত হয়ে গেলে, এটি সমস্ত লগ বার্তার জন্য সেখানে থাকে যতক্ষণ না এটি সরানো হয় বা উত্সটি নিষ্পত্তি না হয় (মনে রাখবেন, এটি একটি স্কোপড উদাহরণ হওয়া উচিত)। কিছু বাস্তবায়নের কাস্টম বৈশিষ্ট্যের জীবনকালের উপর আরও ভাল নিয়ন্ত্রণের প্রয়োজন হতে পারে এবং এটি বিভিন্ন উপায়ে অর্জন করা যেতে পারে।
উদাহরণস্বরূপ, নির্দিষ্ট CustomLogEventProperty
বাস্তবায়ন প্রদান করে অথবা কাস্টম সম্পত্তি সরানো উচিত কিনা তা পরীক্ষা করার জন্য একটি কলব্যাক প্রদান করে।
এই মেয়াদ শেষ হওয়ার যুক্তিটি GetCustomProperties
পদ্ধতিতে ব্যবহার করা যেতে পারে একটি কাস্টম প্রপার্টি প্রাক-চেক করতে এবং এটির মেয়াদ শেষ হয়ে গেলে উৎস থেকে সরিয়ে দিতে।
ঠিক আছে, আদর্শভাবে আমরা ব্যবসা এবং অবকাঠামো কোডের সাথে লগিং সংক্রান্ত উদ্বেগ মিশ্রিত করতে চাই না। এটি বিভিন্ন ডেকোরেটর, মিডলওয়্যার এবং অন্যান্য নিদর্শনগুলির সাথে অর্জন করা যেতে পারে যা লগিং-নির্দিষ্ট কোডের সাথে ব্যবসা এবং অবকাঠামো কোডকে 'মোড়ানো' অনুমতি দেয়।
যাইহোক, এটি সর্বদা সম্ভব নাও হতে পারে, এবং কখনও কখনও, ব্যবসা এবং অবকাঠামো কোডে ICustomLogEnricherSource
এর মতো একটি মধ্যস্থতাকারী বিমূর্ততা ইনজেক্ট করা এবং এটি লগিংয়ের জন্য কাস্টম ডেটা নিবন্ধন করতে দেওয়া আরও সুবিধাজনক হতে পারে।
এইভাবে, ব্যবসা এবং অবকাঠামো কোডের প্রকৃত লগিং সম্পর্কে কিছু জানার প্রয়োজন নেই, যখন এটি এখনও কোডের কিছু লগিং-সচেতন টুকরোগুলির সাথে মিশ্রিত করে।
যাইহোক, এই কোডটি অনেক কম মিলিত এবং অনেক বেশি পরীক্ষাযোগ্য হবে। এমনকি আমরা একটি নো-অপ ইমপ্লিমেন্টেশনের জন্য NullLogEnricherSource
এর মত কিছু প্রবর্তন করতে পারি যা কিছু খুব-হট-পাথ পরিস্থিতির জন্য কোনো কর্মক্ষমতা এবং মেমরি গ্রহণ করবে না।
মাইক্রোসফ্ট যেমন বলে,
লগিং এত দ্রুত হওয়া উচিত যে এটি অ্যাসিঙ্ক্রোনাস কোডের পারফরম্যান্স খরচের মূল্য নয়।
সুতরাং, যখনই আমরা আমাদের লগ বার্তাগুলিকে অতিরিক্ত প্রসঙ্গ সহ সমৃদ্ধ করি, আমাদের কর্মক্ষমতার প্রভাব সম্পর্কে সচেতন হওয়া উচিত৷ সাধারণভাবে বলতে গেলে, সেরিলগ লগ সমৃদ্ধকরণ বাস্তবায়ন খুব দ্রুত, অন্তত এটি মাইক্রোসফ্ট লগিং স্কোপের সমতুল্য, কিন্তু লগ মেসেজ তৈরি করতে আরও সময় লাগবে যদি আমরা তাদের সাথে আরও ডেটা সংযুক্ত করি।
আমরা যত বেশি কাস্টম বৈশিষ্ট্য সংযুক্ত করব, তত বেশি জটিল বস্তুগুলি তাদের মধ্যে থাকবে এবং একটি লগ বার্তা তৈরি করতে তত বেশি সময় এবং মেমরি লাগবে। সুতরাং, একজন বিকাশকারীকে লগ বার্তাগুলিতে কোন ডেটা সংযুক্ত করতে হবে, কখন এটি সংযুক্ত করতে হবে এবং এর জীবনকাল কী হওয়া উচিত সে সম্পর্কে খুব চিন্তাশীল হওয়া উচিত।
নীচে একটি ছোট বেঞ্চমার্ক ফলাফলের সারণী রয়েছে যা সেরিলগ লগ সমৃদ্ধকরণ এবং মাইক্রোসফ্ট লগিং স্কোপ উভয়ের জন্য কর্মক্ষমতা এবং মেমরি খরচ দেখায়। প্রতিটি বেঞ্চমার্ক পদ্ধতি কাস্টম বৈশিষ্ট্যগুলির সাথে সমৃদ্ধ করার বিভিন্ন উপায় সহ 20টি লগ বার্তা তৈরি করবে।
কোনো কাস্টম বৈশিষ্ট্য সংযুক্ত নেই:
| Method | Mean | Error | StdDev | Gen0 | Allocated | | SerilogLoggingNoEnrichment | 74.22 us | 1.194 us | 1.116 us | 1.2207 | 21.25 KB | | MicrosoftLoggingNoEnrichment | 72.58 us | 0.733 us | 0.685 us | 1.2207 | 21.25 KB |
প্রতিটি লগ বার্তায় তিনটি স্ট্রিং সংযুক্ত থাকে:
| Method | Mean | Error | StdDev | Gen0 | Allocated | | SerilogLoggingWithContext | 77.47 us | 0.551 us | 0.516 us | 1.7090 | 28.6 KB | | SerilogLoggingWithEnrichment | 79.89 us | 1.482 us | 2.028 us | 1.7090 | 29.75 KB | | MicrosoftLoggingWithEnrichment | 81.86 us | 1.209 us | 1.131 us | 1.8311 | 31.22 KB |
প্রতিটি লগ মেসেজে তিনটি জটিল বস্তু (রেকর্ড) সংযুক্ত থাকে:
| Method | Mean | Error | StdDev | Gen0 | Allocated | | SerilogLoggingWithObjectContext | 108.49 us | 1.193 us | 1.058 us | 5.3711 | 88.18 KB | | SerilogLoggingWithObjectEnrichment | 106.07 us | 0.489 us | 0.409 us | 5.3711 | 89.33 KB | | MicrosoftLoggingWithObjectEnrichment | 99.63 us | 1.655 us | 1.468 us | 6.1035 | 100.28 KB |The
Serilog*Context
পদ্ধতিগুলি LogContext.PushProperty
ব্যবহার করে, Serilog*Enrichment
পদ্ধতিগুলি নিবন্ধে দেওয়া কাস্টম সমৃদ্ধকরণ এবং উত্স বাস্তবায়ন ব্যবহার করে, যখন Microsoft*
পদ্ধতিগুলি লগিং স্কোপ ব্যবহার করে।
আমরা দেখতে পাচ্ছি যে কর্মক্ষমতা এবং মেমরি খরচ বেশিরভাগ ক্ষেত্রেই একই রকম, এবং জটিল বস্তুগুলিকে সমৃদ্ধ করা সাধারণ ধরনের সমৃদ্ধ করার চেয়ে বেশি ব্যয়বহুল। একমাত্র ব্যতিক্রম হল মাইক্রোসফ্ট বাস্তবায়নের সাথে লগ করা যখন আমরা লগ বার্তাগুলিতে রেকর্ড সংযুক্ত করি।
এর কারণ হল লগিং স্কোপগুলি জটিল বস্তুগুলিকে ধ্বংস করে না এবং লগ মেসেজগুলিতে সংযুক্ত করার সময় সিরিয়ালাইজেশনের জন্য ToString
পদ্ধতি ব্যবহার করে। এটি মাইক্রোসফ্ট বাস্তবায়নকে কিছুটা দ্রুত করে তবে আরও মেমরি খরচ করে।
যদি আমরা ডিফল্ট ToString
বাস্তবায়নের সাথে ক্লাস ব্যবহার করি, মেমরি খরচ অনেক কম হবে, কিন্তু এই কাস্টম অবজেক্টগুলি সম্পূর্ণরূপে যোগ্যতাসম্পন্ন টাইপ নাম হিসাবে লগ করা হবে - সম্পূর্ণ অকেজো।
এবং যে কোনও ক্ষেত্রে, আমরা মাইক্রোসফ্ট বাস্তবায়নের মাধ্যমে এই বস্তুর বৈশিষ্ট্যগুলির উপর ভিত্তি করে লগ বার্তাগুলিকে ধ্বংস না করার কারণে অনুসন্ধান এবং ফিল্টার করতে সক্ষম হব না।
সুতরাং, এটি মাইক্রোসফ্ট লগিং স্কোপের সীমাবদ্ধতা যা আমাদের সচেতন হওয়া উচিত - সাধারণ প্রকারগুলি সূক্ষ্মভাবে লগ করা অবস্থায় এবং অনুসন্ধানযোগ্য এবং ফিল্টারযোগ্য হতে পারে।
দ্রষ্টব্য: এই নিবন্ধের জন্য বেঞ্চমার্ক সোর্স কোড এবং কোড নমুনা GitHub সংগ্রহস্থলে পাওয়া যাবে
সঠিক লগ সমৃদ্ধকরণ হল কোডের উন্নয়ন ও রক্ষণাবেক্ষণের জন্য একটি 'জীবনের উন্নতির মান'। এটি লগ বার্তাগুলির সাথে অতিরিক্ত প্রসঙ্গ সংযুক্ত করার ক্ষমতা দেয় যা লগ বার্তা তৈরির মুহূর্তে অজানা।
এই প্রসঙ্গ যেকোনো কিছু হতে পারে - একটি সাধারণ স্ট্রিং থেকে একটি জটিল বস্তু পর্যন্ত, এবং এটি অবকাঠামো বা ব্যবসায়িক কোডের মধ্যে কিছু মুহুর্তে উত্পাদিত বা পরিচিত হয়।
এটি অবকাঠামো এবং ব্যবসায়িক কোড থেকে লগিং এবং পর্যবেক্ষণযোগ্য কোড আলাদা করার এবং কোডটিকে আরও রক্ষণাবেক্ষণযোগ্য, পরীক্ষাযোগ্য এবং পাঠযোগ্য করার একটি উপায়।