2009-04-28 16 views
8

मुझे एक लॉगिंग क्लास मिला है जिसके लिए आवेदन में लगभग हर जगह से कॉल करने की आवश्यकता है।सिंगलटन, लॉगिंग और वैश्विक सेटिंग्स - अच्छा या बुरा कार्यान्वयन?

हालांकि इसके साथ आवेदन की शुरुआत में सेटअप करना आवश्यक है "जो पथ लिखने के लिए", "स्तर लॉग ऑन" और अगर यह "सक्षम" है या नहीं।

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

हाल ही में मैं कसकर युग्मित कक्षाएं मैं वही गलती फिर से करने के लिए नहीं करना चाहते से बहुत कुछ सहा है, लेकिन इस बारे में सोच के बाद लगता है कि यह केवल अच्छा समाधान है।

अद्यतन:

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

आप इस कार्यान्वयन के बारे में क्या सोचते हैं और तुम क्या करते हो जब आप समान डिजाइन फैसले के पार चलो?

पीएस कृपया "लॉग 4 एक्स लाइब्रेरी का उपयोग करें" जैसे कुछ सुझाव न दें

+0

विषय पर कम लेख: http://misko.hevery.com/2008/10/21/dependency-injection-myth-reference-passing/ – User

उत्तर

4

पहला - क्या आप लॉग लेखक को ट्रेस श्रोता के रूप में लिख सकते हैं, और Trace.Write आदि का उपयोग विधियों से कर सकते हैं?

आप वास्तव में यहाँ एक उदाहरण की जरूरत है? यही कारण है, उपयोगी होगा उदाहरण के लिए, यदि आप एक TextWriter या इसी तरह के रूप में सार को यह चाहते थे - लेकिन अगर यह एक स्टैंडअलोन सिंगलटन होने जा रहा है, तरीकों स्थिर तरीकों का उपयोग नहीं कर सकते हैं सीधे, यानी Log.Write(...) (बजाय एक लॉग उदाहरण में गुजर)?

पुन सामान्य समस्या है - यह प्रकार है कि प्रवेश कर रहे हैं पर निर्भर करता है। "प्रबंधक" (आदि) कक्षाओं के लिए, आप इसे स्वचालित करने के लिए निर्भरता इंजेक्शन (एकता, संरचना मैप, आदि) का उपयोग करने पर विचार कर सकते हैं। हालांकि, मैं आम तौर पर डीटीओ के साथ इंजेक्शन का उपयोग नहीं करता।

+0

बेशक। एक और। –

+0

डीओएच! तुम मुझसे जल्दी हो! –

+0

मुझे लॉग लिखना है और लॉग सक्षम है या नहीं, यह निर्धारित करने की आवश्यकता है। तो मुझे इन विकल्पों को सेट करने के लिए एक उदाहरण की आवश्यकता है। मुझे लगता है कि डी मेरी मदद कर सकता है मैं इसे देख लूंगा। –

3

करें, फिर भी आप नहीं चाहते "Log4X का उपयोग करें" का सुझाव (यद्यपि आप वास्तव में कहना नहीं आता कि क्यों आप पहिया बदलने के लिए चाहते हैं) यह डिजाइन विभिन्न प्रवेश पुस्तकालयों द्वारा किए गए फैसले को देखने के लिए समझदार प्रतीत होता है ।

मेरे अनुभव में लॉगिंग पर लागू होने पर तंग युग्मन की समस्याएं प्रासंगिक नहीं हैं - विशेष रूप से, मैं शायद ही कभी अपने ऐप के लॉगिंग पक्ष का परीक्षण करना चाहता हूं, और मुझे कोई फर्क नहीं पड़ता कि यह कंसोल पर लॉग इन करता है इकाई का परीक्षण।

संक्षेप में, "सामान्य" के पैटर्न:

private static readonly Logger log = LogManager.GetLogger(...); 

(उपयुक्त नाम परिवर्तन आदि के साथ) स्थिर तरीकों के अपने प्रयोग में सौंदर्य की दृष्टि से अनाकर्षक है, लेकिन व्यवहार में बहुत अच्छी तरह से काम करता है। कम से कम, यह मेरा अनुभव रहा है।

+0

