2016-04-25 4 views
10

के लिए अलग लॉगफाइल और निर्देशिका मेरे पास एक विंडोज़ टीसीपी सेवा है, जिसमें कई डिवाइस कनेक्ट हैं, और एक क्लाइंट में एक या अधिक डिवाइस हो सकते हैं।प्रत्येक क्लाइंट और दिनांक

आवश्यकता: प्रत्येक डिवाइस के लिए अलग लॉग फ़ाइल के साथ ग्राहक प्रति

अलग फ़ोल्डर।

तो कुछ इस तरह:

/MyService/25-04-2016/ 
    Client 1/ 
     Device1.txt 
     Device2.txt 
     Device3.txt 

    Client 2/ 
     Device1.txt 
     Device2.txt 
     Device3.txt 

अब मैं log4net या NLog की तरह एक 3 पार्टी पुस्तकालय उपयोग नहीं किया है, मैं एक वर्ग है जो इस संभालती है।

public class xPTLogger : IDisposable 
{ 
    private static object fileLocker = new object(); 

    private readonly string _logFileName; 
    private readonly string _logFilesLocation; 
    private readonly int _clientId; 

    public xPTLogger() : this("General") { } 

    public xPTLogger(string logFileName) 
    { 
     _clientId = -1; 
     _logFileName = logFileName; 
     _logFilesLocation = SharedConstants.LogFilesLocation; // D:/LogFiles/ 
    } 

    public xPTLogger(string logFileName, int companyId) 
    { 
     _clientId = companyId; 
     _logFileName = logFileName; 
     _logFilesLocation = SharedConstants.LogFilesLocation; 
    } 

    public void LogMessage(MessageType messageType, string message) 
    { 
     LogMessage(messageType, message, _logFileName); 
    } 

    public void LogExceptionMessage(string message, Exception innerException, string stackTrace) 
    { 
     var exceptionMessage = innerException != null 
       ? string.Format("Exception: [{0}], Inner: [{1}], Stack Trace: [{2}]", message, innerException.Message, stackTrace) 
       : string.Format("Exception: [{0}], Stack Trace: [{1}]", message, stackTrace); 

     LogMessage(MessageType.Error, exceptionMessage, "Exceptions"); 
    } 

    public void LogMessage(MessageType messageType, string message, string logFileName) 
    { 
     var dateTime = DateTime.UtcNow.ToString("dd-MMM-yyyy"); 

     var logFilesLocation = string.Format("{0}{1}\\", _logFilesLocation, dateTime); 

     if (_clientId > -1) { logFilesLocation = string.Format("{0}{1}\\{2}\\", _logFilesLocation, dateTime, _clientId); } 


     var fullLogFile = string.IsNullOrEmpty(logFileName) ? "GeneralLog.txt" : string.Format("{0}.txt", logFileName); 


     var msg = string.Format("{0} | {1} | {2}\r\n", DateTime.UtcNow.ToString("dd-MMM-yyyy HH:mm:ss"), messageType, message); 

     fullLogFile = GenerateLogFilePath(logFilesLocation, fullLogFile); 

     LogToFile(fullLogFile, msg); 
    } 

    private string GenerateLogFilePath(string objectLogDirectory, string objectLogFileName) 
    { 
     if (string.IsNullOrEmpty(objectLogDirectory)) 
      throw new ArgumentNullException(string.Format("{0} location cannot be null or empty", "objectLogDirectory")); 
     if (string.IsNullOrEmpty(objectLogFileName)) 
      throw new ArgumentNullException(string.Format("{0} cannot be null or empty", "objectLogFileName")); 

     if (!Directory.Exists(objectLogDirectory)) 
      Directory.CreateDirectory(objectLogDirectory); 
     string logFilePath = string.Format("{0}\\{1}", objectLogDirectory, objectLogFileName); 
     return logFilePath; 
    } 

    private void LogToFile(string logFilePath, string message) 
    { 
     if (!File.Exists(logFilePath)) 
     { 
      File.WriteAllText(logFilePath, message); 
     } 
     else 
     { 
      lock (fileLocker) 
      { 
       File.AppendAllText(logFilePath, message); 
      } 
     } 
    } 

    public void Dispose() 
    { 
     fileLocker = new object(); 
    } 
} 

और फिर मैं इसे इस तरह का उपयोग कर सकते हैं:

var _logger = new xPTLogger("DeviceId", 12); 

_logger.LogMessage(MessageType.Info, string.Format("Information Message = [{0}]", 1)); 

ऊपर वर्ग के साथ समस्या यह है कि, क्योंकि सेवा मल्टी-थ्रेडेड है, कुछ धागे में एक ही लॉग फ़ाइल तक पहुँचने का प्रयास है एक ही समय में अपवाद को फेंकने का कारण बनता है।

