I have inherited an ASP.NET application which is using NLog for logging. One of the logged attributes is named module which is hardcoded to "Core". This is not useful at all, because all parts of the application (eg health check, various business contexts) push the same value.
I am trying to use structured logging to override this value in some cases (I cannot afford to refactor the entire logging now).
public static void LogInfoWithModule<T>(this ILogger<T> logger, string message, string module)
{
var escapedMessage = message.Replace("{", "{{");
escapedMessage = escapedMessage.Replace("}", "}}");
logger.Log(LogLevel.Information, "[{module}] " + escapedMessage, module);
}
logger.LogInfoWithModule($"Health check response = {response}", Constants.LoggingModules.Health);
<target name="logJson" xsi:type="File">
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="module" layout="${when:when='${event-properties:item=module}'=='':Core" />
</layout>
</target>
I get want want, that is all existing calls will not provide the module and they will work as before. Any logging passing through LogInfoWithModule
will override the default value.
However, I find this solution very messy because:
I have tried the solution suggested here (and here ), but ${mdlc:item=module}
does not contain anything.
The easy way, but will have a performance hit (Notice ${mdlc:item=module}
is case-sensitive)
public static void LogInfoWithModule<T>(this ILogger<T> logger, string message, string module)
{
using (logger.BeginScope(new [] { new KeyValuePair<string,object>("module", module) }))
{
logger.Log(LogLevel.Information, message);
}
}
The advanced way for injecting properties (Notice ${event-properties:module}
is case-sensitive):
public static void LogInfoWithModule<T>(this ILogger<T> logger, string message, string module)
{
logger.Log(LogLevel.Information, default(EventId), new ModuleLogEvent(message, module), default(Exception), ModuleLogEvent.Formatter);
}
class ModuleLogEvent : IReadOnlyList<KeyValuePair<string, object>>
{
public static Func<ModuleLogEvent, Exception, string> Formatter { get; } = (l, ex) => l.Message;
public string Message { get; }
public string Module { get; }
public MyLogEvent(string message, string module)
{
Message = message;
Module = module;
}
public override string ToString() => Message;
// IReadOnlyList-interface
public int Count => 1;
public KeyValuePair<string, object> this[int index] => new KeyValuePair<string,object>("module", Module);
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => yield return new KeyValuePair<string,object>("module", Module);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Beware typos and compile-errors might be included in the above example code.
Alternative you might find happiness here: https://github.com/viktor-nikolaev/XeonApps.Extensions.Logging.WithProperty :
public static class LoggingExtensions
{
public static NLog.Logger WithModule(this NLog.Logger logger, object propertyValue) =>
logger.WithProperty("module", propertyValue);
public static ILogger WithModule(this ILogger logger, object propertyValue) =>
logger.WithProperty("module", propertyValue);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.