[BUG] LogEventConverter.ConvertToEcs method overwrites the already assigned EcsDocument fields when adding MetaData
- 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
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