c# क्या मुझे लाइब्रेरी के लिए ILogger, ILogger<T>, ILoggerFactory या ILoggerProvider लेना चाहिए?




.net logging (5)

सवाल पर ILogger<T> , मेरा मानना ​​है कि ILogger<T> अन्य विकल्पों में से नीचे की ओर विचार करते हुए सही विकल्प है:

  1. ILoggerFactory करते हुए आपके उपयोगकर्ता को आपकी क्लास लाइब्रेरी के लिए ILoggerFactory ग्लोबल लॉगर फैक्ट्री के नियंत्रण को दूर करने के लिए मजबूर करता है। इसके अलावा, ILoggerFactory को स्वीकार ILoggerFactory आपकी कक्षा अब CreateLogger विधि के साथ किसी भी मनमाने श्रेणी के नाम के साथ लॉग इन कर सकती है। जबकि ILoggerFactory आमतौर पर DI कंटेनर में एक सिंगलटन के रूप में उपलब्ध है, मैं एक उपयोगकर्ता के रूप में संदेह करता हूं कि किसी भी पुस्तकालय को इसका उपयोग करने की आवश्यकता क्यों होगी।
  2. जबकि विधि ILoggerProvider.CreateLogger की तरह दिखता है, यह इंजेक्शन के लिए अभिप्रेत नहीं है। इसका उपयोग ILoggerFactory.AddProvider साथ किया जाता है, ताकि कारखाना प्रत्येक पंजीकृत प्रदाताओं से बनाए गए कई ILogger को लिखता हुआ समग्र ILogger बना सके। यह स्पष्ट है जब आप LoggerFactory.CreateLogger के कार्यान्वयन का निरीक्षण करते हैं
  3. ILogger को स्वीकार करना भी जाने के तरीके जैसा लगता है, लेकिन .NET Core DI के साथ यह असंभव है। यह वास्तव में इस कारण से लगता है कि उन्हें पहली जगह पर ILogger<T> प्रदान करने की आवश्यकता क्यों थी।

तो आखिरकार, हमारे पास ILogger<T> से बेहतर कोई विकल्प नहीं है, अगर हम उन वर्गों में से चुनें।

एक अन्य दृष्टिकोण गैर-जेनेरिक ILogger लपेटने वाले कुछ और को इंजेक्ट करना होगा, जो इस मामले में गैर-जेनेरिक होना चाहिए। विचार यह है कि इसे अपनी कक्षा के साथ लपेटकर, आप इस बात का पूरा नियंत्रण रखते हैं कि उपयोगकर्ता इसे कैसे कॉन्फ़िगर कर सकता है।

यह कुछ हद तक AspNet Core में कंस्ट्रक्टर्स को पास ILogger या ILoggerFactory से संबंधित हो सकता है ? , हालांकि यह विशेष रूप से लाइब्रेरी डिज़ाइन के बारे में है, न कि उन पुस्तकालयों का उपयोग करने वाले वास्तविक अनुप्रयोग कैसे इसके लॉगिंग को लागू करते हैं।

मैं एक .net Standard 2.0 लाइब्रेरी लिख रहा हूं जो Nuget के माध्यम से स्थापित होगी, और उस लाइब्रेरी का उपयोग करने वाले लोगों को कुछ डीबग जानकारी प्राप्त करने की अनुमति देने के लिए, मैं Microsoft.Extensions.Logging.Abstractions पर आधारित हूं।

हालांकि, मैं कई इंटरफेस देख रहा हूं, और वेब पर नमूना कोड कभी-कभी ILoggerFactory का उपयोग करता है और कक्षा के ctor में एक लकड़हारा बनाता है। इसमें ILoggerProvider भी है जो फैक्ट्री के रीड-ओनली संस्करण जैसा दिखता है, लेकिन कार्यान्वयन दोनों इंटरफेस को लागू कर सकता है या नहीं भी कर सकता है, इसलिए मुझे चुनना होगा। (फैक्टरी प्रदाता से अधिक आम लगता है)।

कुछ कोड मैंने देखा है कि गैर-जेनेरिक ILogger इंटरफ़ेस का उपयोग करता है और एक ही लॉगर का एक उदाहरण भी साझा कर सकता है, और कुछ अपने ctor में ILogger<T> लेते हैं और DI कंटेनर से खुले जेनेरिक प्रकार या प्रत्येक के स्पष्ट पंजीकरण का समर्थन करने की अपेक्षा करते हैं। और हर ILogger<T> विविधता मेरे पुस्तकालय का उपयोग करता है।

