Burak Akgerman
Burak Akgerman

Reputation: 21

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)

Upvotes: 2

Views: 2090

Answers (2)

Rolf Kristensen
Rolf Kristensen

Reputation: 19867

Thanks to @bakgerman now NLog.Web.AspNetCore v5.1 re-introduces ${aspnet-request-posted-body} with help from middleware:

app.UseMiddleware<NLog.Web.NLogRequestPostedBodyMiddleware>();

It is no longer necessary to explicit call context.Request.EnableBuffering(); as it is handled by the middleware.

Upvotes: 2

Rolf Kristensen
Rolf Kristensen

Reputation: 19867

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.

Upvotes: 2

Related Questions