在.NET中截断日志文件开头

4
我有一个VB.NET应用程序,以文本格式将状态写入日志文件。随着时间的推移,文件变得越来越大,我想知道是否有一种有效的方法来截断文件的开头。
为了简化事情,我希望指定一个文件大小(比如2-3 MB),并使用StreamWriter编写日志:
Using strm As New IO.StreamWriter(filelocation.log, True)
    strm.WriteLine("msg to write")
    strm.Close()
End Using

我考虑使用strm.BaseStream.Length来确定需要截取文件的大小,但是使用.SetLength会从文件末尾开始截取,这不是我们想要的结果。

6个回答

2
我强烈建议使用log4net来完成日志记录。它非常强大和灵活,并且有一个内置的方式可以根据您指定的大小滚动日志。这不是你在这里寻找的答案,但绝对值得一试。以下是我们在调试期间进行日志记录的示例配置:
<log4net>
    <appender name="GeneralLog" type="log4net.Appender.RollingFileAppender">
        <file value="ClientTools.log"/>
        <appendToFile value="true"/>
        <maximumFileSize value="3000KB"/>
        <rollingStyle value="Size"/>
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%d{HH:mm:ss} [%t] %-5p %c - %m%n"/>
        </layout>
    </appender>
    <root>
        <level value="DEBUG"/>
        <appender-ref ref="GeneralLog"/>
    </root>
    <logger name="NHibernate" additivity="false">
        <level value="DEBUG"/>
        <appender-ref ref="GeneralLog"/>
    </logger>
</log4net>

在app.config文件中,这段代码将创建一个名为ClientTools.log的日志文件,写入特定格式(包括日期和时间),并在3MB时回滚日志,存储在应用程序文件夹中。
要使用记录器,在网页的Init()函数中执行以下操作: public ILog log;
public void InitiateLogging()
{
    log4net.Config.XmlConfigurator.Configure();
    log = LogManager.GetLogger("MyApplication");
}

然后当您想要记录某些内容时,请执行以下操作:
log.Info("No resources available.");
// or
log.Fatal(exception.Message);
// or
log.Warn("Something bad may happen here.");

您不必担心创建流对象、关闭流、处理流等问题。而且这非常符合DRY原则。

我本来不想走这条路,但我认为这是必须要做的。最初设计日志记录非常简单,但为了做好它,这样做是可行的。谢谢。 - hacker

0

以下是我用于此的几个函数:

private void ShrinkLogFile()
{
    var file = new FileInfo(Filename);
    if (file.Exists && file.Length > MaxFileSize)
    {
        MoveLinesToBeginningStartingAt(file.Length - (MaxFileSize / 2));
    }
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
private void MoveLinesToBeginningStartingAt(long offsetToKeep)
{
    // Open the file twice.  We'll read from the end and write to the beginning.
    using (var fileReader = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (var fileWriter = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
    {
        // Find the end of the first line so we start at the beginning of a new line.
        fileReader.Position = offsetToKeep;
        using (var reader = new StreamReader(fileReader, Encoding.UTF8, true, 512, true))   // Note that we leave fileReader open...
        {
            offsetToKeep += reader.ReadLine().Length;       // Advance offset past the (probably) partial first line
            offsetToKeep += Environment.NewLine.Length;     // Advance past the newline
        }

        // Go to new position and copy the rest of the file to the beginning of the same file.
        fileReader.Position = offsetToKeep;
        fileReader.CopyTo(fileWriter);

        // Truncate the file
        var fileInfo = new FileInfo(Filename);
        fileWriter.SetLength(fileInfo.Length - offsetToKeep);
    }
}

还有几件事需要考虑:

  1. 您需要在锁定(xxx)内运行这些操作,以确保在“收缩”发生时没有其他消息会尝试写入文件。
  2. 收缩文件可能需要一些时间,因此您应该在后台线程上进行日志写入。

0

为什么不检查文件的字节长度是否大于3MB,如果是,则覆盖它并重新写入。像这样(我是C#程序员):

System.IO.FileInfo f = new FileInfo(file_name);
if (f.Length > (1024 * 1024 * 3)){ 
    // 文件超过3MB
    //
    // 可能备份一下?
    // 然后...
    using (StreamWriter sw = new StreamWriter(file_name, false))
    {
        // 覆盖内容....
    }
}else{
    // 正常打开以进行追加模式
}

希望这可以帮助到您, 最好的问候, 汤姆。


0

一种方法可能是

  1. 逐行读取原始文件(A)
  2. 跳过不需要的行
  3. 将所需的行写入另一个文件(B)。
  4. 保存文件B。
  5. 删除文件A。
  6. 将文件B重命名为文件A。

0
你可以检查文件长度并以硬(且不太优雅)的方式截断它:
  If New FileInfo("yourFilePathHere").Length > (2 ^ 21) Then
        File.WriteAllText("yourFilePathHere", _
        File.ReadAllText("yourFilePathHere").Substring(ToInt32(2 ^ 21)))
  End If

这个方案可能可行,但如果两个日志条目非常接近,可能会导致写入文件或截断另一个日志条目的问题。 - hacker

0

我使用的快速简便的方法。这个示例会保留日志文件中最近的60行。

dim LogPath as string = "YourLogPath"    
Dim lineCount = File.ReadAllLines(LogPath).Length

        If lineCount > 60 Then ' because I want my log to be 60 lines long
            Dim delLineCount As Integer = lineCount - 60
            Dim lines As List(Of String) = New List(Of String)(File.ReadAllLines(LogPath))
            lines.RemoveRange(0, delLineCount)
            File.WriteAllLines(LogPath, lines.ToArray())
        End If

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接