views:

1917

answers:

12

Hi All,

I have created a web service in .net 2.0, C#. I need to log some information to a file whenever different methods are called by the web service clients.

The problem comes when one user process is writing to a file and another process tries to write to it. I get the error "The process cannot access the file because it is being used by another process".

The solutions that I have tried to implement in C# and failed are as below.

1) Implemented singleton class that contains code that writes to a file. 2) Used lock statement to wrap the code that writes to the file.

I have also tried to use open source logger log4net but it also is not a perfect solution. I know about logging to system event logger, but I do not have that choice.

I want to know if there exists a perfect and complete solution to such a problem?

I have posted this question once before on MSDN but didnt get a proper answer. Hope this time the nut is cracked :)

Thanks! pradeep_tp

A: 

Maybe write a "queue line" of sorts for writing to the file, so when you try to write to the file it keeps checking to see if the file is locked, if it is - it keeps waiting, if it isn't locked - then write to it.

Joel
A: 

You could push the results onto an MSMQ Queue and have a windows service pick the items off of the queue and log them. It's a little heavy, but it should work.

Charles Graham
+5  A: 

The locking is probably failing because your webservice is being run by more than one worker process. You could protect the access with a named mutex, which is shared across processes, unlike the locks you get by using lock(someobject) {...}:

Mutex lock = new Mutex("mymutex", false);

lock.WaitOne();

// access file

lock.ReleaseMutex();
Khoth
Mutex lockobj = new Mutex(false,"mymutex");will compile
Simon Farrow
A: 

Joel and charles. That was quick! :)

Joel: When you say "queue line" do you mean creating a separate thread that runs in a loop to keep checking the queue as well as write to a file when it is not locked?

Charles: I know about MSMQ and windows service combination, but like I said I have no choice other than writing to a file from within the web service :)

thanks pradeep_tp

pradeeptp
A: 

Trouble with all the approached tried so far is that multiple threads can enter the code. That is multiple threads try to acquire and use the file handler - hence the errors - you need a single thread outside of the worker threads to do the work - with a single file handle held open.

Probably easiest thing to do would be to create a thread during application start in Global.asax and have that listen to a synchronized in-memory queue (System.Collections.Generics.Queue). Have the thread open and own the lifetime of the file handle, only that thread can write to the file.

Client requests in ASP will lock the queue momentarily, push the new logging message onto the queue, then unlock.

The logger thread will poll the queue periodically for new messages - when messages arrive on the queue, the thread will read and dispatch the data in to the file.

stephbu
+1  A: 

You don't say how your web service is hosted, so I'll assume it's in IIS. I don't think the file should be accessed by multiple processes unless your service runs in multiple application pools. Nevertheless, I guess you could get this error when multiple threads in one process are trying to write.

I think I'd go for the solution you suggest yourself, Pradeep, build a single object that does all the writing to the log file. Inside that object I'd have a Queue into which all data to be logged gets written. I'd have a separate thread reading from this queue and writing to the log file. In a thread-pooled hosting environment like IIS, it doesn't seem too nice to create another thread, but it's only one... Bear in mind that the in-memory queue will not survive IIS resets; you might lose some entries that are "in-flight" when the IIS process goes down.

Other alternatives certainly include using a separate process (such as a Service) to write to the file, but that has extra deployment overhead and IPC costs. If that doesn't work for you, go with the singleton.

Martin
A: 

Hi All,

To know what I am trying to do in my code, following is the singletone class I have implemented in C#

public sealed class FileWriteTest {

private static volatile FileWriteTest instance;

private static object syncRoot = new Object();

private static Queue logMessages = new Queue();

private static ErrorLogger oNetLogger = new ErrorLogger();

private FileWriteTest() { }

public static FileWriteTest Instance
{
    get
    {
        if (instance == null)
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new FileWriteTest();
                    Thread MyThread = new Thread(new ThreadStart(StartCollectingLogs));
                    MyThread.Start();

                }
            }
        }

        return instance;
    }
}

private static void StartCollectingLogs()
{

    //Infinite loop
    while (true)
    {
        cdoLogMessage objMessage = new cdoLogMessage();
        if (logMessages.Count != 0)
        {
            objMessage = (cdoLogMessage)logMessages.Dequeue();
            oNetLogger.WriteLog(objMessage.LogText, objMessage.SeverityLevel);

        }
    }
}

public void WriteLog(string logText, SeverityLevel errorSeverity)
{
    cdoLogMessage objMessage = new cdoLogMessage();
    objMessage.LogText = logText;
    objMessage.SeverityLevel = errorSeverity;
    logMessages.Enqueue(objMessage);

}

}

When I run this code in debug mode (simulates just one user access), I get the error "stack overflow" at the line where queue is dequeued.

Note: In the above code ErrorLogger is a class that has code to write to the File. objMessage is an entity class to carry the log message.

pradeeptp
Why use volatile, if you do not know how to use it? ;P
leppie
A: 

Alternatively, you might want to do error logging into the database (if you're using one)

Silver Dragon
A: 

Koth,

I have implemented Mutex lock, which has removed the "stack overflow" error. I yet have to do a load testing before I can conclude whether it is working fine in all cases.

I was reading about Mutex objets in one of the websites, which says that Mutex affects the performance. I want to know one thing with putting lock through Mutex.

Suppose User Process1 is writing to a file and at the same time User Process2 tries to write to the same file. Since Process1 has put a lock on the code block, will Process2 will keep trying or just die after the first attempet iteself.?

thanks pradeep_tp

pradeeptp
Process2 will wait until Process1 has finished. To minimise this, do as much as possible outside of the lock.
Khoth
A: 

It will wait until the mutex is released....

CSharpAtl
A: 

Joel: When you say "queue line" do you mean creating a separate thread that runs in a loop to keep checking the queue as well as write to a file when it is not locked?

Yeah, that's basically what I was thinking. Have another thread that has a while loop until it can get access to the file and save, then end.

But you would have to do it in a way where the first thread to start looking gets access first. Which is why I say queue.

Joel
+1  A: 

Thanks everybody.

I have added Mutex object as suggested by Khoth. I didn't get any error so far. Currently I have deployed the code to check if it is working fine under heavy load.

Will update you all with the outcome. Seems like nut has been cracked. Thanks stackoverflow.com :)

pradeep_tp

pradeeptp