diff --git a/README.md b/README.md
index 4057c7673..8d131ff2b 100644
--- a/README.md
+++ b/README.md
@@ -85,17 +85,42 @@ ENV SPRING_PROFILES_ACTIVE docker,mysql
In the `mysql section` of the `application.yml` from the [Configuration repository], you have to change
the host and port of your MySQL JDBC connection string.
+## Custom metrics monitoring
+
+@todo Add default custom dashboards to grafana
+
+Grafana and Prometheus are included in the `docker-compose.yml` configuration, and the public facing applications have been instrumented with [MicroMeter](https://micrometer.io) to collect JVM and custom business metrics.
+
+### Using Prometheus
+
+* Prometheus can be accessed from your local machine at http://localhost:9091
+
+### Using Grafana with Prometheus
+
+* Login to Grafana at http://localhost:3000, the default user/pass is `admin:admin`, you will be prompted to change your password.
+* Setup a prometheus datasource and point the URL to `http://prometheus-server:9090`, leave all the other options set to their default.
+* Add the [Micrometer/SpringBoot dashboard](https://grafana.com/dashboards/4701) via the Import Dashboard menu item. The id for the dashboard is `4701`
+
+### Custom metrics implementation
+
+* `customers-service` application has the following custom metrics enabled:
+ * counter: `create.owner`
+ * counter: `update.owner`
+ * counter: `create.pet`
+ * counter: `update.pet`
+* `visits-service` application has the following custom metrics enabled:
+ * counter: `create.visit`
## Looking for something in particular?
-| Spring Cloud components | Resources |
-|-------------------------|------------|
-| Configuration server | [Config server properties](spring-petclinic-config-server/src/main/resources/application.yml) and [Configuration repository] |
-| Service Discovery | [Eureka server](spring-petclinic-discovery-server) and [Service discovery client](spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/VetsServiceApplication.java) |
-| API Gateway | [Zuul reverse proxy](spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java) and [Routing configuration](https://github.com/spring-petclinic/spring-petclinic-microservices-config/blob/master/api-gateway.yml) |
-| Docker Compose | [Spring Boot with Docker guide](https://spring.io/guides/gs/spring-boot-docker/) and [docker-compose file](docker-compose.yml) |
-| Circuit Breaker | [Circuit Breaker with Hystrix guide](https://spring.io/guides/gs/circuit-breaker/) and [fallback method configuration](spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java) |
-| Graphite Monitoring | TBD |
+| Spring Cloud components | Resources |
+|---------------------------------|------------|
+| Configuration server | [Config server properties](spring-petclinic-config-server/src/main/resources/application.yml) and [Configuration repository] |
+| Service Discovery | [Eureka server](spring-petclinic-discovery-server) and [Service discovery client](spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/VetsServiceApplication.java) |
+| API Gateway | [Zuul reverse proxy](spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java) and [Routing configuration](https://github.com/spring-petclinic/spring-petclinic-microservices-config/blob/master/api-gateway.yml) |
+| Docker Compose | [Spring Boot with Docker guide](https://spring.io/guides/gs/spring-boot-docker/) and [docker-compose file](docker-compose.yml) |
+| Circuit Breaker | TBD |
+| Grafana / Prometheus Monitoring | [Micrometer implementation](https://micrometer.io/) |
Front-end module | Files |
|-------------------|-------|
diff --git a/docker-compose.yml b/docker-compose.yml
index 50efcee44..1209bd7b4 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,8 @@
version: '2'
+
+volumes:
+ graf-data:
+
services:
config-server:
image: mszarlinski/spring-petclinic-config-server
@@ -91,3 +95,22 @@ services:
entrypoint: ["./dockerize","-wait=tcp://discovery-server:8761","-timeout=60s","--","java", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCGroupMemoryLimitForHeap", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
ports:
- 7979:7979
+
+ ## Grafana / Prometheus
+
+ grafana-server:
+ image: grafana/grafana:5.2.4
+ container_name: grafana-server
+ mem_limit: 256M
+ ports:
+ - 3000:3000
+ volumes:
+ - graf-data:/var/lib/grafana
+
+ prometheus-server:
+ build: ./docker/prometheus
+ image: prometheus-local:v2.4.2
+ container_name: prometheus-server
+ mem_limit: 256M
+ ports:
+ - 9091:9090
diff --git a/docker/prometheus/Dockerfile b/docker/prometheus/Dockerfile
new file mode 100644
index 000000000..58626f638
--- /dev/null
+++ b/docker/prometheus/Dockerfile
@@ -0,0 +1,2 @@
+FROM prom/prometheus:v2.4.2
+ADD prometheus.yml /etc/prometheus/
diff --git a/docker/prometheus/prometheus.yml b/docker/prometheus/prometheus.yml
new file mode 100644
index 000000000..30f089b72
--- /dev/null
+++ b/docker/prometheus/prometheus.yml
@@ -0,0 +1,32 @@
+# my global config
+global:
+ scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
+ evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
+ # scrape_timeout is set to the global default (10s).
+
+# A scrape configuration containing exactly one endpoint to scrape:
+# Here it's Prometheus itself.
+scrape_configs:
+- job_name: prometheus
+ static_configs:
+ - targets: ['localhost:9090']
+
+- job_name: api-gateway
+ metrics_path: /actuator/prometheus
+ static_configs:
+ - targets: ['api-gateway:8080']
+
+- job_name: customers-service
+ metrics_path: /actuator/prometheus
+ static_configs:
+ - targets: ['customers-service:8081']
+
+- job_name: visits-service
+ metrics_path: /actuator/prometheus
+ static_configs:
+ - targets: ['visits-service:8082']
+
+- job_name: vets-service
+ metrics_path: /actuator/prometheus
+ static_configs:
+ - targets: ['vets-service:8083']
diff --git a/pom.xml b/pom.xml
index 72c02e268..1958e2487 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,6 +33,7 @@
2.0.4.RELEASE
Finchley.SR2
2.0.0.RC2
+ 1.0.5
2.22.0
@@ -77,6 +78,19 @@
${assertj.version}
test
+
+
+
+ io.micrometer
+ micrometer-core
+ ${micrometer.version}
+
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+ ${micrometer.version}
+
diff --git a/spring-petclinic-api-gateway/pom.xml b/spring-petclinic-api-gateway/pom.xml
index 72370d2c3..ae40e34eb 100644
--- a/spring-petclinic-api-gateway/pom.xml
+++ b/spring-petclinic-api-gateway/pom.xml
@@ -87,6 +87,14 @@
org.projectlombok
lombok
+
+ io.micrometer
+ micrometer-core
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
diff --git a/spring-petclinic-customers-service/pom.xml b/spring-petclinic-customers-service/pom.xml
index 6786f8346..dc5928e47 100644
--- a/spring-petclinic-customers-service/pom.xml
+++ b/spring-petclinic-customers-service/pom.xml
@@ -77,6 +77,14 @@
org.projectlombok
lombok
+
+ io.micrometer
+ micrometer-core
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
diff --git a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java
index da748d0fa..80916f3ef 100644
--- a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java
+++ b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java
@@ -15,6 +15,7 @@
*/
package org.springframework.samples.petclinic.customers.web;
+import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
@@ -40,6 +41,7 @@
class OwnerResource {
private final OwnerRepository ownerRepository;
+ private final MeterRegistry registry;
/**
* Create Owner
@@ -47,6 +49,7 @@ class OwnerResource {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void createOwner(@Valid @RequestBody Owner owner) {
+ registry.counter("create.owner").increment();
ownerRepository.save(owner);
}
@@ -81,6 +84,7 @@ public Owner updateOwner(@PathVariable("ownerId") int ownerId, @Valid @RequestBo
ownerModel.setAddress(ownerRequest.getAddress());
ownerModel.setTelephone(ownerRequest.getTelephone());
log.info("Saving owner {}", ownerModel);
+ registry.counter("update.owner").increment();
return ownerRepository.save(ownerModel);
}
}
diff --git a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java
index c657ca370..34c8c70d6 100644
--- a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java
+++ b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java
@@ -15,6 +15,7 @@
*/
package org.springframework.samples.petclinic.customers.web;
+import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
@@ -36,8 +37,9 @@
class PetResource {
private final PetRepository petRepository;
-
private final OwnerRepository ownerRepository;
+ private final MeterRegistry registry;
+
@GetMapping("/petTypes")
public List getPetTypes() {
@@ -55,6 +57,7 @@ public void processCreationForm(
Owner owner = optionalOwner.orElseThrow(() -> new ResourceNotFoundException("Owner "+ownerId+" not found"));
owner.addPet(pet);
+ registry.counter("create.pet").increment();
save(pet, petRequest);
}
@@ -63,6 +66,7 @@ public void processCreationForm(
public void processUpdateForm(@RequestBody PetRequest petRequest) {
int petId = petRequest.getId();
Pet pet = findPetById(petId);
+ registry.counter("update.pet").increment();
save(pet, petRequest);
}
diff --git a/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java b/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java
index 99313d23e..5b2b3303c 100644
--- a/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java
+++ b/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java
@@ -1,6 +1,8 @@
package org.springframework.samples.petclinic.customers.web;
import java.util.Optional;
+
+import io.micrometer.core.instrument.MeterRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,6 +42,9 @@ class PetResourceTest {
@MockBean
OwnerRepository ownerRepository;
+ @MockBean
+ MeterRegistry registry;
+
@Test
void shouldGetAPetInJSonFormat() throws Exception {
diff --git a/spring-petclinic-vets-service/pom.xml b/spring-petclinic-vets-service/pom.xml
index e7705a18d..99b957703 100644
--- a/spring-petclinic-vets-service/pom.xml
+++ b/spring-petclinic-vets-service/pom.xml
@@ -93,6 +93,14 @@
mysql-connector-java
runtime
+
+ io.micrometer
+ micrometer-core
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
diff --git a/spring-petclinic-visits-service/pom.xml b/spring-petclinic-visits-service/pom.xml
index ea9184d47..c60737406 100644
--- a/spring-petclinic-visits-service/pom.xml
+++ b/spring-petclinic-visits-service/pom.xml
@@ -76,6 +76,14 @@
mysql-connector-java
runtime
+
+ io.micrometer
+ micrometer-core
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
diff --git a/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java b/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java
index 2ebab12e1..5905f3715 100644
--- a/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java
+++ b/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java
@@ -17,6 +17,8 @@
import java.util.List;
import javax.validation.Valid;
+
+import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
@@ -44,6 +46,7 @@
class VisitResource {
private final VisitRepository visitRepository;
+ private final MeterRegistry registry;
@PostMapping("owners/*/pets/{petId}/visits")
@ResponseStatus(HttpStatus.NO_CONTENT)
@@ -53,6 +56,7 @@ void create(
visit.setPetId(petId);
log.info("Saving visit {}", visit);
+ registry.counter("create.visit").increment();
visitRepository.save(visit);
}
diff --git a/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java b/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java
index 25d33f24b..3409b30ab 100644
--- a/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java
+++ b/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java
@@ -1,5 +1,6 @@
package org.springframework.samples.petclinic.visits.web;
+import io.micrometer.core.instrument.MeterRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,6 +30,9 @@ class VisitResourceTest {
@MockBean
VisitRepository visitRepository;
+ @MockBean
+ MeterRegistry registry;
+
@Test
void shouldFetchVisits() throws Exception {
given(visitRepository.findByPetIdIn(asList(111, 222)))