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

£可爱£侵袭症+ 提交于 2019-11-28 14:44:16

问题


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?


回答1:


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



回答2:


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.




回答3:


If that's all one needs, as of NLog version 4.3.3 there's an easier way to declare and access custom variables. Beware: none of these solutions are thread-safe.

Add the following to the NLog.config

<nlog ...
    <!-- optional, add some variables -->  
    ...
    <variable name="myvarone" value="myvalue"/>
    <variable name="myvartwo" value=2/>
     ...
</nlog>

Variables can be changed/accessed in the code by:

LogManager.Configuration.Variables["myvarone"] = "New Value"
LogManager.Configuration.Variables["myvartwo"] = 2

The values can be referenced in NLog.config:

"${var:myvarone}"  -- renders "New Value"
"${var:myvartwo}"  -- renders 2

As I mentioned above var and LogEventInfo objects are global. So if multiple instances are defined, changing a value would change the value for all instances. I'm very interested if anyone knows a way to declare per instance custom variables for NLog.




回答4:


With NLog 4.6.3 this is now easier and thread safe!

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




回答5:


You can use the MDC code:

var logger = LogManager.GetCurrentClassLogger();

MDC.Set("OrderId", 123);
MDC.Set("user", HttpContext.Current.User.Identity.Name);
// ... and so on

also check this http://weblogs.asp.net/drnetjes/archive/2005/02/16/374780.aspx



来源:https://stackoverflow.com/questions/12424301/how-do-i-log-a-custom-field-in-nlog-to-database

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!