0

I am trying to implement locking scenario where same file is accessed by multiple threads at same time or at different time. While testing it seems code is overwriting the existing line instead of appending new line.

namespace SMPPService
{
public static class LogFile
{
    public static void WriteErrorLog(Exception ex)
    {
        byte[] buf = GetBytes(DateTime.Now.ToString() + ": " + ex.Source.ToString().Trim() + "; " + ex.Message.ToString().Trim());

        Lock(HttpRuntime.AppDomainAppPath + "\\Exceptions.txt",
                (f) =>
                {
                    try
                    {
                        f.Write(buf, 0, buf.Length);
                    }
                    catch (IOException ioe)
                    {
                        // handle IOException
                    }
                }, buf);
    }

    public static void WriteErrorLog(string Message)
    {
        byte[] buf = GetBytes(DateTime.Now.ToString() + ": " + Message);           

        Lock(HttpRuntime.AppDomainAppPath + "\\LogFile.txt",
                (f) =>
                {
                    try
                    {
                        f.Write(buf, 0, buf.Length);
                    }
                    catch (IOException ioe)
                    {
                        // handle IOException
                    }
                }, buf);

        System.Threading.Thread.Sleep(60000);

    }

    public static void Lock(string path, Action<FileStream> action,byte [] lines)
    {
        var autoResetEvent = new AutoResetEvent(false);

        while (true)
        {
            try
            {
                using (var file = File.Open(path,
                                            FileMode.OpenOrCreate,
                                            FileAccess.ReadWrite,
                                            FileShare.Write))
                {
                    action(file);                              
                    break;
                }
            }
            catch (IOException)
            {
                var fileSystemWatcher =
                    new FileSystemWatcher(Path.GetDirectoryName(path))
                    {
                        EnableRaisingEvents = true
                    };

                fileSystemWatcher.Changed +=
                    (o, e) =>
                    {
                        if (Path.GetFullPath(e.FullPath) == Path.GetFullPath(path))
                        {
                            autoResetEvent.Set();
                        }
                    };

                autoResetEvent.WaitOne();
            }
        }
    }
    static byte[] GetBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
 }
 }

Usage

LogFile.WriteErrorLog("Requesting SMPP Client from WMAS..." + " " + "Date:" + DateTime.Now + " " + "Source Address:" + msisdn);
Hammad Bukhari
  • 459
  • 2
  • 11
  • 30

2 Answers2

0
FileShare.Write

From MSDN:

Allows subsequent opening of the file for writing.

This is exactly the flag you do not want to use. With files the easiest way to do locking is to let the file system do it for you, use FileShare.Read (so someone can look at the log) and retry if the open fails do to a sharing violation.

Implementing your own locking like that is only going to be re-creating when the file system already does, but badly.

Also, rather than messing handling encoding yourself (getting the edge cases right is not easy):

  • Open as a text file with a specified encoding (UTF-8 would be my choice).
  • Open the file for append, so new content will always be written at the end.

There is an overload of StreanWriter's constructor that does this.

EDIT: A quick check of the reference source: this does use FileShare.Read.

Richard
  • 106,783
  • 21
  • 203
  • 265
0

You should use File.AppendAllLines instead your lock method. File.AppendAllLines is not Thread safe and you have to lock it.

private object lockObject = new object();
private string fileName = Path.Combine(HttpRuntime.AppDomainAppPath, "LogFile.txt");

public static void WriteErrorLog(string Message)
{       
    lock(lockObject)
    {
        File.AppendAllLines(fileName, new string[] { Message + "\n" });
    }
}

note that File.AppendAllLines was introduced in .Net 4 and you have to use File.AppendAllText in older framework

Aik
  • 3,528
  • 3
  • 19
  • 20