Closed
Description
It would be nice with an option to get ecs logging as nested json like
{ "ecs": {"version": "8.4"}, "service": {"name": "myservice"}}
instead of what we get today
{ "ecs.version": "8.4", "service.name" : "myservice"}
The reason for asking is that collecting and processing of logs would be easier.
We are using Spring Boot 3.4.4.
Metadata
Metadata
Assignees
Labels
Type
Projects
Relationships
Development
No branches or pull requests
Activity
nosan commentedon Apr 10, 2025
I'm not sureif this is advisable, as it conflicts with the ECS Schema. Currently, ECS Schema does
not support a format such as
"ecs": {"version":"8.11"}
. Moreover, according to the ECS Schema, theecs.version
field is required and must exist in all events, so supporting this functionalitywould not provide any value from the user's perspective.
UPDATE: My previous statement was incorrect.
For your specific case, you should create a custom implementation of
JsonWriterStructuredLogFormatter
with a format that suits exactly to your needs. This custom formatter can then be configured using thelogging.structured.format.console
property.For more details, please refer to the Spring Boot documentation.
The following
NestedElasticCommonSchemaStructuredLogFormatter
might suit your needs:snicoll commentedon Apr 10, 2025
rafaelma commentedon Apr 10, 2025
Hello, thanks for looking at this :)
ECS stores data in a nested format, our example will be saved as
{ "ecs": {"version": "8.4"} }
, and the mapping definition of the ECS standard needed by e.g. elasticsearch or opensearch defines also the nested format. It’s important to note that the translation from{ "ecs.version": "8.4" }
to{ "ecs": {"version": "8.4"} }
before saving the data according to the ECS standard is done by the Elastic Agent using the decode_json_fields processor in the elastic agent ref: https://www.elastic.co/guide/en/fleet/current/decode-json-fields.html.For reference, you can check the mapping representation of the ECS standard for our example here: https://github.com/elastic/ecs/blob/main/generated/elasticsearch/composable/component/ecs.json The ECS documentation https://www.elastic.co/guide/en/ecs/current/ecs-ecs.html refers to attributes like
ecs.version
, but this is merely a simplified representation of a JSON nested attribute in the documentation for presentation purposes.If you use another agent than the elastic-agent (e.g.fluent-bit) to process logs and spring delivers them as e.g.
{ "ecs.version": "8.4" }
, you have to transform this to the nested version{ "ecs": {"version": "8.4"} }
before you can save it according to the standard.Spring doesn't deliver the logs according to the ECS standard format and it will only work without problems if you collect these logs with the elastic-agent that will convert them to the right ECS format before sending them to storage.
regards,
Rafael Martinez Guerrero
nosan commentedon Apr 10, 2025
I set up Elasticsearch via https://www.elastic.co/cloud and performed another round of testing.
However, in practice, the JSON format currently produced by
ElasticCommonSchemaStructuredLogFormatter
works seamlessly. Elasticsearch accepts them without issue, correctly parsing all fields.Flat JSON example:
Additionally, Elasticsearch fully supports and parses nested JSON structures, like the one below:
Nested JSON example:
In summary, both flat and nested formats are compatible.
Screenshots
rafaelma commentedon Apr 10, 2025
Thank you, @nosan, for investigating this issue further.
"...In summary, both flat and nested formats are compatible ....", as a source, but only when using Elasticsearch as the storage infrastructure because elasticsearch transforms flat format to the standard nested format before saving the log.
If you examine the JSON code of the document saved by Elasticsearch after sending the flat version, you'll see it has been also converted into a nested json document to adhere to the standard.UPDATE:
If elasticsearch works as opensearch this statement is not true: "If you examine the JSON code of the document saved by Elasticsearch after sending the flat version, you'll see it has been also converted into a nested json document to adhere to the standard"
OpenSearch accepts both flat and nested JSON with a nested mapping definition now, but the saved flat JSON document is not converted to nested format.
Check #45063 (comment) for full details.
The ECS standard is now utilized by many products following the merger of OpenTelemetry and Elastic standards to create a unified open standard. Refer to the following links for more information:
It would greatly enhance the Spring project if it could deliver logs in the ECS nested format. This would enable standardized log storage without the need to convert from flat to nested format before saving the log, when used with products other than Elasticsearch, such as Opensearch.
The conversion from flat to nested format to be able to save the standardized log, can be quite tedious and resource-intensive when processing millions of logs if you don't use elasticsearch.
regards
nosan commentedon Apr 10, 2025
According to the ECS Reference:
I also checked the ECS Logging Java repository elastic/ecs-logging-java and found they have a similar issues:
However, the ECS Java logging library continues to use dot notation and relies on this processor afterward. It seems they chose to keep dot notation because they can't ensure that all fields are correctly nested especially considering fields provided through
ILoggingEvent.getKeyValuePairs()
orILoggingEvent.getMDCPropertyMap()
, since these may contain dots as well, and for example if you set a custom field usingMDC
like thisMDC.put("geo.continent_name", "North America")
then according to the ECS Reference , the
ElasticCommonSchemaStructuredLogFormatter
should ideally convert this into nested JSON, as shown below:Personally, if Spring Boot chooses to support the nested JSON format, I don't see any advantage in maintaining support for the current format, as only the nested JSON format fully complies with the ECS specification.
I set up OpenSearch using Docker Compose and submitted several JSON documents to it. Both formats are supported by OpenSearch.
Screenshot
I hope I haven't overlooked anything, and I apologize once again for my earlier incorrect comment: #45063 (comment)
24 remaining items
kajh commentedon Apr 16, 2025
We have now tested with the latest snapshot and it seems to work very well :)
However, we have found something that might be a bug. When using
MDC
with a key namederror
we get the stacktrace bellow, f.x.I have made a demo project in which you can reproduce this by starting the app and go to http://localhost:8080/index.html.
demo.zip
wilkinsona commentedon Apr 16, 2025
@kajh, thanks for trying the snapshot. The problem with an
error
entry in the MDC seems to be unrelated to nested vs flat structure or do you only see this behavior with 3.5 snapshots?wilkinsona commentedon Apr 16, 2025
Answering my own question, this seems to be a regression in 3.5.0-SNAPSHOT. I don't see the problem with 3.5.0-M3 or 3.4.4. I'll re-open this one while we track down the cause.
kajh commentedon Apr 16, 2025
Thank you :)
wilkinsona commentedon Apr 16, 2025
Prior to this change, the JSON looks like this:
With this change, the JSON looks like this:
Technically, this is a breaking change as an MDC entry named
error
will now clash with theerror
object in the JSON just as an MDC entry namederror.type
would have clashed previously.Given the
Map<String, String>
nature of the MDC and it therefore being compatible with thekeyword
data type, I wonder if we should be using thelabels
field here.nosan commentedon Apr 16, 2025
Hmm... I am not sure if it's a valid case to use
MDC.put("error", "value")
, as it goes against the ECS error type defined in ecs-error. The error should be a JSON object, not a string value.Elastic Error Validation
The nested format itself is a breaking change and will affect any users who upgrade to 3.5.0. I believe this information should be included in the noteworthy section.
It is a good option, but doing so would prevent the use of
MDC.put("geo.continent_name", "North America")
with the expectation that Elastic will recognize it as ecs-geo.You can set the
error
field not only throughMDC
but also usingILoggingEvent::getKeyValuePairs
, for example:logger.atError().addKeyValue("error", "demo").log("Some Error", ex)
Here are a few observations:
"service": { "node": {} }
.Throwable
:kajh commentedon Apr 16, 2025
I would just start to say as a disclaimer that I'm very new to ECS. After reading the comments to my bug report regarding using the key
error
, I start to believe that can be looked at as a user error.We have the following code in our error handler:
I was not aware that Spring automatically added
error.type
so I think the fix for this is to removeerror.type
line above from our code.What might be a bug is that the
The name 'error' has already been written
error message is not written in ECS format. For us, I think that means it will not be imported into Opensearch, so we will be able to know what has gone wrong.Do you agree that it should be logged as ECS?
Just to describe two different ways we use MDC:
url.original
as in the code above.Thank you!
philwebb commentedon Apr 17, 2025
The reason we don't currently write the error message in ECS format is because we assuming that writing any other ECS message might fail in the same way. That's why we currently throw an exception and let the logging system report it directly to the console. I've opened #45217 to see if we can log the message without the context data if an exception occurs.
I've opened #45218 to allow users to opt-out of our MDC logging and also relocate the JSON (for example to move it under the labels field as suggested in #45063 (comment)).
Unfortunately our existing
logging.structured.json.rename.*
property can't be used with the ECS nested format.