LOG4NET-554 Use AsyncLocal for LogicalThreadContext
Patch by Thomas Clegg.
closes #52
diff --git a/netstandard/log4net.tests/log4net.tests.xproj b/netstandard/log4net.tests/log4net.tests.xproj
index 00fecb3..92eecae 100644
--- a/netstandard/log4net.tests/log4net.tests.xproj
+++ b/netstandard/log4net.tests/log4net.tests.xproj
@@ -36,6 +36,7 @@
<Compile Include="../../tests/src/Appender/SmtpPickupDirAppenderTest.cs" />
<Compile Include="../../tests/src/Appender/StringAppender.cs" />
<Compile Include="../../tests/src/Appender/TraceAppenderTest.cs" />
+ <Compile Include="../../tests/src/Context/LogicalThreadContextTest.cs" />
<Compile Include="../../tests/src/Context/ThreadContextTest.cs" />
<Compile Include="../../tests/src/Core/**/*.cs" />
<Compile Include="../../tests/src/DateFormatter/**/*.cs" />
diff --git a/netstandard/log4net.tests/project.json b/netstandard/log4net.tests/project.json
index 1e2396d..49af8d7 100644
--- a/netstandard/log4net.tests/project.json
+++ b/netstandard/log4net.tests/project.json
@@ -11,6 +11,7 @@
"../../tests/src/Appender/SmtpPickupDirAppenderTest.cs",
"../../tests/src/Appender/StringAppender.cs",
"../../tests/src/Appender/TraceAppenderTest.cs",
+ "../../tests/src/Context/LogicalThreadContextTest.cs",
"../../tests/src/Context/ThreadContextTest.cs",
"../../tests/src/Core/**/*.cs",
"../../tests/src/DateFormatter/**/*.cs",
diff --git a/netstandard/log4net/log4net.xproj b/netstandard/log4net/log4net.xproj
index ff371a0..083a7a4 100644
--- a/netstandard/log4net/log4net.xproj
+++ b/netstandard/log4net/log4net.xproj
@@ -161,7 +161,7 @@
<Compile Include="../../src/Layout/XmlLayout.cs" />
<Compile Include="../../src/Layout/XmlLayoutBase.cs" />
<Compile Include="../../src/Layout/XmlLayoutSchemaLog4j.cs" />
- <!--<Compile Include="../../src/LogicalThreadContext.cs" />-->
+ <Compile Include="../../src/LogicalThreadContext.cs" />
<Compile Include="../../src/LogManager.cs" />
<Compile Include="../../src/MDC.cs" />
<Compile Include="../../src/NDC.cs" />
@@ -201,9 +201,9 @@
<Compile Include="../../src/Util/ILogExtensions.cs" />
<Compile Include="../../src/Util/LevelMapping.cs" />
<Compile Include="../../src/Util/LevelMappingEntry.cs" />
- <!--<Compile Include="../../src/Util/LogicalThreadContextProperties.cs" />-->
+ <Compile Include="../../src/Util/LogicalThreadContextProperties.cs" />
<Compile Include="../../src/Util/LogicalThreadContextStack.cs" />
- <!--<Compile Include="../../src/Util/LogicalThreadContextStacks.cs" />-->
+ <Compile Include="../../src/Util/LogicalThreadContextStacks.cs" />
<Compile Include="../../src/Util/LogLog.cs" />
<!--<Compile Include="../../src/Util/NativeError.cs" />-->
<Compile Include="../../src/Util/NullDictionaryEnumerator.cs" />
diff --git a/netstandard/log4net/project.json b/netstandard/log4net/project.json
index 75978e5..53164d9 100644
--- a/netstandard/log4net/project.json
+++ b/netstandard/log4net/project.json
@@ -16,7 +16,6 @@
"../../src/Appender/NetSendAppender.cs",
"../../src/Appender/RemotingAppender.cs",
"../../src/Appender/SmtpAppender.cs",
- "../../src/LogicalThreadContext.cs",
"../../src/Config/DOMConfigurator.cs",
"../../src/Config/DOMConfiguratorAttribute.cs",
"../../src/Config/Log4NetConfigurationSectionHandler.cs",
@@ -30,8 +29,6 @@
"../../src/Plugin/RemoteLoggingServerPlugin.cs",
"../../src/Util/PatternStringConverters/AppSettingPatternConverter.cs",
"../../src/Util/PatternStringConverters/EnvironmentFolderPathPatternConverter.cs",
- "../../src/Util/LogicalThreadContextProperties.cs",
- "../../src/Util/LogicalThreadContextStacks.cs",
"../../src/Util/NativeError.cs",
"../../src/Util/WindowsSecurityContext.cs"
]
diff --git a/src/Core/LoggingEvent.cs b/src/Core/LoggingEvent.cs
index fb9a506..eb54a60 100644
--- a/src/Core/LoggingEvent.cs
+++ b/src/Core/LoggingEvent.cs
@@ -461,7 +461,7 @@
#region Protected Instance Constructors
-#if !(NETCF || NETSTANDARD1_3)
+#if !NETCF
/// <summary>
/// Serialization constructor
@@ -814,7 +814,7 @@
{
if (m_data.ThreadName == null && this.m_cacheUpdatable)
{
-#if NETCF || NETSTANDARD1_3
+#if NETCF
// Get thread ID only
m_data.ThreadName = SystemInfo.CurrentThreadId.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
#else
@@ -1394,7 +1394,7 @@
{
compositeProperties.Add(m_eventProperties);
}
-#if !(NETCF || NETSTANDARD1_3)
+#if !NETCF
PropertiesDictionary logicalThreadProperties = LogicalThreadContext.Properties.GetProperties(false);
if (logicalThreadProperties != null)
{
diff --git a/src/Util/LogicalThreadContextProperties.cs b/src/Util/LogicalThreadContextProperties.cs
index fb9684f..904229d 100644
--- a/src/Util/LogicalThreadContextProperties.cs
+++ b/src/Util/LogicalThreadContextProperties.cs
@@ -21,8 +21,13 @@
#if !NETCF
using System;
+#if !NETSTANDARD1_3
using System.Runtime.Remoting.Messaging;
+#endif
using System.Security;
+#if NETSTANDARD1_3
+using System.Threading;
+#endif
namespace log4net.Util
{
@@ -49,7 +54,11 @@
/// <author>Nicko Cadell</author>
public sealed class LogicalThreadContextProperties : ContextPropertiesBase
{
+ #if NETSTANDARD1_3
+ private static readonly AsyncLocal<PropertiesDictionary> AsyncLocalDictionary = new AsyncLocal<PropertiesDictionary>();
+ #else
private const string c_SlotName = "log4net.Util.LogicalThreadContextProperties";
+ #endif
/// <summary>
/// Flag used to disable this context if we don't have permission to access the CallContext.
@@ -105,7 +114,7 @@
// need to be immutable to correctly flow through async/await
PropertiesDictionary immutableProps = new PropertiesDictionary(props);
immutableProps[key] = value;
- SetCallContextData(immutableProps);
+ SetLogicalProperties(immutableProps);
}
}
@@ -129,7 +138,7 @@
{
PropertiesDictionary immutableProps = new PropertiesDictionary(dictionary);
immutableProps.Remove(key);
- SetCallContextData(immutableProps);
+ SetLogicalProperties(immutableProps);
}
}
@@ -147,7 +156,7 @@
if (dictionary != null)
{
PropertiesDictionary immutableProps = new PropertiesDictionary();
- SetCallContextData(immutableProps);
+ SetLogicalProperties(immutableProps);
}
}
@@ -173,11 +182,11 @@
{
try
{
- PropertiesDictionary properties = GetCallContextData();
+ PropertiesDictionary properties = GetLogicalProperties();
if (properties == null && create)
{
properties = new PropertiesDictionary();
- SetCallContextData(properties);
+ SetLogicalProperties(properties);
}
return properties;
}
@@ -214,9 +223,11 @@
#if NET_4_0 || MONO_4_0
[System.Security.SecuritySafeCritical]
#endif
- private static PropertiesDictionary GetCallContextData()
+ private static PropertiesDictionary GetLogicalProperties()
{
-#if NET_2_0 || MONO_2_0 || MONO_3_5 || MONO_4_0
+#if NETSTANDARD1_3
+ return AsyncLocalDictionary.Value;
+#elif NET_2_0 || MONO_2_0 || MONO_3_5 || MONO_4_0
return CallContext.LogicalGetData(c_SlotName) as PropertiesDictionary;
#else
return CallContext.GetData(c_SlotName) as PropertiesDictionary;
@@ -235,9 +246,11 @@
#if NET_4_0 || MONO_4_0
[System.Security.SecuritySafeCritical]
#endif
- private static void SetCallContextData(PropertiesDictionary properties)
+ private static void SetLogicalProperties(PropertiesDictionary properties)
{
-#if NET_2_0 || MONO_2_0 || MONO_3_5 || MONO_4_0
+#if NETSTANDARD1_3
+ AsyncLocalDictionary.Value = properties;
+#elif NET_2_0 || MONO_2_0 || MONO_3_5 || MONO_4_0
CallContext.LogicalSetData(c_SlotName, properties);
#else
CallContext.SetData(c_SlotName, properties);
diff --git a/src/Util/PatternStringConverters/PropertyPatternConverter.cs b/src/Util/PatternStringConverters/PropertyPatternConverter.cs
index 9c2051e..3124b31 100644
--- a/src/Util/PatternStringConverters/PropertyPatternConverter.cs
+++ b/src/Util/PatternStringConverters/PropertyPatternConverter.cs
@@ -68,7 +68,7 @@
{
CompositeProperties compositeProperties = new CompositeProperties();
-#if !(NETCF || NETSTANDARD1_3)
+#if !NETCF
PropertiesDictionary logicalThreadProperties = LogicalThreadContext.Properties.GetProperties(false);
if (logicalThreadProperties != null)
{
diff --git a/src/site/xdoc/release/framework-support.xml b/src/site/xdoc/release/framework-support.xml
index 7cf2b34..f8e25b8 100644
--- a/src/site/xdoc/release/framework-support.xml
+++ b/src/site/xdoc/release/framework-support.xml
@@ -614,9 +614,6 @@
<li>anything related to ASP.NET (trace appender
and several pattern converters)</li>
<li>.NET Remoting</li>
- <li><code>log4net.LogicalThreadContext</code>
- and the associated properties and stack
- classes</li>
<li>the colored console appender</li>
<li>the event log appender</li>
<li>The <code>NetSendAppender</code></li>
@@ -630,6 +627,13 @@
<code>EnvironmentFolderPathPatternConverter</code></li>
<li>Impersonation of Windows accounts</li>
</ul>
+
+ <p><code>log4net.LogicalThreadContext</code> and
+ the associated properties and stack classes use
+ <code>AsyncLocal</code> rather than
+ <code>CallContext</code>. Prior to log4net 2.0.8
+ they haven't been supported for .NET Standard at
+ all.</p>
</section>
<section id="net1.0" name="Microsoft .NET Framework 1.0">
diff --git a/tests/src/Context/LogicalThreadContextTest.cs b/tests/src/Context/LogicalThreadContextTest.cs
index f3f0a19..849d963 100644
--- a/tests/src/Context/LogicalThreadContextTest.cs
+++ b/tests/src/Context/LogicalThreadContextTest.cs
@@ -17,7 +17,7 @@
//
#endregion
-#if NET_4_5
+#if NET_4_5 || NETSTANDARD1_3
using System;
using System.Threading.Tasks;
using System.Linq;
diff --git a/tests/src/Utils.cs b/tests/src/Utils.cs
index 5cbfb9c..14e55d3 100644
--- a/tests/src/Utils.cs
+++ b/tests/src/Utils.cs
@@ -114,7 +114,7 @@
internal static void RemovePropertyFromAllContexts() {
GlobalContext.Properties.Remove(PROPERTY_KEY);
ThreadContext.Properties.Remove(PROPERTY_KEY);
-#if !(NETCF || NETSTANDARD1_3)
+#if !NETCF
LogicalThreadContext.Properties.Remove(PROPERTY_KEY);
#endif
}