लकड़हारा आवरण सबसे अच्छा अभ्यास


91

मैं अपने आवेदन में एक nlogger का उपयोग करना चाहता हूं, शायद भविष्य में मुझे लॉगिंग सिस्टम को बदलने की आवश्यकता होगी। इसलिए मैं एक लॉगिंग मुखौटा का उपयोग करना चाहता हूं।

क्या आप मौजूदा उदाहरणों के लिए कोई अनुशंसा जानते हैं कि उन लोगों को कैसे लिखना है? या बस मुझे इस क्षेत्र में कुछ सर्वोत्तम अभ्यास का लिंक दें।


4
यह पहले से ही मौजूद है: netcommon.sourceforge.net
आर। मार्टिनो फर्नांडीस


जवाबों:


206

मैं लॉगिंग facades का उपयोग करता था जैसे कि Common.Logging (यहां तक ​​कि अपनी खुद की CutEdge.Logging पुस्तकालय को छिपाने के लिए ), लेकिन आजकल मैं डिपेंडेंसी इंजेक्शन पैटर्न का उपयोग करता हूं और इससे मुझे अपने (सरल) अपवर्तन के पीछे लॉगर्स को छिपाने की अनुमति मिलती है जो दोनों निर्भरता का पालन ​​करता है। उलटा सिद्धांत और इंटरफ़ेस अलगाव सिद्धांत(आईएसपी) क्योंकि इसमें एक सदस्य है और क्योंकि इंटरफ़ेस मेरे अनुप्रयोग द्वारा परिभाषित किया गया है; बाहरी पुस्तकालय नहीं। इस ज्ञान को कम करना कि आपके आवेदन के मुख्य हिस्सों में बाहरी पुस्तकालयों के अस्तित्व के बारे में बेहतर है; यहां तक ​​कि अगर आपके पास अपनी लॉगिंग लाइब्रेरी को बदलने का कोई इरादा नहीं है। बाहरी पुस्तकालय पर कठिन निर्भरता आपके कोड का परीक्षण करना कठिन बना देती है, और आपके एप्लिकेशन को एक ऐसे एपीआई के साथ जोड़ देती है, जिसे कभी आपके आवेदन के लिए विशेष रूप से तैयार नहीं किया गया था।

यह वह अमूर्त है जो अक्सर मेरे अनुप्रयोगों में दिखता है:

public interface ILogger
{
    void Log(LogEntry entry);
}

public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };

// Immutable DTO that contains the log information.
public class LogEntry 
{
    public readonly LoggingEventType Severity;
    public readonly string Message;
    public readonly Exception Exception;

    public LogEntry(LoggingEventType severity, string message, Exception exception = null)
    {
        if (message == null) throw new ArgumentNullException("message");
        if (message == string.Empty) throw new ArgumentException("empty", "message");

        this.Severity = severity;
        this.Message = message;
        this.Exception = exception;
    }
}

वैकल्पिक रूप से, इस अमूर्त को कुछ सरल विस्तार विधियों (इंटरफ़ेस को संकीर्ण रहने और आईएसपी का पालन करने की अनुमति देने) के साथ बढ़ाया जा सकता है। यह इस इंटरफ़ेस के उपभोक्ताओं के लिए कोड को बहुत सरल बनाता है:

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message) {
        logger.Log(new LogEntry(LoggingEventType.Information, message));
    }

    public static void Log(this ILogger logger, Exception exception) {
        logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception));
    }

    // More methods here.
}

चूँकि इंटरफ़ेस में केवल एक विधि है, आप आसानी से एक ILoggerकार्यान्वयन बना सकते हैं जो log4net को , Serilog , Microsoft.Extension.Logging , NLog या किसी अन्य लॉगिंग लाइब्रेरी को प्रॉक्सी करता है और इसे कक्षाओं में रखने के लिए अपने DI कंटेनर को कॉन्फ़िगर करता है:ILogger उनके उनके पास हैं। निर्माता।

