Şu satırı dahil ederiz
Thread'e ön ek (prefix) verebilmeyi sağlarimport org.springframework.scheduling.concurrent.CustomizableThreadFactory;
Thread'e ön ek (prefix) verebilmeyi sağlarimport org.springframework.scheduling.concurrent.CustomizableThreadFactory;
management.endpoint.info.enabled: true
management.endpoints.web.exposure.include=info,health,loggers
http://localhost:8080/actuator/info
buildEnabled by default: trueGoal: Exposes build information.Requires: you to generate build informationConfiguration property: management.info.build.enabledenvEnabled by default: false (since Spring Boot 2.6. For the older Spring Boot version, this contributor is enabled by default!)Goal: Exposes any property from the SpringEnvironment .Required: to specify properties with names that start with info.*in you application.yml or application.propertiesConfiguration property: management.info.env.enabledgitEnabled by default: trueGoal: exposes git information.Requires: you to generate git informationConfiguration property: management.info.git.enabledjavaEnabled by default: falseGoal: exposes Java runtime information.Configuration property: management.info.java.enabled
Who never needed to quickly know which branch is deployed in the development server to know if a feature of bug-fix is already available?
If the branch name, commit id, and timestamp are not enough, you can enable displaying more information by setting the property below in your application properties:management.info.git.mode=fullThis will add a lot more information, like commit message, commit author, and ahead/behind commits information.
<plugin> <groupId>io.github.git-commit-id</groupId> <artifactId>git-commit-id-maven-plugin</artifactId> </plugin>
{ "git": { "branch": "main", "commit": { "id": "d2035e1", "time": "2023-03-01T18:55:58Z" } } }
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><goals><goal>build-info</goal></goals><configuration><additionalProperties><java.version>${java.version}</java.version><some.custom.property>some value</some.custom.property></additionalProperties></goals></execution></executions></plugin>
When build-info of spring-boot-maven-plugin is run, it generates a property file containing all the build information. By default, it is located at ${project.build.outputDirectory}/META-INF/build-info.properties, but you can customize it by providing outputFile parameter.
When Spring detects there is this file on the classpath, it creates BuildProperties bean unless it is explicitly declared.
management.info.env.enabled
management.info.env.enabled=true info.app.name=Spring Boot Actuator Dashboard info.app.description=Spring Boot backend to demonstrate actuator APIs info.app.version=1.0
{ "app": { "name": "Spring Boot Actuator Dashboard", "description": "Spring Boot backend to demonstrate actuator APIs", "version": "1.0" } }
## Configuring info endpointinfo.app.name=Spring Sample Applicationinfo.app.description=This is my first spring boot applicationinfo.app.version=1.0.0info.java-vendor = ${java.specification.vendor}
spring: application: name: example-app info: application: name: ${spring.application.name} description: Very cool Spring Boot application version: '@project.version@' spring-cloud-version: '@spring-cloud.version@' spring-boot-version: '@project.parent.version@'
curl http://localhost:8080/actuator/info | jq { "application": { "name": "example-app", "description": "Very cool Spring Boot application", "version": "0.0.1-SNAPSHOT", "spring-cloud-version": "2020.0.4", "spring-boot-version": "2.5.7" } }
Şimdi bir istek gönderelim@Componentpublic class DemoInfoContributor implements InfoContributor {@Overridepublic void contribute(Info.Builder builder) {builder.withDetail("demo",Collections.singletonMap("info-timestamp", LocalDateTime.now().toString()));}}
GET http://127.0.0.1:8080/actuator/infoÇıktı olarak şunu alırız
{"app": {"name": "actuator-test","description": "null"},"git": {"branch": "master","commit": {"id": "ff889ec","time": "2020-10-29T22:36:04Z"}},"build": {"artifact": "actuator-test","name": "actuator-test","time": "2020-10-30T21:53:59.493Z","version": "0.0.1-SNAPSHOT","group": "com.relaximus"},"demo": {"info-timestamp": "2020-10-30T23:23:07.241981"}}
Shows the configuration of loggers and modify them
It provides a feature to view and update the logs level.
management.endpoints.web.exposure.include=info,health,loggers
GET http://127.0.0.1:8080/actuator/loggers/com.relaximus.actuatortest
{"configuredLevel": "INFO","effectiveLevel": "INFO"}
POST http://127.0.0.1:8080/actuator/loggers/com.relaximus.actuatortestContent-Type: application/vnd.spring-boot.actuator.v3+json{"configuredLevel": "DEBUG"}
`curl -X "POST" "http://localhost:8080/loggers/<class or package name>" -H "Content-
Type: application/json; charset=utf-8" -d $'{
"configuredLevel": "ERROR"
}'`
import org.springframework.kafka.core.DefaultKafkaProducerFactory;import org.springframework.kafka.core.KafkaTemplate;import org.springframework.kafka.core.ProducerFactory;
BOOTSTRAP_SERVERS_CONFIG: This is the key for the Kafka bootstrap servers configuration.KEY_SERIALIZER_CLASS_CONFIG: Serializer class for Producer Factory keyVALUE_SERIALIZER_CLASS_CONFIG: Serializer class for Producer Factory Value
@Configurationpublic class KafkaProducerConfig {@Beanpublic ProducerFactory producerFactory(KafkaProperties kafkaProperties) {HashMap<String, Object> properties = new HashMap<>();properties.putAll(kafkaProperties.buildProducerProperties());properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");StringSerializer keySerializer = new StringSerializer();StringSerializer valueSerializer = new StringSerializer();return new DefaultKafkaProducerFactory<>(properties, keySerializer, valueSerializer);}@Beanpublic KafkaTemplate kafkaTemplate(ProducerFactory producerFactory) {KafkaTemplate kafkaTemplate = new KafkaTemplate<>(producerFactory);return kafkaTemplate;}}
@Configurationpublic class KafkaProducerConfig {@Beanpublic ProducerFactory producerFactory(KafkaProperties kafkaProperties) {HashMap<String, Object> properties = new HashMap<>();properties.putAll(kafkaProperties.buildProducerProperties());properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");StringSerializer keySerializer = new StringSerializer();StringSerializer valueSerializer = new StringSerializer();return new DefaultKafkaProducerFactory<>(properties, keySerializer, valueSerializer);}@Beanpublic KafkaTemplate kafkaTemplate(ProducerFactory producerFactory) {KafkaTemplate kafkaTemplate = new KafkaTemplate<>(producerFactory);return kafkaTemplate;}}
Örnek - Avro@Configurationpublic class KafkaProducerConfig {@Autowiredprivate KafkaProperties kafkaProperties;@Beanpublic Map<String, Object> producerConfigs() {Map<String, Object> props =new HashMap<>(kafkaProperties.buildProducerProperties());props.put(org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class);props.put(org.apache.kafka.clients.producer.ProducerConfig
.VALUE_SERIALIZER_CLASS_CONFIG,JsonSerializer.class);return props;}@Beanpublic ProducerFactory<String, Object> producerFactory() {return new DefaultKafkaProducerFactory<>(producerConfigs());}@Beanpublic KafkaTemplate<String, Object> kafkaTemplate() {return new KafkaTemplate<>(producerFactory());}}
@Bean public Map<String, Object> producerConfigs() { Map<String, Object> props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class); props.put(ProducerConfig.CLIENT_ID_CONFIG, "kafka-producer"); props.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistry); props.put(ProducerConfig.ACKS_CONFIG, "all"); props.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, "10000"); props.put(ProducerConfig.RETRIES_CONFIG, "0"); return props; }
@Bean public ProducerFactory<String, PaymentSent> producerFactory( @Value("${kafka.bootstrap-servers}") final String bootstrapServers, @Value("${kafka.schema.registry.url}") final String schemaRegistryUrl) { Map<String, Object> config = new HashMap<>(); config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class); config.put(KafkaAvroSerializerConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl); config.put(KafkaAvroSerializerConfig.AUTO_REGISTER_SCHEMAS, false); return new DefaultKafkaProducerFactory<>(config); }
The critical properties are spring.r2dbc.url and spring.r2dbc.username and spring.r2dbc.passwordÖrnek - mysql
The standard spring.datasource.* properties are not relevant for R2DBC.
spring.r2dbc.url=r2dbc:pool:mysql://localhost:3306/SQL_RX_TEST? zeroDateTimeBehavior=convertToNull&useSSL=false&useServerPrepareStatement=true
Örnek - loglamaspring.r2dbc.url=r2dbc:postgresql://postgres@localhost:5432/reactivelogging.level.org.springframework.r2dbc=DEBUG
# I can enable logging using application.properties to see the executed queries— handy. logging.level.org.springframework.data.repository=DEBUG logging.level.org.springframework.r2dbc.core=DEBUG
public interface MemberRepository extends R2dbcRepository<Member, Long> {Mono<Member> findByName(String name);}
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
@LoadBalanced is a marker annotation. It is used to indicate that RestTemplate or WebClient should use a RibbonLoadBalancerClient or LoadBalancerClient for interacting with the services. In turn, this allows you to use “logical identifiers” for the URLs we pass to the RestTemplate or WebClient. These logical identifiers are typically the name of a service registered in service discovery.Note:- @RibbonClient(spring-cloud-starter-netflix-ribbon) and @LoadBalancerClient(spring-cloud-commons) are optional. This both works as a load-balancer on the client-side and gives us control over the HTTP and TCP clients. If we are using Service Discovery then we are good to go with default settings. we don't need to explicitly use these annotations.
Used as a marker annotation indicating that the annotated RestTemplate should use a RibbonLoadBalancerClient for interacting with your service(s).In turn, this allows you to use "logical identifiers" for the URLs you pass to the RestTemplate. These logical identifiers are typically the name of a service.
restTemplate.getForObject("http://some-service-name/user/{id}", String.class, 1);
name Alanı@LoadBalanced@BeanRestTemplate getRestTemplate() {return new RestTemplate();}
... if we are not using any service discovery or need to customize the settings of Ribbon or LoadBalancer for a particular client then we need to use any of the two.name - the service we are calling Ribbon or LoadBlancer but need additional customizations for how Ribbon or LoadBlancer interacts with that service.configuration - set it to an @Configuration class with all of our customizations defined as @Beans .
@Configuration @LoadBalancerClient (name ="custom-config", configuration = SomeCustomConfiguration.class) // @RibbonClient (name = "custom-config", configuration = SomeCustomConfiguration.class) public class WebClientConfigurationLB { }
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Spring ile Aspect Oriented Programlama Yapmak için iki seçenek var.AspectJ is a runtime resolution, which has a performance drawback.
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>
Spring AOP runs everything through proxies which sadly can't be everywhere.
....
Proxies are created only for singletons (@Bean) so we are greatly limited when we want to secure methods on specific objects (such as JPA @Entities) that are not beans. Proxies also won't be called within calling objects (bean calling its methods in context of self - this).
@Aspect@Componentpublic class LoggingAspect {@Before("@annotation(LogMethod)")public void logMethodName(JoinPoint joinPoint) {String method = joinPoint.getSignature().getName();String params = Arrays.toString(joinPoint.getArgs());System.out.println("Method [" + method + "] gets called with parameters " + params);}}
@Aspect
public class LoggingAspect {
@Pointcut("execution(* com.tlc.tracker.v01.controller.*.*(..))")
public void controllerMethods() { }
@Pointcut("execution(* com.tlc.tracker.v01.service.imp.ProjectServiceImpl.*(..))")
public void serviceMethods(){ }
@Pointcut("execution(* com.tlc.tracker.v01.repository.*.*(..))")
public void respositoryMethods(){ }
@Before("serviceMethods()")
public void Areturn(JoinPoint point){
System.out.println("Hola");
log.info("HOLA");
}
@AfterThrowing(pointcut = "controllerMethods() && serviceMethods()
&& respositoryMethods()", throwing = "exception")
public void logAfterThrowing(JoinPoint joinPoint, Throwable exception){
if(exception instanceof BusinessServiceException){
//Get the parts of the exception message
String exceptions[] = exception.getMessage().split(":", 2);
//Get the message contained in the exception message
String message = exceptions.length == 1 ? "" : exceptions[1];
log.error("[" + joinPoint.getSignature().getName()
+ "] - Type: " + exceptions[0] + ". Message: "+ message);
}
if(exception instanceof Exception){
log.error("[" + joinPoint.getSignature().getName()
+ "] - Type: ServerError. Message: " + exception.getMessage());
}
}