2013-02-15 52 views
0

我使用的外部Windows服務維護着一個單獨的基於文本的日誌文件,它不斷附加到它。這個日誌文件隨着時間的推移無限增長。我想定期修剪這個日誌文件來維護,比如說最近的5MB日誌條目。如何在C#.NET 4.0中高效地實現文件I/O代碼以修剪文件來說5mb?使用C#.NET 4.0修剪簡單文本日誌文件

更新: 設置服務依賴關係的方式,我的服務始終在外部服務之前啓動。這意味着如果需要,我可以獨佔訪問日誌文件以截斷它。一旦啓動外部服務,我將不會訪問日誌文件。我可以在桌面啓動時獲得對該文件的獨佔訪問權限。問題是 - 日誌文件的大小可能有幾千兆字節,我正在尋找一種有效的方法來截斷它。

+0

你甚至確定你可以截斷這個日誌文件 - 也許服務已經打開它。您是否可以不定期地停止並重新啓動服務以使您能夠將文件垃圾? (ServiceController.Stop等) – 2013-02-15 22:19:04

+0

@Ramhound我用File.ReadLines()(介紹在.NET 4.0中)的文件讀取到內存中的集合,然後用只有最後X項寫出一個新的日誌文件考慮。想知道是否有更簡單的方法來截斷文本文件中的前n行。 – amo 2013-02-15 22:21:15

+0

@StephenByrne服務重啓不會重新初始化日誌文件。重新啓動時,它將繼續附加到現有文件。但是,我知道外部服務已停止,因此可以通過運行截斷例程來獲得文件的獨佔訪問權限。在這種情況下,對日誌文件的獨佔訪問不是問題。 – amo 2013-02-15 22:22:09

回答

1

這將需要存儲的內存量來處理「新」日誌文件,但如果你只想要5Mb,那麼它應該沒問題。如果您在談論Gb +,那麼您可能還有其他問題;然而,它仍然可以使用臨時文件和一些鎖定來完成。

如前所述,您可能會遇到競爭條件,但如果這是寫入此文件的唯一線程,則情況並非如此。這會將您當前的文字替換爲文件。

const int MAX_FILE_SIZE_IN_BYTES = 5 * 1024 * 1024; //5Mb; 
const string LOG_FILE_PATH = @"ThisFolder\log.txt"; 
string newLogMessage = "Hey this happened"; 


#region Use one or the other, I mean you could use both below if you really want to. 
//Use this one to save an extra character 
if (!newLogMessage.StartsWith(Environment.NewLine)) 
    newLogMessage = Environment.NewLine + newLogMessage; 

//Use this one to imitate a write line 
if (!newLogMessage.EndsWith(Environment.NewLine)) 
    newLogMessage = newLogMessage + Environment.NewLine; 
#endregion 

int newMessageSize = newLogMessage.Length*sizeof (char); 
byte[] logMessage = new byte[MAX_FILE_SIZE_IN_BYTES]; 
//Append new log to end of "file" 
System.Buffer.BlockCopy(newLogMessage.ToCharArray(), 0, logMessage, MAX_FILE_SIZE_IN_BYTES - newMessageSize, logMessage.Length); 


FileStream logFile = File.Open(LOG_FILE_PATH, FileMode.Open, FileAccess.ReadWrite); 

int sizeOfRetainedLog = (int)Math.Min(MAX_FILE_SIZE_IN_BYTES - newMessageSize, logFile.Length); 
//Set start position/offset of the file 
logFile.Position = logFile.Length - sizeOfRetainedLog; 
//Read remaining portion of file to beginning of buffer 
logFile.Read(logMessage, logMessage.Length, sizeOfRetainedLog); 

//Clear the file 
logFile.SetLength(0); 
logFile.Flush(); 

//Write the file 
logFile.Write(logMessage, 0, logMessage.Length); 

我寫這真快,我很抱歉,如果我走了1個地方。

+0

「//使用這一個來保存一個額外的字符」這樣可以避免字符被截斷,因爲每條日誌消息都以NewLine開始,而不是以一個結尾開始。所以最後的消息在末尾不會有exta NewLine字符 – Monso 2013-02-15 22:54:41

+0

這很好,謝謝!將不得不檢查這將如何工作的Unicode。應該爲我工作一些調整。 – amo 2013-02-15 23:12:41

0

取決於它被寫入的頻率我想說你可能會面臨競爭條件來修改文件而不會損壞日誌。你總是可以嘗試編寫一個服務來監視文件大小,一旦它達到某個點就鎖定文件,重複並清除整個文件並關閉它。然後將數據存儲在服務輕鬆控制大小的另一個文件中。或者,您可以查看外部服務是否有登錄數據庫的選項,這可以很容易地推出最舊的數據。

+0

我不會運行到出現這種情況的競爭條件。請參閱我對原文的更新。 – amo 2013-02-15 22:30:39

0

你可以使用一個文件觀察員監視文件:

FileSystemWatcher logWatcher = new FileSystemWatcher(); 
     logWatcher.Path = @"c:\example.log" 
     logWatcher.Changed += logWatcher_Changed; 

然後,當事件引發你可以使用一個StreamReader讀取文件

private void logWatcher_Changed(object sender, FileSystemEventArgs e) 
    { 
    using (StreamReader readFile = new StreamReader(path)) 
     { 
      string line; 
      string[] row; 
      while ((line = readFile.ReadLine()) != null) 
      { 
       // Here you delete the lines you want or move it to another file, so that your log keeps small. Then save the file. 
      } 
     } 
    } 

It's的選項。

+0

順便說一下,在這個解決方案,您可以使用FileInfo類來檢查文件大小和其他屬性可能會在 – user1959018 2013-02-15 22:29:53

+1

如果你正在尋找一個更簡單的方法,只是截斷文件中insterested,你可以使用FileStream去。SetLength方法(http://msdn.microsoft.com/en-us/library/system.io.filestream.setlength.aspx) – user1959018 2013-02-15 22:35:40

+0

FileStream.SetLength()看起來很有趣,但會截斷文件的結尾。在我的情況下,它是感興趣的文件結尾(最近寫入的條目)。 SetLength()可以爲此工作嗎? – amo 2013-02-15 22:44:02