Skip to content

Commit ad14b2e

Browse files
snakefootyupliner
authored andcommitted
LibLog - Fixed NLog + Log4net callsite. Added support for NLog structured logging (#876)
1 parent 0713e84 commit ad14b2e

File tree

1 file changed

+78
-57
lines changed

1 file changed

+78
-57
lines changed

Source/EasyNetQ/Logging/LibLog.cs

Lines changed: 78 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "EasyNetQ.Logging")]
4545
[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "EasyNetQ.Logging.Logger.#Invoke(EasyNetQ.Logging.LogLevel,System.Func`1<System.String>,System.Exception,System.Object[])")]
4646

47-
// If you copied this file manually, you need to change all "EasyNetQ" so not to clash with other libraries
47+
// If you copied this file manually, you need to change all "YourRootNameSpace" so not to clash with other libraries
4848
// that use LibLog
4949
#if LIBLOG_PROVIDERS_ONLY
5050
namespace EasyNetQ.LibLog
@@ -618,7 +618,7 @@ static ILog GetCurrentClassLogger()
618618
static ILog GetLogger(Type type, string fallbackTypeName = "System.Object")
619619
{
620620
// If the type passed in is null then fallback to the type name specified
621-
return GetLogger(type != null ? type.FullName : fallbackTypeName);
621+
return GetLogger(type != null ? type.ToString() : fallbackTypeName);
622622
}
623623

624624
/// <summary>
@@ -1018,7 +1018,7 @@ internal class NLogLogger
10181018
{
10191019
private readonly dynamic _logger;
10201020

1021-
private static Func<string, object, string, Exception, object> _logEventInfoFact;
1021+
private static Func<string, object, string, object[], Exception, object> _logEventInfoFact;
10221022

10231023
private static readonly object _levelTrace;
10241024
private static readonly object _levelDebug;
@@ -1027,6 +1027,8 @@ internal class NLogLogger
10271027
private static readonly object _levelError;
10281028
private static readonly object _levelFatal;
10291029

1030+
private static readonly bool _structuredLoggingEnabled;
1031+
10301032
static NLogLogger()
10311033
{
10321034
try
@@ -1057,6 +1059,7 @@ static NLogLogger()
10571059
ParameterExpression loggerNameParam = Expression.Parameter(typeof(string));
10581060
ParameterExpression levelParam = Expression.Parameter(typeof(object));
10591061
ParameterExpression messageParam = Expression.Parameter(typeof(string));
1062+
ParameterExpression messageArgsParam = Expression.Parameter(typeof(object[]));
10601063
ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception));
10611064
UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType);
10621065

@@ -1066,12 +1069,14 @@ static NLogLogger()
10661069
loggerNameParam,
10671070
Expression.Constant(null, typeof(IFormatProvider)),
10681071
messageParam,
1069-
Expression.Constant(null, typeof(object[])),
1072+
messageArgsParam,
10701073
exceptionParam
10711074
);
10721075

1073-
_logEventInfoFact = Expression.Lambda<Func<string, object, string, Exception, object>>(newLoggingEventExpression,
1074-
loggerNameParam, levelParam, messageParam, exceptionParam).Compile();
1076+
_logEventInfoFact = Expression.Lambda<Func<string, object, string, object[], Exception, object>>(newLoggingEventExpression,
1077+
loggerNameParam, levelParam, messageParam, messageArgsParam, exceptionParam).Compile();
1078+
1079+
_structuredLoggingEnabled = IsStructuredLoggingEnabled();
10751080
}
10761081
catch { }
10771082
}
@@ -1089,17 +1094,25 @@ public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception
10891094
return IsLogLevelEnable(logLevel);
10901095
}
10911096