@ जोन विचार समान समस्याओं के लिए समाधान की सामान्य समझ प्राप्त करने वाले पहिया को पुन: पेश नहीं कर रहा है। लॉगिंग एक स्पष्ट समस्या है, इसलिए मैंने सोचा कि यह समझना अच्छा होगा कि इन समस्याओं से कैसे निपटें। स्थिर उपयोग के लिए धन्यवाद यह सिंगलटन से बेहतर दिखता है। –

+1

समस्या यह है कि मेरे अनुभव में लॉगिंग के समान कई समस्याएं नहीं हैं। लगभग कहीं और, मैं DI का उपयोग करना चाहता हूं - लेकिन लॉगिंग के रूप में कोड के शेष व्यवहार को प्रभावित नहीं करना चाहिए, यह आसान दृष्टिकोण लेने के लिए अपेक्षाकृत हानिकारक है। –

+1

क्या आपका मतलब सौंदर्यशास्त्र से परे मूल्य है? विधर्म! – CurtainDog

0

इस ASP.Net है? यदि ऐसा है, तो आप Global.asax में त्रुटि ईवेंट का उपयोग कर सकते हैं।

अपने कई निर्भरता के लिए, आप एक निर्भरता इंजेक्शन ढांचे का उपयोग कर विचार किया है?

अद्यतन

मुझे यकीन है कि प्रदर्शन निहितार्थ है और न ही कितना प्रासंगिक प्रदर्शन अपने अनुप्रयोग के लिए है नहीं कर रहा हूँ, लेकिन इस ढांचे दिलचस्प लग रहा है: PostSharp, A blog post about it

आप भी Conditional attribute का लाभ उठाने में सक्षम हो सकते हैं।

यदि आप पोस्टशर्प का उपयोग करते हैं, तो मुझे दिलचस्पी होगी कि यह कैसे काम करता है।

+0

यह विनफॉर्म है, मैं कार्यान्वयन ओवरहेड के कारण डीआई शुरू नहीं करना चाहता था लेकिन शायद मुझे चाहिए। –

2

आप शायद यहां एक सिंगलटन का उपयोग कर सकते हैं। आपके पास एप्लिकेशन और लॉगर क्लास में प्रत्येक वर्ग के बीच एक कड़ी युग्मन होगी लेकिन यदि प्रत्येक वर्ग में लॉगर क्लास और वैश्विक सेटिंग्स वर्ग की वास्तव में आवश्यकता होती है तो यह स्वीकार्य हो सकता है।

0

लॉगिंग और सेटिंग्स वास्तव में दो अलग-अलग तरीकों से संभाली जाती हैं, इसलिए यदि मैं सही ढंग से समझ गया, तो आपका वास्तविक प्रश्न असेंबली के बीच वैश्विक सेटिंग्स को संभालने से संबंधित था।

लॉगिंग के संबंध में, चीजें बहुत स्पष्ट हैं - इसके लिए एक वैश्विक सिंगलटन का उपयोग करना आम है, हालांकि यह आपके लाइब्रेरी को लॉग लाइब्रेरी में कसकर जोड़ता है। ट्रेस श्रोताओं का उपयोग करना आईएमएचओ का एक बेहतर समाधान है।

लेकिन जब आवेदन सेटिंग्स के बारे में बात कर रहे हैं, तो आप निश्चित रूप से से बचना चाहिए उन्हें विश्व बना रही है। सभी एप्लिकेशन संबंधित सेटिंग्स को केवल एक ही स्थान पर स्थित रखें (जिन्हें जारी रखा जाना चाहिए), लेकिन अन्य पुस्तकालयों के लिए स्थिर रूप से उपलब्ध नहीं है। इसलिए, अन्य असेंबली के लिए उचित सेटिंग्स को पास करना कॉलर की ज़िम्मेदारी होगी, इसके विपरीत नहीं।

+0

जब यह कॉलर की ज़िम्मेदारी बन जाती है और इसे 30 अन्य वर्गों में पास किया जाता है तो वह ओवरकिलिंग की तरह नहीं है? मेरा मतलब है कि कोड में किसी भी अच्छे कारण के लिए बहुत सारे पैरामीटर शामिल नहीं होंगे और लगभग कोई अतिरिक्त लाभ नहीं होगा। - यह एक बयान से अधिक प्रश्न है :) - –

+0

