Skip to content

Commit

Permalink
Merge pull request serilog#648 from colin-young/logcontext-588
Browse files Browse the repository at this point in the history
Enable LogContext for .Net Core.
  • Loading branch information
nblumhardt committed Feb 16, 2016
2 parents 946f0c9 + c65dc5b commit 505f6c8
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 42 deletions.
14 changes: 14 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
for path in src/*/project.json; do
dirname="$(dirname "${path}")"
dnu restore ${dirname}
dnu build ${dirname} --framework dotnet5.4 --configuration Release --out .\artifacts\testbin;
dnu pack ${dirname} --framework dotnet5.4 --configuration Release --out .\artifacts\packages;
done

for path in test/*/project.json; do
dirname="$(dirname "${path}")"
dnu restore ${dirname}
dnu build ${dirname} --framework dotnet5.4 --configuration Release --out .\artifacts\testbin;
dnx -p ${dirname} test;
done

19 changes: 13 additions & 6 deletions src/Serilog/Context/ImmutableStack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,36 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#if LOGCONTEXT
using System;
using System.Collections.Generic;
#if REMOTING
using System.Runtime.Serialization;
#endif

namespace Serilog.Context
{
// Needed because of the shallow-copying behaviour of
// LogicalCallContext.
#if REMOTING
[Serializable]
class ImmutableStack<T> : IEnumerable<T>, ISerializable
#else
class ImmutableStack<T> : IEnumerable<T>
#endif
{
readonly ImmutableStack<T> _under;
readonly T _top;

#if REMOTING
public ImmutableStack(SerializationInfo info, StreamingContext context)
{
}

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
#endif

ImmutableStack()
{
}
Expand Down Expand Up @@ -68,9 +79,5 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

public T Top => _top;

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
}
#endif
}
104 changes: 85 additions & 19 deletions src/Serilog/Context/LogContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#if LOGCONTEXT

using System;
#if REMOTING
using System.Runtime.Remoting.Messaging;
#endif
#if ASYNCLOCAL
using System.Collections.Generic;
using System.Threading;
#endif
using Serilog.Core;
using Serilog.Core.Enrichers;
using Serilog.Events;
Expand Down Expand Up @@ -42,19 +48,24 @@ namespace Serilog.Context
/// </code>
/// </example>
/// <remarks>The scope of the context is the current logical thread, using
/// <see cref="CallContext.LogicalGetData"/> (and so is
/// preserved across async/await calls).</remarks>
#if ASYNCLOCAL
/// <seealso cref="AsyncLocal{T}"/>
#else
/// <seealso cref="CallContext.LogicalGetData"/>
#endif
/// (and so is preserved across async/await calls).</remarks>
public static class LogContext
{
#if ASYNCLOCAL
static readonly AsyncLocal<ImmutableStack<ILogEventEnricher>> Data = new AsyncLocal<ImmutableStack<ILogEventEnricher>>();
#else
#if DOTNET5_1
[ThreadStatic]
static ImmutableStack<ILogEventEnricher> Data;
#else
static readonly string DataSlotName = typeof(LogContext).FullName;

/// <summary>
/// When calling into appdomains without Serilog loaded, e.g. via remoting or during unit testing,
/// it may be necesary to set this value to true so that serialization exceptions are avoided. When possible,
/// using the <see cref="Suspend"/> method in a using block around the call has a lower overhead and
/// should be preferred.
/// </summary>
public static bool PermitCrossAppDomainCalls { get; set; }
#endif
#endif

/// <summary>
/// Push a property onto the context, returning an <see cref="IDisposable"/>
Expand Down Expand Up @@ -132,33 +143,87 @@ static ImmutableStack<ILogEventEnricher> GetOrCreateEnricherStack()
return enrichers;
}

static ImmutableStack<ILogEventEnricher> Enrichers
#if ASYNCLOCAL
static ImmutableStack<ILogEventEnricher> Enrichers
{
get
{
return Data.Value;
}
set
{
Data.Value = GetContext(value);
}
}
#else