25-Apr-2016 13:07:00 | Error | Exception: The process cannot access the file 'D:\LogFiles\25-Apr-2016\0\LogFile.txt' because it is being used by another process. 

जो कभी-कभी मेरी सेवा को क्रैश करने का कारण बनता है।

मैं अपने लॉगर वर्ग को बहु-थ्रेडेड सेवाओं में कैसे काम कर सकता हूं? लॉगर कक्षा

public class xPTLogger : IDisposable 
{ 
    private object fileLocker = new object(); 

    private readonly string _logFileName; 
    private readonly string _logFilesLocation; 
    private readonly int _companyId; 

    public xPTLogger() : this("General") { } 

    public xPTLogger(string logFileName) 
    { 
     _companyId = -1; 
     _logFileName = logFileName; 
     _logFilesLocation = SharedConstants.LogFilesLocation; // "D:\\MyLogs"; 
    } 

    public xPTLogger(string logFileName, int companyId) 
    { 
     _companyId = companyId; 
     _logFileName = logFileName; 
     _logFilesLocation = SharedConstants.LogFilesLocation; 
    } 

    public void LogMessage(MessageType messageType, string message) 
    { 
     LogMessage(messageType, message, _logFileName); 
    } 

    public void LogExceptionMessage(string message, Exception innerException, string stackTrace) 
    { 
     var exceptionMessage = innerException != null 
       ? string.Format("Exception: [{0}], Inner: [{1}], Stack Trace: [{2}]", message, innerException.Message, stackTrace) 
       : string.Format("Exception: [{0}], Stack Trace: [{1}]", message, stackTrace); 

     LogMessage(MessageType.Error, exceptionMessage, "Exceptions"); 
    } 

    public void LogMessage(MessageType messageType, string message, string logFileName) 
    { 
     if (messageType == MessageType.Debug) 
     { 
      if (!SharedConstants.EnableDebugLog) 
       return; 
     } 

     var dateTime = DateTime.UtcNow.ToString("dd-MMM-yyyy"); 

     var logFilesLocation = string.Format("{0}{1}\\", _logFilesLocation, dateTime); 

     if (_companyId > -1) { logFilesLocation = string.Format("{0}{1}\\{2}\\", _logFilesLocation, dateTime, _companyId); } 


     var fullLogFile = string.IsNullOrEmpty(logFileName) ? "GeneralLog.txt" : string.Format("{0}.txt", logFileName); 


     var msg = string.Format("{0} | {1} | {2}\r\n", DateTime.UtcNow.ToString("dd-MMM-yyyy HH:mm:ss"), messageType, message); 

     fullLogFile = GenerateLogFilePath(logFilesLocation, fullLogFile); 

     LogToFile(fullLogFile, msg); 
    } 

    private string GenerateLogFilePath(string objectLogDirectory, string objectLogFileName) 
    { 
     if (string.IsNullOrEmpty(objectLogDirectory)) 
      throw new ArgumentNullException(string.Format("{0} location cannot be null or empty", "objectLogDirectory")); 
     if (string.IsNullOrEmpty(objectLogFileName)) 
      throw new ArgumentNullException(string.Format("{0} cannot be null or empty", "objectLogFileName")); 

     if (!Directory.Exists(objectLogDirectory)) 
      Directory.CreateDirectory(objectLogDirectory); 
     string logFilePath = string.Format("{0}\\{1}", objectLogDirectory, objectLogFileName); 
     return logFilePath; 
    } 

    private void LogToFile(string logFilePath, string message) 
    { 
     lock (fileLocker) 
     { 
      try 
      { 
       if (!File.Exists(logFilePath)) 
       { 
        File.WriteAllText(logFilePath, message); 
       } 
       else 
       { 
        File.AppendAllText(logFilePath, message); 
       } 
      } 
      catch (Exception ex) 
      { 
       var exceptionMessage = ex.InnerException != null 
           ? string.Format("Exception: [{0}], Inner: [{1}], Stack Trace: [{2}]", ex.Message, ex.InnerException.Message, ex.StackTrace) 
           : string.Format("Exception: [{0}], Stack Trace: [{1}]", ex.Message, ex.StackTrace); 

       var logFilesLocation = string.Format("{0}{1}\\", _logFilesLocation, DateTime.UtcNow.ToString("dd-MMM-yyyy")); 

       var logFile = GenerateLogFilePath(logFilesLocation, "FileAccessExceptions.txt"); 

       try 
       { 
        if (!File.Exists(logFile)) 
        { 
         File.WriteAllText(logFile, exceptionMessage); 
        } 
        else 
        { 
         File.AppendAllText(logFile, exceptionMessage); 
        } 
       } 
       catch (Exception) { } 
      } 

     } 
    } 

    public void Dispose() 
    { 
     //fileLocker = new object(); 
     //_logFileName = null; 
     //_logFilesLocation = null; 
     //_companyId = null; 
    } 
} 
+0