यदि आप अपने पुस्तकालयों का पुन: उपयोग करने की योजना बना रहे हैं तो अन्य असेंबली में स्थिर वस्तुओं के संदर्भ होने के लिए यह बहुत अच्छा नहीं है। यदि प्रत्येक वर्ग को एक ही सेटिंग कोड (या इसका एक बड़ा हिस्सा) की आवश्यकता होती है, तो इससे संकेत मिलता है कि कोड स्वयं ही कसकर पहले ही जोड़ रहा है। – Groo

1

मैं व्यक्तिगत रूप से इस तरह के मामले में स्थिर वर्ग का उपयोग करता हूं। कक्षा में स्थिर कॉन्फ़िगरेशन फ़ील्ड (मैन्युअल प्रयोग के लिए) हैं और कुछ फ़ंक्शन को उचित .config फ़ाइल से कॉन्फ़िगरेशन का उपयोग करके पॉप्युलेट करने के लिए कुछ फ़ंक्शन हैं।

यह प्रभावशाली है कि आप DI के साथ क्या करेंगे, क्योंकि आप नई कॉन्फ़िगरेशन "इंजेक्ट" कर सकते हैं। नए मॉडल में कॉन्फ़िगरेशन बदलने के लिए, मैं बस .config फ़ाइल फ़ील्ड में बदलता हूं जो "सक्रिय" कॉन्फ़िगरेशन अनुभाग रखता है।

यह इस्तेमाल करने में आसान, बनाए रखने के लिए आसान है, और हर कोई यह समझता है ... मैं इसके बारे में किसी विशेष दोष नहीं दिख रहा है ...

+0

+1 स्पॉट पर। वास्तव में मैं क्या करता हूं। – Finglas

+0

क्या इससे अन्य परियोजनाओं में आपके बनाए गए पुस्तकालयों का फिर से उपयोग करना मुश्किल नहीं होता है क्योंकि सभी परियोजनाओं को एक ही इंटरफेस के साथ एक स्थिर वर्ग जोड़ा जाना चाहिए? – danio

+0

जरूरी नहीं है। आप कन्स्ट्रक्टर में पुन: प्रयोज्य मॉड्यूल के लिए कॉन्फ़िगरेशन मान पास कर सकते हैं। – majkinetor

0

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

0

कुछ इसी तरह की:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using log4net; 
using log4net.Config; 
using log4net.Appender; 
using System.Reflection; 
using System.IO; 
using System.Globalization; 
using log4net.Core; 
using System.Web; 

namespace GenApp.Utils 
{ 
    ///<summary> Wrapper around log4net with dynamically adjustable verbosity</summary> 
    public class Logger 
    { 

    private static Logger inst = new Logger(); 
    public static Logger Inst() 
    { 
     inst.ConfigureLogging(); 
     return inst; 
    } 


    public enum DebugLevel : int 
    { 
     Fatal_Msgs = 0, 
     Fatal_Error_Msgs = 1, 
     Fatal_Error_Warn_Msgs = 2, 
     Fatal_Error_Warn_Info_Msgs = 3, 
     Fatal_Error_Warn_Info_Debug_Msgs = 4 
    } 

    public static void Debug (GenApp.Bo.User objUser, ILog logger, string msg) 
    { 
     DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel; 
     string strLogLevel = Logger.GetLogTypeString (debugLevel); 
     inst.SetLogingLevel (strLogLevel); 
     logger.Debug (msg); 

    } //eof method 


    public static void Info (GenApp.Bo.User objUser, ILog logger, string msg) 
    { 
     DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel; 
     string strLogLevel = Logger.GetLogTypeString (debugLevel); 
     inst.SetLogingLevel (strLogLevel); 
     logger.Info (msg); 

    } //eof method 


    public static void Warn (GenApp.Bo.User objUser, ILog logger, string msg) 
    { 
     DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel; 
     string strLogLevel = Logger.GetLogTypeString (debugLevel); 
     inst.SetLogingLevel (strLogLevel); 
     logger.Warn (msg); 

    } //eof method 


    public static void Error (GenApp.Bo.User objUser, ILog logger, string msg) 
    { 
     DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel; 
     string strLogLevel = Logger.GetLogTypeString (debugLevel); 
     inst.SetLogingLevel (strLogLevel); 
     logger.Error (msg); 
    } //eof method 