1092-
var callsiteMessageFunc = messageFunc;
1093-
messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters);
1094-
10951097
if (_logEventInfoFact != null)
10961098
{
10971099
if (IsLogLevelEnable(logLevel))
10981100
{
1101+
string formatMessage = messageFunc();
1102+
if (!_structuredLoggingEnabled)
1103+
{
1104+
IEnumerable<string> patternMatches;
1105+
formatMessage =
1106+
LogMessageFormatter.FormatStructuredMessage(formatMessage,
1107+
formatParameters,
1108+
out patternMatches);
1109+
formatParameters = null; // Has been formatted, no need for parameters
1110+
}
1111+
10991112
Type callsiteLoggerType = typeof(NLogLogger);
11001113
#if !LIBLOG_PORTABLE
11011114
// Callsite HACK - Extract the callsite-logger-type from the messageFunc
1102-
var methodType = callsiteMessageFunc.Method.DeclaringType;
1115+
var methodType = messageFunc.Method.DeclaringType;
11031116
if (methodType == typeof(LogExtensions) || (methodType != null && methodType.DeclaringType == typeof(LogExtensions)))
11041117
{
11051118
callsiteLoggerType = typeof(LogExtensions);
@@ -1110,13 +1123,14 @@ public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception
11101123
}
11111124
#endif
11121125
var nlogLevel = this.TranslateLevel(logLevel);
1113-
var nlogEvent = _logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception);
1126+
var nlogEvent = _logEventInfoFact(_logger.Name, nlogLevel, formatMessage, formatParameters, exception);
11141127
_logger.Log(callsiteLoggerType, nlogEvent);
11151128
return true;
11161129
}
11171130
return false;
11181131
}
11191132

1133+
messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters);
11201134
if (exception != null)
11211135
{
11221136
return LogException(logLevel, messageFunc, exception);
@@ -1260,6 +1274,33 @@ private object TranslateLevel(LogLevel logLevel)
12601274
throw new ArgumentOutOfRangeException("logLevel", logLevel, null);
12611275
}
12621276
}
1277+
1278+
private static bool IsStructuredLoggingEnabled()
1279+
{
1280+
var configFactoryType = Type.GetType("NLog.Config.ConfigurationItemFactory, NLog");
1281+
if (configFactoryType != null)
1282+
{
1283+
PropertyInfo parseMessagesProperty = configFactoryType.GetPropertyPortable("ParseMessageTemplates");
1284+
if (parseMessagesProperty != null)
1285+
{
1286+
PropertyInfo defaultProperty = configFactoryType.GetPropertyPortable("Default");
1287+
if (defaultProperty != null)
1288+
{
1289+
object configFactoryDefault = defaultProperty.GetValue(null, null);
1290+
if (configFactoryDefault != null)
1291+
{
1292+
Nullable<bool> parseMessageTemplates = parseMessagesProperty.GetValue(configFactoryDefault, null) as Nullable<bool>;
1293+
if (parseMessageTemplates != false)
1294+
{
1295+
return true;
1296+
}
1297+
}
1298+
}
1299+
}
1300+
}
1301+
1302+
return false;
1303+
}
12631304
}
12641305
}
12651306

@@ -1380,8 +1421,6 @@ private static Func<string, object> GetGetLoggerMethodCall()
13801421
internal class Log4NetLogger
13811422
{
13821423
private readonly dynamic _logger;
1383-
private static Type s_callerStackBoundaryType;
1384-
private static readonly object CallerStackBoundaryTypeSync = new object();
13851424

13861425
private static readonly object _levelDebug;
13871426
private static readonly object _levelInfo;
@@ -1551,41 +1590,31 @@ public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception
15511590
return false;
15521591
}
15531592

