diff --git a/README.md b/README.md index 8d131ff2b..5f5fa6532 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This microservices branch was initially derived from [AngularJS version](https:/ ## Starting services locally without Docker Every microservice is a Spring Boot application and can be started locally using IDE or `../mvnw spring-boot:run` command. Please note that supporting services (Config and Discovery Server) must be started before any other application (Customers, Vets, Visits and API). -Tracing server and Admin server startup is optional. +Startup of Tracing server, Admin server, Grafana and Prometheus is optional. If everything goes well, you can access the following services at given location: * Discovery Server - http://localhost:8761 * Config Server - http://localhost:8888 @@ -14,6 +14,8 @@ If everything goes well, you can access the following services at given location * Customers, Vets and Visits Services - random port, check Eureka Dashboard * Tracing Server (Zipkin) - http://localhost:9411/zipkin/ (we use [openzipkin](https://github.com/openzipkin/zipkin/tree/master/zipkin-server)) * Admin Server (Spring Boot Admin) - http://localhost:9090 +* Grafana Dashboards - http://localhost:3000 +* Prometheus - http://localhost:9091 * Hystrix Dashboard for Circuit Breaker pattern - http://localhost:7979 - On the home page is a form where you can enter the URL for an event stream to monitor, for example the `api-gateway` service running locally: `http://localhost:8080/actuator/hystrix.stream` or running into docker: `http://api-gateway:8080/actuator/hystrix.stream` @@ -87,29 +89,36 @@ 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. -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. +A JMeter load testing script is available to stress the application and generate metrics: [petclinic_test_plan.jmx](spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx) + +![Grafana metrics dashboard](docs/grafana-custom-metrics-dashboard.png) ### Using Prometheus -* Prometheus can be accessed from your local machine at http://localhost:9091 +* 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` +* An anonymous access and a Prometheus datasource are setup. +* A `Spring Petclinic Metrics` Dashboard is available at the URL [http://localhost:3000/d/69JXeR0iw/spring-petclinic-metrics](). +You will find the JSON configuration file here: [docker/grafana/dashboards/grafana-petclinic-dashboard.json](). +* You may create your own dashboard or import the [Micrometer/SpringBoot dashboard](https://grafana.com/dashboards/4701) via the Import Dashboard menu item. +The id for this dashboard is `4701`. + +### Custom metrics -### Custom metrics implementation +Spring Boot registers a lot number of core metrics: JVM, CPU, Tomcat, Logback... +The Spring Boot auto-configuration enables the instrumentation of requests handled by Spring MVC. +All those three REST controllers `OwnerResource`, `PetResource` and `VisitResource` have been instrumented by the `@Timed` Micrometer annotation at class level. * `customers-service` application has the following custom metrics enabled: - * counter: `create.owner` - * counter: `update.owner` - * counter: `create.pet` - * counter: `update.pet` + * @Timed: `petclinic.owner` + * @Timed: `petclinic.pet` * `visits-service` application has the following custom metrics enabled: - * counter: `create.visit` + * @Timed: `petclinic.visit` ## Looking for something in particular? @@ -119,8 +128,8 @@ Grafana and Prometheus are included in the `docker-compose.yml` configuration, a | 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/) | +| Circuit Breaker | [Hystrix fallback method](spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java) | +| Grafana / Prometheus Monitoring | [Micrometer implementation](https://micrometer.io/), [Spring Boot Actuator Production Ready Metrics] | Front-end module | Files | |-------------------|-------| @@ -149,3 +158,4 @@ For pull requests, editor preferences are available in the [editor config](.edit [Configuration repository]: https://github.com/spring-petclinic/spring-petclinic-microservices-config +[Spring Boot Actuator Production Ready Metrics]: https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html diff --git a/docker-compose.yml b/docker-compose.yml index 1209bd7b4..2fffc3ebe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,5 @@ version: '2' -volumes: - graf-data: - services: config-server: image: mszarlinski/spring-petclinic-config-server @@ -99,17 +96,14 @@ services: ## Grafana / Prometheus grafana-server: - image: grafana/grafana:5.2.4 + build: ./docker/grafana 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: diff --git a/docker/grafana/Dockerfile b/docker/grafana/Dockerfile new file mode 100644 index 000000000..c11b3d911 --- /dev/null +++ b/docker/grafana/Dockerfile @@ -0,0 +1,4 @@ +FROM grafana/grafana:5.2.4 +ADD ./provisioning /etc/grafana/provisioning +ADD ./grafana.ini /etc/grafana/grafana.ini +ADD ./dashboards /var/lib/grafana/dashboards diff --git a/docker/grafana/dashboards/grafana-petclinic-dashboard.json b/docker/grafana/dashboards/grafana-petclinic-dashboard.json new file mode 100644 index 000000000..6d79a8d5f --- /dev/null +++ b/docker/grafana/dashboards/grafana-petclinic-dashboard.json @@ -0,0 +1,772 @@ +{ + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "5.0.0" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "5.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "5.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "5.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1539967676482, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 11, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_sum{status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{ status!~\"5..\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - AVG", + "refId": "A" + }, + { + "expr": "max(http_server_requests_seconds_max{status!~\"5..\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - MAX", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "HTTP Request Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 9, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 0.5, + "points": true, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_count[1m]))", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "request - ok", + "refId": "A" + }, + { + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[1m]))", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "request - err", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "HTTP Request Activity", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 2, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(petclinic_owner_seconds_count{method=\"PUT\", status=\"200\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Owners Updated", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 9 + }, + "id": 3, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(petclinic_owner_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "title": "Owners Created", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 9 + }, + "id": 4, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(petclinic_pet_seconds_count{method=\"POST\", status=\"204\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "title": "Pets Created", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 9 + }, + "id": 5, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "Value", + "targets": [ + { + "expr": "sum(petclinic_visit_seconds_count{method=\"POST\", status=\"204\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "title": "Visit Created", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 7, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(petclinic_owner_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "owner create", + "refId": "A" + }, + { + "expr": "sum(petclinic_pet_seconds_count{method=\"POST\", status=\"204\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "pet create", + "refId": "B" + }, + { + "expr": "sum(petclinic_visit_seconds_count{method=\"POST\", status=\"204\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "visit create", + "refId": "C" + }, + { + "expr": "sum(petclinic_owner_seconds_count{method=\"PUT\", status=\"200\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "owner update", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "SPC Business Histogram", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "refresh": "30s", + "schemaVersion": 16, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "auto": true, + "auto_count": 1, + "auto_min": "10s", + "current": { + "text": "auto", + "value": "$__auto_interval_timeRange" + }, + "hide": 0, + "label": null, + "name": "timeRange", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_timeRange" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "12h", + "value": "12h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + }, + { + "selected": false, + "text": "7d", + "value": "7d" + }, + { + "selected": false, + "text": "14d", + "value": "14d" + }, + { + "selected": false, + "text": "30d", + "value": "30d" + } + ], + "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d", + "refresh": 2, + "type": "interval" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Spring Petclinic Metrics", + "uid": "69JXeR0iw", + "version": 1 +} diff --git a/docker/grafana/grafana.ini b/docker/grafana/grafana.ini new file mode 100644 index 000000000..2919aa463 --- /dev/null +++ b/docker/grafana/grafana.ini @@ -0,0 +1,27 @@ +##################### Spring Petclinic Microservices Grafana Configuration ##################### + +# possible values : production, development +app_mode = development + +#################################### Paths #################################### +[paths] +# folder that contains provisioning config files that grafana will apply on startup and while running. +provisioning = /etc/grafana/provisioning + +#################################### Server #################################### +[server] +# enable gzip +enable_gzip = true + +#################################### Anonymous Auth ########################## +# Anonymous authentication has been enabled in the Petclinic sample with Admin role +# Do not do that in Production environment +[auth.anonymous] +# enable anonymous access +enabled = true + +# specify organization name that should be used for unauthenticated users +org_name = Main Org. + +# specify role for unauthenticated users +org_role = Admin diff --git a/docker/grafana/provisioning/dashboards/all.yml b/docker/grafana/provisioning/dashboards/all.yml new file mode 100644 index 000000000..3b978e625 --- /dev/null +++ b/docker/grafana/provisioning/dashboards/all.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: +- name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 #how often Grafana will scan for changed dashboards + options: + path: /var/lib/grafana/dashboards diff --git a/docker/grafana/provisioning/datasources/all.yml b/docker/grafana/provisioning/datasources/all.yml new file mode 100644 index 000000000..9c88fce5a --- /dev/null +++ b/docker/grafana/provisioning/datasources/all.yml @@ -0,0 +1,13 @@ +# config file version +apiVersion: 1 + +# list of datasources to insert/update depending what's available in the database +datasources: +- name: Prometheus + type: prometheus + access: proxy + org_id: 1 + url: http://prometheus-server:9090 + is_default: true + version: 1 + editable: true diff --git a/docs/grafana-custom-metrics-dashboard.png b/docs/grafana-custom-metrics-dashboard.png new file mode 100644 index 000000000..56e87b47e Binary files /dev/null and b/docs/grafana-custom-metrics-dashboard.png differ diff --git a/pom.xml b/pom.xml index 1958e2487..3627c00de 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,6 @@ 2.0.4.RELEASE Finchley.SR2 2.0.0.RC2 - 1.0.5 2.22.0 @@ -79,18 +78,6 @@ 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 ae40e34eb..790845120 100644 --- a/spring-petclinic-api-gateway/pom.xml +++ b/spring-petclinic-api-gateway/pom.xml @@ -87,10 +87,6 @@ org.projectlombok lombok - - io.micrometer - micrometer-core - io.micrometer micrometer-registry-prometheus diff --git a/spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx b/spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx new file mode 100644 index 000000000..9e88f1a8e --- /dev/null +++ b/spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx @@ -0,0 +1,568 @@ + + + + + + false + true + false + + + + PETCLINC_HOST + localhost + = + + + PETCLINIC_PORT + 8080 + = + + + + + + + + + + + ${PETCLINC_HOST} + ${PETCLINIC_PORT} + + + + 6 + + + + + + + + Accept + application/json, text/plain, */* + + + Content-Type + application/json;charset=UTF-8 + + + Accept-Encoding + gzip, deflate, br + + + + + + + + 1 + 0 + 0 + 300 + 30 + + + + false + -1 + + continue + + + + PET_TYPE + + 1 + 6 + + false + + + + 300 + 100.0 + + + + + + + + + + + /api/customer/owners + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "firstName":"Firstname", + "lastName":"Lastname", + "address":"Adress", + "city":"City", + "telephone":"0000000000" +} + = + + + + + + + + /api/customer/owners + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + OWNER_ID + $.id + + + + + + + + + + + + + /api/gateway/owners/${OWNER_ID} + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "id":"${OWNER_ID}", + "firstName":"Firstname", + "lastName":"Lastname${OWNER_ID}", + "address":"Adress${OWNER_ID}", + "city":"City${OWNER_ID}", + "telephone":"1111111111" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID} + PUT + true + false + true + false + + + + + + + + 204 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "name":"Pet", + "birthDate":"2018-12-31T23:00:00.000Z", + "typeId":"${PET_TYPE}" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID}/pets + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + PET_ID + $.id + + + + + + 1 + + + + true + + + + false + { + "name":"Pet", + "birthDate":"2018-12-31T23:00:00.000Z", + "typeId":"${PET_TYPE}" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID}/pets + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + + + true + + + + false + { + "id": ${PET_ID}, + "name":"Pet${OWNER_ID}", + "birthDate":"2018-12-31T23:00:00.000Z", + "typeId":"${PET_TYPE}" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID}/pets/${PET_ID} + PUT + true + false + true + false + + + + + + + + 204 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "date":"2019-03-15", + "description":"Visit" +} + = + + + + + + + + /api/visit/owners/${OWNER_ID}/pets/${PET_ID}/visits + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + + 1 + + + + true + + + + false + { + "date":"2019-03-15", + "description":"Visit" +} + = + + + + + + + + /api/visit/owners/${OWNER_ID}/pets/${PET_ID}/visits + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + + + + + + + + + + /api/vet/vets + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/spring-petclinic-customers-service/pom.xml b/spring-petclinic-customers-service/pom.xml index dc5928e47..9c411deb6 100644 --- a/spring-petclinic-customers-service/pom.xml +++ b/spring-petclinic-customers-service/pom.xml @@ -77,10 +77,6 @@ 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 80916f3ef..c9ea4b929 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,7 +15,7 @@ */ package org.springframework.samples.petclinic.customers.web; -import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -36,21 +36,20 @@ */ @RequestMapping("/owners") @RestController +@Timed("petclinic.owner") @RequiredArgsConstructor @Slf4j class OwnerResource { private final OwnerRepository ownerRepository; - private final MeterRegistry registry; /** * Create Owner */ @PostMapping @ResponseStatus(HttpStatus.CREATED) - public void createOwner(@Valid @RequestBody Owner owner) { - registry.counter("create.owner").increment(); - ownerRepository.save(owner); + public Owner createOwner(@Valid @RequestBody Owner owner) { + return ownerRepository.save(owner); } /** @@ -73,7 +72,8 @@ public List findAll() { * Update Owner */ @PutMapping(value = "/{ownerId}") - public Owner updateOwner(@PathVariable("ownerId") int ownerId, @Valid @RequestBody Owner ownerRequest) { + @ResponseStatus(HttpStatus.NO_CONTENT) + public void updateOwner(@PathVariable("ownerId") int ownerId, @Valid @RequestBody Owner ownerRequest) { final Optional owner = ownerRepository.findById(ownerId); final Owner ownerModel = owner.orElseThrow(() -> new ResourceNotFoundException("Owner "+ownerId+" not found")); @@ -84,7 +84,6 @@ 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); + 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 34c8c70d6..3b8bdd887 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,7 +15,7 @@ */ package org.springframework.samples.petclinic.customers.web; -import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -32,13 +32,13 @@ * @author Maciej Szarlinski */ @RestController +@Timed("petclinic.pet") @RequiredArgsConstructor @Slf4j class PetResource { private final PetRepository petRepository; private final OwnerRepository ownerRepository; - private final MeterRegistry registry; @GetMapping("/petTypes") @@ -47,8 +47,8 @@ public List getPetTypes() { } @PostMapping("/owners/{ownerId}/pets") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void processCreationForm( + @ResponseStatus(HttpStatus.CREATED) + public Pet processCreationForm( @RequestBody PetRequest petRequest, @PathVariable("ownerId") int ownerId) { @@ -57,8 +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); + return save(pet, petRequest); } @PutMapping("/owners/*/pets/{petId}") @@ -66,11 +65,10 @@ 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); } - private void save(final Pet pet, final PetRequest petRequest) { + private Pet save(final Pet pet, final PetRequest petRequest) { pet.setName(petRequest.getName()); pet.setBirthDate(petRequest.getBirthDate()); @@ -79,7 +77,7 @@ private void save(final Pet pet, final PetRequest petRequest) { .ifPresent(pet::setType); log.info("Saving pet {}", pet); - petRepository.save(pet); + return petRepository.save(pet); } @GetMapping("owners/*/pets/{petId}") 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 5b2b3303c..04a6e0079 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 @@ -2,7 +2,6 @@ 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; @@ -42,9 +41,6 @@ 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 99b957703..9f3c58cd7 100644 --- a/spring-petclinic-vets-service/pom.xml +++ b/spring-petclinic-vets-service/pom.xml @@ -93,10 +93,6 @@ 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 c60737406..2bf6cc5d0 100644 --- a/spring-petclinic-visits-service/pom.xml +++ b/spring-petclinic-visits-service/pom.xml @@ -76,10 +76,6 @@ 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 5905f3715..3bcf7f76d 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 @@ -18,7 +18,7 @@ import java.util.List; import javax.validation.Valid; -import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -43,21 +43,20 @@ @RestController @RequiredArgsConstructor @Slf4j +@Timed("petclinic.visit") class VisitResource { private final VisitRepository visitRepository; - private final MeterRegistry registry; @PostMapping("owners/*/pets/{petId}/visits") - @ResponseStatus(HttpStatus.NO_CONTENT) - void create( + @ResponseStatus(HttpStatus.CREATED) + Visit create( @Valid @RequestBody Visit visit, @PathVariable("petId") int petId) { visit.setPetId(petId); log.info("Saving visit {}", visit); - registry.counter("create.visit").increment(); - visitRepository.save(visit); + return visitRepository.save(visit); } @GetMapping("owners/*/pets/{petId}/visits") 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 3409b30ab..25d33f24b 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,6 +1,5 @@ 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; @@ -30,9 +29,6 @@ class VisitResourceTest { @MockBean VisitRepository visitRepository; - @MockBean - MeterRegistry registry; - @Test void shouldFetchVisits() throws Exception { given(visitRepository.findByPetIdIn(asList(111, 222)))