我们如何使用ElasticClient(NEST)提取记录到Elasticsearch的Serilog .ForContext()对象?

问题描述 投票:0回答:1

我们希望使用ELK堆栈更好地集中,构建和搜索我们的日志。我们当前正在登录数据库表,我们有一个EventLog POCO,它捕获填充这些行所需的字段。我们将这些数据引入Elasticsearch的第一步就是通过Serilog简单地记录事件,如下所示:

EventLog eventLog = ...;
log.ForContext("EventLog", eventLog, true).Write(eventLog.EventMessage);

这导致了一个Serilog“logevent”类型的文档,其中“field”包含我们的自定义结构,并且可以获得我们想要的大部分内容:

{
  "_index": "logstash-2019.03.14",
  "_type": "logevent",
  "_id": "p_amfGkBaphWeJXGjjSW",
  "_version": 1,
  "_score": null,
  "_source": {
    "@timestamp": "2019-03-14T09:41:21.6924251-05:00",
    "level": "Information",
    "messageTemplate": "blah",
    "message": "blah",
    "fields": {
      "EventLog": {
        "_typeTag": "EventLog",
        "EventMessage": "blah",
        "EventId": 3112,
...

Kibana允许我们在我们的自定义结构中对我们的日志进行临时搜索,我们已经非常高兴了。

现在我们想以编程方式搜索我们的日志,我们正在寻找NEST,但我们无法找到正确的咒语。 ElasticClient.Search<T>似乎是我们需要的,我们不确定T需要什么,也不确定NEST如何解释搜索条件或返回的数据。

我们尝试过的:

client.Search<EventLog>();   // returns zero results
client.Search<Serilog.Events.LogEvent>();   // throws: Error converting string to Serilog.Events.MessageTemplate
client.Search<EventLog>(s => s.Type("logevent")); // returns results!, but the EventLog objects are all empty (properties all have default values)

似乎让我们接近的唯一电话是:

client.Search<Object>(s => s.Type("logevent")); // returns the raw JSON source of each log entry

所以,我们觉得我们在这里缺少一些简单而基本的东西。 NEST文档都基于直接索引到Elasticsearch的Project类型,但是我们需要做些什么来提取和查询我们使用Serilog的ForContext添加到日志中的自定义结构?

更新:似乎我们可能需要构建一组独立的POCO来复制Serilog事件的结构,例如:

        private class DummyFields
        {
            public EventLog EventLog { get; set; }
        }

        private class Dummy
        {
            public DummyFields Fields { get; set; }
        }

在NEST中查询Dummy确实让我们能够访问我们的内部结构,但似乎应该有更好的方法来做到这一点......

更新2:我们只是为了匹配查询响应而实现POCO,但现在似乎在我们的嵌套结构中匹配字段时存在一些其他问题:

client.Search<Dummy>(s => s
    .AllTypes()
    .Query(q => q
        .Match(t => t
            .Field(f => f.Message).Query("blah"); // returns some results

client.Search<Dummy>(s => s
    .AllTypes()
    .Query(q => q
        .Match(t => t
            .Field(f => f.Fields.EventLog.EventMessage).Query("blah"); // returns zero results

为什么第二个查询没有“看到”我们的嵌套事件消息?

更新3:

奇怪的是,这也有效:

client.Search<Dummy>(s => s
    .AllTypes()
    .Query(q => q
        .Match(t => t
            .Field(f => new Field("fields.EventLog.EventMessage")).Query("blah");

所以看起来NEST只是不匹配嵌套字段的类型安全版本。我们显然不想在这里依赖魔术弦......

更新4:

我在ES中启用了查询日志记录,发现文字查询和表达式查询之间存在大小写差异。文字查询(使用PascalCased属性)被ES看作是Pascal并找到文档,而类型安全表达式转换为camelCase(“fields.eventLog.eventMessage”),并且不匹配任何内容。果然,如果我用一个文字的camelCased字段路径查询,我也得不到任何回报。

如何告诉NEST使用正确的案例敏感度?

.net elasticsearch nest serilog
1个回答
0
投票

发送到Elasticsearch的原始JSON文档包含在搜索响应中每个匹配的_source属性中

{
    "@timestamp": "2019-03-14T09:41:21.6924251-05:00",
    "level": "Information",
    "messageTemplate": "blah",
    "message": "blah",
    "fields": {
      "EventLog": {
        "_typeTag": "EventLog",

我相信你想要的"EventLog"是在JSON路径"fields.EventLog",所以任何映射到这个并且JSON将被反序列化的CLR POCO将需要符合这个结构,因为你的Dummy类型在上面。

您可以采取两种方法来改变这种情况:

  1. "EventLog"序列化为_source中的整个JSON文档。然后,您就可以将其直接映射到"EventLog"类型。
  2. 实现一个自定义序列化程序,只将"fields.EventLog"反序列化为给定的EventLog POCO类型。您可以使用JsonNetSerializer执行此操作并为JsonConverter类型实现自定义EventLog。虽然这样做有两个复杂性;首先,这只会作为读取模型运行,因为当希望再次序列化时,生成的JSON将只是EventLog的JSON,其次,使用JsonNetSerializer与NEST使用的内部序列化器相比具有性能开销。
© www.soinside.com 2019 - 2024. All rights reserved.