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


113

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

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

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

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

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

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

जवाबों:


113

परिभाषा

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

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

ILoggerProvider : का एक उदाहरण बनाने के लिए जिम्मेदार है ILogger(आप ILoggerProviderलकड़हारा बनाने के लिए सीधे उपयोग करने वाले नहीं हैं )

ILoggerFactory : आप ILoggerProviderकारखाने के साथ एक या अधिक एस दर्ज कर सकते हैं , जो बदले में इन सभी का उपयोग करके एक उदाहरण बनाता है ILoggerILoggerFactoryका संग्रह रखती है ILoggerProviders

नीचे दिए गए उदाहरण में, हम कारखाने के साथ 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

तो लकड़हारा ही, ILoggers का संग्रह बनाए हुए है , और यह उन सभी को लॉग संदेश लिखता है। लकड़हारा स्रोत कोड को देखते हुए हम पुष्टि कर सकते हैं कि (यानी ) Loggerकी एक सरणी है , और एक ही समय में यह इंटरफ़ेस लागू कर रहा है।ILoggersLoggerInformation[]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>अनावश्यक शोर बढ़ जाता है। देखें सरल इंजेक्टर का : अधिक जानकारी के लिए ब्लॉग ASP.NET कोर डि अमूर्त के साथ क्या है गलत?

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


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

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

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


3
आपका उत्तर और स्टीवन का लेख एक ही समय में सबसे सही और सबसे निराशाजनक है।
बोकिबेग

30

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

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


धन्यवाद। यह मुझे लगता है कि ILoggerFactoryकेवल 1 निर्भरता होने से उपभोक्ताओं के लिए DI को तार करने का एक आसान तरीका के रूप में बहुत अच्छा होगा ( "बस मुझे एक कारखाना दें और मैं अपने खुद के लॉगर बनाऊंगा" ), लेकिन मौजूदा लकड़हारा का उपयोग करने से रोकना होगा ( जब तक उपभोक्ता कुछ रैपर का इस्तेमाल नहीं करता)। एक ILoggerजेनेरिक या नहीं - लेने से उपभोक्ता मुझे एक लकड़हारा दे सकता है जिसे वे विशेष रूप से तैयार करते हैं, लेकिन डि सेटअप को संभवतः अधिक जटिल बनाता है (जब तक कि ओपन जेनरिक का समर्थन करने वाला डीआई कंटेनर का उपयोग नहीं किया जाता है)। क्या वह आवाज सही है? उस मामले में, मुझे लगता है कि मैं कारखाने के साथ जाऊँगा।
माइकल Stum

3
@MichaelStum - मुझे यकीन नहीं है कि मैं यहाँ आपके तर्क का पालन करूँगा। आप अपने उपभोक्ताओं से DI प्रणाली का उपयोग करने की उम्मीद कर रहे हैं, लेकिन फिर अपने पुस्तकालय में निर्भरता को मैन्युअल रूप से प्रबंधित करना चाहते हैं? यह सही दृष्टिकोण क्यों प्रतीत होगा?
डेमियन___बेलिवर्स

@Damien_The_Unbeliever यह एक अच्छी बात है। फैक्ट्री लेना अजीब लगता है। मुझे लगता है कि एक लेने के बजाय ILogger<T>, मैं ILogger<MyClass>सिर्फ एक या यहां तक ​​कि ले ILoggerजाऊंगा - इस तरह, उपयोगकर्ता इसे केवल एक पंजीकरण के साथ तार कर सकता है, और उनके डीआई कंटेनर में खुले जेनरिक की आवश्यकता के बिना। गैर-जेनेरिक की ओर झुकाव ILogger, लेकिन सप्ताहांत में बहुत अधिक प्रयोग करेंगे।
माइकल Stum

10

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

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

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

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


1
ILogger बनाम ILogger <T> का DI से क्या संबंध है? या तो इंजेक्शन लगाया जाएगा, नहीं?
मैथ्यू होस्टलर

यह वास्तव में MS डॉक्स में ILogger <TCategoryName> है। यह ILogger से निकला है और लॉग करने के लिए वर्ग नाम के अलावा कोई नई कार्यक्षमता नहीं जोड़ता है। यह एक अद्वितीय प्रकार भी प्रदान करता है इसलिए DI सही रूप से नामित लकड़हारा को इंजेक्ट करता है। Microsoft एक्सटेंशन गैर-जेनेरिक का विस्तार करते हैं this ILogger
सैमुअल डेनियलसन

8

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

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

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

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


6

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

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

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

loggerFactory.CreateLogger("MyLibrary");

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

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

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

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

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

5

पुस्तकालय डिजाइन के लिए अच्छा दृष्टिकोण होगा:

1. उपभोक्ताओं को अपनी कक्षाओं में लकड़हारा इंजेक्ट करने के लिए मजबूर न करें। बस NullLoggerFactory गुजर एक और ctor बनाएँ।

class MyClass
{
    private readonly ILoggerFactory _loggerFactory;

    public MyClass():this(NullLoggerFactory.Instance)
    {

    }
    public MyClass(ILoggerFactory loggerFactory)
    {
      this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
    }
}

2. जब आप उपभोक्ताओं को आसानी से फ़िल्टरिंग लॉग को कॉन्फ़िगर करने की अनुमति देने के लिए लकड़हारा बनाते हैं तो आपके द्वारा उपयोग की जाने वाली श्रेणियों की संख्या। this._loggerFactory.CreateLogger(Consts.CategoryName)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.