NLog: MDLC implementation uses mutable dictionary which causes reference updates between threads
The MDLC implementation MappedDiagnosticsLogicalContext.cs uses a mutable dictionary which causes reference updates between threads since CallContext uses shallow copy when flagged as write on copy during thread/task delegation. It basically means that an update of a value in a child thread will also update the value in the parent thread, which should not be the expected behavior.
You should consider using a immutable dictionary. Or why not use the CallContext directly by prefixing the items with the dictionary key? That will be more effective than recreating a dictionary every time a change needs to occur.
I refer to the great CallContext post created by Mr Cleary explaining this even further. http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html
Also, allowing object types to be inserted is a potential problem since CallContext.LogicalSetData requires the data to be marked as Serializable or it will throw an exception. See parameters description at https://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.logicalsetdata(v=vs.110).aspx
I give you an example of how you con simplify the implementation and get rid of the above problems. It lacks the Contains, Remove and Clear methods but they should be fairly simple to implement.
public static class LogicalThreadContext
{
private const string KeyPrefix = "NLog.LogicalThreadContext";
private static string GetCallContextKey(string key)
{
return string.Format("{0}.{1}", KeyPrefix, key);
}
private static string GetCallContextValue(string key)
{
return CallContext.LogicalGetData(GetCallContextKey(key)) as string ?? string.Empty;
}
private static void SetCallContextValue(string key, string value)
{
CallContext.LogicalSetData(GetCallContextKey(key), value);
}
public static string Get(string item)
{
return GetCallContextValue(item);
}
public static string Get(string item, IFormatProvider formatProvider)
{
if ((formatProvider == null) && (LogManager.Configuration != null))
{
formatProvider = LogManager.Configuration.DefaultCultureInfo;
}
return string.Format(formatProvider, "{0}", GetCallContextValue(item));
}
public static void Set(string item, string value)
{
SetCallContextValue(item, value);
}
}
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 1
- Comments: 33 (31 by maintainers)
NLog 4.3.9 is live! https://www.nuget.org/packages/NLog/4.3.9
Regarding current behavior I also think it’s not really useful and could be removed. If so - the CompatibilityMode flag and its usage should be removed from my proposed changes.
Should I make pull request to get MDLC fixed in the next NLog version?