我们希望使用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使用正确的案例敏感度?
发送到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
类型在上面。
您可以采取两种方法来改变这种情况:
"EventLog"
序列化为_source
中的整个JSON文档。然后,您就可以将其直接映射到"EventLog"
类型。"fields.EventLog"
反序列化为给定的EventLog
POCO类型。您可以使用JsonNetSerializer
执行此操作并为JsonConverter
类型实现自定义EventLog
。虽然这样做有两个复杂性;首先,这只会作为读取模型运行,因为当希望再次序列化时,生成的JSON将只是EventLog
的JSON,其次,使用JsonNetSerializer
与NEST使用的内部序列化器相比具有性能开销。