Replies: 4 comments 4 replies
-
To the best of my knowledge, SLF4J has one message type composed of a formatting pattern and an array of arguments; whereas Log4j API has an extendable abstract message type with several default implementations: Put another way, Log4j API supports arbitrary message representations, whereas SLF4J supports only parameterized strings. Hence, if SLF4J is your lowest common denominator, you cannot log arbitrarily structured JSON. That said... You can leverage MDC/NDC supported by both APIs to have some influence on the final JSON structure generated. AFAIU, you want to log a JSON document composed of following fields:
You can add these as MDC fields in SLF4J:
which is analogous to the following in Log4j API:
Later, you can refer to these MDC keys in your JSON Template Layout
If you use Log4j API, you can use it in various ways. For instance,
That sounds like "a bug waiting to be exploited in disguise of a feature" to me. I suggest you to simply leverage MDC fields instead. If that doesn't work for you, please let us know why; we might assist you further. |
Beta Was this translation helpful? Give feedback.
-
Thanks for your complete answer. We already manage to log a json structure with different information, using the MDC. My question was more about the message itself, which in the end is just one field of this JSON structure. As of now when we do {
"level": "INFO",
"message": "Hello World", ⬅️
"cold_start": true,
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:my-function",
"function_memory_size": 512,
"function_name": "my-function",
"function_request_id": "93cace4a-f913-4d37-9a53-a6e6b21c49d6",
"function_version": "$LATEST",
"service": "helloworld",
"xray_trace_id": "1-6564a2e9-3a68920768b0b2f2661d87f1",
"timestamp": "2023-11-27T14:08:47.527Z"
} And we already permit users to add stuff in the MDC (context). The problem is when you do this {
"level": "INFO",
"message": "{\"products\":[{\"id\":123,\"name\":\"Nintendo DS\",\"price\":249.99},{\"id\":456,\"name\":\"Playstation 5\",\"price\":499.99}]}",
"cold_start": true,
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:my-function",
"function_memory_size": 512,
"function_name": "my-function",
"function_request_id": "93cace4a-f913-4d37-9a53-a6e6b21c49d6",
"function_version": "$LATEST",
"service": "helloworld",
"xray_trace_id": "1-6564a2e9-3a68920768b0b2f2661d87f1",
"timestamp": "2023-11-27T14:08:47.527Z"
} Here it's not so hard, the json is small, but when we log the incoming function event or something bigger, it's impossible to read and troubleshoot... What we wanted is (having the message included as a json structure in the global log event): {
"level": "INFO",
"message": {
"products": [
{
"id": 123,
"name": "Nintendo DS",
"price": 249.99
},
{
"id": 456,
"name": "Playstation 5",
"price": 499.99
}
]
},
"cold_start": true,
"function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:my-function",
"function_memory_size": 512,
"function_name": "my-function",
"function_request_id": "93cace4a-f913-4d37-9a53-a6e6b21c49d6",
"function_version": "$LATEST",
"service": "helloworld",
"xray_trace_id": "1-6564a2e9-3a68920768b0b2f2661d87f1",
"timestamp": "2023-11-27T14:08:47.527Z"
} I managed to do this in our custom |
Beta Was this translation helpful? Give feedback.
-
As I remarked in the SO question, SLF4J is not an API that allows to log structured data. If I remember my history lesson, the lack of structured logging (specifically an equivalent of A common misconception is that SLF4J is the only logging API that allows you to use a variety of logging backends. That is a simple anachronism that repeats information that was true in 2010. The Log4j API has many implementations (we maintain three: Maybe you should consider basing v2 of your tools on the Log4j API? Beyond the flexibility offered by the With the Log4j API you could:
Users could simply use your message factory like this: private static final Logger LOG = LogManager.getLogger(MyClass.class, new JacksonMessageFactory(mapper));
public void doSomeLogging() {
final Object object = ...;
LOG.trace(object);
} The advantage of this approach is that serialization is lazy: if the |
Beta Was this translation helpful? Give feedback.
-
FWIW, you could probably create a JsonObjectMessage that accepts an object and renders it as JSON when getFormattedMessage is called. MapMessage will also format to JSON but your example shows nested objects, not a simple Map. |
Beta Was this translation helpful? Give feedback.
-
I recently asked a question on stack over flow.
I'm the author of Powertools for AWS Lambda Java. We're providing different libraries to help developers adopt best practices on AWS Lambda.
Between others, we're providing a logging module to log information in JSON format with additional context (function name, arn, ...). At the moment, it is log4j only but we're working on a v2 where it will be slf4j and either log4j or logback.
My problem is that JSON messages (for example doing
LOG.info(mapper.writeValueAsString(object))
) are logged as simple String and thus all the quotes are escaped and it appears dirty in CloudWatch Logs, very hard to read.Looking at the
MessageResolver
, to get proper formatting, we should useObjectMessage
, but who really use this? I didn't even know it exists before diving deeper in this resolver.In the end, I wrote my own resolver, I'm checking if the message is a JSON (using Jackson
readTree
), and if that's the case, I use theJsonWriter.writeRawString()
to avoid the escaped quotes.If there is a better way, let me know...
Beta Was this translation helpful? Give feedback.
All reactions