ध्यान दें कि एक एकल विधि के साथ एक अंतरफलक के शीर्ष पर स्थिर विस्तार विधियाँ कई सदस्यों के साथ एक अंतरफलक होने से काफी अलग है। विस्तार विधियाँ केवल सहायक विधियाँ हैं जो एक LogEntryसंदेश बनाती हैं और इसे ILoggerइंटरफ़ेस पर एकमात्र विधि के माध्यम से पारित करती हैं । विस्तार विधियां उपभोक्ता के कोड का हिस्सा बन जाती हैं; अमूर्तन का हिस्सा नहीं है। न केवल यह विस्तार विधियों को अमूर्तता, विस्तार के तरीकों और ए को बदलने की आवश्यकता के बिना विकसित करने की अनुमति देता हैLogEntryजब हमेशा उस लकड़हारे को ठूंसा / मजाक किया जाता है, तब भी कंस्ट्रक्टर को निष्पादित किया जाता है। यह टेस्ट सूट में चलने पर लकड़हारे को कॉल की शुद्धता के बारे में अधिक निश्चितता देता है। एक-सदस्यीय इंटरफ़ेस परीक्षण को बहुत आसान बनाता है; कई सदस्यों के साथ अमूर्त होने से कार्यान्वयन (जैसे कि नकली, एडेप्टर और डेकोरेटर) बनाना मुश्किल हो जाता है।

जब आप ऐसा करते हैं, तो शायद ही कभी कुछ स्थिर अमूर्तता की आवश्यकता होती है जो लॉगिंग facades (या किसी अन्य पुस्तकालय) की पेशकश कर सकते हैं।


4
@GabrielEspinoza: यह पूरी तरह से नामस्थान यू पर निर्भर करता है विस्तार विधियों को अंदर रखें। यदि यू इसे उसी नामस्थान में रखें जहां इंटरफ़ेस या रूट प्रोजेक्ट के मूल नाम स्थान में समस्या मौजूद नहीं होगी।
स्टीवन

2
@ user1829319 यह सिर्फ एक उदाहरण है। मुझे यकीन है कि आप इस जवाब के आधार पर कार्यान्वयन के साथ आ सकते हैं जो आपकी विशेष आवश्यकताओं के अनुरूप है।
स्टीवन

2
मैं अभी भी नहीं मिला ... जहां ILogger के extenstions के रूप में 5 लकड़हारा विधियों होने और ILogger के सदस्य नहीं होने का लाभ है?
एलिजाबेथ

3
@ एलिसबेथ लाभ यह है कि आप किसी भी लॉगिंग फ्रेमवर्क के लिए मुखौटा इंटरफ़ेस को अनुकूलित कर सकते हैं, बस एक फ़ंक्शन को लागू करके: "ILogger :: लॉग।" विस्तार विधियां यह सुनिश्चित करती हैं कि हमारे पास 'सुविधा' एपीआई (जैसे कि "लॉगरर," "लॉगवर्निंग," आदि) तक पहुंच हो, भले ही आप किस फ्रेमवर्क का उपयोग करने का निर्णय लें। यह C # इंटरफ़ेस के साथ काम करने के बावजूद आम 'बेस क्लास' कार्यक्षमता को जोड़ने का एक राउंडअबाउट तरीका है।
BTownTKD

2
मुझे एक कारण फिर से जोर देने की आवश्यकता है कि यह महान क्यों है। अपना कोड DotNetFramework से DotNetCore में परिवर्तित करना। जिन परियोजनाओं में मैंने यह किया है, मुझे केवल एक नया ठोस लिखना था। जहाँ मैं नहीं था .... gaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa! मुझे खुशी है कि मुझे यह "रास्ता वापस" मिला।
ग्रेनडाकोडर

10

मैंने https://github.com/uhaciogullari/NLog.Interface से छोटे इंटरफ़ेस आवरण + एडेप्टर का उपयोग किया, जो कि NuGet के माध्यम से भी उपलब्ध है :

PM> Install-Package NLog.Interface 

10
NL4 पुस्तकालय में v4.0 द्वारा ILogger इंटरफ़ेस है। अब आपको इस लाइब्रेरी की आवश्यकता नहीं है
Jowen

8

