Skip to content

Commit

Permalink
Merge pull request #22 from selimyanat/expose-application-metrics
Browse files Browse the repository at this point in the history
build(metrics): expose microservice metrics
  • Loading branch information
selimyanat authored Mar 15, 2022
2 parents 2727733 + b5473b6 commit 7b0eacd
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 33 deletions.
13 changes: 11 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
app:
image: 'selimyanat/coladay:latest'
build:
context: .
context: ../..
container_name: coladay
ports:
- "9090:8080"
Expand Down Expand Up @@ -33,4 +33,13 @@ services:
- "9001:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
- POSTGRES_PASSWORD=secret
prometheus:
image: 'prom/prometheus:v2.31.1'
command: --config.file=/etc/prometheus/prometheus.yml --log.level=debug
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "7070:9090"
9 changes: 4 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
Expand All @@ -72,7 +72,6 @@
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<!-- <version>3.4.1</version>-->
</dependency>
<dependency>
<groupId>io.vavr</groupId>
Expand Down
14 changes: 14 additions & 0 deletions prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
global:
scrape_interval: 15s
evaluation_interval: 30s

scrape_configs:
- job_name: prometheus
honor_labels: true
static_configs:
- targets: ["localhost:9090"]
- job_name: coladay
honor_labels: true
metrics_path: /actuator/prometheus
static_configs:
- targets: ["host.docker.internal:9091"]
29 changes: 16 additions & 13 deletions src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,20 @@ The Reservation resources is used to create, delete, update and list resources.
[[resources_create_reservation]]
=== Creating a reservation

A `POST` request is used to create a reservation.
A `POST` request used to create a reservation.

operation::create-a-reservation[snippets='curl-request,http-request,http-response']

[[resources_list_reservations]]
=== List reservations

A `GET` request is used to list all reservations.
A `GET` request used to list all reservations.

operation::get-all-reservations-by-page[snippets='curl-request,http-request,http-response']

=== Cancel a reservation

A `DELETE` request is used to cancel a reservation.
A `DELETE` request used to cancel a reservation.

operation::cancel-a-reservation[snippets='curl-request,http-request,http-response']

Expand All @@ -144,7 +144,7 @@ operation::cancel-a-reservation[snippets='curl-request,http-request,http-respons
[[resources_list_rooms]]
=== List rooms

A `GET` request is used to list all rooms and their availabilities.
A `GET` request used to list all rooms and their availabilities.

operation::list-all-rooms[snippets='curl-request,http-request,http-response']

Expand All @@ -155,32 +155,35 @@ operation::list-all-rooms[snippets='curl-request,http-request,http-response']
[[resources_list_users]]
=== List users

A `GET` request is used to list all users.
A `GET` request used to list all users.

operation::list-all-users[snippets='curl-request,http-request,http-response']

[[resources_metrics]]
== Metrics

IMPORTANT: For testing purpose, the examples below use the default application port 8080 to access
the metric endpoints. However, in production the metric endpoints are accessible by default on port
8081.

=== List all metrics endpoints

A `GET` request is used to list all metrics endpoints.
A `GET` request used to list all metrics endpoints.

operation::list-all-metrics-endpoints[snippets='curl-request,http-request,http-response']

=== List all application metrics

A `GET` request is used to list all application metrics.
A `GET` request used to list all application metrics.

operation::list-all-applications-metrics[snippets='curl-request,http-request,http-response']


=== Read application health

A `GET` request is used to read application health.
A `GET` request used to read application health.

operation::read-application-health[snippets='curl-request,http-request,http-response']

=== List all application metrics in prometheus format

A `GET` request used to list all metrics endpoints in prometheus format. This endpoint is
generally called by Prometheus to scrap application metrics.

operation::read-application-health[snippets='curl-request,http-request,http-response']
operation::list-all-applications-metrics-in-prometheus-format[snippets='curl-request,http-request,http-response']
5 changes: 4 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
spring.application.name="coladay"

# Quota configuration
coke.quota= ${COKE_QUOTA:100}
pepsi.quota= ${PEPSI_QUOTA:100}
Expand All @@ -18,8 +20,9 @@ spring.jpa.properties.hibernate.default_schema=coladay


# Actuator
management.metrics.tags.application=coladay
management.security.enabled=false
management.endpoints.web.exposure.include=health, metrics
management.endpoints.web.exposure.include=health, metrics, prometheus
management.server.port=8081

# Log configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public void beforeAll(ExtensionContext extensionContext) throws Exception {

@Override
public void afterAll(ExtensionContext extensionContext) throws Exception {
postgres.stop();
}

}
56 changes: 48 additions & 8 deletions src/test/java/com/sy/coladay/metrics/MetricsControllerIT.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sy.coladay.metrics;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
Expand All @@ -9,6 +10,7 @@
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