1554-
string message = messageFunc();
1555-
15561593
IEnumerable<string> patternMatches;
1557-
15581594
string formattedMessage =
1559-
LogMessageFormatter.FormatStructuredMessage(message,
1595+
LogMessageFormatter.FormatStructuredMessage(messageFunc(),
15601596
formatParameters,
15611597
out patternMatches);
15621598

1563-
// determine correct caller - this might change due to jit optimizations with method inlining
1564-
if (s_callerStackBoundaryType == null)
1565-
{
1566-
lock (CallerStackBoundaryTypeSync)
1567-
{
1599+
Type callerStackBoundaryType = typeof(Log4NetLogger);
15681600
#if !LIBLOG_PORTABLE
1569-
StackTrace stack = new StackTrace();
1570-
Type thisType = GetType();
1571-
s_callerStackBoundaryType = Type.GetType("LoggerExecutionWrapper");
1572-
for (var i = 1; i < stack.FrameCount; i++)
1573-
{
1574-
if (!IsInTypeHierarchy(thisType, stack.GetFrame(i).GetMethod().DeclaringType))
1575-
{
1576-
s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType;
1577-
break;
1578-
}
1579-
}
1601+
// Callsite HACK - Extract the callsite-logger-type from the messageFunc
1602+
var methodType = messageFunc.Method.DeclaringType;
1603+
if (methodType == typeof(LogExtensions) || (methodType != null && methodType.DeclaringType == typeof(LogExtensions)))
1604+
{
1605+
callerStackBoundaryType = typeof(LogExtensions);
1606+
}
1607+
else if (methodType == typeof(LoggerExecutionWrapper) || (methodType != null && methodType.DeclaringType == typeof(LoggerExecutionWrapper)))
1608+
{
1609+
callerStackBoundaryType = typeof(LoggerExecutionWrapper);
1610+
}
15801611
#else
1581-
s_callerStackBoundaryType = typeof (LoggerExecutionWrapper);
1612+
callerStackBoundaryType = typeof(LoggerExecutionWrapper);
15821613
#endif
1583-
}
1584-
}
15851614

15861615
var translatedLevel = TranslateLevel(logLevel);
15871616

1588-
object loggingEvent = _createLoggingEvent(_logger, s_callerStackBoundaryType, translatedLevel, formattedMessage, exception);
1617+
object loggingEvent = _createLoggingEvent(_logger, callerStackBoundaryType, translatedLevel, formattedMessage, exception);
15891618

15901619
PopulateProperties(loggingEvent, patternMatches, formatParameters);
15911620

@@ -1596,27 +1625,17 @@ public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception
15961625

15971626
private void PopulateProperties(object loggingEvent, IEnumerable<string> patternMatches, object[] formatParameters)
15981627
{
1599-
IEnumerable<KeyValuePair<string, object>> keyToValue =
1628+
if (patternMatches.Count() > 0)
1629+
{
1630+
IEnumerable<KeyValuePair<string, object>> keyToValue =
16001631
patternMatches.Zip(formatParameters,
16011632
(key, value) => new KeyValuePair<string, object>(key, value));
16021633

1603-
foreach (KeyValuePair<string, object> keyValuePair in keyToValue)
1604-
{
1605-
_loggingEventPropertySetter(loggingEvent, keyValuePair.Key, keyValuePair.Value);
1606-
}
1607-
}
1608-
1609-
private static bool IsInTypeHierarchy(Type currentType, Type checkType)
1610-
{
1611-
while (currentType != null && currentType != typeof(object))
1612-
{
1613-
if (currentType == checkType)
1634+
foreach (KeyValuePair<string, object> keyValuePair in keyToValue)
16141635
{
1615-
return true;
1636+
_loggingEventPropertySetter(loggingEvent, keyValuePair.Key, keyValuePair.Value);
16161637
}
1617-
currentType = currentType.GetBaseTypePortable();
16181638
}
1619-
return false;
16201639
}
16211640

16221641
private bool IsLogLevelEnable(LogLevel logLevel)
@@ -1790,7 +1809,6 @@ public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception
17901809
return _shouldLog(_loggerName, severity);
17911810
}
17921811

1793-
17941812
messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters);
17951813
if (exception != null)
17961814
{
@@ -2297,14 +2315,13 @@ private static string ReplaceFirst(string text, string search, string replace)
22972315

22982316
public static string FormatStructuredMessage(string targetMessage, object[] formatParameters, out IEnumerable<string> patternMatches)
22992317
{
2300-
if (formatParameters.Length == 0)
2318+
if (formatParameters == null || formatParameters.Length == 0)
23012319
{
23022320
patternMatches = Enumerable.Empty<string>();
23032321
return targetMessage;
23042322
}
23052323

2306-
List<string> processedArguments = new List<string>();
2307-
patternMatches = processedArguments;
2324+
List<string> processedArguments = null;
23082325

23092326
foreach (Match match in Pattern.Matches(targetMessage))
23102327
{
@@ -2313,6 +2330,7 @@ public static string FormatStructuredMessage(string targetMessage, object[] form
23132330
int notUsed;
23142331
if (!int.TryParse(arg, out notUsed))
23152332
{
2333+
processedArguments = processedArguments ?? new List<string>(formatParameters.Length);
23162334
int argumentIndex = processedArguments.IndexOf(arg);
23172335
if (argumentIndex == -1)
23182336
{
@@ -2321,9 +2339,12 @@ public static string FormatStructuredMessage(string targetMessage, object[] form
23212339
}
23222340

23232341
targetMessage = ReplaceFirst(targetMessage, match.Value,
2324-
"{" + argumentIndex + match.Groups["format"].Value + "}");
2342+
string.Concat("{", argumentIndex.ToString(), match.Groups["format"].Value, "}"));
23252343
}
23262344
}
2345+
2346+
patternMatches = processedArguments ?? Enumerable.Empty<string>();
2347+
23272348
try
23282349
{
23292350
return string.Format(CultureInfo.InvariantCulture, targetMessage, formatParameters);

0 commit comments

Comments
 (0)