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
         }