Expand Down Expand Up @@ -57,13 +59,15 @@ public void getActuator_return_200() {
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
// In case, we upgrade spring boot this test can catch regression and new functionality
.andExpect(jsonPath("$._links.length()", is(6)))
// In case, we upgrade spring boot or its configuration, this test can catch regression
// and new functionality
.andExpect(jsonPath("$._links.length()", is(7)))
.andExpect(jsonPath("$._links.health-component-instance", notNullValue()))
.andExpect(jsonPath("$._links.health-component", notNullValue()))
.andExpect(jsonPath("$._links.health", notNullValue()))
.andExpect(jsonPath("$._links.metrics", notNullValue()))
.andExpect(jsonPath("$._links.metrics-requiredMetricName", notNullValue()))
.andExpect(jsonPath("$._links.prometheus", notNullValue()))
.andDo(document("list-all-metrics-endpoints"))
;
}
Expand All @@ -86,13 +90,8 @@ public void getMetrics_return_200() {
.andExpect(jsonPath("$.names", hasItem("jvm.memory.max")))
.andExpect(jsonPath("$.names", hasItem("jdbc.connections.min")))
.andExpect(jsonPath("$.names", hasItem("jdbc.connections.max")))
.andExpect(jsonPath("$.names", hasItem("jdbc.connections.max")))
.andExpect(jsonPath("$.names", hasItem("jdbc.connections.active")))
.andExpect(jsonPath("$.names", hasItem("system.cpu.usage")))
.andExpect(jsonPath("$.names", hasItem("hikaricp.connections.max")))
.andExpect(jsonPath("$.names", hasItem("hikaricp.connections.min")))
.andExpect(jsonPath("$.names", hasItem("hikaricp.connections.idle")))
.andExpect(jsonPath("$.names", hasItem("hikaricp.connections.pending")))
.andExpect(jsonPath("$.names", hasItem("hikaricp.connections.usage")))
.andExpect(jsonPath("$.names", hasItem("process.uptime")))
.andExpect(jsonPath("$.names", hasItem("process.cpu.usage")))
.andExpect(jsonPath("$.names", hasItem("process.start.time")))
Expand All @@ -113,4 +112,45 @@ public void getHealth_return_200() {
.andDo(document("read-application-health"));
}

@Test
@SneakyThrows
public void getPrometheusMetrics_returns200() {

mockMvc.perform(get("/actuator/prometheus")
.accept(MediaType.TEXT_PLAIN_VALUE))
.andDo(print())
// NOTE: This is a use case for consumer contract testing!
.andExpect(status().isOk())
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"heap\",id=\"G1 Eden Space\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"heap\",id=\"G1 Old Gen\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"nonheap\",id=\"Metaspace\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"heap\",id=\"G1 Survivor Space\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"nonheap\",id=\"CodeHeap 'non-nmethods'\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"nonheap\",id=\"CodeHeap 'non-profiled nmethods'\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"nonheap\",id=\"Compressed Class Space\",}")))
.andExpect(content().string(containsString("jvm_memory_used_bytes{application=\"coladay\",area=\"nonheap\",id=\"CodeHeap 'profiled nmethods'\",}")))
.andExpect(content().string(containsString("system_cpu_usage{application=\"coladay\",}")))
.andExpect(content().string(containsString("jdbc_connections_min{application=\"coladay"
+ "\",name=\"dataSource\",}")))
.andExpect(content().string(containsString("jdbc_connections_max{application=\"coladay"
+ "\",name=\"dataSource\",}")))
.andExpect(content().string(containsString("jdbc_connections_active{application=\"coladay"
+ "\",name=\"dataSource\",}")))
.andExpect(content().string(containsString("jvm_threads_states_threads{application"
+ "=\"coladay\",state=\"runnable\",}")))
.andExpect(content().string(containsString("jvm_threads_states_threads{application"
+ "=\"coladay\",state=\"blocked\",}")))
.andExpect(content().string(containsString("jvm_threads_states_threads"
+ "{application=\"coladay\",state=\"waiting\",}")))
.andExpect(content().string(containsString("jvm_threads_states_threads{application"
+ "=\"coladay\",state=\"timed-waiting\",}")))
.andExpect(content().string(containsString("jvm_threads_states_threads{application"
+ "=\"coladay\",state=\"new\",}")))
.andExpect(content().string(containsString("process_uptime_seconds")))
.andExpect(content().string(containsString("process_cpu_usage")))
.andExpect(content().string(containsString("process_start_time_seconds")))
.andDo(document("list-all-applications-metrics-in-prometheus-format"))
;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ class ReservationControllerIT {
User cokeUser;

User pepsiUser;
//
// @Autowired
// private DataSource dataSource;


@BeforeEach
void setUp(WebApplicationContext webApplicationContext,
Expand Down

0 comments on commit 7b0eacd

Please sign in to comment.