    public static void Fatal (GenApp.Bo.User objUser, ILog logger, string msg) 
    { 
     DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel; 
     string strLogLevel = Logger.GetLogTypeString (debugLevel); 
     inst.SetLogingLevel (strLogLevel); 
     logger.Fatal (msg); 
    } //eof method 


    /// <summary> 
    /// Activates debug level 
    /// </summary> 
    /// <sourceurl>http://geekswithblogs.net/rakker/archive/2007/08/22/114900.aspx</sourceurl> 
    private void SetLogingLevel (string strLogLevel) 
    { 

     this.ConfigureLogging(); 
     string strChecker = "WARN_INFO_DEBUG_ERROR_FATAL"; 

     if (String.IsNullOrEmpty (strLogLevel) == true || strChecker.Contains (strLogLevel) == false) 
     throw new ArgumentOutOfRangeException (" The strLogLevel should be set to WARN , INFO , DEBUG ,"); 



     log4net.Repository.ILoggerRepository[] repositories = log4net.LogManager.GetAllRepositories(); 

     //Configure all loggers to be at the debug level. 
     foreach (log4net.Repository.ILoggerRepository repository in repositories) 
     { 
     repository.Threshold = repository.LevelMap[strLogLevel]; 
     log4net.Repository.Hierarchy.Hierarchy hier = (log4net.Repository.Hierarchy.Hierarchy)repository; 
     log4net.Core.ILogger[] loggers = hier.GetCurrentLoggers(); 
     foreach (log4net.Core.ILogger logger in loggers) 
     { 
      ((log4net.Repository.Hierarchy.Logger)logger).Level = hier.LevelMap[strLogLevel]; 
     } 
     } 

     //Configure the root logger. 
     log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository(); 
     log4net.Repository.Hierarchy.Logger rootLogger = h.Root; 
     rootLogger.Level = h.LevelMap[strLogLevel]; 
    } 

    ///<summary> 
    ///0 -- prints only FATAL messages 
    ///1 -- prints FATAL and ERROR messages 
    ///2 -- prints FATAL , ERROR and WARN messages 
    ///3 -- prints FATAL , ERROR , WARN and INFO messages 
    ///4 -- prints FATAL , ERROR , WARN , INFO and DEBUG messages 
    ///</summary> 
    private static string GetLogTypeString (DebugLevel debugLevel) 
    { 

     string srtLogLevel = String.Empty; 
     switch (debugLevel) 
     { 
     case DebugLevel.Fatal_Msgs: 
      srtLogLevel = "FATAL"; 
      break; 
     case DebugLevel.Fatal_Error_Msgs: 
      srtLogLevel = "ERROR"; 
      break; 
     case DebugLevel.Fatal_Error_Warn_Msgs: 
      srtLogLevel = "WARN"; 
      break; 
     case DebugLevel.Fatal_Error_Warn_Info_Msgs: 
      srtLogLevel = "INFO"; 
      break; 
     case DebugLevel.Fatal_Error_Warn_Info_Debug_Msgs: 
      srtLogLevel = "DEBUG"; 
      break; 
     default: 
      srtLogLevel = "FATAL"; 
      break; 
     } //eof switch 
     return srtLogLevel; 

    } //eof GetLogTypeString 


