elastic / elastic/ecs-dotnet

[BUG] LogEventConverter.ConvertToEcs method overwrites the already assigned EcsDocument fields when adding MetaData

Open
#546 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
HTML
Stars
137
Forks
70
PR merge metrics
No merged PRs in 30d

Description

**ECS project:** Elastic.CommonSchema.Serilog

**ECS schema version**: latest

**ECS .NET assembly version**: latest

**.NET framework / OS**: net9.0 / Windows

**Description of the problem, including expected versus actual behavior**:
If in our message template we use named parameter which `EcsDocument` already has the same property name during the conversion of `LogEvent` to `EcsDocment` the main property value of `EcsDocument` will be replaced by the value of the named parameter. To simply reproduce the issue we need to use message template that contains named parameters with the same name of `EcsDocument` fields. You can also have a look at the unit test below. You can add this test to the `Elastic.CommonSchema.Serilog.Tests.MessageTests` class.

```
[Fact]
public void SeesMessageWithMessageProp() => TestLogger((logger, getLogEvents) =>
{
logger.Information("Info {Message}", "X");

var logEvents = getLogEvents();
logEvents.Should().HaveCount(1);

var ecsEvents = ToEcsEvents(logEvents);

var (_, info) = ecsEvents.First();
info.Message.Should().Be("Info X");
info.Labels.Should().ContainKey("X");

var x = info.Labels["Message"];
x.Should().NotBeNull().And.Be("X");
});
```
In my sample above I used `Message` named parameter in the message template.

The test above will be failed with the following message.
_Expected info.Message to be "Info X" with a length of 6, but "X" has a length of 1, differs near "X" (index 0)._

If you have a look at the test output that contains final serialized json you can see the `message` field value is "X" but the expected is "Info X". Compare it with the `MessageTemplate` field.

Standard output
`{
"@timestamp": "2026-02-12T09:48:29.4267576+01:00",
"log.level": "Information",
"message": "X",
"ecs.version": "9.0.0",
"log": {
"logger": "Elastic.CommonSchema.Serilog.Tests.MessageTests"
},
"labels": {
"MessageTemplate": "Info {Message}",
"ThreadName": ".NET TP Worker"
},
"agent": {
"type": "Elastic.CommonSchema.Serilog",
"version": "9.0.1-canary.0.9+e5480ff35c391fcc72ec6024d448d8c36b572127"
},
"event": {
"created": "2026-02-12T09:48:29.4267576+01:00",
"severity": 2,
"timezone": "Central Europe Standard Time"
},
"host": {
"os": {
"full": "Microsoft Windows 10.0.26200",
"platform": "Win32NT",
"version": "10.0.26200.0"
},
"architecture": "X64",
"hostname": "mhahost",
"name": "PRG10564"
},
"process": {
"name": "testhost",
"pid": 42228,
"thread.id": 11,
"thread.name": ".NET TP Worker",
"title": ""
},
"server": {
"user": {
"name": "AMUST\\mhosseinalizadeh"
}
},
"service": {
"name": "Elastic.CommonSchema",
"type": "dotnet",
"version": "9.0.1-canary.0.9+e5480ff35c391fcc72ec6024d448d8c36b572127"
},
"user": {
"domain": "Test",
"name": "mhosseinalizadeh"
}
}`

As far as I checked the issue is in `LogEventConverter.ConvertToEcs` method. Within this method we try to fetch all the relevant ecs data and fill those ecs properties and at the end there is a call to `GetMetadata` method that fetches all the remaining properties that are not ECS relevant properties (including the named parameters used in the message template). Then we call `ecsEvent.AssignField` method to set metadata.

```
.
.
.

var metaData = GetMetadata(logEvent, configuration.LogEventPropertiesToFilter);
foreach (var kv in metaData)
ecsEvent.AssignField(kv.Key, kv.Value);
.
.
.
```

The point is that `AssignField` may set the value for all the fields of `EcsDocument` not only `Metadata` or `Labels`.
So in my example because I have `Message` named parameter, the path to be set in `EcsDocument` is "message" and because the `EcsDocument` has the `Message` field then the value will be replaced by the value of `Message` named parameter which is wrong.

Before calling `AssignField` method the value of the `EcsDocument.Message` property is correct because it is filled by `ecsEvent.Message = logEvent.RenderMessage(configuration.MessageFormatProvider);` and there is no problem. The issue happens after filling metadata because it replaces the `Message` field value.

Please note that the same issue may happen for other `EcsDocument` string properties like `SpanId`, `TraceId` and `TransactionId`.

**Possible solution:**
I think the solution would be using another method like `AssignField` that fills only `Metadata` or `Labels` not other fields in this case. Because they are already set correctly. So that we can ensure we will not replace the values that already set for `EcsDocument` properties by `ConvertToEcs` method. Unfortunately we do not have such a method currently :(

Contributor guide

Open the contributing guide

Research direction

Start at LogEventConverter.ConvertToEcs, especially the metadata loop that calls AssignField after rendering the message. Review GetMetadata and the Elastic.CommonSchema.Serilog.Tests.MessageTests example, then verify that a named Message parameter remains in metadata or labels without replacing the rendered ECS message; also consider the mentioned SpanId, TraceId, and TransactionId cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend, observability-sre
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.