Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional logs injection fallback for NLog 1.x #1614

Merged
merged 3 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/Datadog.Trace/Logging/CustomNLogLogProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@

namespace Datadog.Trace.Logging
{
/// <summary>
/// <para>
/// Log provider that performs more efficient logs injection by adding a custom type
/// into the NLog MDC which can later be rendered with the properties of the active
/// Datadog scope.
/// </para>
///
/// <para>
/// Note: This logger is intended to be used when the application uses NLog &gt;= 4.1.
/// When the application uses NLog versions older than 4.1, use
/// <see cref="FallbackNLogLogProvider"/> which utilizes the original
/// Set(string, string) API to perform logs injection.
/// </para>
/// </summary>
internal class CustomNLogLogProvider : NLogLogProvider, ILogProviderWithEnricher
{
public ILogEnricher CreateEnricher() => new LogEnricher(this);
Expand Down Expand Up @@ -68,7 +82,7 @@ protected override OpenMdc GetOpenMdcMethod()
private static bool IsSetObjectAvailable()
{
var mdcContextType = FindType("NLog.MappedDiagnosticsContext", "NLog");
return mdcContextType.GetMethod("Set", typeof(string), typeof(object)) != null;
return mdcContextType?.GetMethod("Set", typeof(string), typeof(object)) != null;
}
}
}
82 changes: 82 additions & 0 deletions src/Datadog.Trace/Logging/FallbackNLogLogProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// <copyright file="FallbackNLogLogProvider.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using System.Linq.Expressions;
using Datadog.Trace.Logging.LogProviders;

namespace Datadog.Trace.Logging
{
/// <summary>
/// <para>
/// Log provider that enhances the built-in LibLog NLogLogProvider by adding
/// MDC support for NLog 1.0. The built-in NLogLogProvider only looked for
/// API's present on NLog 2.0 and newer.
/// </para>
///
/// <para>
/// Note: This logger is intended to be used when the application uses NLog &lt; 4.1.
/// When the application uses NLog versions 4.1 and newer, use
/// <see cref="CustomNLogLogProvider"/> which utilizes the Set(string, object)
/// API to perform logs injection more efficiently.
/// </para>
/// </summary>
internal class FallbackNLogLogProvider : NLogLogProvider
{
protected override OpenMdc GetOpenMdcMethod()
{
// This is a copy/paste of the base GetOpenMdcMethod, with an additional NLog 1.x fallback
var keyParam = Expression.Parameter(typeof(string), "key");

var ndlcContextType = FindType("NLog.NestedDiagnosticsLogicalContext", "NLog");
if (ndlcContextType != null)
{
var pushObjectMethod = ndlcContextType.GetMethod("PushObject", typeof(object));
if (pushObjectMethod != null)
{
// NLog 4.6 introduces SetScoped with correct handling of logical callcontext (MDLC)
var mdlcContextType = FindType("NLog.MappedDiagnosticsLogicalContext", "NLog");
if (mdlcContextType != null)
{
var setScopedMethod = mdlcContextType.GetMethod("SetScoped", typeof(string), typeof(object));
if (setScopedMethod != null)
{
var valueObjParam = Expression.Parameter(typeof(object), "value");
var setScopedMethodCall = Expression.Call(null, setScopedMethod, keyParam, valueObjParam);
var setMethodLambda = Expression.Lambda<Func<string, object, IDisposable>>(setScopedMethodCall, keyParam, valueObjParam).Compile();
return (key, value, _) => setMethodLambda(key, value);
}
}
}
}

var mdcContextType = FindType("NLog.MappedDiagnosticsContext", "NLog");
if (mdcContextType is null)
{
// Modification: Add fallback for NLog version 1.x
mdcContextType = FindType("NLog.MDC", "NLog");
}

var setMethod = mdcContextType.GetMethod("Set", typeof(string), typeof(string));
var removeMethod = mdcContextType.GetMethod("Remove", typeof(string));
var valueParam = Expression.Parameter(typeof(string), "value");
var setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam);
var removeMethodCall = Expression.Call(null, removeMethod, keyParam);

var set = Expression
.Lambda<Action<string, string>>(setMethodCall, keyParam, valueParam)
.Compile();
var remove = Expression
.Lambda<Action<string>>(removeMethodCall, keyParam)
.Compile();

return (key, value, _) =>
{
set(key, value.ToString());
return new DisposableAction(() => remove(key));
};
}
}
}
24 changes: 15 additions & 9 deletions src/Datadog.Trace/Logging/LibLogScopeEventSubscriber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,26 +264,32 @@ private static void InitResolvers()
// - NLog
// - Log4net

// Register the custom log4net provider
// Register the custom Serilog provider
LogProvider.LogProviderResolvers.Insert(
0,
Tuple.Create<LogProvider.IsLoggerAvailable, LogProvider.CreateLogProvider>(
CustomLog4NetLogProvider.IsLoggerAvailable,
() => new CustomLog4NetLogProvider()));
CustomSerilogLogProvider.IsLoggerAvailable,
() => new CustomSerilogLogProvider()));

// Register the custom NLog provider
// Register the custom NLog providers
LogProvider.LogProviderResolvers.Insert(
0,
1,
Tuple.Create<LogProvider.IsLoggerAvailable, LogProvider.CreateLogProvider>(
CustomNLogLogProvider.IsLoggerAvailable,
() => new CustomNLogLogProvider()));

// Register the custom Serilog provider
LogProvider.LogProviderResolvers.Insert(
0,
2,
Tuple.Create<LogProvider.IsLoggerAvailable, LogProvider.CreateLogProvider>(
CustomSerilogLogProvider.IsLoggerAvailable,
() => new CustomSerilogLogProvider()));
FallbackNLogLogProvider.IsLoggerAvailable,
() => new FallbackNLogLogProvider()));

// Register the custom log4net provider
LogProvider.LogProviderResolvers.Insert(
3,
Tuple.Create<LogProvider.IsLoggerAvailable, LogProvider.CreateLogProvider>(
CustomLog4NetLogProvider.IsLoggerAvailable,
() => new CustomLog4NetLogProvider()));
}

private void SetDefaultValues()
Expand Down