2012-09-06 114 views
2

環境:任何.Net Framework歡迎。 我有一個日誌文件被寫入24/7。附加和讀取文本文件

我想創建一個應用程序,它將讀取日誌文件並處理數據。

什麼是有效地讀取日誌文件的最佳方式?我想象用FileSystemWatcher之類的東西來監視文件。但是,如何確保我的應用程序處理完成後不會讀取相同的數據?或者說應用程序由於某種未知的原因而中止,它將如何從最後一次停止的地方恢復?

通常在日誌文件中有效負載的周圍有一個頁眉和頁腳。也可能是內容中的ID字段。還不確定關於ID字段在那裏。

我也想到了可能在某處保存行讀取計數,可能會使用它作爲書籤。

回答

1

那麼,你將不得不爲自己的具體情況找出你的魔法。如果你打算使用衆所周知的文本編碼,它可能是非常簡單的thoght。看向System.IO.StreamReader,它是ReadLine(),DiscardBufferedData()方法和BaseStream屬性。您應該能夠記住您在文件中的最後位置,並稍後回退到該位置,並重新開始閱讀,因爲您確定只附加了該文件。還有其他的事情需要考慮,並且沒有單一的普遍答案。

就像一個簡單的例子(您可能仍然需要調整很多,使其工作):

static void Main(string[] args) 
    { 
     string filePath = @"c:\log.txt"; 
     using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
     { 
      using (var streamReader = new StreamReader(stream,Encoding.Unicode)) 
      { 
       long pos = 0; 
       if (File.Exists(@"c:\log.txt.lastposition")) 
       { 
        string strPos = File.ReadAllText(@"c:\log.txt.lastposition"); 
        pos = Convert.ToInt64(strPos); 
       } 
       streamReader.BaseStream.Seek(pos, SeekOrigin.Begin); // rewind to last set position. 
       streamReader.DiscardBufferedData(); // clearing buffer 
       for(;;) 
       { 
        string line = streamReader.ReadLine(); 
        if(line==null) break; 

        ProcessLine(line); 
       } 
       // pretty sure when everything is read position is at the end of file. 
       File.WriteAllText(@"c:\log.txt.lastposition",streamReader.BaseStream.Position.ToString()); 
      } 
     } 
    } 
+0

「您應該能夠記住您在文件中的最後位置,並稍後回退到該位置並重新開始閱讀」有什麼建議嗎?保存最後一行是否是一個好主意?什麼倒退到一個位置看起來像在代碼中? – Rod

+0

我的意思是以某種方式保存文件中的位置,即像streamReader.BaseStream.Position這樣的值,對於長度爲150個字符的1000行可能是150000。存儲最後一行讀取沒有意義,因爲可能有重複。如果每個字符串都是唯一的,它可能是有用的,但它需要一些時間來讀取文件中的所有行,直到文件中的那一行,而不是即時Seek()操作。 – aiodintsov

1

出於顯而易見的原因,從日誌讀取文件的全部內容,以及消除線文件(將它們加載到應用程序之後)無疑是不成問題的。

作爲一個部分解決方案,我能想到的是擁有一個小型數據庫(可能比完整的MySQL/MS SQL/PostgreSQL實例小得多),並且使用從日誌文件中讀取的內容填充表。我很確定,即使斷電並再次啓動計算機,大多數關係數據庫也應該能夠輕鬆恢復其狀態。此解決方案需要一些可用於識別日誌文件中的行的數據(例如:記錄的動作的確切時間,動作發生的機器等)。

1

我想你會發現文件。與LINQ結合使用的讀線(文件名)函數對於類似的東西來說非常方便。 ReadAllLines()會將整個文本文件作爲字符串[]數組加載到內存中,但ReadLines將允許您在遍歷整個文件時立即開始枚舉這些行。這不僅可以節省您的時間,而且可以保持內存使用量非常低,因爲它一次處理每條線。使用語句很重要,因爲如果此程序中斷,它將關閉文件流以刷新寫入程序並將未寫入的內容保存到文件中。然後,當它啓動時,它將跳過所有已讀取的文件。

int readCount = File.ReadLines("readLogs.txt").Count(); 
using (FileStream readLogs = new FileStream("readLogs.txt", FileMode.Append)) 
using (StreamWriter writer = new StreamWriter(readLogs)) 
{ 
    IEnumerable<string> lines = File.ReadLines(bigLogFile.txt).Skip(readCount); 
    foreach (string line in lines) 
    { 
     // do something with line or batch them if you need more than one 
     writer.WriteLine(line); 
    } 
} 

正如MaciekTalaska提到的,我會強烈建議使用一個數據庫,如果這是一個用24/7的東西,會得到相當大。文件系統根本沒有裝備來處理這種數量,你將花費大量時間試圖發明解決方案,使數據庫能夠輕鬆完成。

1

是否有原因記錄到文件?文件很棒,因爲它們使用起來很簡單,而且作爲最低的共同標準,出現的錯誤相對較少。但是,文件是有限的。正如您所說,不能保證在讀取文件時寫入文件將會完成。寫入日誌的多個應用程序可能會相互干擾。沒有簡單的排序或過濾機制。日誌文件可能會非常快速地增長,並且沒有簡單的方法可以將舊事件(比如24小時以上)移動到單獨的文件中進行備份和保留。

相反,我會考慮將日誌寫入數據庫。表結構可以非常簡單,但您可以獲得事務處理的優勢(因此您可以輕鬆地提取或備份),並使用幾乎普遍理解的語法進行搜索,排序和篩選。如果您擔心加載峯值,請使用消息隊列,如SQL Server的http://msdn.microsoft.com/en-us/library/ms190495.aspx

爲了簡化轉換,可以考慮使用日誌框架,如log4net。它將大部分代碼從代碼中抽象出來。

另一種替代方法是使用類似syslog的系統,或者如果您有多個服務器和大量日誌,則爲flume。通過將日誌文件移出源計算機,您可以更有效地將它們存儲或檢查到不同的計算機上。但是,這些可能是目前問題的矯枉過正。