Hi, I have been able to hook nlog into an asp.net core application as described here
Now i need to do add log event info properties like described here but how do i do that in the action method. The issue is that Ilogger does not accept LogEventInfo object.
How do i pass this to the nloger through Ilogger interface? or otherwise?
You could try and inject the LogEventInfo object as a parameter that is not referenced by the Log-Text:
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "");
theEvent.Properties["MyValue"] = "My custom string";
_logger.LogInformation("Index page says hello", theEvent);
Then it should automatically merge the Properties from theEvent.
@snakefoot It does not seem to do that. I am trying to create a custom layout
[LayoutRenderer("custom")]
public class CustomLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(logEvent.Properties["MyValue"]);
}
}
But the logeventinfo object has null parameters when i debug. theEvents is not added to the parameters like you said.
@yehonathan I wrote:
automatically merge the Properties from theEvent
This means that the properties you add to theEvent will be appended to the Properties-dictionary of the actual LogEventInfo.
Instead of checking the LogEventInfo.Parameters then you should look at LogEventInfo.Properties.
@yehonathan and @304NotModified I now see my mistake. I thought that NLog was able to handle the Microsoft-ILogger parameters, but it only handles the formatted message.
The following code is missing from NLogLogger.cs (NLog.Extensions):
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
...
var eventInfo = LogEventInfo.Create(nLogLogLevel, _logger.Name, message);
eventInfo.Exception = exception;
// Only allocate LogEventInfo-Properties-Dictionary if needed
if (eventId.Id != 0 || eventId.Name != null)
{
// TODO Cache string.Concat() result, instead of always making string-allocations
eventInfo.Properties["EventId" + _options.EventIdSeparator + "Id"] = eventId.Id;
eventInfo.Properties["EventId" + _options.EventIdSeparator + "Name"] = eventId.Name;
eventInfo.Properties["EventId"] = eventId;
}
var formattedLogValues = state as IReadOnlyList<KeyValuePair<string, object>>;
if (formattedLogValues != null)
{
for (int i = 0; i < formattedLogValues.Count; ++i)
{
var logEvent = formattedLogValues[i].Value as NLog.LogEventInfo;
if (logEvent != null && logEvent.HasProperties)
{
foreach (var prop in logEvent.Properties)
{
eventInfo.Properties.Add(prop.Key, prop.Value);
}
}
}
}
_logger.Log(eventInfo);
}
Ofcourse the above code is completely untested, so I have no idea if it works.
@yehonathan and @304NotModified I now see my mistake. I thought that NLog was able to handle the Microsoft-ILogger parameters, but it only handles the formatted message
I was also thinking it should work. But I think the formatter (see code) is breaking it.
var formattedLogValues = state as IReadOnlyList
>;
I'm not keen on this. It's a breaking change and I think it's also influence performance. Also I doubting if we should use the code that's already in NLog (nlog.dll), e.g. by passing the logeventinfo to it.
Another option is to create a new class (e.g. LogProperties), that will be captures here.
(so var formattedLogValues = state as LogProperties;)
That is backwardscompatible.
also, isn't eventid=0 not a valid evenid?
@304NotModified Yes ofcourse that would be so much simpler:
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
...
var eventInfo = state as NLog.LogEventInfo;
if (eventInfo == null)
{
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
var message = formatter(state, exception);
eventInfo = LogEventInfo.Create(nLogLogLevel, _logger.Name, message);
eventInfo.Exception = exception;
// Only allocate LogEventInfo-Properties-Dictionary if needed
if (eventId.Id != 0 || eventId.Name != null)
{
// TODO Cache string.Concat() result, instead of always making string-allocations
eventInfo.Properties["EventId" + _options.EventIdSeparator + "Id"] = eventId.Id;
eventInfo.Properties["EventId" + _options.EventIdSeparator + "Name"] = eventId.Name;
eventInfo.Properties["EventId"] = eventId;
}
}
else
{
eventInfo.LogLevel = nLogLogLevel;
if (exception != null && eventInfo.Exception == null)
eventInfo.Exception = exception;
}
_logger.Log(eventInfo);
}
Then it is just a matter of making an extension method like this:
private static Func<NLog.LogEventInfo, Exception, string> _nlogFormatter = (l,e) =>
l.FormattedMessage;
public static void LogEvent([NotNull] this ILogger logger, LogLevel logLevel, NLog.LogEventInfo state)
{
logger.Log(logLevel, 0, state, state.Exception, _nlogFormatter);
}
also, isn't eventid=0 not a valid eventid?
I guess one could add a new option to NLogProviderOptions, whether it should attempt to hurt performance by capturing EventId = 0 (Two string allocations, dictionary-allocation and 3 dictionary-inserts for every log-message).
I assume your question has been answered, if not, let us know!