Search code examples
c#asp.net-mvclogginglog4net

How to Download log files generated from log4Net


I'm using log4net in a ASP.NET MVC 4 application and I'm trying to download the generated log file from log4Net.

I have in mind to download the log file with a FileResult, like:

[Authorize(Roles = "Admin")]
public FileResult DownloadUserInterfaceLog()
{
    // Get the path of the log file
    string path = (LogManager.GetCurrentLoggers()[0].Logger.Repository.GetAppenders()[0] as FileAppender).File;
    // Get the file
    byte[] fileBytes = System.IO.File.ReadAllBytes(path);
    string fileName = "Log.txt";
    // Return the expected file
    return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}

My problem is that when I call System.IO.File.ReadAllBytes(path);I get an Exception, cause the log file is already used by another process.

I'm already put <lockingModel value="log4net.Appender.FileAppender+MinimalLock" /> (which should release the file after any modification) in my Web.Config with no success.

Begin of log4Net configuration in Web.Config:

<log4net>
    <appender name="Appender-root" type="log4net.Appender.RollingFileAppender">
        <lockingModel value="log4net.Appender.FileAppender+MinimalLock" />
        <file value="Path..." />
        <appendToFile value="true" />

Solution

  • Here is an article about similar trouble.

    File.ReadAllBytes() function is written such that it uses a call like the following to instantiate a FileStream:

    FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)
    

    Notice that the FileShare is specified as Read. That means if you have another piece of code writing to the same file at the same time, you will get an error even if the other piece of code created the FileStream by specifying the share mode as “ReadWrite” like so:

    FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)
    

    Solution is to write your own custom ReadAllBytes:

    public static byte[] ReadAllBytes(String path)
    {
        byte[] bytes;
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            int index = 0;
            long fileLength = fs.Length;
            if (fileLength > Int32.MaxValue)
                throw new IOException(“File too long”);
            int count = (int)fileLength;
            bytes = new byte[count];
            while (count > 0)
            {
                int n = fs.Read(bytes, index, count);
                if (n == 0)
                    throw new InvalidOperationException(“End of file reached before expected”);
                index += n;
                count -= n;
            }
        }
    return bytes;
    }