#if DOTNET5_1
static ImmutableStack<ILogEventEnricher> Enrichers
{
get
{
return Data;
}
set
{
Data = GetContext(value);
}
}

#else
static ImmutableStack<ILogEventEnricher> Enrichers
{
get
{
var data = CallContext.LogicalGetData(DataSlotName);

ImmutableStack<ILogEventEnricher> context;
#if REMOTING
if (PermitCrossAppDomainCalls)
{
context = ((Wrapper) data)?.Value;
context = ((Wrapper)data)?.Value;
}
else
{
context = (ImmutableStack<ILogEventEnricher>)data;
}

#else
context = data;
#endif
return context;
}
set
{

var context = !PermitCrossAppDomainCalls ? (object)value : new Wrapper { Value = value };

var context = GetContext(value);
CallContext.LogicalSetData(DataSlotName, context);
}
}
#endif
#endif

#if REMOTING
/// <summary>
/// When calling into appdomains without Serilog loaded, e.g. via remoting or during unit testing,
/// it may be necesary to set this value to true so that serialization exceptions are avoided. When possible,
/// using the <see cref="Suspend"/> method in a using block around the call has a lower overhead and
/// should be preferred.
/// </summary>
public static bool PermitCrossAppDomainCalls { get; set; }

static object GetContext(ImmutableStack<ILogEventEnricher> value)
{
var context = !PermitCrossAppDomainCalls ? (object) value : new Wrapper
{
Value = value
};
return context;
}
#else
static ImmutableStack<ILogEventEnricher> GetContext(ImmutableStack<ILogEventEnricher> value)
{
return value;
}
#endif

internal static void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
Expand Down Expand Up @@ -187,10 +252,11 @@ public void Dispose()
}
}