'एनएलओजी' थ्रेड सुरक्षित है। आप इसे बहुप्रचारित सेवाओं के लिए उपयोग कर सकते हैं। [यह उत्तर] देखें (http://stackoverflow.com/a/5706633/579895) – Pikoh

+0

क्या आपने LogToFile विधि की संपूर्ण सामग्री को लपेटने के लिए 'लॉक (fileLocker)' लॉक को स्थानांतरित करने का प्रयास किया है? अर्थात। WriteAllText और AppendAllText को दो अलग-अलग धागे से उसी फ़ाइल पर कॉल करने के लिए रोकने के लिए। – user469104

+0

@ user469104 नहीं, मैं इसे –

उत्तर

5

यदि आप मौजूदा समाधानों का उपयोग नहीं करना चाहते हैं, तो आपके लॉगर में मल्टीथ्रेडेड लिखने के लिए उचित दृष्टिकोण कतार का उपयोग करना है।

public class LogQueue : IDisposable { 
    private static readonly Lazy<LogQueue> _isntance = new Lazy<LogQueue>(CreateInstance, true); 
    private Thread _thread; 
    private readonly BlockingCollection<LogItem> _queue = new BlockingCollection<LogItem>(new ConcurrentQueue<LogItem>()); 

    private static LogQueue CreateInstance() { 
     var queue = new LogQueue(); 
     queue.Start(); 
     return queue; 
    } 

    public static LogQueue Instance => _isntance.Value; 

    public void QueueItem(LogItem item) { 
     _queue.Add(item); 
    } 

    public void Dispose() { 
     _queue.CompleteAdding(); 
     // wait here until all pending messages are written 
     _thread.Join(); 
    } 

    private void Start() { 
     _thread = new Thread(ConsumeQueue) { 
      IsBackground = true 
     }; 
     _thread.Start(); 
    } 

    private void ConsumeQueue() { 
     foreach (var item in _queue.GetConsumingEnumerable()) { 
      try { 
       // append to your item.TargetFile here      
      } 
      catch (Exception ex) { 
       // do something or ignore 
      } 
     } 
    } 
} 

public class LogItem { 
    public string TargetFile { get; set; } 
    public string Message { get; set; } 
    public MessageType MessageType { get; set; } 
} 
फिर अपने लकड़हारा वर्ग में

:

private void LogToFile(string logFilePath, string message) { 
    LogQueue.Instance.QueueItem(new LogItem() { 
     TargetFile = logFilePath, 
     Message = message 
    }); 
} 

यहाँ हम अलग वर्ग है जो संदेशों एक के बाद एक लोग इन लिखते हैं करने के लिए वास्तविक प्रवेश प्रतिनिधि, इसलिए किसी भी बहु सूत्रण मुद्दों नहीं हो सकता है यहाँ एक संक्षिप्त वर्णन है। इस तरह के दृष्टिकोण का अतिरिक्त लाभ यह है कि लॉगिंग असीमित रूप से होती है और इस तरह वास्तविक कार्य को धीमा नहीं करता है।

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

+0

की तलाश में हूं, मैंने कुछ दिनों पहले इस विधि को लागू किया था - और यह अब उत्पादन में है। प्रदर्शन के संबंध में यह अच्छा है - सभी लॉगिंग करने के लिए एक कतार/थ्रेड होने पर ठीक काम कर रहा है, अब कोई फ़ाइल एक्सेस अपवाद नहीं है - धन्यवाद –

3

को

संपादित

परिवर्तन हालांकि यह शायद सबसे सुरुचिपूर्ण समाधान नहीं है, आप फिर से प्रयास करें तर्क में बनाया गया हो सकता था उदाहरण के लिए:।

int retries = 0; 
while(retries <= 3){ 
    try{ 
     var _logger = new xPTLogger("DeviceId", 12); 

     _logger.LogMessage(MessageType.Info, string.Format("Information Message = [{0}]", 1)); 
     break; 
    } 
    catch (Exception ex){ 
     //Console.WriteLine(ex.Message); 
     retries++; 
    } 
} 

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

मुझे लॉग 4नेट या एनएलओजी के साथ कोई अनुभव नहीं है, इसलिए वहां कोई टिप्पणी नहीं है। शायद उन पैकेजों में से एक के माध्यम से एक मीठा समाधान है। सौभाग्य!

+0

ओह भी, कोड को कम करने के लिए लॉगमेसेज विधि में उस रेट्री ब्लॉक को रखना संभवतः एक बेहतर विचार होगा। – bseyeph

+0

मैं अपने प्रश्न में उल्लेख करना भूल गया कि कभी-कभी सेवा मेरे xPTLogger का उपयोग करके क्रैश हो जाती है, इसलिए मैं वैकल्पिक –

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