How do I log a custom field in NLog to database?

Here is one approach using the GlobalContext.

Configuration:

<target type="Database" name="database" connectionstring="Server=localhost;Database=NLog;Trusted_Connection=True;">
  <commandText>
    INSERT INTO NLogEntries ([Origin], [Message], [LogLevel],[CreatedOn],[OrderId]) VALUES (@Origin,@Message,@LogLevel,@Date, @OrderId);
  </commandText>
  <parameter name="@Date" layout="${date}"/>
  <parameter name="@Origin" layout="${callsite}"/>
  <parameter name="@LogLevel" layout="${level}"/>
  <parameter name="@message" layout="${message}"/>
  <parameter name="@OrderId" layout="${gdc:OrderId}"/> <!-- custom field! -->
</target>

Call site:

var logger = LogManager.GetCurrentClassLogger();
GlobalDiagnosticsContext.Set("OrderId",123);
logger.Debug("What is going on here"); //If you use the logging configuration above, 123 will be logged to the OrderId column in your database

With a little more effort, you could wrap the NLog logger using one of the techniques illustrated here.

Or, you could create your own "context" object and write a custom LayoutRenderer to pull the values from it and write them to the log. Custom LayourRenderers are easy to write. You can see one example in my first answer to this question. There, I show how to write your own LayoutRenderer that appends the current value of System.Diagnostics.Trace.CorrelationManager.ActivityId to the log message.


NLog 4.5 introduces structured logging, so you can do this:

logger.Debug("What is going on here. OrderId={MyOrderId}", orderId);

With NLog 4.6.3 it possible to create temporary Logger, that is imbued with the desired property using WithProperty:

Call

int orderId = 123; 
logger.WithProperty("MyOrderId", orderId).Info("This is my message!"); 

Config:

<target type="Database" name="database" connectionstring="Server=localhost;Database=NLog;Trusted_Connection=True;">
  <commandText>
    INSERT INTO NLogEntries ([Origin], [Message], [LogLevel],[CreatedOn],[OrderId]) VALUES (@Origin,@Message,@LogLevel,@Date, @OrderId);
  </commandText>
  <parameter name="@Date" layout="${date}" dbType="DbType.Date"/>
  <parameter name="@Origin" layout="${callsite}"/>
  <parameter name="@LogLevel" layout="${level}"/>
  <parameter name="@message" layout="${message}"/>
  <parameter name="@OrderId" layout="${event-properties:MyOrderId}" dbType="DbType.Int32"/> <!-- custom field! Note also the DB Type -->
</target>

Note, NLog 4.6 has also support for DbType - See https://nlog-project.org/2019/03/20/nlog-4-6-is-live.html


UPDATE 11 Feb 2022: newer versions of NLOG have other solutions- see Julian’s answer.

Rather than using GDC, which is for global static data and fails on concurrent logging, it is better to use EventProperties-Layout-Renderer that allows to pass custom  properties specific for the event

LogEventInfo theEvent = new LogEventInfo(logLevel, "", message);
theEvent.Properties["OrderId"] =orderId;`

log.Log(theEvent);

... and in your NLog.config file: 
${event-context:item=OrderId}  -- obsolete
${event-properties:item=OrderId} -- renders OrderId

Tags:

C#

Nlog