首页 > 解决方案 > 如何处理 NLog 的所有事件属性布局渲染器产生的空值?

问题描述

我正在为 PostgreSQL 配置 NLog 的数据库目标,并且我想使用hstore来存储所有事件属性。我正在为此使用all-event-properties布局渲染器

这是我当前的目标配置:

<target name="database"
        xsi:type="Database"
        dbProvider="Npgsql.NpgsqlConnection, Npgsql"
        connectionString="Server=localhost;Port=5432;Database=db;User Id=postgres">
  <install-command>
    <text>
      CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA public;
      CREATE TABLE IF NOT EXISTS logs (
      id bigserial PRIMARY KEY,
      moment timestamp NOT NULL,
      level text NOT NULL,
      message text NOT NULL,
      args hstore NOT NULL
      );
    </text>
  </install-command>
  <commandText>
    INSERT INTO logs (moment, level, message, args)
    VALUES (@moment::timestamp, @level, @message, @args::hstore);
  </commandText>
  <parameter name="@moment" layout="${longdate:universalTime=true}" />
  <parameter name="@level" layout="${level}" />
  <parameter name="@message" layout="${message}" />

  <!-- (!) Here's where the format of event properties adopted for hstore syntax (!)-->
  <parameter name="@args" layout="${all-event-properties:format=[key]=>[value]" />

</target>

它适用于不带参数的日志,但带参数会生成以下 SQL:

INSERT INTO logs (moment, level, message, args) VALUES (
'2019-05-24 18:44:49.7494'::timestamp,
'Info',
'Message text here',
'a=>1, b=>2, c=>3, EventId_Id=>555, EventId_Name=>, EventId=>555'::hstore);
--                           no value here--------^

这是无效的语法,因为 PostgreSQL 语法需要NULL关键字作为值或根本不包含键:

这是错误:

ERROR:  Syntax error near 'E' at position 51

当我手动执行此操作时,它会完全重现,而当我移除EventId_Name=>,密钥时它会消失。所以,我很确定我需要以某种方式跳过/处理那些空键才能开心。

我也可以使用 SQL 中的任何解决方案,但找不到一种简单而强大的方法来处理这些值。

标签: c#postgresqlnlognpgsqlhstore

解决方案


为了回答这个问题,我阅读了 hstore格式文档

包含空格、逗号、=s 或 >s 的双引号键和值。要在键或值中包含双引号或反斜杠,请使用反斜杠对其进行转义。

不确定什么是=sand >s,我决定在转义双引号和反斜杠的同时对所有内容进行双引号。这是我的解决方案:

LayoutRenderer.Register("all-event-properties-hstore", e =>
{
    var sb = new StringBuilder();
    var first = true;
    var fp = e.FormatProvider ?? CultureInfo.InvariantCulture;
    string GetString(object o) =>
        Convert.ToString(o, fp).Replace("\\", "\\\\").Replace("\"", "\\\"");
    foreach (var (key, value) in e.Properties)
    {
        if (first)
            first = false;
        else
            sb.Append(",");
        sb.Append("\"");
        sb.Append(GetString(key));
        sb.Append("\"=>\"");
        sb.Append(GetString(value));
        sb.Append("\"");
    }
    return sb.ToString();
});

推荐阅读