अभी, मुझे लगता है कि ILogger<T> सही दृष्टिकोण है, और शायद एक ctor है जो उस तर्क को नहीं लेता है और इसके बजाय सिर्फ Null Logger पास करता है। इस तरह, यदि कोई लॉगिंग की आवश्यकता नहीं है, तो कोई भी उपयोग नहीं किया जाता है। हालांकि, कुछ DI कंटेनर सबसे बड़ा ctor उठाते हैं और इस तरह वैसे भी विफल हो जाते हैं।

मैं उत्सुक हूं कि मैं उपयोगकर्ताओं के लिए कम से कम सिरदर्द बनाने के लिए यहां क्या करने वाला हूं, जबकि वांछित होने पर उचित लॉगिंग समर्थन की अनुमति देता हूं।


परिभाषा

हमारे पास 3 इंटरफेस हैं: ILogger , ILoggerProvider और ILoggerFactory । उनकी जिम्मेदारियों का पता लगाने के लिए स्रोत कोड देखें :

ILogger : मुख्य जिम्मेदारी किसी दिए गए लॉग स्तर के लॉग संदेश को लिखना है।

ILoggerProvider : केवल एक जिम्मेदारी है और वह एक ILogger बनाना है।

ILoggerFactory : ILogger बनाने और ILoggerProvider जोड़ने के लिए 2 जिम्मेदारियां हैं।

ध्यान दें कि हम कारखाने में एक या अधिक प्रदाता (कंसोल, फ़ाइल, आदि) पंजीकृत कर सकते हैं। जब हम कारखाने का उपयोग करके एक लकड़हारा बनाते हैं, तो यह सभी पंजीकृत प्रदाताओं को उनके संगत लकड़हारे बनाने के लिए उपयोग करता है।

ILoggerFactory factory = new LoggerFactory().AddConsole();    // add console provider
factory.AddProvider(new LoggerFileProvider("c:\\log.txt"));   // add file provider
Logger logger = factory.CreateLogger(); // <-- creates a console logger and a file logger

तो लकड़हारा लकड़हारा की एक सूची बनाए हुए है, और यह उन सभी को लॉग संदेश लिखता है। लकड़हारा स्रोत कोड को देखते हुए हम यह पुष्टि कर सकते हैं कि Logger पास ILoggers (यानी LoggerInformation []) की एक सरणी है, और साथ ही यह ILogger इंटरफ़ेस को लागू कर रहा है।

निर्भरता अन्तःक्षेपण

एमएस प्रलेखन एक लकड़हारा इंजेक्शन लगाने के लिए 2 तरीके प्रदान करता है:

1. कारखाने इंजेक्शन:

public TodoController(ITodoRepository todoRepository, ILoggerFactory logger)
{
    _todoRepository = todoRepository;
    _logger = logger.CreateLogger("TodoApi.Controllers.TodoController");
}

श्रेणी = TodoApi.Controllers.TodoController के साथ एक लकड़हारा बनाता है

2. एक सामान्य ILogger<T> :

public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger)
{
    _todoRepository = todoRepository;
    _logger = logger;
}

श्रेणी के साथ एक लकड़हारा बनाता है = टी का पूरी तरह से योग्य प्रकार का नाम

मेरी राय में, दस्तावेज़ीकरण को भ्रमित करने वाली बात यह है कि इसमें गैर-जेनेरिक, ILogger इंजेक्ट करने के बारे में कुछ भी उल्लेख नहीं है। ऊपर एक ही उदाहरण में, हम एक गैर-जेनेरिक ITodoRepository को इंजेक्ट कर रहे हैं, और फिर भी यह स्पष्ट नहीं करता है कि हम ILogger लिए ऐसा क्यों नहीं कर रहे हैं।

मार्क सेमैन के अनुसार:

एक इंजेक्शन निर्माता को निर्भरता प्राप्त करने से अधिक नहीं करना चाहिए।

नियंत्रक में किसी कारखाने को इंजेक्ट करना एक अच्छा दृष्टिकोण नहीं है, क्योंकि लॉगर (एसआरपी का उल्लंघन) को प्रारंभ करना नियंत्रक की जिम्मेदारी नहीं है। एक ही समय में एक सामान्य ILogger<T> इंजेक्ट करने से अनावश्यक शोर ILogger<T> साधारण इंजेक्टर ब्लॉग पर स्टीवन की पोस्ट देखें

क्या इंजेक्ट किया जाना चाहिए (कम से कम ऊपर के लेख के अनुसार) एक गैर-जेनेरिक ILogger , लेकिन फिर, यह कुछ ऐसा नहीं है जो Microsoft का अंतर्निहित DI कंटेनर कर सकता है, और आपको एक 3 पार्टी DI लाइब्रेरी का उपयोग करने की आवश्यकता है।

निकोला मालोविक का यह एक अन्य लेख है, जिसमें वह आईओसी के अपने 5 कानूनों की व्याख्या करता है।