    /// <summary> 
    /// The path where the configuration is read from. 
    /// This value is set upon a call to ConfigureLogging(). 
    /// </summary> 
    private string configurationFilePath; 
    public void ConfigureLogging() 
    { 
     lock (this) 
     { 
     bool configured = false; 


     #region ConfigureByThePathOfTheEntryAssembly 
     // Tells the logging system the correct path. 
     Assembly a = Assembly.GetEntryAssembly(); 

     if (a != null && a.Location != null) 
     { 
      string path = a.Location + ".config"; 

      if (File.Exists (path)) 
      { 
      log4net.Config.DOMConfigurator.Configure (
       new FileInfo (path)); 
      configurationFilePath = path; 
      configured = true; 
      } 
      else 
      { 
      path = FindConfigInPath (Path.GetDirectoryName (a.Location)); 
      if (File.Exists (path)) 
      { 
       log4net.Config.DOMConfigurator.Configure (
       new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
      } 
      } 
     } 
     #endregion ConfigureByThePathOfTheEntryAssembly 


     #region ConfigureByWeb.config 
     // Also, try web.config. 
     if (!configured) 
     { 
      if (HttpContext.Current != null && 
      HttpContext.Current.Server != null && 
      HttpContext.Current.Request != null) 
      { 
      string path = HttpContext.Current.Server.MapPath (
       HttpContext.Current.Request.ApplicationPath); 

      path = path.TrimEnd ('\\') + "\\Web.config"; 

      if (File.Exists (path)) 
      { 
       log4net.Config.DOMConfigurator.Configure (
       new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
      } 
      } 
     } 
     #endregion ConfigureByWeb.config 


     #region ConfigureByThePathOfTheExecutingAssembly 
     if (!configured) 
     { 
      // Tells the logging system the correct path. 
      a = Assembly.GetExecutingAssembly(); 

      if (a != null && a.Location != null) 
      { 
      string path = a.Location + ".config"; 

      if (File.Exists (path)) 
      { 
       log4net.Config.DOMConfigurator.Configure (
       new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
      } 
      else 
      { 
       path = FindConfigInPath (Path.GetDirectoryName (a.Location)); 
       if (File.Exists (path)) 
       { 
       log4net.Config.DOMConfigurator.Configure (
        new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
       } 
      } 
      } 
     } 
     #endregion ConfigureByThePathOfTheExecutingAssembly 


     #region ConfigureByThePathOfTheCallingAssembly 
     if (!configured) 
     { 
      // Tells the logging system the correct path. 
      a = Assembly.GetCallingAssembly(); 

      if (a != null && a.Location != null) 
      { 
      string path = a.Location + ".config"; 

      if (File.Exists (path)) 
      { 
       log4net.Config.DOMConfigurator.Configure (
       new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
      } 
      else 
      { 
       path = FindConfigInPath (Path.GetDirectoryName (a.Location)); 
       if (File.Exists (path)) 
       { 
       log4net.Config.DOMConfigurator.Configure (
        new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
       } 
      } 
      } 
     } 
     #endregion ConfigureByThePathOfTheCallingAssembly 


     #region ConfigureByThePathOfTheLibIsStored 
     if (!configured) 
     { 
      // Look in the path where this library is stored. 
      a = Assembly.GetAssembly (typeof (Logger)); 

      if (a != null && a.Location != null) 
      { 
      string path = FindConfigInPath (Path.GetDirectoryName (a.Location)); 
      if (File.Exists (path)) 
      { 
       log4net.Config.DOMConfigurator.Configure (
       new FileInfo (path)); 
       configurationFilePath = path; 
       configured = true; 
      } 
      } 
     } 
     #endregion ConfigureByThePathOfTheLibIsStored 



     } //eof lock 
    } //eof method 



    /// <summary> 
    /// Searches for a configuration file in the given path. 
    /// </summary> 
    private string FindConfigInPath (
     string path) 
    { 
     string[] files = Directory.GetFiles (path); 

     if (files != null && files.Length > 0) 
     { 
     foreach (string file in files) 
     { 
      if (Path.GetExtension (file).Trim ('.').ToLower (
      CultureInfo.CurrentCulture) == "config") 
      { 
      return file; 
      } 
     } 
     } 

     // Not found. 
     return string.Empty; 
    } //eof method 



    /// <summary> 
    /// Remove dynamically appenders 
    /// </summary> 
    /// <param name="appenderName"></param> 
    /// <param name="threshold"></param> 
    public static void SetThreshold (string appenderName, Level threshold) 
    { 
     foreach (AppenderSkeleton appender in LogManager.GetRepository().GetAppenders()) 
     { 
     if (appender.Name == appenderName) 
     { 
      appender.Threshold = threshold; 
      appender.ActivateOptions(); 

      break; 
     } 
     } 
    } //eof method 



    } //eof class 


} //eof namespace 
0

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

लेकिन यदि आप फ़ाइल या ईवेंट लॉग में विभिन्न स्रोतों पर जानकारी लॉग करते हैं तो विभिन्न कॉन्फ़िगरेशन के लिए लॉगिंग क्लास के विभिन्न उदाहरण बनाएं।

+1

यह एक सिंगलटन को औचित्य क्यों देता है? –

संबंधित मुद्दे