'layout="${aspnet-request-posted-body}" in the NLog.config now causes an internal NLog exception

NLog, NLog.Database, NLog.Extensions.Logging, and NLog.Web.AspNetCore are on version 5.0.0. The platform is .NET Core 3.1.

This used to work fine in the NLog 4.x series, but now this causes an exception and I have not found a workaround. The only change I made to the NLog.config file was to add a reference to the NLog.Database assembly. When I remove the line in the NLog.config for layout="${aspnet-request-posted-body}" it works fine.

NLog.config Contents

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      throwConfigExceptions="true"
      internalLogLevel="Warn" internalLogFile="c:\temp\nlog-internal.log">
    <!-- enable asp.net core layout renderers -->
  <extensions>
      <add assembly="NLog.Database"/>
      <add assembly="NLog.Web.AspNetCore"/>
  </extensions>
  <targets async="true">
    <target name="database" xsi:type="Database">
      <dbProvider>Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient</dbProvider>
      <connectionString>Server=xxxxxx;Database=xxxxxx;Trusted_Connection=True;Encrypt=True;</connectionString>
      <commandText>
          INSERT INTO CisServiceLog
          (
          [Date],
          [Thread],
          [Level],
          [Logger],
          [Message],
          [Exception],
          [ApplicationDomain],
          [MachineName],
          [Action],
          [Controller],
          [Url],
          [Method],
          [RemoteIp],
          [IISSiteName],
          [ContentType],
          [Host],
          [QueryString],
          [Request],
          [ASPNetTraceID],
          [UserAgent]
          )
          VALUES
          (
          @log_date,
          @thread,
          @log_level,
          @logger,
          @message,
          @exception,
          @appdomain,
          @machine_name,
          @action,
          @controller,
          @url,
          @method,
          @ip,
          @iissitename,
          @contenttype,
          @host,
          @querystring,
          @request,
          @aspnettraceid,
          @useragent
          )
      </commandText>
      <parameter name="@log_date"  layout="${date}" />
      <parameter name="@thread"    layout="${threadid}" size="10" />
      <parameter name="@log_level" layout="${level}"    size="8" />
      <parameter name="@logger"    layout="${logger}"   size="256" />
      <parameter name="@message"   layout="${message}"  size="8000" />
      <parameter name="@exception" layout="${exception:tostring}"  size="8000" />
      <parameter name="@appdomain" layout="${appdomain}"           size="256" />
      <parameter name="@machine_name" layout="${machinename}"      size="256" />
        <parameter name="@action"       layout="${aspnet-mvc-action}"       size="256" />
        <parameter name="@controller"   layout="${aspnet-mvc-controller}"   size="256" />
        <parameter name="@url"          layout="${aspnet-request-url}"      size="1024" />
        <parameter name="@method"       layout="${aspnet-request-method}"   size="16" />
        <parameter name="@ip"           layout="${aspnet-request-ip:CheckForwardedForHeader=true}" size="32" />
        <parameter name="@iissitename"  layout="${iis-site-name}"           size="256" />
        <parameter name="@contenttype"  layout="${aspnet-request-contenttype}"   size="256" />
        <parameter name="@host"         layout="${aspnet-request-host}"          size="256" />
        <parameter name="@querystring"  layout="${aspnet-request-querystring}"   size="4096" />
        <parameter name="@request"      layout="${aspnet-request-posted-body}"   size="8000" />
        <parameter name="@aspnettraceid" layout="${aspnet-traceidentifier}"      size="64" />
        <parameter name="@useragent"    layout="${aspnet-request-useragent}"     size="1024" />
    </target>
  </targets>
  <rules>
    <logger name="*" minlevel="Warn" writeTo="database" />
  </rules>
</nlog>

Exception Stack Trace:

