I have a LoggerBase class, it looks like:
public class BatchLoggerBase : IDisposable
{
protected string LogFilePath { private get; set; }
protected object _synRoot;
BatchLoggerBase(string logFilePath)
{
LogFilePath = logFilePath;
}
protected virtual void WriteToLog(string message)
{
Task.Factory.StartNew(() =>
{
lock (_synRoot)
{
System.IO.File.AppendAllText(LogFilePath, message);
}
});
}
//Other code...
}
I have another class inherit from this base class, like:
public sealed class TransactionBatchLogger : BatchLoggerBase
{
public TransactionBatchLogger()
{
_synRoot = new object();
string directory = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings["Batch.TransactionLog.Path"];
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
LogFilePath = string.Format("{0}{1}_{2}.txt", directory, "TransactionLog", DateTime.Now.ToString("yyyy-MM-dd"));
}
public void LogLoyaltyPointProcess(IEnumerable<CustomerTierOverrideItem> listOfCustomerTierItem)
{
Task.Factory.StartNew(() =>
{
//Construct message...
WriteToLog(message);
});
}
}
public sealed class LoyaltyPointBatchLogger : BatchLoggerBase
{
public LoyaltyPointBatchLogger()
{
_synRoot = new object();
string directory = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings["Batch.LoyaltyPointLog.Path"];
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
LogFilePath = string.Format("{0}{1}_{2}.txt", directory, "LoyaltyPointLog", DateTime.Now.ToString("yyyy-MM-dd"));
}
public void LogLoyaltyPointProcess(IEnumerable<CustomerTierOverrideItem> listOfCustomerTierItem)
{
Task.Factory.StartNew(() =>
{
//Construct message...
WriteToLog(message);
});
}
}
The LoyaltyPointBatchLogger and TransactionBatchLogger write log content to different log files(one is for transactionLog, another one is for LoayltyPointLog), but they all call same virtual method from base class.
The batch program process data batch by batch(like, 45000 total data and 10000 each batch) these two logger may called by different batches successively, so I do not want the log files are accessed by different batches logger thread.
The Question is:
Should I instantiate the _synRoot in derived class LoyaltyPointBatchLogger and TransactionBatchLogger, or in base class ?
_synRoot instantiated in LoyaltyPointBatchLogger and TransactionBatchLogger are different references, so the LoyaltyPointBatchLogger and TransactionBatchLogger will not wait each other when they go into lock statement, right?
I haven’t had any experience using a “syncroot” locking pattern, but if I understand your question correctly, if I would attack this by having each subclass type declare its own unique SyncRoot statically by type. This way all instances of that type share the same SyncRoot. Additionally, I would require that the base logger be provided a SyncRoot object via the constructor rather than hope that the subclass assigns it (and assigns it in time). Additionally, I would make it immutable so subclasses can’t do evil stuff.
BatchLoggerBase
LoyaltyPointBatchLogger
TransactionBatchLogger
EDIT: Note that this still means subclasses can ignore your intent. For example:
In this case,
evilandevil2will not share the same locking object and can interfere. But if you have control over the logger implementations, you can avoid shooting yourself in the foot.