How to disable management context access log in Spring Boot using undertow - spring-boot

I'm using Spring Boot 2.2.4 with embedded Undertow.
I've enabled the access log using server.underdow.accesslog.enabled=true and everything works as expected.
I'm utilizing the actuator endpoints on a different port which sets up a child context. I do not want requests to the actuator to be logged. Currently they automatically go to management_access.log where access. is the prefix of my main access log.
Any ideas on how to disable that access log? I know Spring is creating a separate WebServer via Factory for the actuator context, but I haven't found a way to customize the factory.

I found my own answer (spent way too much time doing it).
It's a little bit of a hack, but it works:
New configuration class: foo.ManagementConfig
package foo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
#ManagementContextConfiguration
public class ManagementConfig {
#Bean
WebServerFactoryCustomizer<UndertowServletWebServerFactory> actuatorCustomizer(#Value("${management.server.port}") int managementPort) {
return factory -> {
if (managementPort == factory.getPort()) {
factory.setAccessLogEnabled(false);
}
};
}
}
Created resources/META-INF/spring.factories so that it gets picked up by the ManagementContext:
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=foo.ManagementConfig
The part that's a bit of a hack is the if statement. It would have been great if it applied only to the management context, but for some reason it's trying to apply to both. With the if statement, it just doesn't do anything for the primary context.
This would have unintended consequences if management.server.port was undefined or if it was the same as the primary context.

Related

How can I pass my config parameters to the frontend via Spring Boot?

I have a question. I have created 2 config files. I can already use the parameters in the application.properties. My goal is to be able to display and edit the parameters later in the frontend. Like normal settings of any application. Unfortunately, I do not have an exact idea how I can implement this.
Does anyone have an idea for me how I can send my config via RestApi to the frontend?
My application.properties
#ConfigMaintenance
config.maintenance.next-Available-Update-Version=1.28.5
config.maintenance.next-Available-Update-Date= 18.05.2022
config.maintenance.automatic-update= true
config.maintenance.update-Api-Token= sk021=!2jsn?=jacmw
config.maintenance.latest-Version= 1.28.4
#ConfigGeneral
config.general.volume=56
config.general.dark-Mode=false
config.general.night-Mode=false
config.general.fps=144
config.general.screen-Resolution=1920 x 1080
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
#Configuration
#ConfigurationProperties(prefix = "config.general")
#Data
public class ConfigGeneral {
private int volume;
private Boolean nightModus;
private Boolean darkMode;
private int fps;
private String screenResolution;
}
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
#Configuration
#ConfigurationProperties(prefix = "config.maintenance")
#Data
public class ConfigMaintenance {
private String nextAvailableUpdateVersion;
private String nextAvailableUpdateDate;
private Boolean automaticUpdate;
private String updateApiToken;
private Boolean latestVersion;
}
Basically I would be interested in a solution, but a good report or even just a suggestion would be enough for me :).
Thanks in advance
I have created 2 config files. I can already use the parameters in the
application.properties. My goal is to be able to display and edit the
parameters later in the frontend. Like normal settings of any
application.
I will add my notice here, because the question of how to pass them to UI may be valid but the outcome may be wrong for you.
In a Spring Boot application, the properties that you use and load via #Configuration
#ConfigurationProperties(prefix = "config.general") are loaded only during spring context initialization which happens when the application starts up. So considering that those properties already exist in your application.properties file, your Frontend would not be able to persist the changed properties somewhere.
1 Possible scenario I see for this requirement, is that those properties are loaded from Database and not application.properties, and then when edited the database records are edited to be persistent. Then you have to either restart the application or try to manually refresh the spring context for those properties to take effect. For this solution you could just create an endpoint from your spring-boot application that delivers all properties that exist in database and then another endpoint to edit those properties. Like simple crud operations.
2 Possible scenario but may be a bit overkill is to use cloud config server. Search for spring cloud bus and spring actuator refresh to see how you can apply some out of the box solutions for an application that needs dynamic properties.

How to trace incoming requests with AWS X-Ray on Spring Boot WebFlux?

I have multiple microservices running on AWS ECS and I want to try out AWS X-Ray. Following this developer guide I added a WebConfig.java file with a tracing filter.
Added lines to build.gradle:
implementation "javax.servlet:javax.servlet-api:4.0.1"
implementation "com.amazonaws:aws-xray-recorder-sdk-core:2.8.0"
New file WebConfig.java:
import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
import javax.servlet.Filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class WebConfig {
#Bean
public Filter tracingFilter() {
return new AWSXRayServletFilter("ordermicroservice");
}
}
However, I don't think this is correct, mostly because I had to add the extra dependency for the javax.servlet.Filter. I think this is because I'm using spring-boot-webflux and not spring-boot-web. So I have a Netty webserver and not a Tomcat webserver.
My questions are:
How can I add logging to a servlet filter, to make sure that every incoming HTTP request is passing correctly through the filter?
What is the correct way to write a web filter in spring-boot-webflux projects, which use Netty and not Tomcat?
Edit:
By now I figured out how to write a filter with Spring Boot WebFlux. I will add it to the question here for future reference:
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
#Component
class MyCustomWebFilter : WebFilter {
override fun filter(webExchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
print("Successfully reached the WebFilter.\n")
val request = webExchange.request
val response = webExchange.response
// TODO: Do the actual filtering by looking at the request and modifying the response
return chain.filter(webExchange)
}
}
As mentioned by Michael, you may need to implement the WebFilter interface. Since the AWSXRayServletFilter is a servlet filter, it won't work with WebFilter. Unfortunately there is no built-in support in X-Ray SDK for WebFlux yet. What you'll need to do is within your WebFilter chain, implement an interceptor of your own for tracing incoming requests by creating a segment and adding relevant data to it. You can reference how the AWSXRayServletFilter traces incoming requests here: https://github.com/aws/aws-xray-sdk-java/blob/master/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/javax/servlet/AWSXRayServletFilter.java
Or, you can use the OpenTelemetry Java SDK to instrument your application and use AWS Collector to send trace data to X-Ray. The OTel SDK has support for WebFlux framework. You can find more info below.
OTel Java SDK instrumentation: https://github.com/open-telemetry/opentelemetry-java-instrumentation
AWS OTel Collector: https://aws-otel.github.io/docs/getting-started/collector

changing custom annotations on the fly from Spring Cloud Config Server. Is it possible?

Context: I need to provide a way to change parameter values during production on lower performance cost as possible.
Goal: I want change annotation values on fly and apply it at once on all microservices instances.
Personal background and limitations: I know I can use Spring Cloud Config to change parameters on the fly like explained in this article and I Know there is some challenges and pitfalls involved on changing annotations on the fly also like discussed in stackoveflow question.
I know that Spring Cloud Config can be used for setting up a centralized configuration applied to all microservice instances during boot/start. I have used it a bit. I am wondering if I can use it for centralizing parameters that can affect customized annotations on fly.
An imagined solution is:
... whenever I need somepRopertyValue
#Value("${config.somePropertyValue}")
private String somePropertyValue;
#Bean
public String somePropertyValue(){
return somePropertyValue;
}
A config client in all microservices endpoint that must be call not only when the application start but whenever somePropertyValue managed in Spring Cloud Config Server bootstrap.properties is updated:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class SpringConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringConfigClientApplication.class, args);
}
}
#RefreshScope
#RestController
class MessageRestController {
#Value("${server.somePropertyValue:Unable to connect to config server}")
private String somePropertyValue;
#RequestMapping("/server/somePropertyValue")
String getSomePropertyValue() {
return this.somePropertyValue;
}
}
And somehow somePropertyValue is maintened in Spring Cloud Config and if change during production time it affects on demand everywhere somePropertyValue is annoted in all microservice instances.
I am currently reaching this behaviour by adding a kafka consumer in all SpringBoot microservices that listen/observe a topic and when it receives a new messagge it changes on the fly the parameter value. It seems so odd that I created a Kafka dependency in all company microservices. Since I have used Spring Config for a bit similar scenario I am wondering if there is a better alternative using some out-of-box Spring approach. Also performance is highly important in my case and a bit delay on syncronize all parameters isn't an issue. By delay I mean that two or three seconds to update parameters in all microservices isn't an issue.
There are two ways to do that:
i- There's a refresh endpoint, and you can actually call that for a service, and it'll actually refresh its configurations without restarting itself, which is pretty neat. e.g. MS-A is listing on 8080 then do a POST request at this endpoint:
localhost:8080/refresh.
NOTE: Spring Actuator actually adds a RefreshEndpoint to the app automatically when we annotate a controller in MS-A with #RefreshScope.
ii- What you can also do is use Spring Cloud Bus, and broadcast an event, and then every service listens on that and refreshes itself. That's handy if you have dozens of services all using the Config Server, and you don't want to go one by one and hit a /refresh endpoint as we have did in 1st approach. You just want to broadcast a message to a bus and have all these things automatically pick it up.
Reference: Both concepts I've learnt while taking course at Pluralsight

