简体   繁体   中英

Reading rotation log files and file locking

I have a Python service spitting out logs to text files. It rotates them every ~400KB. So the Python service opens up a handle on the file, let's call it app.log . It then writes content to the file every now and again flushes it to the disk. When it reaches a certain size, it closes it's handle, and move it to app.log.1 and starts a new handle on app.log .

So I can't change this service, but I have a C# application that will read those logs. I ran into 3 scenarios:

  • If I just try to read those those logs using new FileStream(path, FileMode.Open); , it won't allow me as the Python service has an handle on it.
  • If I try to open it using new FileStream(path, FileMode.Open, FileAccess.Read); , this allows me to read it, but if the service tries to rotate the log, it won't be able to as my C# application now has a handle on the file.
  • And if I try to open the file using new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Delete); , my Python service won't fail on deleting the file, but it will fail creating a new handle on app.log as the C# application would still have a handle on it.

The only solution which I'm aware of would be using Windows Shadow Copy (VSS) to create a snapshot of the logs and then read that snapshot but this would be quite expensive as we need to query the logs at every 5 minutes.

Also, I'm not interested in reading the rotated logs, app.log.1 , app.log.2 etc.

Logging to text files under Windows seems to be a pain what with all the locking/handles. Does anyone have any suggestion?

You should be able to open your file as Dmitry Popov suggested in his answer as below and not affect Python writing to it, however it depends upon what locks the Python application holds on the file, it can lock you out completely and there is nothing you to do to prevent that without hacking Windows.

FileSream fs = File.Open(@"c:\Test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)

The FileStream object created in this way will still be connected to the same file after an operating system file move operation has been performed on it to rename it.

So lets assume your python app opens a file called Test.log and starts writing to it. You can read any data written to it (after python flushes its buffer) using the file stream returned from the line above. The python application can close and reopen the file as often as it wants writing each time and the reading application will remain connected to it. When the python application issues a File Move operation to rename the file to Test1.log, the file stream returned above will still be connected to the file which is now called Test1.log so you can continue reading to the end of the file before starting the new log file if that is what you want. There is one caveat to this. The Python application needs to use a Move/Rename operation rather than copying the file to a new one and deleting the old one, I'd be surprised if that is what it does though.

There is a possibility that your reading application will reach the end of the file before your writing application has finished reading from it. In this case fs.Read will keep returning 0 after a timeout until the writing application opens the file and writes some more. You can make the time out very long / infinite if you want.

As you don't want to read to the end of one file before starting the new one you could just close and reopen the file at regular intervals. The log file without the numeric suffix should always be the most recent.

If however you want your reading application to read to the end of one log file before starting at the beginning of the next one you will need to work out when the writing application has finished writing to the log file. Also it needs to find out what the file is now called so it can read n-1 next. Is there some marker written by the python application that you could look for to denote the end of a file? Does it write 'End Of Log' or something similar?

Be warned also that there are going to be short periods of time when LogFile n-1 does not exist. This is because if you have log files 0, 1, 2 and 3 it needs to make log file 3 into log file 4 before it can make log file 2 into log file 3. While it is doing this there will be a short period of time when you have log files 0, 1, 2, 4 and no 3.

Personally I would find the developer that wrote the logging for your Python application give him/her the evil eye for causing this headache in the first place. What is wrong with having the most recent log file have the largest number?

using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
  //Do works
}

C# thread don't lock the file in this case, your Python script can write and close the file to create another one without deadlock.

You can combine FileShare flags:

FileShare.Write | FileShare.Delete

Here's a demo:

using (var cSharp = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Write | FileShare.Delete))
{
    // The Python service will be able to change and to rename the file:
    using (var python = new FileStream(filename, FileMode.Open, FileAccess.Write, FileShare.Read))
    {
    }
    File.Move(filename, newFilename);
}

You will have to deal with concurrency. You can use FileSystemWatcher to monitor file changes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM