I'm working on a Kotlin spring-boot project that is using slf4j over log4j2 and I would like to log in a key-value way in order to add filters based on the keys to the data analyzer (splunk) I'm using.
According the slf4j docs that should be possible using the fluent-api but that is only available from the version 2.0.0 and the project is using 1.7.30 and I was unable to figure it out how to update it.
Now I'm just logging with a plain string like following:
log.info("My log")
I was reading that the output should be in a JSON format but I don't know what to configure and how to make that possible.
Any ideas about how solve this?
Thank you!
Remark: Spring Boot offers you 3 logging APIs out-of-the-box: java.util.logging, SLF4J and Log4j2 API. To exploit the full power of Log4j2 Core (without loosing any data if you decide to switch to Logback), you should use the last one.
The solution mostly depends upon the relationship between the key/value pairs and the message being logged.
If their are not related to the message, but give the message a context, use the ThreadContext:
try (Instance c = CloseableThreadContext.put("userId", ...)
.put("requestId", ...)
.put("otherData", ...)) {
...
log.info("Message");
}
If the message itself is a map, use a StringMapMessage:
log.info(StringMapMessage.newInstance(map));
If you use Log4j2 Core, you can log both maps as JSON using the JSON template layout. The ThreadContext can be logged using the mdc resolver, while the MapMessage using the map or message resolvers, e.g.:
{
"tags": {
"$resolver": "mdc"
},
"message": {
"$resolver": "message"
}
}
See the documentation for details.
Edit: If your main purpose is filtering and classifying, you can also use markers. These are not key/value pairs, but a way to split the domain of all your logs into subdomains.
Related
I am writing a custom OpenApiConfigurator that adds some examples to my api dynamically.
When I add the examples using the value field of io.smallrye.openapi.api.models.examples.ExampleImpl, which is an object, the example is null in swagger-ui. It only works when I added the actual json.
To add the actual json I have to generate it from my response dto using Jackson. But how can I access the quarkus object mapper, for which I have some customisations using ObjectMapperCustomizer, if in the OpenApiConfigurator CDI is not available?
It's actually possible to access the CDI container statically with Arc.container().instance(ObjectMapper::class.java).get()
That solved it for me.
I have a Spring application which uses the Hibernate schema strategy and a TenantContext class to store the tenant identifier (same design shown here: https://vladmihalcea.com/hibernate-database-schema-multitenancy/).
Everything works fine when dealing with synchronous HTTP requests handled by Spring.
Besides that, I have some Camel routes which are triggered by chron jobs. They use the JPA component to read from or write to a datasource. The Exchange object knows the tenant identifier. How to transfer that information to Hibernate?
I was thinking about using a listener or interceptor to get the tenant id from the Exchange and set the TenantContext object at every step in the route. The TenantContext will then be used by the Hibernate CurrentTenantIdentifierResolver class to resolve the tenant.
How should the TenantContext look like? Is it ThreadLocal a viable option? What about async threads?
In general, do you have any good solution to support Hibernate multitenancy when using Camel?
In general, it depends on your use case ;)
But we have done something similar, using a ThreadLocal in the CurrentTenantIdentifierResolver.
Then in the places where we need to set the tenant (e.g. at the start of the job being executed by the cronjob, in your case), we have an instance of tenantIdentifierResolver used like so:
tenantIdentifierResolver.withTenantId(tenant, () -> {
try {
doWhatever(param1, param2, etc);
} catch (WhateverException we) {
throw new MyBusinessException(we);
}
});
referring to quesition/answer in How to log MDC with Spring Sleuth?
I think this has/will change(d) with spring-cloud 2.0 as there is no SpanLogger or Slf4jSpanLogger anymore (or I don't find it)
Wouldn't it be nice if application properties spring.sleuth.baggage-keys and spring.sleuth.propagation-keys if set would also be put in MDC I think inside Slf4jCurrentTraceContext (as this class is currently final I cannot subclass it)
If not, how could I achieve this with spring-cloud 2.0 accordingly?
We don't want to put all entries in MDC (that really doesn't make a lot of sense). You can however either copy the Slf4jCurrentTraceContext and extend it in the way you want to (and register it as a bean) or maybe create your own implementation of CurrentTraceContext that would wrap the existing CurrentTraceContext via a Bean Post Processor and perform additional logic. I guess the first option is more preferable.
In version 2.1.0, Slf4jScopeDecorator was introduced and it will automatically add baggage values to MDC as long as they are whitelisted in the spring.sleuth.log.slf4j.whitelisted-mdc-keys configuration.
For example, if you have the following configuration:
spring.sleuth.baggage-keys=key1,key2
spring.sleuth.log.slf4j.whitelisted-mdc-keys=key2
Only the value of key2 will be automatically added MDC, but not the value of key1.
For more info, see: https://cloud.spring.io/spring-cloud-sleuth/reference/html/#prefixed-fields
How can I debug the transactions in my spring boot application ?
Is there a way to see how method calls get grouped in a transaction context or another ?
You have asked 2 broad questions here.
1. How to debug a transaction (assuming from your post title, you are looking for logging)
The simplest way to achieve is to use the bundled java.util.logging.Logger in SpringBoot.
Below code snippet should tell you how:
public class EmojiController {
private final static Logger logger = Logger
.getLogger(EmojiController.class.getName());
public ModelAndView getEmoji() {
logger.info("emoji: " + emojiId + " lookup initiated");
---do something---
}
By default, If you use the ‘Starters’, Logback will be used for logging. Appropriate Logback routing is also included to ensure that dependent libraries that use Java Util Logging, Commons Logging, Log4J or SLF4J will all work correctly.
2. Grouping the method calls related to one transaction:
This is a very wide topic and has even wider application when asked in the context of distributed application architecture like, micro-services. The point is, how do you relate various log entries with each other to group them for a wholistic view.
The solution for that lies in the concept called Distributed Tracing. You can read in detail about that in this wonderful post from Josh Long.
More detailed documentation on the discussed technologies can be found here -
Sleuth - http://cloud.spring.io/spring-cloud-sleuth/spring-cloud-sleuth.html
ZipKin - http://zipkin.io/
It should help you achieve what you want but in case you have more questions on their usage, please raise another question.
--- EDIT ---
There's a section about Logging in the Spring Reference.
It shows how to configure different logging frameworks, among them log4j
In your case the last line of the config would be:
log4j.logger.org.springframework.transaction=DEBUG
In all of my requests, there is a header (request id) that I want to log, in case of anything - on any log level.
Is there a way to inject this into the sl4fj logging? So that the logger always tried to log the request id, in for example logging an exception, but the request.
Or do I always need to add this as a parameter to the logging?
This is not really related to Guice.
You have in slf4j a concept of MDC (Mapped Diagnostic Context). You can put variables in an MDC. These variables are local to the thread and are added to each log generated by this thread. The typical use case of an MDC is to add in every log the user associated with an HTTP request, or a session-id (ie, your use case).
See http://logback.qos.ch/manual/mdc.html
For a short example, you put a variable in MDC like this:
MDC.put("userId", currentUser);
and you can add in an appender format the variable with:
%X{userId}
Theoretically, it could be possible to implement this feature with Guice by injecting a request-scoped logger, but it's really more costly and less integrated with the logging framework. I didn't advise you to do this kind of things!