Expose GraphqlExceptions using spring boot starter

When using the Spring Boot starter for graphql, all of the exceptions thrown while data fetching appear in the output console as "Internal Server Error(s) while executing query" I would like the just the e.message() string of the GraphQLException I throw to come out of the "message" part of the error field in Graphql so that the front end of this API can see what went wrong.
I've googled this to no avail. Most people expose the errors by editing the Servlet, but since I'm using the Spring Boot starter I cannot do this easily. I know that the graphql-servlet used by starter has a class called DefaultGraphQLErrorHandler found here but I do not know how to override or change it or just somehow get those errors to display.
I tried overriding the SimpleDataFetcherExceptionHandler in graphql, creating a CustomException that overrides GraphQlException but neither worked, the SimpleDataFetcherExceptionHandler implementation was never called during a debug.
I only ever see this:
Thanks
So I figured this out. I had to provide a custom implementation of a GraphQLErrorHandler and add it to the list of beans by marking as a #Component. I then overrided the processErrors() function in the error handler. When marked as a component, this bean was autowired into the graphql serverlet configurator in the graphql spring boot starter package, replacing the default one.
the CustomGraphQlErrorHandler I added is seen below:
import graphql.GraphQLError
import graphql.servlet.GenericGraphQLError
import graphql.servlet.GraphQLErrorHandler
import org.springframework.stereotype.Component
#Component
class CustomGraphQlErrorHandler: GraphQLErrorHandler {
override fun processErrors(errors: MutableList<GraphQLError>?): MutableList<GraphQLError> {
val errorList = mutableListOf<GraphQLError>()
for(error in errors!!){
errorList.add(GenericGraphQLError(error.message))
}
return errorList
}
}
the output looks like this now:
SimpleDataFetcherExceptionHandler implementation will be called when you have graphql.servlet.exception-handlers-enabled=true in application.properties
Great solution works like a charm.
A more compact way of writing it in kotlin would look like this:
#Component
class CustomGraphQlErrorHandler : GraphQLErrorHandler {
override fun processErrors(errors: MutableList<GraphQLError>?) =
errors?.map { GenericGraphQLError(it.message) }?.toMutableList() ?: mutableListOf()
}
I really like the functional style of this

How to get Spring 3.1.1 works with App Engine datastore

Could everyone show me a tutorial on how to make Spring 3.1.1 running on Google App Engine, please?
I've followed severals tutorial and managed to get the dead simple HelloWorld example to work on App Engine. However, when I go futher, I stuck at the persistent process between Spring and Datastore. I looked at this thread Configuring JDO in Spring 3.1?, too, but it works on localhost but it doesn't work when I deploy to app engine due to the javax.naming.NamingException.
Therefore, I'm looking for a not-too-simple tutorial that covers basic aspects of a real life application such as the view, the model and the database.
Jappstart is a good place to see a working example of GAE that uses Spring and the Datastore (via JPA) and is also a good starting point for building a basic GAE/J app.
Having spent about a day trying to make this work, I thought I'd add some additional useful information here. First take a look at this project https://github.com/hleinone/spring-gae-jdo and this issue: http://code.google.com/p/googleappengine/issues/detail?id=1240 -- comment 24 is the useful one.
In case anyone wants to get this working with annotation-driven configuration, here's how I did it:
package com.domain.yourcode.configuration;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jdo.GAETransactionAwarePersistenceManagerFactoryProxy;
import org.springframework.orm.jdo.JdoTransactionManager;
//import org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy;
#Configuration
public class JDOConfiguration {
private static final PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("transactions-optional");
#Bean
public GAETransactionAwarePersistenceManagerFactoryProxy proxyPmf() {
GAETransactionAwarePersistenceManagerFactoryProxy proxy =
new GAETransactionAwarePersistenceManagerFactoryProxy();
proxy.setTargetPersistenceManagerFactory(pmf);
proxy.setAllowCreate(false);
return proxy;
}
#Bean
public JdoTransactionManager transactionManager() {
JdoTransactionManager mgr = new JdoTransactionManager();
mgr.setPersistenceManagerFactory(pmf);
return mgr;
}
}
You'll still want <tx:annotation-driven/> in your applicationContext.xml

Resources