#if REMOTING
sealed class Wrapper : MarshalByRefObject
{
public ImmutableStack<ILogEventEnricher> Value { get; set; }
}
}
}
#endif
}
}
2 changes: 0 additions & 2 deletions src/Serilog/Enrichers/LogContextEnricher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#if LOGCONTEXT
using Serilog.Context;
using Serilog.Core;
using Serilog.Events;
Expand All @@ -27,4 +26,3 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
}
}
}
#endif
2 changes: 0 additions & 2 deletions src/Serilog/LoggerConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ namespace Serilog
public static class LoggerConfigurationExtensions
{

#if LOGCONTEXT
/// <summary>
/// Enrich log events with properties from <see cref="Context.LogContext"/>.
/// </summary>
Expand All @@ -53,7 +52,6 @@ public static LoggerConfiguration FromLogContext(
if (enrichmentConfiguration == null) throw new ArgumentNullException(nameof(enrichmentConfiguration));
return enrichmentConfiguration.With<LogContextEnricher>();
}
#endif

/// <summary>
/// Enrich log events with a ThreadId property containing the current <see cref="Thread.ManagedThreadId"/>.
Expand Down
7 changes: 5 additions & 2 deletions src/Serilog/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"net45": {
"compilationOptions": {
"keyFile": "../../assets/Serilog.snk",
"define": [ "APPSETTINGS", "LOGCONTEXT", "PROCESS", "FILE_IO", "PERIODIC_BATCHING" ]
"define": [ "APPSETTINGS", "PROCESS", "FILE_IO", "PERIODIC_BATCHING", "REMOTING" ]
},
"frameworkAssemblies": {
"System.Configuration": ""
Expand All @@ -30,6 +30,7 @@
"System.Linq": "4.0.1-beta-23516",
"System.Reflection.Extensions": "4.0.1-beta-23516",
"System.Runtime.Extensions": "4.0.11-beta-23516",
"System.Runtime.Serialization.Primitives": "4.1.0-beta-23516",
"System.Text.RegularExpressions": "4.0.11-beta-23516",
"System.Threading": "4.0.11-beta-23516",
"System.Threading.Thread": "4.0.0-beta-23516"
Expand All @@ -38,7 +39,8 @@
"dotnet5.4": {
"compilationOptions": {
"keyFile": "../../assets/Serilog.snk",
"define": [ "PROCESS", "FILE_IO", "PERIODIC_BATCHING", "NO_TIMER", "NO_APPDOMAIN" ]
"define": [ "ASYNCLOCAL", "PROCESS", "FILE_IO", "PERIODIC_BATCHING", "NO_TIMER", "NO_APPDOMAIN", "USERNAMEFROMENV" ]

},
"dependencies": {
"Microsoft.CSharp": "4.0.1-beta-23516",
Expand All @@ -52,6 +54,7 @@
"System.Linq": "4.0.1-beta-23516",
"System.Reflection.Extensions": "4.0.1-beta-23516",
"System.Runtime.Extensions": "4.0.11-beta-23516",
"System.Runtime.Serialization.Primitives": "4.1.0-beta-23516",
"System.Text.RegularExpressions": "4.0.11-beta-23516",
"System.Threading": "4.0.11-beta-23516",
"System.Threading.Thread": "4.0.0-beta-23516"
Expand Down
18 changes: 14 additions & 4 deletions test/Serilog.Tests/Context/LogContextTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#if LOGCONTEXT
using System;
using System;
using System.IO;
using System.Runtime.Remoting.Messaging;
using Xunit;
using Serilog.Context;
using Serilog.Events;
using Serilog.Core.Enrichers;
using Serilog.Tests.Support;
#if REMOTING
using System.Runtime.Remoting.Messaging;
#endif
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -16,8 +17,12 @@ public class LogContextTests
{
public LogContextTests()
{
#if REMOTING
LogContext.PermitCrossAppDomainCalls = false;
#endif
#if !ASYNCLOCAL
CallContext.LogicalSetData(typeof(LogContext).FullName, null);
#endif
}

[Fact]
Expand Down Expand Up @@ -109,6 +114,7 @@ public async Task ContextPropertiesCrossAsyncCalls()
}
}

#if REMOTING
[Fact]
public async Task ContextPropertiesPersistWhenCrossAppDomainCallsAreEnabled()
{
Expand Down Expand Up @@ -137,7 +143,9 @@ public async Task ContextPropertiesPersistWhenCrossAppDomainCallsAreEnabled()
Assert.NotSame(pre, post);
}
}
#endif

#if !NO_APPDOMAIN
// Must not actually try to pass context across domains,
// since user property types may not be serializable.
// Fails if the Serilog assemblies cannot be loaded in the
Expand Down Expand Up @@ -180,6 +188,7 @@ public void DoesNotPreventCrossDomainCalls()
AppDomain.Unload(domain);
}
}
#endif

[Fact]
public void WhenSuspendedAllPropertiesAreRemovedFromTheContext()
Expand All @@ -205,6 +214,7 @@ public void WhenSuspendedAllPropertiesAreRemovedFromTheContext()
}
}

#if REMOTING
public class RemotelyCallable : MarshalByRefObject
{
public bool IsCallable()
Expand All @@ -223,5 +233,5 @@ public bool IsCallable()
return s == "42";
}
}
}
#endif
}
2 changes: 0 additions & 2 deletions test/Serilog.Tests/Settings/AppSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ public void ProcessIdEnricherIsApplied()
}
#endif

#if LOGCONTEXT
[Fact]
public void LogContextEnricherIsApplied()
{
Expand All @@ -172,7 +171,6 @@ public void LogContextEnricherIsApplied()
Assert.NotNull(evt.Properties["A"].LiteralValue() as int?);
Assert.Equal(1, (int)evt.Properties["A"].LiteralValue());
}
#endif
}
}
#endif
2 changes: 0 additions & 2 deletions test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ public void FindsEventEnrichersWithinAnAssembly()
.ToList();


#if LOGCONTEXT
Assert.True(eventEnrichers.Contains("FromLogContext"));
#endif
#if !DOTNET5_1
Assert.True(eventEnrichers.Contains("WithEnvironmentUserName"));
Assert.True(eventEnrichers.Contains("WithMachineName"));
Expand Down
Loading

0 comments on commit 505f6c8

Please sign in to comment.