I tried to sample up a logging class from this question and answer(I want to use this class from C# DLL). Made slight modification mainly to file name, and also how data is written. I am interested if this can be considered thread safe logging class? I am particularly concerned by the comment under the answer. See below. I didn't particularly understand the comment.
You should also ditch the TextWriter.SyncTextWriter if you are going this way. Every method call on SyncTextWriter takes a lock on the instance. This is not necessary if you are wrapping the calls to the TextWriter in your own Logger classs methods. If you are passing a raw SyncTextWriter` around you can deadlock like this: lock(textWriter){ textWriter.WriteLine("test"); // oops deadlock
Here is the class:
public static class Logger
{
private static readonly object _syncObject = new object();
static readonly TextWriter tw;
static Logger()
{
// Note1:
// My intent was that the log file name is same as the date on which the log
// entry was added by the user - but apparently this approach as below is not
// correct since it is in static constructor and the same date will be set for the file names even if log entries were added at different dates
// Note2: The current answer doesn't address this, I would appreciate if someone can tell me where to move this code too to achieve what I want
const string FMT = "yyyy-MM-dd";
DateTime now1 = DateTime.Now;
string strDate = now1.ToString(FMT);
tw = TextWriter.Synchronized(File.AppendText(SPath() + "\\Log " + strDate + ".txt"));
}
public static string SPath()
{
return ConfigManager.GetAppSetting("logPath");
}
public static void Write(string logMessage)
{
try
{
Log(logMessage, tw);
}
catch (IOException e)
{
tw.Close();
}
}
public static void Log(string logMessage, TextWriter w) {
// only one thread can own this lock, so other threads
// entering this method will wait here until lock is
// available.
lock(_syncObject) {
w.WriteLine("{0} {1} {2}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString(), logMessage);
// Where to call flush??
w.Flush();
}
}
}
PS. You can see classes like NLog have some requirements when they must be called from DLL(like config file must be in exe file which is calling DLL etc.). I'd like to avoid such things, as well as configurations which these classes require.