Note about your example timestamp
Your sample log already shows:
"@timestamp":"2026-05-13T15:12:12.316+0200"
That is already Copenhagen summer time on May 13, 2026 (+0200, CEST). So if your concern is specifically that logs are in UTC, your sample does not show UTC. It shows a local European offset. If you need to guarantee this consistently, explicitly setting timeZone in the custom template is still the safest option. This is my inference based on the offset shown in your sample. (logging.apache.org)
Custom template
Use a custom JSON event template instead of the stock classpath:EcsLayout.json, and define @timestamp with a timestamp resolver that explicitly sets:
format
timeZone: "Europe/Copenhagen"
Log4j2’s JsonTemplateLayout supports this directly in the event template JSON. (logging.apache.org)
1. Create a custom template file
For example: configuration/log/EcsLayout-Copenhagen.json
{
"@timestamp": {
"$resolver": "timestamp",
"pattern": {
"format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"timeZone": "Europe/Copenhagen"
}
},
"ecs.version": "1.2.0",
"log.level": {
"$resolver": "level",
"field": "name"
},
"message": {
"$resolver": "message",
"stringified": true
},
"process.thread.name": {
"$resolver": "thread",
"field": "name"
},
"log.logger": {
"$resolver": "logger",
"field": "name"
},
"error.type": {
"$resolver": "exception",
"field": "className"
},
"error.message": {
"$resolver": "exception",
"field": "message"
},
"error.stack_trace": {
"$resolver": "exception",
"field": "stackTrace",
"stackTrace": {
"stringified": true
}
},
"labels": {
"$resolver": "mdc",
"flatten": true,
"stringified": true
}
}
2. Reference that template from your appenders
Replace:
<JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
with something like (make sure you use the absolute path):
<JsonTemplateLayout eventTemplateUri="file:///ivy/configuration/EcsLayout-Copenhagen.json"/>