Kjensen Kjensen - 2 months ago 62
C# Question

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

I currently use NLog on a lot of projects. On some, I log to a database.

Here is what I would like to do:

CREATE TABLE [dbo].[NLogEntries](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Origin] [nvarchar](100) NOT NULL,
[LogLevel] [nvarchar](20) NOT NULL,
[Message] [nvarchar](3600) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[OrderId] [int] NULL --Custom field!
)


And NLog.config with this target:

<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="${orderId}"/> <!-- custom field! -->
</target>


And then log something like this:

var logger = LogManager.GetCurrentClassLogger();
var orderId = 123;
logger.Debug("What is going on here", orderId);


Is there a good way to do this and keep using NLog? Or do I have to roll my own logger and skip NLog when these are the requirements?

Answer

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.