NLog.NLogConfigurationException: Exception when loading configuration C:\git\Commercial_BOLCISServices\BusinessOnline.WebApi\bin\Debug\netcoreapp3.1\Logging.config
 ---> NLog.NLogConfigurationException: 'DatabaseParameterInfo' cannot assign property 'Layout'='${aspnet-request-posted-body}'. Error: Error parsing layout aspnet-request-posted-body
 ---> NLog.NLogConfigurationException: Error parsing layout aspnet-request-posted-body
 ---> System.ArgumentException: LayoutRenderer symbol-name is unknown: 'aspnetrequestpostedbody'. Extension NLog.Web.AspNetCore not included?
   at NLog.Config.Factory`2.CreateInstance(String itemName)
   at NLog.Layouts.LayoutParser.GetLayoutRenderer(String typeName, ConfigurationItemFactory configurationItemFactory, Nullable`1 throwConfigExceptions)
   --- End of inner exception stack trace ---
   at NLog.Layouts.LayoutParser.GetLayoutRenderer(String typeName, ConfigurationItemFactory configurationItemFactory, Nullable`1 throwConfigExceptions)
   at NLog.Layouts.LayoutParser.ParseLayoutRenderer(ConfigurationItemFactory configurationItemFactory, SimpleStringReader stringReader, Nullable`1 throwConfigExceptions)
   at NLog.Layouts.LayoutParser.CompileLayout(ConfigurationItemFactory configurationItemFactory, SimpleStringReader sr, Nullable`1 throwConfigExceptions, Boolean isNested, String& text)
   at NLog.Layouts.LayoutParser.CompileLayout(String value, ConfigurationItemFactory configurationItemFactory, Nullable`1 throwConfigExceptions, String& text)
   at NLog.Layouts.SimpleLayout.SetLayoutText(String value, Nullable`1 throwConfigExceptions)
   at NLog.Layouts.SimpleLayout..ctor(String txt, ConfigurationItemFactory configurationItemFactory, Nullable`1 throwConfigExceptions)
   at NLog.Layouts.SimpleLayout..ctor(String txt, ConfigurationItemFactory configurationItemFactory)
   at NLog.Internal.PropertyHelper.TryParseLayoutValue(String stringValue, ConfigurationItemFactory configurationItemFactory)
   at NLog.Internal.PropertyHelper.TryNLogSpecificConversion(Type propertyType, String value, ConfigurationItemFactory configurationItemFactory, Object& newValue)
   at NLog.Internal.PropertyHelper.SetPropertyFromString(Object targetObject, PropertyInfo propInfo, String stringValue, ConfigurationItemFactory configurationItemFactory)
   --- End of inner exception stack trace ---
   at NLog.Internal.PropertyHelper.SetPropertyFromString(Object targetObject, PropertyInfo propInfo, String stringValue, ConfigurationItemFactory configurationItemFactory)
   at NLog.Internal.PropertyHelper.SetPropertyFromString(Object targetObject, String propertyName, String stringValue, ConfigurationItemFactory configurationItemFactory)
   at NLog.Config.LoggingConfigurationParser.SetPropertyValueFromString(Object targetObject, String propertyName, String propertyValue, ValidatedConfigurationElement element)
   at NLog.Config.LoggingConfigurationParser.ConfigureObjectFromAttributes(Object targetObject, ValidatedConfigurationElement element, Boolean ignoreType)
   at NLog.Config.LoggingConfigurationParser.ConfigureFromAttributesAndElements(Object targetObject, ValidatedConfigurationElement element, Boolean ignoreTypeProperty)
   at NLog.Config.LoggingConfigurationParser.ParseArrayItemFromElement(Type elementType, ValidatedConfigurationElement element)
   at NLog.Config.LoggingConfigurationParser.AddArrayItemFromElement(Object o, PropertyInfo propInfo, ValidatedConfigurationElement element)
   at NLog.Config.LoggingConfigurationParser.SetPropertyValuesFromElement(Object o, ValidatedConfigurationElement childElement, ILoggingConfigurationElement parentElement)
   at NLog.Config.LoggingConfigurationParser.ParseTargetElement(Target target, ValidatedConfigurationElement targetElement, Dictionary`2 typeNameToDefaultTargetParameters)
   at NLog.Config.LoggingConfigurationParser.ParseTargetsElement(ValidatedConfigurationElement targetsElement)
   at NLog.Config.LoggingConfigurationParser.ParseNLogSection(ILoggingConfigurationElement configSection)
   at NLog.Config.XmlLoggingConfiguration.ParseNLogSection(ILoggingConfigurationElement configSection)
   at NLog.Config.LoggingConfigurationParser.LoadConfig(ILoggingConfigurationElement nlogConfig, String basePath)
   at NLog.Config.XmlLoggingConfiguration.ParseNLogElement(ILoggingConfigurationElement nlogElement, String filePath, Boolean autoReloadDefault)
   at NLog.Config.XmlLoggingConfiguration.ParseTopLevel(NLogXmlElement content, String filePath, Boolean autoReloadDefault)
   at NLog.Config.XmlLoggingConfiguration.Initialize(XmlReader reader, String fileName, Boolean ignoreErrors)


Solution 1:[1]

NLog.Web.AspNetCore ver. 5.0 removed ${aspnet-request-posted-body} since its implementation was not threadsafe, and introduced unpredictable behavior.

https://github.com/NLog/NLog/wiki/AspNet-Request-posted-body-layout-renderer

Think the correct solution is to use middleware to capture/buffer the posted-body, and then inject into a safe-location like HttpContext.Items (${aspnet-item}) or NLog ScopeContext (${scopeproperty})

Pull-Requests are most welcome to provide a prototype of such middleware.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1