अब तक, सबसे अच्छा शर्त Microsoft.Extensions.Logging पैकेज ( जूलियन द्वारा बताया गया है ) का उपयोग करना है। इसके साथ अधिकांश लॉगिंग ढांचे का उपयोग किया जा सकता है।

अपने स्वयं के इंटरफ़ेस को परिभाषित करना, जैसा कि स्टीवन के उत्तर में बताया गया है, सरल मामलों के लिए ठीक है, लेकिन इसमें कुछ चीजें याद आती हैं जिन्हें मैं महत्वपूर्ण मानता हूं:

  • संरचित लॉगिंग और डे-स्ट्रक्चरिंग ऑब्जेक्ट (सेरिलोग और एनएलग में @ नोटेशन)
  • विलंबित स्ट्रिंग निर्माण / प्रारूपण: जैसा कि यह एक स्ट्रिंग लेता है, इसे कॉल करने पर सब कुछ का मूल्यांकन / प्रारूप करना पड़ता है, भले ही अंत में घटना लॉग न हो क्योंकि यह थ्रेशोल्ड (प्रदर्शन लागत से नीचे है, पिछले बिंदु देखें)
  • सशर्त जाँच जैसे IsEnabled(LogLevel)कि आप चाहते हैं, प्रदर्शन कारणों के लिए एक बार फिर से

आप शायद अपने खुद के अमूर्त में यह सब लागू कर सकते हैं, लेकिन उस बिंदु पर आप पहिया को फिर से स्थापित करेंगे।


4

आम तौर पर मैं एक इंटरफ़ेस बनाना पसंद करता हूं

public interface ILogger
{
 void LogInformation(string msg);
 void LogError(string error);
}

और रनटाइम में मैं इस इंटरफ़ेस से कार्यान्वित कंक्रीट क्लास को इंजेक्ट करता हूं।


11
और भूल नहीं है LogWarningऔर LogCriticalतरीकों और उनके सभी भार के। ऐसा करते समय आप इंटरफ़ेस अलगाव सिद्धांत का उल्लंघन करेंगे । ILoggerएक एकल Logविधि के साथ इंटरफ़ेस को परिभाषित करना पसंद करें ।
स्टीवन

2
मुझे वास्तव में खेद है, यह मेरा इरादा नहीं था। शर्माने की जरूरत नहीं है। मैं इस डिज़ाइन को वास्तव में अक्सर देखता हूं क्योंकि कई डेवलपर्स लोकप्रिय log4net का उपयोग करते हैं (जो इस सटीक डिज़ाइन का उपयोग करता है) एक उदाहरण के रूप में। दुर्भाग्य से वह डिजाइन वास्तव में अच्छा नहीं है।
स्टीवन

2
मैं @ स्टीवन के जवाब को पसंद करता हूं। वह एक निर्भरता का परिचय देता है LogEntry, और इस तरह एक निर्भरता का LoggingEventTypeILoggerकार्यान्वयन इन के साथ सौदा करना चाहिए LoggingEventTypes, शायद हालांकि case/switch, जो एक है कोड गंधLoggingEventTypesनिर्भरता क्यों छिपाते हैं ? कार्यान्वयन को लॉगिंग स्तरों को वैसे भी संभालना चाहिए , इसलिए यह स्पष्ट करना बेहतर होगा कि एक सामान्य तर्क के साथ एकल विधि के पीछे इसे छिपाने के बजाय कार्यान्वयन क्या करना चाहिए।
धर्मरत्न

1
एक चरम उदाहरण के रूप में, कल्पना कीजिए कि ICommandजिसके पास Handleएक है objectcase/switchइंटरफ़ेस के अनुबंध को पूरा करने के लिए कार्यान्वयन संभव प्रकारों से अधिक होना चाहिए । यह आदर्श नहीं है। एक अमूर्त मत बनो जो एक निर्भरता को छुपाता है जिसे वैसे भी संभाला जाना चाहिए। इसके बजाय एक इंटरफ़ेस है जो स्पष्ट रूप से बताता है कि क्या अपेक्षित है: "मुझे उम्मीद है कि सभी लॉगर चेतावनी, त्रुटियों, फैटल, आदि को संभालने के लिए"। यह "मुझे लगता है कि सभी लकड़हारे संदेशों को संभालने की उम्मीद करते हैं , जिनमें चेतावनी, त्रुटियां, फैटल आदि शामिल हैं।
धर्मरत्न