निकोला का IoC का 4 वाँ नियम

एक वर्ग के प्रत्येक निर्माणकर्ता को हल करने के लिए अपनी स्वयं की निर्भरता के एक सेट को स्वीकार करने के अलावा कोई अन्य कार्यान्वयन नहीं होना चाहिए।


ILoggerProvider को छोड़कर वे सभी वैध हैं। ILogger और ILogger <T> वे हैं जो आप लॉगिंग के लिए उपयोग करने वाले हैं। ILogger प्राप्त करने के लिए, आप एक ILoggerFactory का उपयोग करते हैं। ILogger <T> एक विशेष श्रेणी के लिए लकड़हारा पाने के लिए एक शॉर्टकट है (श्रेणी के प्रकार के लिए शॉर्टकट)।

जब आप लॉगिंग करने के लिए ILogger का उपयोग करते हैं, तो पंजीकृत ILoggerProviders में से प्रत्येक को उस लॉग संदेश को संभालने का मौका मिलता है। यह वास्तव में ILoggerProvider में सीधे कॉल करने के लिए कोड का उपभोग करने के लिए मान्य नहीं है।


ILogger<T> वास्तविक था जिसे DI के लिए बनाया गया था। ILogger फैक्ट्री पैटर्न को और अधिक आसानी से लागू करने में मदद करने के लिए आया था, इसके बजाय आप अपने सभी डि और फैक्ट्री के तर्क पर लिख रहे थे, जो asp.net कोर में सबसे चतुर निर्णयों में से एक था।

आप इनमें से चुन सकते हैं:

ILogger<T> यदि आपको अपने कोड में कारखाने और DI पैटर्न का उपयोग करने की आवश्यकता है या आप ILogger उपयोग कर सकते हैं, बिना किसी DI की आवश्यकता के सरल लॉगिंग को लागू करने के लिए।

यह देखते हुए कि, ILoggerProvider पंजीकृत लॉग के प्रत्येक संदेश को संभालने के लिए सिर्फ एक पुल है। इसका उपयोग करने की कोई आवश्यकता नहीं है, क्योंकि यह किसी भी चीज को प्रभावित नहीं करता है जिसे आपको कोड में हस्तक्षेप करना चाहिए, यह पंजीकृत ILoggerProvider को सुनता है और संदेशों को संभालता है। यह इसके बारे में।


डिफ़ॉल्ट दृष्टिकोण का मतलब ILogger<T> । इसका मतलब यह है कि लॉग में विशिष्ट वर्ग से लॉग स्पष्ट रूप से दिखाई देंगे क्योंकि वे संदर्भ के रूप में पूर्ण वर्ग का नाम शामिल करेंगे। उदाहरण के लिए यदि आपकी कक्षा का पूरा नाम MyLibrary.MyClass तो आप इस वर्ग द्वारा बनाई गई लॉग प्रविष्टियों में इसे प्राप्त करेंगे। उदाहरण के लिए:

MyLibrary.MyClass: सूचना: मेरी जानकारी लॉग

यदि आप अपना स्वयं का संदर्भ निर्दिष्ट करना चाहते हैं, तो आपको ILoggerFactory उपयोग करना चाहिए। उदाहरण के लिए कि आपकी लाइब्रेरी के सभी लॉग्स में हर वर्ग के बजाय एक ही लॉग संदर्भ है। उदाहरण के लिए:

loggerFactory.CreateLogger("MyLibrary");

और फिर लॉग इस तरह दिखेगा:

MyLibrary: सूचना: मेरी जानकारी लॉग

यदि आप सभी वर्गों में ऐसा करते हैं तो संदर्भ सभी वर्गों के लिए सिर्फ MyLibrary होगा। मुझे लगता है कि आप लाइब्रेरी के लिए ऐसा करना चाहते हैं यदि आप लॉग में आंतरिक वर्ग संरचना को उजागर नहीं करना चाहते हैं।

वैकल्पिक लॉगिंग के बारे में। मुझे लगता है कि आपको हमेशा कंस्ट्रक्टर में ILogger या ILoggerFactory की आवश्यकता होती है और इसे बंद करने के लिए लाइब्रेरी के उपभोक्ता को छोड़ना चाहिए या एक लकड़हारा प्रदान करना चाहिए जो निर्भरता इंजेक्शन में कुछ भी नहीं करता है यदि वे लॉगिंग नहीं चाहते हैं। कॉन्फ़िगरेशन में किसी विशिष्ट संदर्भ के लिए लॉगिंग को चालू करना बहुत आसान है। उदाहरण के लिए:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "MyLibrary": "None"
    }
  }
}




.net-standard