【问题标题】:How do I log a custom field in NLog to database?如何将 NLog 中的自定义字段记录到数据库?
【发布时间】:2012-09-07 14:32:23
【问题描述】:

我目前在很多项目中使用 NLog。在某些情况下,我登录到数据库。

这是我想做的:

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!
)

和带有这个目标的 NLog.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}"/>
  <parameter name="@Origin" layout="${callsite}"/>
  <parameter name="@LogLevel" layout="${level}"/>
  <parameter name="@message" layout="${message}"/>
  <parameter name="@OrderId" layout="${orderId}"/> <!-- custom field! -->
</target>

然后记录如下内容:

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

有没有什么好的方法可以做到这一点并继续使用 NLog?或者当有这些要求时,我是否必须滚动自己的记录器并跳过 NLog?

【问题讨论】:

  • 也许这会有所帮助:nlog-forum.1685105.n2.nabble.com/…
  • string.Format() 有什么问题?
  • Rich>> 你的意思是除了以后在日志文件中查询之外会很困难(需要拆分等)?我们拥有在不同字段中包含不同数据的关系数据库是有原因的。

标签: c# nlog


【解决方案1】:

与其使用用于全局静态数据并在并发日志记录中失败的 GDC,不如使用允许传递特定于事件的自定义属性的 EventProperties-Layout-Renderer 更好

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

【讨论】:

  • 您能否添加一个说明 GDC 和 MDC 现已过时的消息来源?我首先尝试了您的解决方案,但由于它更干净,所以转到了标记答案的解决方案 - 我不必定义新变量并手动设置日志级别和记录器名称。关于他们的“弃用”,我唯一能找到的是他们的名字已经从 GDC/MDC 变成了标记答案使用的东西(GlobalDiagnosticsContext) - nlog-project.org/2009/10/19/…
  • 根据github.com/nlog/nlog/wiki/Layout-Renderers MDC 和 NDC 提供了与 log4net 的兼容性。
  • 使用自定义 LogEventInfo 实例,您还必须手动设置 Timestamp / LoggerName,这不如使用 GDC 方便。
  • ${event-context} 现在已弃用,您应该改用${event-properties}
  • @Doruk,“当您想让当前进程中的每个记录器都可以使用某些信息时,请使用全局诊断上下文。”来自github.com/nlog/nlog/wiki/Gdc-Layout-Renderer
【解决方案2】:

这是使用 GlobalContext 的一种方法。

配置:

<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>

调用站点:

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

稍加努力,您就可以使用here 中说明的一种技术封装 NLog 记录器。

或者,您可以创建自己的“上下文”对象并编写自定义 LayoutRenderer 以从中提取值并将它们写入日志。自定义 LayourRenderers 很容易编写。您可以在我对this question 的第一个回答中看到一个示例。在那里,我展示了如何编写自己的 LayoutRenderer,将 System.Diagnostics.Trace.CorrelationManager.ActivityId 的当前值附加到日志消息中。

【讨论】:

  • GlobalDiagnosticsContext 有一些静态方法,但它不是字典,有什么变化吗?
  • 你说得对,它是 GlobalContetxt 而不是 GlobalDiagnosticContext 也不是字典,但它确实包含一个字典,它通过 Properties 属性公开。
  • 忽略最后一条评论。我已经更新了示例代码,使其正确(或者更正确,无论如何)。
  • 不会导致并发写入问题吗? - 我的意思是一个线程设置值,另一个线程覆盖该值,然后由第一个线程写入
  • 查看下面 Michael Freidgeim 的回答,此解决方案因并发写入而失败。
【解决方案3】:

NLog 4.5 引入了structured logging,所以你可以这样做:

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

使用 NLog 4.6.3 可以创建临时 Logger,使用 WithProperty 填充所需的属性:

打电话

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

配置:

<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>

注意,NLog 4.6 还支持 DbType - 请参阅 https://nlog-project.org/2019/03/20/nlog-4-6-is-live.html

【讨论】:

  • 我认为这是新版本 NLog 的最佳解决方案。 +1。
  • 完美!很好的答案!
  • 完美运行! +1
【解决方案4】:

如果您只需要这些,从 NLog 4.3.3 版开始,就有一种更简单的方法来声明和访问自定义变量。 注意: 这些解决方案都不是线程安全的。

将以下内容添加到 NLog.config

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

可以通过以下方式在代码中更改/访问变量:

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

值可以在 NLog.config 中引用:

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

正如我上面提到的 varLogEventInfo 对象是全局的。因此,如果定义了多个实例,更改一个值将更改所有实例的值。如果有人知道为 NLog 声明每个实例自定义变量的方法,我很感兴趣。

【讨论】:

  • 非常简单的解决方案 - 快速完成工作。
  • Michael 上面的回答是线程安全的。它没有使用全局 LogEventInfo,他正在创建一个新实例。
【解决方案5】:

您可以使用 MDC 代码:

var logger = LogManager.GetCurrentClassLogger();

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

也可以查看http://weblogs.asp.net/drnetjes/archive/2005/02/16/374780.aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    相关资源
    最近更新 更多