मैं @Steven और @DharmaTurtle दोनों से सहमत हूं। इसके अलावा LoggingEventTypeबुलाया जाना चाहिए के LoggingEventLevelरूप में वर्ग हैं और OOP में इस तरह के रूप में कोडित किया जाना चाहिए। मेरे लिए इंटरफ़ेस विधि का उपयोग नहीं करने और संबंधित enumमूल्य का उपयोग नहीं करने के बीच कोई अंतर नहीं है । इसके बजाय का उपयोग करें ErrorLoggger : ILogger, InformationLogger : ILoggerजहां हर लकड़हारा परिभाषित करता है यह अपने स्तर है। फिर डीआई को आवश्यक लॉगर को इंजेक्ट करने की आवश्यकता है, शायद एक कुंजी (एनम) के माध्यम से, लेकिन यह कुंजी अब इंटरफ़ेस का हिस्सा नहीं है। (आप अब SOLID हैं)।
राउटर

4

इस समस्या का एक बड़ा समाधान LibLog परियोजना के रूप में सामने आया है ।

LibLog एक लॉगिंग एब्स्ट्रैक्शन है जिसमें सेरिलॉग, NLog, Log4net और एंटरप्राइज लॉगर सहित प्रमुख लॉगर के लिए अंतर्निहित समर्थन है। यह एक .dll संदर्भ के बजाय एक स्रोत (.cs) फ़ाइल के रूप में एक लक्ष्य पुस्तकालय में NuGet पैकेज प्रबंधक के माध्यम से स्थापित किया गया है। यह दृष्टिकोण बाहरी निर्भरता पर पुस्तकालय को मजबूर करने के बिना लॉगिंग अमूर्तता को शामिल करने की अनुमति देता है। यह एक पुस्तकालय लेखक को पुस्तकालय में एक लकड़हारा प्रदान करने के लिए उपभोग्य आवेदन के बिना लॉगिंग को शामिल करने की अनुमति देता है। LibLog इस बात का पता लगाने के लिए प्रतिबिंब का उपयोग करता है कि कौन सा ठोस लकड़हारा उपयोग में है और लाइब्रेरी प्रोजेक्ट (एस) में किसी भी स्पष्ट वायरिंग कोड के बिना इसे हुक अप करें।

तो, LibLog पुस्तकालय परियोजनाओं के भीतर प्रवेश करने के लिए एक महान समाधान है। बस अपने मुख्य अनुप्रयोग या सेवा में एक ठोस लकड़हारा (जीत के लिए सेरिलॉग) को कॉन्फ़िगर करें और अपने पुस्तकालयों में लिब्लॉग जोड़ें!


मैं log4net ब्रेकिंग चेंज इश्यू ( yuck ) ( wiktorzychla.com/2012/03/pathetic-breaking-change-between.html ) को अतीत में लाने के लिए इसका इस्तेमाल कर चुका हूं। अगर आप इसे nuget से प्राप्त करते हैं, तो यह वास्तव में acs फ़ाइल बनाएगा। अपने कोड में precompiled dll के संदर्भ जोड़ने के बजाय। .Cs फ़ाइल को आपकी परियोजना में नाम दिया गया है। इसलिए यदि आपके पास अलग-अलग परतें (csprojs) हैं, तो आपके पास कई संस्करण होंगे, या आपको एक साझा cspjj को समेकित करने की आवश्यकता होगी। जब आप इसका उपयोग करने का प्रयास करेंगे तो आप इसका पता लगा लेंगे। लेकिन जैसा कि मैंने कहा, यह एक लाइफसेवर था जिसमें लॉग 4नेट ब्रेकिंग चेंज मुद्दा था।
ग्रानैडकोडर


2

यदि आप .NET कोर अनुप्रयोगों का निर्माण कर रहे हैं तो 2015 से आप .NET कोर लॉगिंग का भी उपयोग कर सकते हैं ।

NLog को हुक करने के लिए पैकेज है:

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