1

I am logging to file with the following method

public static void LogDataContractToFile(string XMLStringToLog, string filePathAndName)
{
    FileInfo fileinfoMaster;
    FileInfo fileinfoLog;
    string fileName = string.Empty;
    int tmpInt = 0;
    filePathAndName = filePathAndName.ToLower();

    while (true)
    {
        lock (LogDataContractToFileLock)
        {
            if (!_workingWithFiles.Contains(filePathAndName))
            {
                _workingWithFiles.Add(filePathAndName);
                break;
            }
        }
        Thread.Sleep(100);
    }

    try
    {
        #region Create XMLFile

        if ((tmpInt = filePathAndName.LastIndexOf('.')) > 0)
            fileName = filePathAndName.Remove(tmpInt, filePathAndName.Length - tmpInt);
        else
            fileName = filePathAndName;

        fileinfoMaster = new FileInfo(fileName + ".xml");
        fileinfoLog = new FileInfo(fileinfoMaster.DirectoryName + "\\" + Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log");

        if ((fileinfoMaster.Exists && !fileinfoLog.Exists) ||
            (!fileinfoMaster.Exists && fileinfoLog.Exists))
        {
            fileinfoMaster.Delete();
            fileinfoLog.Delete();
        }

        //Se så att filen är 50 MB eller mindre annars arkivera
        if (fileinfoMaster.Exists && fileinfoLog.Length > 52428800)
        {
            tmpInt = FileWriter.FileCount(fileinfoLog.DirectoryName, Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + "*.log");
            fileinfoLog.MoveTo(Path.Combine(fileinfoLog.DirectoryName, Path.GetFileNameWithoutExtension(fileinfoLog.Name) + "_" + tmpInt.ToString() + ".log"));
            CreateLogDataMasterFile(Path.Combine(fileinfoMaster.DirectoryName, Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + "_" + tmpInt.ToString() + ".xml"), fileinfoLog.Name);
            fileinfoLog = new FileInfo(fileinfoMaster.DirectoryName + "\\" + Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log");
        }

        if (!fileinfoMaster.Exists)
        {
            DirectoryInfo info = new DirectoryInfo(fileinfoMaster.DirectoryName);
            if (info.Exists == false)
                info.Create();

            CreateLogDataMasterFile(fileinfoMaster.FullName, Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log");
        }

        #endregion

        using (StreamWriter sw = File.AppendText(fileinfoMaster.DirectoryName + "\\" + Path.GetFileNameWithoutExtension(fileinfoMaster.Name) + ".log"))
        {
            sw.Write(XMLStringToLog);
            sw.Flush();
        }
    }
    finally
    {
        lock (LogDataContractToFileLock)
        {
            _workingWithFiles.Remove(filePathAndName);
        }
    }

}

private static void CreateLogDataMasterFile(string filepathAndName, string dataFileName)
{
    XmlTextWriter xmlWriter;
    using (xmlWriter = new XmlTextWriter(filepathAndName, System.Text.Encoding.UTF8))
    {
        xmlWriter.Formatting = Formatting.Indented;
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteDocType("DataLog", null, null, "<!ENTITY data SYSTEM \"" + dataFileName + "\">");
        xmlWriter.WriteStartElement("root");
        xmlWriter.WriteRaw("&data;");
        xmlWriter.WriteEndElement();
        xmlWriter.Flush();
    }
}

Several different threads might write to file at the same time and in some cases they will write to the same file. To avoid problems with access denied, used by other process I have implemented some logic as you can see.

The problem is that I am still getting access denied, used by other process from time to time.

I have looked at the build in tracers like the TextWriterTraceListener and here I can see the following note :

If an attempt is made to write to a file that is in use or unavailable, the file name is automatically prefixed by a GUID.

So it looks like even Microsoft has problems with this? Is it a Windows problem? Is there any way to handle this becides generating a new file? Generating a new file will mess of the data flow in files a lot!

cramopy
  • 3,459
  • 6
  • 28
  • 42
Banshee
  • 15,376
  • 38
  • 128
  • 219
  • Another option (per @spender answer) is to block callers. I am unsure if you did it correctly, would expect a `lock` around write to file method. – Sinatr Jun 18 '15 at 12:06
  • Im blocking other callers to the same file with the first while and lock code. So if there is already a thread writing to the specific file no other will be able to until it is "checked in" again. – Banshee Jun 18 '15 at 12:24
  • `lock` before and after writing will not ensure (from my point of view) what 2 or more threads will not be able to pass first lock and won't try concurrently write something into file. E.g. think about locking call to a whole `LogDataContractToFile` method. Lock has to be hold until operation is completed, you are either release it earlier or locking (second `lock`) when it's already too late. – Sinatr Jun 18 '15 at 12:30

1 Answers1

1

Yes. Don't write to a file from multiple threads. Create a single thread that is reponsible for file writes, and have it read what to write from a ConcurrentQueue. Any thread that needs to write should just add data to the queue.

spender
  • 117,338
  • 33
  • 229
  • 351
  • So I should create one thread (and only one) that writes to all files and then have a queue with objects(containing data and file metadata) for this one thread that all other threads places objects in? – Banshee Jun 18 '15 at 12:09
  • "to write all files"... no not necessarily, but for a single thread to take all responsibility for writing data to a single file, such that it's not possible for two threads to simultaneously write to the same file. – spender Jun 18 '15 at 12:11
  • Okay, so what you suggests is that I have a collection of objects that holds a thread and metadata about that file. When another thread arrives I take out this object, uses the thread in this object and writes to the specific file. The question is what will happen if 2 threads instructs this stored thread to write to a file at the same time? Will I have to handle some kind of synchronization? – Banshee Jun 18 '15 at 12:18
  • That's why you have the queue. You don't instruct a write, you queue it. The writing thread dequeues write messages one at a time. – spender Jun 18 '15 at 13:08