From e700139ca1aafd32ab01d2a00f21b8ef3bcd7fe2 Mon Sep 17 00:00:00 2001 From: David Pequegnot Date: Mon, 10 Jun 2024 11:38:55 +0200 Subject: [PATCH 01/10] fix(fraudetect): align test package name to the main one --- .../FraudetectServiceApplicationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename fraudetect-service/src/test/java/com/worldline/easypay/{fraudetect_service => fraudetect}/FraudetectServiceApplicationTests.java (79%) diff --git a/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect_service/FraudetectServiceApplicationTests.java b/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java similarity index 79% rename from fraudetect-service/src/test/java/com/worldline/easypay/fraudetect_service/FraudetectServiceApplicationTests.java rename to fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java index 96a5336..4016122 100644 --- a/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect_service/FraudetectServiceApplicationTests.java +++ b/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java @@ -1,4 +1,4 @@ -package com.worldline.easypay.fraudetect_service; +package com.worldline.easypay.fraudetect; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; From 83f8379ccb9d7c79c7ee313e64d62dd0739e36ee Mon Sep 17 00:00:00 2001 From: David Pequegnot Date: Sun, 16 Jun 2024 23:49:06 +0200 Subject: [PATCH 02/10] Add the whole observability stack and features --- .gitignore | 5 +- api-gateway/src/main/docker/Dockerfile | 13 +- .../src/main/resources/application.yml | 18 + .../src/main/resources/logback-spring.xml | 48 +- compose.yml | 34 +- config-server/build.gradle.kts | 1 - config-server/src/main/docker/Dockerfile | 9 + .../PrometheusRegistryConfiguration.java | 17 + .../src/main/resources/application.properties | 1 + .../resources/config-server/application.yml | 13 +- .../src/main/resources/logback-spring.xml | 48 +- discovery-server/src/main/docker/Dockerfile | 9 + .../PrometheusRegistryConfiguration.java | 17 + .../CompressedStackTraceConverter.java | 18 - .../easypay/logging/CustomJsonLayout.java | 41 - .../src/main/resources/logback-spring.xml | 48 +- docker/alloy/config.alloy | 36 +- docker/grafana/dashboards/jdbc.json | 1717 ++++++++ docker/grafana/dashboards/jvm.json | 3767 +++++++++++++++++ docker/prometheus/prometheus.yml | 27 +- easypay-service/build.gradle.kts | 2 +- .../easypay/EasypayServiceApplication.java | 6 +- .../PrometheusRegistryConfiguration.java | 17 + .../payment/boundary/PaymentResource.java | 17 +- .../payment/control/CardValidator.java | 12 +- .../payment/control/PaymentService.java | 10 +- .../control/bank/BankAuthorService.java | 8 +- .../main/resources/application-mdc.properties | 1 + .../src/main/resources/application.properties | 5 +- .../src/main/resources/logback-spring.xml | 48 +- easypay.sh | 11 + fraudetect-service/src/main/docker/Dockerfile | 9 + .../PrometheusRegistryConfiguration.java | 17 + .../CompressedStackTraceConverter.java | 18 - .../easypay/logging/CustomJsonLayout.java | 41 - .../src/main/resources/logback-spring.xml | 48 +- gradle/wrapper/gradle-wrapper.properties | 2 +- k6-script.js | 6 - k6/01-payment-only.js | 42 + k6/02-payment-smartbank.js | 53 + .../src/main/docker/Dockerfile | 9 + .../CompressedStackTraceConverter.java | 18 - .../easypay/logging/CustomJsonLayout.java | 41 - .../PrometheusRegistryConfiguration.java | 17 + .../src/main/resources/logback-spring.xml | 48 +- smartbank-gateway/build.gradle.kts | 19 +- .../boundary/BankAuthorResource.java | 19 +- .../control/AuthorizationService.java | 16 +- .../easypay/smartbank/cache/CacheRecord.java | 9 + .../smartbank/cache/CacheRepository.java | 30 + .../PrometheusRegistryConfiguration.java | 17 + .../src/main/resources/logback-spring.xml | 48 +- smartbank.sh | 9 + 53 files changed, 6072 insertions(+), 488 deletions(-) create mode 100644 config-server/src/main/java/com/worldline/easypay/configserver/config/PrometheusRegistryConfiguration.java create mode 100644 discovery-server/src/main/java/com/worldline/easypay/discoveryserver/config/PrometheusRegistryConfiguration.java delete mode 100644 discovery-server/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java delete mode 100644 discovery-server/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java create mode 100644 docker/grafana/dashboards/jdbc.json create mode 100644 docker/grafana/dashboards/jvm.json create mode 100644 easypay-service/src/main/java/com/worldline/easypay/config/PrometheusRegistryConfiguration.java create mode 100644 easypay-service/src/main/resources/application-mdc.properties create mode 100755 easypay.sh create mode 100644 fraudetect-service/src/main/java/com/worldline/easypay/fraudetect/config/PrometheusRegistryConfiguration.java delete mode 100644 fraudetect-service/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java delete mode 100644 fraudetect-service/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java delete mode 100644 k6-script.js create mode 100644 k6/01-payment-only.js create mode 100644 k6/02-payment-smartbank.js delete mode 100644 merchant-backoffice/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java delete mode 100644 merchant-backoffice/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java create mode 100644 merchant-backoffice/src/main/java/com/worldline/easypay/merchantbo/config/PrometheusRegistryConfiguration.java create mode 100644 smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRecord.java create mode 100644 smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRepository.java create mode 100644 smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/config/PrometheusRegistryConfiguration.java create mode 100755 smartbank.sh diff --git a/.gitignore b/.gitignore index 4ab3a07..3a1fdb5 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,7 @@ out/ logs/*.log logs/*.json logs/*.logfmt -logs/*.gz \ No newline at end of file +logs/*.gz + +### Custom user certificates ### +docker/ca-trust/* \ No newline at end of file diff --git a/api-gateway/src/main/docker/Dockerfile b/api-gateway/src/main/docker/Dockerfile index 7088b5c..d1dbc93 100644 --- a/api-gateway/src/main/docker/Dockerfile +++ b/api-gateway/src/main/docker/Dockerfile @@ -1,4 +1,11 @@ FROM eclipse-temurin:21-jdk-alpine as build + +ENV USE_SYSTEM_CA_CERTS=1 + +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + WORKDIR /workspace/app ENV SERVICE=api-gateway @@ -9,11 +16,15 @@ COPY gradle gradle COPY gradlew . COPY settings.gradle.kts . -RUN --mount=type=cache,target=/root/.gradle ./gradlew :${SERVICE}:clean :${SERVICE}:build -x test +RUN --mount=type=cache,target=/root/.gradle /__cacert_entrypoint.sh ./gradlew :${SERVICE}:clean :${SERVICE}:build -x test RUN mkdir -p ${SERVICE}/build/dependency && (cd ${SERVICE}/build/dependency && jar -xf ../libs/*-SNAPSHOT.jar) FROM eclipse-temurin:21-jre-alpine +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + ENV SERVICE=api-gateway ENV LOGS_DIRECTORY=/logs/ diff --git a/api-gateway/src/main/resources/application.yml b/api-gateway/src/main/resources/application.yml index 1ecc644..687716e 100644 --- a/api-gateway/src/main/resources/application.yml +++ b/api-gateway/src/main/resources/application.yml @@ -23,5 +23,23 @@ spring: uri: lb://FRAUDETECT-SERVICE predicates: - Path=/api/fraudetect/** + loadbalancer: + cache: + ttl: 1s # refresh: # enabled: false # AOT / Native Image does not support Spring Cloud Refresh Scope + +management: + endpoint: + gateway: + enabled: true + endpoints: + web: + exposure: + include: "*" + metrics: + distribution: + percentiles-histogram: + http: + server: + requests: true \ No newline at end of file diff --git a/api-gateway/src/main/resources/logback-spring.xml b/api-gateway/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/api-gateway/src/main/resources/logback-spring.xml +++ b/api-gateway/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/compose.yml b/compose.yml index 7d956f2..a22847d 100644 --- a/compose.yml +++ b/compose.yml @@ -200,9 +200,11 @@ services: - GF_PATHS_PROVISIONING=/etc/grafana/provisioning - GF_AUTH_ANONYMOUS_ENABLED=true - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + volumes: + - ./docker/grafana/datasources:/etc/grafana/provisioning/dashboards entrypoint: - sh - - -euc + - -xeuc - | mkdir -p /etc/grafana/provisioning/datasources cat < /etc/grafana/provisioning/datasources/ds.yaml @@ -214,9 +216,16 @@ services: orgId: 1 url: http://loki:3100 basicAuth: false - isDefault: false + isDefault: true version: 1 - editable: false + editable: true + jsonData: + derivedFields: + - datasourceUid: tempo + matcherRegex: 'trace_id' + matcherType: 'label' + name: TraceID + urlDisplayLabel: 'View Trace' - name: Prometheus type: prometheus uid: prometheus @@ -226,24 +235,37 @@ services: basicAuth: false isDefault: false version: 1 - editable: false + editable: true jsonData: httpMethod: GET + exemplarTraceIdDestinations: + - datasourceUid: tempo + name: TraceID - name: Tempo type: tempo access: proxy orgId: 1 url: http://tempo:3200 basicAuth: false - isDefault: true + isDefault: false version: 1 - editable: false + editable: true apiVersion: 1 uid: tempo jsonData: httpMethod: GET serviceMap: datasourceUid: prometheus + tracesToLogsV2: + datasourceUid: loki + filterBySpanID: false + filterByTraceID: true + tags: + - application + tracesToMetrics: + datasourceUid: prometheus + tags: + - application EOF /run.sh image: grafana/grafana:latest diff --git a/config-server/build.gradle.kts b/config-server/build.gradle.kts index 20e5dbf..c634485 100644 --- a/config-server/build.gradle.kts +++ b/config-server/build.gradle.kts @@ -19,7 +19,6 @@ extra["springCloudVersion"] = "2023.0.2" dependencies { implementation("org.springframework.cloud:spring-cloud-config-server") - implementation("org.springframework.boot:spring-boot-starter-actuator") // Expose metrics with Micrometer using a Prometheus registry implementation("org.springframework.boot:spring-boot-starter-actuator") diff --git a/config-server/src/main/docker/Dockerfile b/config-server/src/main/docker/Dockerfile index bd48aae..c13912b 100644 --- a/config-server/src/main/docker/Dockerfile +++ b/config-server/src/main/docker/Dockerfile @@ -1,4 +1,9 @@ FROM eclipse-temurin:21-jdk-alpine as build + +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + WORKDIR /workspace/app ENV SERVICE=config-server @@ -14,6 +19,10 @@ RUN mkdir -p ${SERVICE}/build/dependency && (cd ${SERVICE}/build/dependency && j FROM eclipse-temurin:21-jre-alpine +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + ENV SERVICE=config-server ENV LOGS_DIRECTORY=/logs/ diff --git a/config-server/src/main/java/com/worldline/easypay/configserver/config/PrometheusRegistryConfiguration.java b/config-server/src/main/java/com/worldline/easypay/configserver/config/PrometheusRegistryConfiguration.java new file mode 100644 index 0000000..31f0b74 --- /dev/null +++ b/config-server/src/main/java/com/worldline/easypay/configserver/config/PrometheusRegistryConfiguration.java @@ -0,0 +1,17 @@ +package com.worldline.easypay.configserver.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.prometheus.metrics.tracer.otel_agent.OpenTelemetryAgentSpanContext; + + +@Configuration +public class PrometheusRegistryConfiguration { + + @Bean + @ConditionalOnClass(name="io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span") + public OpenTelemetryAgentSpanContext exemplarConfigSupplier() { + return new OpenTelemetryAgentSpanContext(); + } +} diff --git a/config-server/src/main/resources/application.properties b/config-server/src/main/resources/application.properties index b1a5d5e..883f33b 100644 --- a/config-server/src/main/resources/application.properties +++ b/config-server/src/main/resources/application.properties @@ -9,5 +9,6 @@ spring.cloud.config.server.native.searchLocations=classpath:/config-server #spring.cloud.refresh.enabled=false management.server.port=8890 +management.endpoints.web.exposure.include=* logging.file.name=${LOGS_DIRECTORY:../logs}/${spring.application.instance:${spring.application.name:spring}}.log \ No newline at end of file diff --git a/config-server/src/main/resources/config-server/application.yml b/config-server/src/main/resources/config-server/application.yml index 57db98e..158c927 100644 --- a/config-server/src/main/resources/config-server/application.yml +++ b/config-server/src/main/resources/config-server/application.yml @@ -28,10 +28,21 @@ eureka: management: security: enabled: false - endpoint: + endpoints: web: exposure: include: "*" + metrics: + tags: + application: ${spring.application.name} + instance: ${spring.application.instance} + namespace: local + metrics: + distribution: + percentiles-histogram: + http: + server: + requests: true logging: level: diff --git a/config-server/src/main/resources/logback-spring.xml b/config-server/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/config-server/src/main/resources/logback-spring.xml +++ b/config-server/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/discovery-server/src/main/docker/Dockerfile b/discovery-server/src/main/docker/Dockerfile index e591489..953b093 100644 --- a/discovery-server/src/main/docker/Dockerfile +++ b/discovery-server/src/main/docker/Dockerfile @@ -1,4 +1,9 @@ FROM eclipse-temurin:21-jdk-alpine as build + +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + WORKDIR /workspace/app ENV SERVICE=discovery-server @@ -14,6 +19,10 @@ RUN mkdir -p ${SERVICE}/build/dependency && (cd ${SERVICE}/build/dependency && j FROM eclipse-temurin:21-jre-alpine +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + ENV SERVICE=discovery-server ENV LOGS_DIRECTORY=/logs/ diff --git a/discovery-server/src/main/java/com/worldline/easypay/discoveryserver/config/PrometheusRegistryConfiguration.java b/discovery-server/src/main/java/com/worldline/easypay/discoveryserver/config/PrometheusRegistryConfiguration.java new file mode 100644 index 0000000..c1e68c5 --- /dev/null +++ b/discovery-server/src/main/java/com/worldline/easypay/discoveryserver/config/PrometheusRegistryConfiguration.java @@ -0,0 +1,17 @@ +package com.worldline.easypay.discoveryserver.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.prometheus.metrics.tracer.otel_agent.OpenTelemetryAgentSpanContext; + + +@Configuration +public class PrometheusRegistryConfiguration { + + @Bean + @ConditionalOnClass(name="io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span") + public OpenTelemetryAgentSpanContext exemplarConfigSupplier() { + return new OpenTelemetryAgentSpanContext(); + } +} diff --git a/discovery-server/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java b/discovery-server/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java deleted file mode 100644 index d7ac5e2..0000000 --- a/discovery-server/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.worldline.easypay.logging; - -import ch.qos.logback.classic.pattern.ThrowableProxyConverter; -import ch.qos.logback.classic.spi.IThrowableProxy; - -/** - * From: https://stackoverflow.com/a/47771943 - */ -public class CompressedStackTraceConverter extends ThrowableProxyConverter { - @Override - protected String throwableProxyToString(IThrowableProxy tp) { - String original = super.throwableProxyToString(tp); - - // replace the new line characters with something, - // use your own replacement value here - return original.replaceAll("\n", " ~~ "); - } -} diff --git a/discovery-server/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java b/discovery-server/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java deleted file mode 100644 index f182c9f..0000000 --- a/discovery-server/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.worldline.easypay.logging; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.contrib.json.classic.JsonLayout; -import java.util.Map; - - -public class CustomJsonLayout extends JsonLayout { - - private static final String APPLICATION_NAME = "application"; - private static final String APPLICATION_INSTANCE = "instance"; - - private String applicationName; - private String applicationInstance; - - public CustomJsonLayout() { - super(); - } - - @Override - protected void addCustomDataToJsonMap(Map map, ILoggingEvent event) { - add(APPLICATION_NAME, applicationName != null, applicationName, map); - add(APPLICATION_INSTANCE, applicationInstance != null, applicationInstance, map); - } - - public String getApplicationName(String applicationName) { - return applicationName; - } - - public void setApplicationName(String applicationName) { - this.applicationName = applicationName; - } - - public String getApplicationInstance(String applicationInstance) { - return applicationInstance; - } - - public void setApplicationInstance(String applicationInstance) { - this.applicationInstance = applicationInstance; - } -} \ No newline at end of file diff --git a/discovery-server/src/main/resources/logback-spring.xml b/discovery-server/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/discovery-server/src/main/resources/logback-spring.xml +++ b/discovery-server/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/docker/alloy/config.alloy b/docker/alloy/config.alloy index a56c006..4827a70 100644 --- a/docker/alloy/config.alloy +++ b/docker/alloy/config.alloy @@ -1,5 +1,5 @@ logging { - level = "info" + level = "debug" format = "logfmt" } @@ -30,13 +30,14 @@ loki.source.file "jsonlogfiles" { loki.process "jsonlogs" { forward_to = [loki.write.endpoint.receiver] - //stage.luhn { } + // stage.luhn { } stage.json { expressions = { // timestamp = "timestamp", application = "context.properties.applicationName", instance = "context.properties.instance", + trace_id = "mdc.trace_id", } } @@ -44,6 +45,7 @@ loki.process "jsonlogs" { values = { application = "application", instance = "instance", + trace_id = "trace_id", } } @@ -78,7 +80,35 @@ otelcol.receiver.otlp "default" { output { metrics = [otelcol.processor.batch.default.input] logs = [otelcol.processor.batch.default.input] - traces = [otelcol.processor.batch.default.input] + traces = [otelcol.processor.tail_sampling.actuator.input] + } +} + +otelcol.processor.tail_sampling "actuator" { + policy { + name = "filter_http_url" + type = "string_attribute" + string_attribute { + key = "http.url" + values = ["/actuator/health", "/actuator/prometheus"] + enabled_regex_matching = true + invert_match = true + } + } + + policy { + name = "filter_url_path" + type = "string_attribute" + string_attribute { + key = "url.path" + values = ["/actuator/health", "/actuator/prometheus"] + enabled_regex_matching = true + invert_match = true + } + } + + output { + traces = [otelcol.processor.batch.default.input] } } diff --git a/docker/grafana/dashboards/jdbc.json b/docker/grafana/dashboards/jdbc.json new file mode 100644 index 0000000..045b0dd --- /dev/null +++ b/docker/grafana/dashboards/jdbc.json @@ -0,0 +1,1717 @@ +{ + "__inputs": [ + { + "name": "Prometheus", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Spring Boot (Micrometer) with JDBC and HikariCP.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 12, + "panels": [ + { + "datasource": "Prometheus", + "description": "History of idle/active.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Min" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Active" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 19, + "x": 0, + "y": 1 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "width": 80 + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(jdbc_connections_active{namespace=\"$namespace\", application=\"$application\", name=\"$jdbc_connection_name\"})", + "legendFormat": "Active", + "range": true, + "refId": "Active" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(jdbc_connections_idle{namespace=\"$namespace\", application=\"$application\", name=\"$jdbc_connection_name\"})", + "hide": false, + "legendFormat": "Idle", + "range": true, + "refId": "Idle" + } + ], + "title": "Idle / Active", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Current number of active connections that have been allocated from the data source.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 1 + }, + "id": 5, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "jdbc_connections_active{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", name=\"$jdbc_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Active", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Number of established but idle connections.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 5 + }, + "id": 4, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "jdbc_connections_idle{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", name=\"$jdbc_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Idle", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "History of min/max.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Min" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Active" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 19, + "x": 0, + "y": 9 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "width": 80 + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(jdbc_connections_max{namespace=\"$namespace\", application=\"$application\", name=\"$jdbc_connection_name\"})", + "hide": false, + "legendFormat": "Max", + "range": true, + "refId": "Max" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(jdbc_connections_min{namespace=\"$namespace\", application=\"$application\", name=\"$jdbc_connection_name\"})", + "hide": false, + "legendFormat": "Min", + "range": true, + "refId": "Min" + } + ], + "title": "Min / Max", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Maximum number of active connections that can be allocated at the same time.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 9 + }, + "id": 3, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "jdbc_connections_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", name=\"$jdbc_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Max", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Minimum number of idle connections in the pool.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 13 + }, + "id": 2, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "jdbc_connections_min{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", name=\"$jdbc_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Min", + "type": "stat" + } + ], + "repeat": "jdbc_connection_name", + "repeatDirection": "h", + "title": "JDBC: $jdbc_connection_name", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 7, + "panels": [ + { + "datasource": "Prometheus", + "description": "History of idle/active.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Min" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Active" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Pending" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 19, + "x": 0, + "y": 2 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "width": 80 + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(hikaricp_connections_active{namespace=\"$namespace\", application=\"$application\", pool=\"$hikaricp_connection_name\"})", + "legendFormat": "Active", + "range": true, + "refId": "Active" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(hikaricp_connections_pending{namespace=\"$namespace\", application=\"$application\", pool=\"$hikaricp_connection_name\"})", + "hide": false, + "legendFormat": "Pending", + "range": true, + "refId": "Pending" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(hikaricp_connections_idle{namespace=\"$namespace\", application=\"$application\", pool=\"$hikaricp_connection_name\"})", + "hide": false, + "legendFormat": "Idle", + "range": true, + "refId": "Idle" + } + ], + "title": "Idle / Active / Pending", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Active connections.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 2 + }, + "id": 16, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_active{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Active", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Idle connections.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 6 + }, + "id": 15, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_idle{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Idle", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "History of min/max.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Min" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Active" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 19, + "x": 0, + "y": 10 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "width": 80 + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(hikaricp_connections_max{namespace=\"$namespace\", application=\"$application\", pool=\"$hikaricp_connection_name\"})", + "hide": false, + "legendFormat": "Max", + "range": true, + "refId": "Max" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(hikaricp_connections_min{namespace=\"$namespace\", application=\"$application\", pool=\"$hikaricp_connection_name\"})", + "hide": false, + "legendFormat": "Min", + "range": true, + "refId": "Min" + } + ], + "title": "Min / Max", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Max connections.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 10 + }, + "id": 14, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Max", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Min connections.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 14 + }, + "id": 13, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_min{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Min", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "History of timeouts.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Min" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Active" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Pending" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Usage" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Creation" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 12, + "w": 19, + "x": 0, + "y": 18 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "width": 80 + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(irate(hikaricp_connections_usage_seconds_sum{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}[$__rate_interval]) / irate(hikaricp_connections_usage_seconds_count{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}[$__rate_interval]))", + "legendFormat": "Usage", + "range": true, + "refId": "Usage" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(irate(hikaricp_connections_acquire_seconds_sum{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}[$__rate_interval]) / irate(hikaricp_connections_acquire_seconds_count{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}[$__rate_interval]))", + "hide": false, + "legendFormat": "Acquire", + "range": true, + "refId": "Acquire" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(irate(hikaricp_connections_creation_seconds_sum{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}[$__rate_interval]) / irate(hikaricp_connections_creation_seconds_count{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}[$__rate_interval]))", + "hide": false, + "legendFormat": "Creation", + "range": true, + "refId": "Creation" + } + ], + "title": "Usage / Acquire / Creation", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Max time on usage.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 18 + }, + "id": 27, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_usage_seconds_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Max usage", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Max time on acquiring.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 22 + }, + "id": 26, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_acquire_seconds_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Max acquire", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Max time on creation.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 26 + }, + "id": 25, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0-68002", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "hikaricp_connections_creation_seconds_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\", pool=\"$hikaricp_connection_name\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Max creation", + "type": "stat" + } + ], + "repeat": "hikaricp_connection_name", + "repeatDirection": "h", + "title": "HikariCP: $hikaricp_connection_name", + "type": "row" + } + ], + "refresh": "", + "revision": 8, + "schemaVersion": 39, + "tags": [ + "prometheus", + "spring-boot", + "micrometer", + "jdbc", + "hikaricp" + ], + "templating": { + "list": [ + { + "current": {}, + "datasource": "Prometheus", + "definition": "label_values(jdbc_connections_max, namespace)", + "description": "Namespace in k8s.", + "hide": 0, + "includeAll": false, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [], + "query": { + "query": "label_values(jdbc_connections_max, namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": "Prometheus", + "definition": "label_values(jdbc_connections_max{namespace=\"$namespace\"}, application)", + "description": "Name of Spring Boot Application.", + "hide": 0, + "includeAll": false, + "label": "Application", + "multi": false, + "name": "application", + "options": [], + "query": { + "query": "label_values(jdbc_connections_max{namespace=\"$namespace\"}, application)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": "Prometheus", + "definition": "label_values(jdbc_connections_max{namespace=\"$namespace\", application=\"$application\"}, instance)", + "description": "Instance of application.", + "hide": 0, + "includeAll": true, + "label": "Instance", + "multi": false, + "name": "instance", + "options": [], + "query": { + "query": "label_values(jdbc_connections_max{namespace=\"$namespace\", application=\"$application\"}, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": "Prometheus", + "definition": "label_values(jdbc_connections_max{namespace=\"$namespace\",application=\"$application\", instance=~\"$instance\"}, name)", + "description": "Name of JDBC DataSource.", + "hide": 0, + "includeAll": true, + "label": "JDBC", + "multi": false, + "name": "jdbc_connection_name", + "options": [], + "query": { + "query": "label_values(jdbc_connections_max{namespace=\"$namespace\",application=\"$application\", instance=~\"$instance\"}, name)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": "Prometheus", + "definition": "label_values(hikaricp_connections_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\"}, pool)", + "description": "Name of HikariCP.", + "hide": 0, + "includeAll": true, + "label": "HikariCP", + "multi": false, + "name": "hikaricp_connection_name", + "options": [], + "query": { + "query": "label_values(hikaricp_connections_max{namespace=\"$namespace\", application=\"$application\", instance=~\"$instance\"}, pool)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "", + "title": "Spring Boot JDBC & HikariCP", + "uid": "spring-boot-jdbc-hikaricp", + "weekStart": "", + "gnetId": 20729 + } \ No newline at end of file diff --git a/docker/grafana/dashboards/jvm.json b/docker/grafana/dashboards/jvm.json new file mode 100644 index 0000000..79fd2f8 --- /dev/null +++ b/docker/grafana/dashboards/jvm.json @@ -0,0 +1,3767 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "enable": true, + "expr": "resets(process_uptime_seconds{application=\"$application\", instance=~\"$instance\"}[1m]) > 0", + "iconColor": "rgba(255, 96, 96, 1)", + "name": "Restart Detection", + "showIn": 0, + "step": "1m", + "tagKeys": "restart-tag", + "textFormat": "uptime reset", + "titleFormat": "Restart" + } + ] + }, + "description": "Dashboard for Micrometer instrumented applications (Java, Spring Boot, Micronaut)", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 4701, + "graphTooltip": 1, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 139, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "Quick Facts", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "decimals": 1, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 63, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "process_uptime_seconds{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "range": true, + "refId": "A", + "step": 14400 + } + ], + "title": "Uptime", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 92, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "process_start_time_seconds{application=\"$application\", instance=~\"$instance\"}*1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "range": true, + "refId": "A", + "step": 14400 + } + ], + "title": "Start time", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "decimals": 2, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 70 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 65, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", area=\"heap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=~\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "range": true, + "refId": "A", + "step": 14400 + } + ], + "title": "Heap used", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "decimals": 2, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + }, + { + "options": { + "from": -1e+32, + "result": { + "text": "N/A" + }, + "to": 0 + }, + "type": "range" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 70 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 75, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.0.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", area=\"nonheap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=~\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 14400 + } + ], + "title": "Non-Heap used", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 140, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "I/O Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 111, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=~\"$instance\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP", + "refId": "A" + } + ], + "title": "Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "HTTP" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#890f02", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP - 5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#bf1b00", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 112, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=~\"$instance\", status=~\"5..\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - 5xx", + "refId": "A" + } + ], + "title": "Errors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 113, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(http_server_requests_seconds_sum{application=\"$application\", instance=~\"$instance\", status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=~\"$instance\", status!~\"5..\"}[1m]))", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "HTTP - AVG", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "max(http_server_requests_seconds_max{application=\"$application\", instance=~\"$instance\", status!~\"5..\"})", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "HTTP - MAX", + "refId": "B" + } + ], + "title": "Duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 119, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "tomcat_threads_busy_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "TOMCAT - BSY", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "tomcat_threads_current_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "TOMCAT - CUR", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "tomcat_threads_config_max_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "TOMCAT - MAX", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jetty_threads_busy{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "JETTY - BSY", + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jetty_threads_current{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "JETTY - CUR", + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jetty_threads_config_max{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "JETTY - MAX", + "refId": "F" + } + ], + "title": "Utilisation", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 141, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "JVM Memory", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 13 + }, + "id": 24, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=~\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=~\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "title": "JVM Heap", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 13 + }, + "id": 25, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=~\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=~\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "title": "JVM Non-Heap", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 13 + }, + "id": 26, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=~\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=~\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "title": "JVM Total", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 13 + }, + "id": 86, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_memory_vss_bytes{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "vss", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_memory_rss_bytes{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "rss", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_memory_swap_bytes{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "swap", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_memory_rss_bytes{application=\"$application\", instance=~\"$instance\"} + process_memory_swap_bytes{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "D" + } + ], + "title": "JVM Process Memory", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 142, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "JVM Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 21 + }, + "id": 106, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "system_cpu_usage{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "system", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_cpu_usage{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "process", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "avg_over_time(process_cpu_usage{application=\"$application\", instance=~\"$instance\"}[15m])", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "process-15m", + "refId": "C" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 21 + }, + "id": 93, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "system_load_average_1m{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "system-1m", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "system_cpu_count{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cpus", + "refId": "B" + } + ], + "title": "Load", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 21 + }, + "id": 32, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_threads_live_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "live", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_threads_daemon_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "daemon", + "metric": "", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_threads_peak_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "peak", + "refId": "C", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "process", + "refId": "D", + "step": 2400 + } + ], + "title": "Threads", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "blocked" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#bf1b00", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "new" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#fce2de", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "runnable" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7eb26d", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "terminated" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#511749", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "timed-waiting" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#c15c17", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "waiting" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#eab839", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 21 + }, + "id": 124, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_threads_states_threads{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{state}}", + "refId": "A" + } + ], + "title": "Thread States", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "The percent of time spent on Garbage Collection over all CPUs assigned to the JVM process.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 28 + }, + "id": 138, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=~\"$instance\"}[1m])) by (application, instance) / on(application, instance) system_cpu_count", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "CPU time spent on GC", + "refId": "A" + } + ], + "title": "GC Pressure", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "opm" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "debug" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#1F78C1", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#BF1B00", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "info" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#508642", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "trace" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "warn" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 6, + "y": 28 + }, + "id": 91, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(logback_events_total{application=\"$application\", instance=~\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{level}}", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "title": "Log Events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 28 + }, + "id": 61, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_files_open_files{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "open", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_files_max_files{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "B", + "step": 2400 + } + ], + "title": "File Descriptors", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 143, + "panels": [], + "repeat": "persistence_counts", + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "JVM Memory Pools (Heap)", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 36 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_memory_pool_heap", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "title": "$jvm_memory_pool_heap", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 144, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "JVM Memory Pools (Non-Heap)", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 44 + }, + "id": 78, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_memory_pool_nonheap", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "title": "$jvm_memory_pool_nonheap", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 58 + }, + "id": 145, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "Garbage Collection", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 59 + }, + "id": 98, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=~\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "{{action}} ({{cause}})", + "refId": "A" + } + ], + "title": "Collections", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 59 + }, + "id": 101, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=~\"$instance\"}[1m])/rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=~\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 1, + "legendFormat": "avg {{action}} ({{cause}})", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_gc_pause_seconds_max{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 1, + "legendFormat": "max {{action}} ({{cause}})", + "refId": "B" + } + ], + "title": "Pause Durations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 59 + }, + "id": 99, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(jvm_gc_memory_allocated_bytes_total{application=\"$application\", instance=~\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "allocated", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(jvm_gc_memory_promoted_bytes_total{application=\"$application\", instance=~\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "promoted", + "refId": "B" + } + ], + "title": "Allocated/Promoted", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 66 + }, + "id": 146, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "Classloading", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 67 + }, + "id": 37, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_classes_loaded_classes{application=\"$application\", instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "loaded", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "title": "Classes loaded", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 67 + }, + "id": 38, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "delta(jvm_classes_loaded_classes{application=\"$application\",instance=~\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "delta-1m", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "title": "Class delta", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 74 + }, + "id": 147, + "panels": [], + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "refId": "A" + } + ], + "title": "Buffer Pools", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "count" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "buffers" + }, + "properties": [ + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 75 + }, + "id": 131, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_buffer_pool", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_buffer_pool\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_buffer_pool\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "capacity", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "jvm_buffer_count_buffers{application=\"$application\", instance=~\"$instance\", id=~\"$jvm_buffer_pool\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "buffers", + "refId": "C" + } + ], + "title": "$jvm_buffer_pool", + "type": "timeseries" + } + ], + "refresh": "30s", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "", + "hide": 0, + "includeAll": false, + "label": "Application", + "multi": false, + "name": "application", + "options": [], + "query": "label_values(application)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "", + "hide": 0, + "includeAll": true, + "label": "Instance", + "multi": false, + "multiFormat": "glob", + "name": "instance", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\"}, instance)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "", + "hide": 2, + "includeAll": true, + "label": "JVM Memory Pools Heap", + "multi": false, + "multiFormat": "glob", + "name": "jvm_memory_pool_heap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", area=\"heap\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "", + "hide": 2, + "includeAll": true, + "label": "JVM Memory Pools Non-Heap", + "multi": false, + "multiFormat": "glob", + "name": "jvm_memory_pool_nonheap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=~\"$instance\", area=\"nonheap\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 2, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "", + "hide": 2, + "includeAll": true, + "label": "JVM Buffer Pools", + "multi": false, + "multiFormat": "glob", + "name": "jvm_buffer_pool", + "options": [], + "query": "label_values(jvm_buffer_memory_used_bytes{application=\"$application\", instance=~\"$instance\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "JVM (Micrometer)", + "uid": "fdohhxix2m39ce", + "version": 5, + "weekStart": "" + } \ No newline at end of file diff --git a/docker/prometheus/prometheus.yml b/docker/prometheus/prometheus.yml index 26aa399..aac72aa 100644 --- a/docker/prometheus/prometheus.yml +++ b/docker/prometheus/prometheus.yml @@ -19,7 +19,7 @@ scrape_configs: metrics_path: /actuator/prometheus static_configs: - targets: - - config-server:8889 + - config-server:8890 - job_name: prometheus-eureka-server scrape_interval: 5s @@ -37,29 +37,6 @@ scrape_configs: - server: http://discovery-server:8761/eureka refresh_interval: 5s relabel_configs: - - source_labels: [__meta_eureka_app_name] - target_label: application - source_labels: [__meta_eureka_app_instance_metadata_metrics_path] target_label: __metrics_path__ - - source_labels: [__address__] - target_label: instance - # Discover targets from Eureka and probe the health endpoints via the Blackbox Exporter (Blackbox monitoring) - # - job_name: blackbox-via-service-discovery - # params: - # module: - # - http_2xx - # scrape_interval: 15s - # scrape_timeout: 15s - # metrics_path: /probe - # eureka_sd_configs: - # - server: http://prometheus-eureka-server:8761/eureka - # refresh_interval: 30s - # relabel_configs: - # - source_labels: [__meta_eureka_app_name] - # target_label: application - # - source_labels: [__meta_eureka_app_instance_healthcheck_url] - # target_label: __param_target - # - source_labels: [__address__] - # target_label: instance - # - target_label: __address__ - # replacement: blackbox-exporter:9115 + diff --git a/easypay-service/build.gradle.kts b/easypay-service/build.gradle.kts index 7ae4fa2..39340ec 100644 --- a/easypay-service/build.gradle.kts +++ b/easypay-service/build.gradle.kts @@ -2,6 +2,7 @@ plugins { java id("org.springframework.boot") version "3.2.5" id("io.spring.dependency-management") version "1.1.4" + id("org.graalvm.buildtools.native") version "0.10.2" } group = "com.worldline.easypay" @@ -26,7 +27,6 @@ dependencies { implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client") implementation("org.springframework.cloud:spring-cloud-starter-loadbalancer") implementation("org.springframework.cloud:spring-cloud-starter-openfeign") - // implementation("io.github.danielliu1123:httpexchange-spring-boot-starter:3.2.5") implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j") implementation("org.springframework.cloud:spring-cloud-stream") diff --git a/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java b/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java index 74761cb..1944289 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java +++ b/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java @@ -1,13 +1,12 @@ package com.worldline.easypay; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; -import com.worldline.easypay.payment.control.bank.BankAuthorClient; - - @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @@ -16,5 +15,4 @@ public class EasypayServiceApplication { public static void main(String[] args) { SpringApplication.run(EasypayServiceApplication.class, args); } - } diff --git a/easypay-service/src/main/java/com/worldline/easypay/config/PrometheusRegistryConfiguration.java b/easypay-service/src/main/java/com/worldline/easypay/config/PrometheusRegistryConfiguration.java new file mode 100644 index 0000000..461dae4 --- /dev/null +++ b/easypay-service/src/main/java/com/worldline/easypay/config/PrometheusRegistryConfiguration.java @@ -0,0 +1,17 @@ +package com.worldline.easypay.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.prometheus.metrics.tracer.otel_agent.OpenTelemetryAgentSpanContext; + + +@Configuration +public class PrometheusRegistryConfiguration { + + @Bean + @ConditionalOnClass(name="io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span") + public OpenTelemetryAgentSpanContext exemplarConfigSupplier() { + return new OpenTelemetryAgentSpanContext(); + } +} diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java b/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java index 713c58d..7197790 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java @@ -4,12 +4,18 @@ import java.util.List; import java.util.UUID; +import org.jboss.logging.MDC; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @@ -23,11 +29,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import org.springframework.http.HttpStatus; @RestController @RequestMapping("/payments") public class PaymentResource { + private static final Logger LOG = LoggerFactory.getLogger(PaymentResource.class); + PaymentService paymentService; public PaymentResource(PaymentService paymentService) { @@ -67,7 +76,8 @@ public ResponseEntity findById( @ApiResponse(responseCode = "201", description = "Payment processed", content = @Content(mediaType = "application/json")) public ResponseEntity processPayment( @Parameter(description = "The payment to be processed", required = true) @Valid @NotNull @RequestBody PaymentRequest paymentRequest) { - + MDC.put("context", paymentRequest); + LOG.info("Processing new payment: {}", paymentRequest); PaymentProcessingContext paymentContext = new PaymentProcessingContext(paymentRequest); paymentService.accept(paymentContext); @@ -76,7 +86,8 @@ public ResponseEntity processPayment( URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") .buildAndExpand(response.paymentId()).toUri(); - return ResponseEntity.created(location).body(response); + var httpResponse = ResponseEntity.created(location).body(response); + MDC.clear(); + return httpResponse; } - } diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/control/CardValidator.java b/easypay-service/src/main/java/com/worldline/easypay/payment/control/CardValidator.java index 3a25ce6..331d57b 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/control/CardValidator.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/control/CardValidator.java @@ -34,8 +34,9 @@ public boolean checkExpiryDate(String expiryDate) { YearMonth expiration = YearMonth.parse(expiryDate, DATE_TIME_FORMATTER); YearMonth today = YearMonth.now(); boolean rc = expiration.equals(today) || expiration.isAfter(today); - if (!rc) + if (!rc) { log.warn("checkExpiryDate NOK: outdated: {}", expiryDate); + } return rc; } catch (DateTimeParseException e) { log.warn("checkExpiryDate NOK: bad format: {}", expiryDate); @@ -77,8 +78,6 @@ public boolean isBlackListed(String cardNumber) { probe.blackListed = true; long count = cardRepository.count(Example.of(probe)); - // long count = em.createNamedQuery("CardRef.isBlackListed", - // Long.class).setParameter("cardNumber", cardNumber).getSingleResult(); if (count != 0) { log.warn("cardNumber {} is black listed", cardNumber); return true; @@ -89,13 +88,14 @@ public boolean isBlackListed(String cardNumber) { public boolean checkCardNumber(String cardNumber) { - // Check format - String card = cardNumber.replaceAll("[^0-9]+", ""); // remove all non-numerics + //* Check format + String card = cardNumber.replaceAll("[^0-9]+", ""); //* remove all non-numerics if ((card == null) || (card.length() < 13) || (card.length() > 19)) { + log.warn("Invalid card format"); return false; } - // Check Luhn key + //* Check Luhn key return checkLuhnKey(card); } diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/control/PaymentService.java b/easypay-service/src/main/java/com/worldline/easypay/payment/control/PaymentService.java index c3a2672..0115462 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/control/PaymentService.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/control/PaymentService.java @@ -20,7 +20,7 @@ @Service public class PaymentService { - // add 1 + //* Instantiate Logger private static final Logger log = LoggerFactory.getLogger(PaymentService.class); private CardValidator cardValidator; @@ -49,6 +49,7 @@ private void process(PaymentProcessingContext context) { } if (!cardValidator.checkCardNumber(context.cardNumber)) { + log.warn("Card validation KO: {}", context.cardNumber); context.responseCode = PaymentResponseCode.INVALID_CARD_NUMBER; return; } @@ -67,9 +68,9 @@ private void process(PaymentProcessingContext context) { if (context.amount > authorThreshold) { if (!bankAuthorService.authorize(context)) { - // Authorization refused: locally (AMOUNT_EXCEEDED) or remotely by the Bank - // (AUTHORIZATION_DENIED)? - // log.info("JJS => refused authorization, context=" + context); + //* Authorization refused: locally (AMOUNT_EXCEEDED) or remotely by the Bank + //* (AUTHORIZATION_DENIED)? + log.info("JJS => refused authorization, context=" + context); context.responseCode = context.processingMode.equals(ProcessingMode.STANDARD) ? PaymentResponseCode.AUTHORIZATION_DENIED : PaymentResponseCode.AMOUNT_EXCEEDED; @@ -103,7 +104,6 @@ private void store(PaymentProcessingContext context) { } @Transactional(Transactional.TxType.REQUIRED) - // @TrackedPayment public void accept(PaymentProcessingContext paymentContext) { process(paymentContext); store(paymentContext); diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java b/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java index f10939b..a39cf03 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java @@ -24,12 +24,10 @@ public class BankAuthorService { @Value("${payment.max.amount.fallback:20000}") Long maxAmountFallback; - - @Autowired private BankAuthorClient client; - public BankAuthorService(/*BankAuthorClient client*/) { - //this.client = client; + public BankAuthorService(BankAuthorClient client) { + this.client = client; } private BankAuthorRequest initRequest(PaymentProcessingContext context) { @@ -56,7 +54,7 @@ public boolean authorize(PaymentProcessingContext context) { } public boolean acceptByDelegation(PaymentProcessingContext context, Throwable throwable) { - log.debug("Accept by delegation. Error was: {}", throwable.getMessage()); + log.info("Accept by delegation. Error was: {}", throwable.getMessage()); context.bankCalled = false; context.processingMode = ProcessingMode.FALLBACK; return context.amount < maxAmountFallback; diff --git a/easypay-service/src/main/resources/application-mdc.properties b/easypay-service/src/main/resources/application-mdc.properties new file mode 100644 index 0000000..7aedc53 --- /dev/null +++ b/easypay-service/src/main/resources/application-mdc.properties @@ -0,0 +1 @@ +logging.pattern.console=%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %clr(CONTEXT=){faint}%clr(%X{context:-null}) %m%n%wEx \ No newline at end of file diff --git a/easypay-service/src/main/resources/application.properties b/easypay-service/src/main/resources/application.properties index eade731..4a577fe 100644 --- a/easypay-service/src/main/resources/application.properties +++ b/easypay-service/src/main/resources/application.properties @@ -1,6 +1,8 @@ spring.application.name=easypay-service spring.application.instance=${spring.application.name}:${random.uuid} +server.port=8081 + spring.config.import=optional:configserver:${CONFIG_SERVER_URL:http://localhost:8888} # AOT / Native Image does not support Spring Cloud Refresh Scope @@ -18,4 +20,5 @@ resilience4j.retry.instances.BankAuthorService.enable-exponential-backoff=true resilience4j.retry.instances.BankAuthorService.exponential-backoff-multiplier=2 resilience4j.retry.instances.BankAuthorService.retry-exceptions=feign.RetryableException -spring.cloud.inetutils.preferred-networks=192.168. \ No newline at end of file +spring.cloud.inetutils.preferred-networks=192.168. +spring.cloud.refresh.enabled=false \ No newline at end of file diff --git a/easypay-service/src/main/resources/logback-spring.xml b/easypay-service/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/easypay-service/src/main/resources/logback-spring.xml +++ b/easypay-service/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/easypay.sh b/easypay.sh new file mode 100755 index 0000000..5a96015 --- /dev/null +++ b/easypay.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +export OTEL_SERVICE_NAME=easypay-service +export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" +export OTEL_EXPORTER_OTLP_PROTOCOL=grpc +export OTEL_RESOURCE_ATTRIBUTES="source=agent" + +export SERVER_PORT=8081 +export LOGS_DIRECTORY="$(pwd)/logs" + +java -Xms512m -Xmx512m -javaagent:$(pwd)/instrumentation/grafana-opentelemetry-java.jar -jar "$(pwd)/easypay-service/build/libs/easypay-service-0.0.1-SNAPSHOT.jar" "$@" diff --git a/fraudetect-service/src/main/docker/Dockerfile b/fraudetect-service/src/main/docker/Dockerfile index 80517f0..a3b609f 100644 --- a/fraudetect-service/src/main/docker/Dockerfile +++ b/fraudetect-service/src/main/docker/Dockerfile @@ -1,4 +1,9 @@ FROM eclipse-temurin:21-jdk-alpine as build + +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + WORKDIR /workspace/app ENV SERVICE=fraudetect-service @@ -14,6 +19,10 @@ RUN mkdir -p ${SERVICE}/build/dependency && (cd ${SERVICE}/build/dependency && j FROM eclipse-temurin:21-jre-alpine +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + ENV SERVICE=fraudetect-service ENV LOGS_DIRECTORY=/logs/ diff --git a/fraudetect-service/src/main/java/com/worldline/easypay/fraudetect/config/PrometheusRegistryConfiguration.java b/fraudetect-service/src/main/java/com/worldline/easypay/fraudetect/config/PrometheusRegistryConfiguration.java new file mode 100644 index 0000000..39aef18 --- /dev/null +++ b/fraudetect-service/src/main/java/com/worldline/easypay/fraudetect/config/PrometheusRegistryConfiguration.java @@ -0,0 +1,17 @@ +package com.worldline.easypay.fraudetect.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.prometheus.metrics.tracer.otel_agent.OpenTelemetryAgentSpanContext; + + +@Configuration +public class PrometheusRegistryConfiguration { + + @Bean + @ConditionalOnClass(name="io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span") + public OpenTelemetryAgentSpanContext exemplarConfigSupplier() { + return new OpenTelemetryAgentSpanContext(); + } +} diff --git a/fraudetect-service/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java b/fraudetect-service/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java deleted file mode 100644 index d7ac5e2..0000000 --- a/fraudetect-service/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.worldline.easypay.logging; - -import ch.qos.logback.classic.pattern.ThrowableProxyConverter; -import ch.qos.logback.classic.spi.IThrowableProxy; - -/** - * From: https://stackoverflow.com/a/47771943 - */ -public class CompressedStackTraceConverter extends ThrowableProxyConverter { - @Override - protected String throwableProxyToString(IThrowableProxy tp) { - String original = super.throwableProxyToString(tp); - - // replace the new line characters with something, - // use your own replacement value here - return original.replaceAll("\n", " ~~ "); - } -} diff --git a/fraudetect-service/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java b/fraudetect-service/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java deleted file mode 100644 index f182c9f..0000000 --- a/fraudetect-service/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.worldline.easypay.logging; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.contrib.json.classic.JsonLayout; -import java.util.Map; - - -public class CustomJsonLayout extends JsonLayout { - - private static final String APPLICATION_NAME = "application"; - private static final String APPLICATION_INSTANCE = "instance"; - - private String applicationName; - private String applicationInstance; - - public CustomJsonLayout() { - super(); - } - - @Override - protected void addCustomDataToJsonMap(Map map, ILoggingEvent event) { - add(APPLICATION_NAME, applicationName != null, applicationName, map); - add(APPLICATION_INSTANCE, applicationInstance != null, applicationInstance, map); - } - - public String getApplicationName(String applicationName) { - return applicationName; - } - - public void setApplicationName(String applicationName) { - this.applicationName = applicationName; - } - - public String getApplicationInstance(String applicationInstance) { - return applicationInstance; - } - - public void setApplicationInstance(String applicationInstance) { - this.applicationInstance = applicationInstance; - } -} \ No newline at end of file diff --git a/fraudetect-service/src/main/resources/logback-spring.xml b/fraudetect-service/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/fraudetect-service/src/main/resources/logback-spring.xml +++ b/fraudetect-service/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..e2b3e26 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip -networkTimeout=10000 +networkTimeout=60000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/k6-script.js b/k6-script.js deleted file mode 100644 index 6906661..0000000 --- a/k6-script.js +++ /dev/null @@ -1,6 +0,0 @@ -import http from 'k6/http'; -import { sleep } from 'k6'; - -export default function() { - -} \ No newline at end of file diff --git a/k6/01-payment-only.js b/k6/01-payment-only.js new file mode 100644 index 0000000..166076e --- /dev/null +++ b/k6/01-payment-only.js @@ -0,0 +1,42 @@ +import http from 'k6/http'; +import { sleep } from 'k6'; +import { check } from 'k6'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; + +const cardNumbers = [ + '5204460010000007', + '5204460010000015', + '5204460010000023', + '5204460010000031', + '5204460010000049', + '5204460010000056', + '5204460010000057' +] +const posIds = ["POS-01", "POS-02"] +const url = 'http://localhost:8080/api/easypay/payments' +const params = { + headers: { + 'Content-Type': 'application/json', + 'Accept': '*/*', + }, + }; + +export default function() { + const randomCardNumber = randomItem(cardNumbers); + const randomPosId = randomItem(posIds); + + const payload = JSON.stringify({ + 'posId': randomPosId, + 'cardNumber': randomCardNumber, + 'expiryDate': '09/25', + 'amount': "40000" + }) + + const res = http.post(url, payload, params); + + check(res, { + + 'is status 201': (r) => r.status === 201, + + }); +} \ No newline at end of file diff --git a/k6/02-payment-smartbank.js b/k6/02-payment-smartbank.js new file mode 100644 index 0000000..c7fa0fd --- /dev/null +++ b/k6/02-payment-smartbank.js @@ -0,0 +1,53 @@ +import http from 'k6/http'; +import { sleep } from 'k6'; +import { check } from 'k6'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; + +const cardNumbers = [ + '5204460010000007', + '5204460010000015', + '5204460010000023', + '5204460010000031', + '5204460010000049', + '5204460010000056', + //'5204460010000057' +] +const posIds = ["POS-01"] +const url = 'http://localhost:8080/api/easypay/payments' +const params = { + headers: { + 'Content-Type': 'application/json', + 'Accept': '*/*', + }, + }; + +export default function() { + const randomCardNumber = randomItem(cardNumbers); + const randomPosId = randomItem(posIds); + + const payload = JSON.stringify({ + 'posId': randomPosId, + 'cardNumber': randomCardNumber, + 'expiryDate': '09/25', + 'amount': "40000" + }) + + const res = http.post(url, payload, params); + + check(res, { + + 'is status 201': (r) => r.status === 201, + + }); + + if (res.json('bankCalled') === false) { + console.log('Warning: Bank was not called!!!!! Unavailable?') + } else { + const authorId = res.json('authorId'); + const bankRes = http.get(`http://localhost:8080/api/smartbank/authors/${authorId}`); + + check(bankRes, { + 'is status 200': (r) => r.status === 200, + }) + } +} \ No newline at end of file diff --git a/merchant-backoffice/src/main/docker/Dockerfile b/merchant-backoffice/src/main/docker/Dockerfile index 4e94314..9fd942a 100644 --- a/merchant-backoffice/src/main/docker/Dockerfile +++ b/merchant-backoffice/src/main/docker/Dockerfile @@ -1,4 +1,9 @@ FROM eclipse-temurin:21-jdk-alpine as build + +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + WORKDIR /workspace/app ENV SERVICE=merchant-backoffice @@ -14,6 +19,10 @@ RUN mkdir -p ${SERVICE}/build/dependency && (cd ${SERVICE}/build/dependency && j FROM eclipse-temurin:21-jre-alpine +# INSTALL CA Certificates (useful for companies with SSL inspection) +ADD --chmod=644 docker/ca-trust/* /certificates/ +RUN export USE_SYSTEM_CA_CERTS=1 && /__cacert_entrypoint.sh + ENV SERVICE=merchant-backoffice ENV LOGS_DIRECTORY=/logs/ diff --git a/merchant-backoffice/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java b/merchant-backoffice/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java deleted file mode 100644 index d7ac5e2..0000000 --- a/merchant-backoffice/src/main/java/com/worldline/easypay/logging/CompressedStackTraceConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.worldline.easypay.logging; - -import ch.qos.logback.classic.pattern.ThrowableProxyConverter; -import ch.qos.logback.classic.spi.IThrowableProxy; - -/** - * From: https://stackoverflow.com/a/47771943 - */ -public class CompressedStackTraceConverter extends ThrowableProxyConverter { - @Override - protected String throwableProxyToString(IThrowableProxy tp) { - String original = super.throwableProxyToString(tp); - - // replace the new line characters with something, - // use your own replacement value here - return original.replaceAll("\n", " ~~ "); - } -} diff --git a/merchant-backoffice/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java b/merchant-backoffice/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java deleted file mode 100644 index f182c9f..0000000 --- a/merchant-backoffice/src/main/java/com/worldline/easypay/logging/CustomJsonLayout.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.worldline.easypay.logging; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.contrib.json.classic.JsonLayout; -import java.util.Map; - - -public class CustomJsonLayout extends JsonLayout { - - private static final String APPLICATION_NAME = "application"; - private static final String APPLICATION_INSTANCE = "instance"; - - private String applicationName; - private String applicationInstance; - - public CustomJsonLayout() { - super(); - } - - @Override - protected void addCustomDataToJsonMap(Map map, ILoggingEvent event) { - add(APPLICATION_NAME, applicationName != null, applicationName, map); - add(APPLICATION_INSTANCE, applicationInstance != null, applicationInstance, map); - } - - public String getApplicationName(String applicationName) { - return applicationName; - } - - public void setApplicationName(String applicationName) { - this.applicationName = applicationName; - } - - public String getApplicationInstance(String applicationInstance) { - return applicationInstance; - } - - public void setApplicationInstance(String applicationInstance) { - this.applicationInstance = applicationInstance; - } -} \ No newline at end of file diff --git a/merchant-backoffice/src/main/java/com/worldline/easypay/merchantbo/config/PrometheusRegistryConfiguration.java b/merchant-backoffice/src/main/java/com/worldline/easypay/merchantbo/config/PrometheusRegistryConfiguration.java new file mode 100644 index 0000000..d0e7895 --- /dev/null +++ b/merchant-backoffice/src/main/java/com/worldline/easypay/merchantbo/config/PrometheusRegistryConfiguration.java @@ -0,0 +1,17 @@ +package com.worldline.easypay.merchantbo.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.prometheus.metrics.tracer.otel_agent.OpenTelemetryAgentSpanContext; + + +@Configuration +public class PrometheusRegistryConfiguration { + + @Bean + @ConditionalOnClass(name="io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span") + public OpenTelemetryAgentSpanContext exemplarConfigSupplier() { + return new OpenTelemetryAgentSpanContext(); + } +} diff --git a/merchant-backoffice/src/main/resources/logback-spring.xml b/merchant-backoffice/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/merchant-backoffice/src/main/resources/logback-spring.xml +++ b/merchant-backoffice/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/smartbank-gateway/build.gradle.kts b/smartbank-gateway/build.gradle.kts index cbecde4..58ddf73 100644 --- a/smartbank-gateway/build.gradle.kts +++ b/smartbank-gateway/build.gradle.kts @@ -20,12 +20,29 @@ extra["springCloudVersion"] = "2023.0.1" extra["springDocVersion"] = "2.5.0" dependencies { - implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.cloud:spring-cloud-starter-config") implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client") + + // Expose metrics with Micrometer using a Prometheus registry + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + + // Add opentelemetry exemplars support + implementation(platform("io.opentelemetry:opentelemetry-bom:1.38.0")) + implementation("io.opentelemetry:opentelemetry-api") + implementation("io.prometheus:prometheus-metrics-tracer-otel-agent:1.3.1") + + // Logging + implementation("ch.qos.logback.contrib:logback-json-classic:0.1.5") + implementation("ch.qos.logback.contrib:logback-jackson:0.1.5") + + // Add caching support + implementation("org.springframework.boot:spring-boot-starter-cache") + implementation("com.hazelcast:hazelcast-all:4.2.8") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${property("springDocVersion")}") developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("com.h2database:h2") diff --git a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/boundary/BankAuthorResource.java b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/boundary/BankAuthorResource.java index 9c12065..aebfbe6 100644 --- a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/boundary/BankAuthorResource.java +++ b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/boundary/BankAuthorResource.java @@ -17,6 +17,7 @@ import com.worldline.easypay.smartbank.bankauthor.control.AuthorizationService; import com.worldline.easypay.smartbank.bankauthor.control.BankAuthorBoundaryControl; +import com.worldline.easypay.smartbank.cache.CacheRepository; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -24,17 +25,19 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; - @RestController @RequestMapping("/authors") public class BankAuthorResource { BankAuthorBoundaryControl bankAuthors; AuthorizationService authorizationService; + CacheRepository cacheRepository; - public BankAuthorResource(BankAuthorBoundaryControl boundaryControl, AuthorizationService validationService) { + public BankAuthorResource(BankAuthorBoundaryControl boundaryControl, AuthorizationService validationService, + CacheRepository cacheRepository) { this.bankAuthors = boundaryControl; this.authorizationService = validationService; + this.cacheRepository = cacheRepository; } @GetMapping("/count") @@ -43,7 +46,6 @@ public ResponseEntity count() { return ResponseEntity.ok().body(new BankAuthorCountResponse(this.bankAuthors.count())); } - @GetMapping @Operation(summary = "Get all payment authorizations", description = "Get all payment authorizations") @ApiResponse(responseCode = "200", description = "List of payment authorizations found") @@ -51,18 +53,23 @@ public ResponseEntity> findAll() { return ResponseEntity.ok().body(this.bankAuthors.findAll()); } - @GetMapping(value = "/{id}") @Operation(summary = "Get a payment authorization", description = "Get a payment authorization by its ID") @ApiResponse(responseCode = "200", description = "Payment authorization found") public ResponseEntity findById( @Parameter(description = "The ID of the payment authorization to retrieve") @PathVariable("id") String id) { try { + var author = (BankAuthorResponse) cacheRepository.get(id); + if (author != null) { + return ResponseEntity.ok().body(author); + } + var authorizationId = UUID.fromString(id); Optional response = this.bankAuthors.findById(authorizationId); if (response.isEmpty()) { return ResponseEntity.notFound().build(); } + cacheRepository.put(id, response.get()); return ResponseEntity.ok().body(response.get()); } catch (IllegalArgumentException e) { return ResponseEntity.badRequest().build(); @@ -74,9 +81,7 @@ public ResponseEntity findById( @ApiResponse(responseCode = "201", description = "Payment authorization request processed successfully") @Transactional ResponseEntity authorize(@Valid @NotNull @RequestBody BankAuthorRequest request) { - var authorized = this.authorizationService.authorize(request); - var response = new BankAuthorResponse(UUID.randomUUID(), request.merchantId(), request.cardNumber(), - request.expiryDate(), request.amount(), authorized); + var response = this.authorizationService.authorize(request); URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") .buildAndExpand(response.authorId()).toUri(); return ResponseEntity.created(location).body(response); diff --git a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/control/AuthorizationService.java b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/control/AuthorizationService.java index 2d3ba6b..ac612a3 100644 --- a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/control/AuthorizationService.java +++ b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/bankauthor/control/AuthorizationService.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import com.worldline.easypay.smartbank.bankauthor.boundary.BankAuthorRequest; +import com.worldline.easypay.smartbank.bankauthor.boundary.BankAuthorResponse; import com.worldline.easypay.smartbank.bankauthor.entity.BankAuthor; import com.worldline.easypay.smartbank.bankauthor.entity.BankAuthorRepository; @@ -24,7 +25,7 @@ public AuthorizationService(BankAuthorRepository repository, @Value("${smartbank } @Transactional(Transactional.TxType.MANDATORY) - public boolean authorize(BankAuthorRequest bankAuthorRequest) { + public BankAuthorResponse authorize(BankAuthorRequest bankAuthorRequest) { var authorized = bankAuthorRequest.amount() <= maxAmount; // Record the bank author @@ -38,6 +39,17 @@ public boolean authorize(BankAuthorRequest bankAuthorRequest) { this.repository.save(author); - return authorized; + return toResponse(author); + } + + public BankAuthorResponse toResponse(BankAuthor bankAuthor) { + return new BankAuthorResponse( + bankAuthor.id, + bankAuthor.merchantId, + bankAuthor.cardNumber, + bankAuthor.expiryDate, + bankAuthor.amount, + bankAuthor.authorized + ); } } diff --git a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRecord.java b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRecord.java new file mode 100644 index 0000000..2a9c885 --- /dev/null +++ b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRecord.java @@ -0,0 +1,9 @@ +package com.worldline.easypay.smartbank.cache; + +public record CacheRecord( + Object entry, + byte[] ioBuffer) { + public CacheRecord(Object entry) { + this(entry, new byte[1048576]); + } +} diff --git a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRepository.java b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRepository.java new file mode 100644 index 0000000..a47848f --- /dev/null +++ b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/cache/CacheRepository.java @@ -0,0 +1,30 @@ +package com.worldline.easypay.smartbank.cache; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class CacheRepository { + + private static final Logger LOGGER = LoggerFactory.getLogger(CacheRepository.class); + + private Map cache = new HashMap<>(); + + public void put(String key, Object value) { + cache.put(key, new CacheRecord(value)); + LOGGER.info("New heap size is now: {}/{}|{}", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().maxMemory(), Runtime.getRuntime().freeMemory()); + } + + public Object get(String key) { + var record = cache.get(key); + if (record == null) { + return null; + } + return record.entry(); + } + +} diff --git a/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/config/PrometheusRegistryConfiguration.java b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/config/PrometheusRegistryConfiguration.java new file mode 100644 index 0000000..d4e7f7b --- /dev/null +++ b/smartbank-gateway/src/main/java/com/worldline/easypay/smartbank/config/PrometheusRegistryConfiguration.java @@ -0,0 +1,17 @@ +package com.worldline.easypay.smartbank.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.prometheus.metrics.tracer.otel_agent.OpenTelemetryAgentSpanContext; + + +@Configuration +public class PrometheusRegistryConfiguration { + + @Bean + @ConditionalOnClass(name="io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span") + public OpenTelemetryAgentSpanContext exemplarConfigSupplier() { + return new OpenTelemetryAgentSpanContext(); + } +} diff --git a/smartbank-gateway/src/main/resources/logback-spring.xml b/smartbank-gateway/src/main/resources/logback-spring.xml index 53b23f9..b3fc0f6 100644 --- a/smartbank-gateway/src/main/resources/logback-spring.xml +++ b/smartbank-gateway/src/main/resources/logback-spring.xml @@ -8,39 +8,21 @@ - - - - - - ${FILE_LOG_THRESHOLD} - - - ${LOG_FILE}.json - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} - - - - - - - + + + ${FILE_LOG_THRESHOLD} + + + ${LOG_FILE}.json + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7} + + diff --git a/smartbank.sh b/smartbank.sh new file mode 100755 index 0000000..de547ad --- /dev/null +++ b/smartbank.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +export OTEL_SERVICE_NAME=smartbank-gateway +export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" +export OTEL_EXPORTER_OTLP_PROTOCOL=grpc +export OTEL_RESOURCE_ATTRIBUTES="source=agent" +export LOGS_DIRECTORY="$(pwd)/logs" + +java -Xms512m -Xmx512m -javaagent:$(pwd)/instrumentation/grafana-opentelemetry-java.jar -jar "$(pwd)/smartbank-gateway/build/libs/smartbank-gateway-0.0.1-SNAPSHOT.jar" "$@" From 2c35b2949d44773d6581f4a0c512894624b8a4ea Mon Sep 17 00:00:00 2001 From: Alexandre Touret Date: Thu, 20 Jun 2024 18:03:23 +0200 Subject: [PATCH 03/10] feat: Keep working on documentation --- docs/workshop.md | 69 ++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/docs/workshop.md b/docs/workshop.md index 30f6681..219ad27 100644 --- a/docs/workshop.md +++ b/docs/workshop.md @@ -152,15 +152,16 @@ Duration: 0:05:00 We will assume you will use GitPod for this workshop :) -[![Open in Gitpod](img/open-in-gitpod.svg)](https://gitpod.io/#github.com/alexandre-touret/observability-workshop.git) +[![Open in Gitpod](img/open-in-gitpod.svg)](https://gitpod.io/#github.com/worldline/observability-workshop.git) ### Start the infrastructure The "infrastructure stack" is composed of the following components: * One [PostgreSQL](https://www.postgresql.org/) instance per micro service * One [Kafka broker](https://kafka.apache.org/) -* ONe [Service Discovery](https://spring.io/guides/gs/service-registration-and-discovery) microservice to enable load balancing & loose coupling. +* One [Service Discovery](https://spring.io/guides/gs/service-registration-and-discovery) microservice to enable load balancing & loose coupling. * One [Configuration server](https://docs.spring.io/spring-cloud-config/) is also used to centralise the configuration of our microservices. +* The following microservices: API Gateway, Merchant BO, Fraud Detect, Smart Bank Gateway To run it, execute the following command @@ -172,9 +173,7 @@ To check if all the services are up, you can run this command: ``` bash $ docker compose ps -a ``` -> aside negative -> -> TODO ajouter retour commande +And check the status of every service. ### Start the rest of our microservices @@ -188,39 +187,12 @@ Run the following command: $ ./gradlew :easypay-service:bootRun -x test ``` -#### The Merchant BO -Run the following command: - -```bash -$ ./gradlew :merchant-backoffice:bootRun -x test -``` -#### The Fraud System -Run the following command: - -```bash -$ ./gradlew :frauddetect-service:bootRun -x test -``` - -#### The Smart Bank Gateway -Run the following command: - -```bash -$ ./gradlew :smartbank-gateway:bootRun -x test -``` - -#### The API Gateway - -Run the following command: - -```bash -$ ./gradlew :api-gateway:bootRun -x test -``` #### Validation Open the Eureka website started during the infrastructure setup -If you run this workshop on your desktop, you can go to this URL. -If you run it on GitPod, you can go the corresponding URL (e.g., TODO) instead. +If you run this workshop on your desktop, you can go to this URL: http://localhost:8761. +If you run it on GitPod, you can go to the corresponding URL (e.g., https://8761-worldline-observability-w98vrd59k5h.ws-eu114.gitpod.io) instead. You can now reach our platform to initiate a payment: @@ -327,7 +299,7 @@ You should get these log entries in JSON format. Open one of these files and che > aside positive > -> As you can see, the logs are not helpful for getting more information such as the business or user context.~~~~ +> As you can see, the logs are not helpful for getting more information such as the business or user context. > > If you want to dig into this particular topic, you can check out [this article](https://blog.worldline.tech/2020/01/22/back-to-basics-logging.html). @@ -421,9 +393,9 @@ You can go further and add as many log you think it would help in production. You can restart your easy pay service by typing ``CTRL+C`` in your console prompt, and run the following command: -> aside negative -> -> TODO mettre la commande + le résultat dans les logs +```bash +$ ./gradlew :easypay-service:bootRun -x test +``` Now you can run the same commands ran earlier and check again the logs. @@ -725,10 +697,25 @@ Normally you will see the used JVM Heap reaching the maximum allowed. ## Traces Duration: 0:20:00 -Stop the easypay service +Stop the easypay service. + +Open the ``easypay.sh`` script file. You will then how is configured the JVM startup with the ``-javaagent`` parameter. + +```shell +#!/usr/bin/env bash + +export OTEL_SERVICE_NAME=easypay-service +export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" +export OTEL_EXPORTER_OTLP_PROTOCOL=grpc +export OTEL_RESOURCE_ATTRIBUTES="source=agent" + +export SERVER_PORT=8081 +export LOGS_DIRECTORY="$(pwd)/logs" + +java -Xms512m -Xmx512m -javaagent:$(pwd)/instrumentation/grafana-opentelemetry-java.jar -jar "$(pwd)/easypay-service/build/libs/easypay-service-0.0.1-SNAPSHOT.jar" "$@" +``` -Open the ``easypay.sh`` script file. You will then see the ``-javaagent`` parameter. -During this workshop, we will use a OpenTelemetry agent for broadcasting traces through Alloy to Tempo. +During this workshop, we will use an OpenTelemetry agent for broadcasting traces through Alloy to Tempo. Check the environment variables used: From 72bdb32ee727eca9df1074177449b61a923e6341 Mon Sep 17 00:00:00 2001 From: David Pequegnot Date: Thu, 20 Jun 2024 19:25:06 +0200 Subject: [PATCH 04/10] test: fix failing test because of missing database --- easypay-service/build.gradle.kts | 4 ++- .../EasypayServiceApplicationTests.java | 30 ++++++++++++++++++- .../src/test/resources/application.properties | 2 ++ fraudetect-service/build.gradle.kts | 5 ++++ .../FraudetectServiceApplicationTests.java | 30 ++++++++++++++++++- .../src/test/resources/application.yml | 3 ++ merchant-backoffice/build.gradle.kts | 3 ++ .../MerchantBackofficeApplicationTests.java | 30 ++++++++++++++++++- .../src/test/resources/application.properties | 2 ++ smartbank-gateway/build.gradle.kts | 3 ++ .../SmartbankGatewayApplicationTests.java | 30 ++++++++++++++++++- .../src/test/resources/application.yml | 3 ++ 12 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 easypay-service/src/test/resources/application.properties create mode 100644 fraudetect-service/src/test/resources/application.yml create mode 100644 merchant-backoffice/src/test/resources/application.properties create mode 100644 smartbank-gateway/src/test/resources/application.yml diff --git a/easypay-service/build.gradle.kts b/easypay-service/build.gradle.kts index 39340ec..de79f0b 100644 --- a/easypay-service/build.gradle.kts +++ b/easypay-service/build.gradle.kts @@ -2,7 +2,6 @@ plugins { java id("org.springframework.boot") version "3.2.5" id("io.spring.dependency-management") version "1.1.4" - id("org.graalvm.buildtools.native") version "0.10.2" } group = "com.worldline.easypay" @@ -52,6 +51,9 @@ dependencies { runtimeOnly("com.h2database:h2") runtimeOnly("org.postgresql:postgresql") testImplementation("org.springframework.boot:spring-boot-starter-test") + + // Testcontainers PostgreSQL + testImplementation("org.testcontainers:postgresql") } dependencyManagement { diff --git a/easypay-service/src/test/java/com/worldline/easypay/easypayservice/EasypayServiceApplicationTests.java b/easypay-service/src/test/java/com/worldline/easypay/easypayservice/EasypayServiceApplicationTests.java index f1f4081..60c4d4e 100644 --- a/easypay-service/src/test/java/com/worldline/easypay/easypayservice/EasypayServiceApplicationTests.java +++ b/easypay-service/src/test/java/com/worldline/easypay/easypayservice/EasypayServiceApplicationTests.java @@ -1,11 +1,39 @@ package com.worldline.easypay.easypayservice; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class EasypayServiceApplicationTests { + @LocalServerPort + private Integer port; + + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine"); + + @BeforeAll + static void beforeAll() { + postgres.start(); + } + + @AfterAll + static void afterAll() { + postgres.stop(); + } + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + @Test void contextLoads() { } diff --git a/easypay-service/src/test/resources/application.properties b/easypay-service/src/test/resources/application.properties new file mode 100644 index 0000000..7f23d6f --- /dev/null +++ b/easypay-service/src/test/resources/application.properties @@ -0,0 +1,2 @@ +spring.cloud.config.enabled=false +eureka.client.enabled=false diff --git a/fraudetect-service/build.gradle.kts b/fraudetect-service/build.gradle.kts index df8b327..a18a0ae 100644 --- a/fraudetect-service/build.gradle.kts +++ b/fraudetect-service/build.gradle.kts @@ -42,10 +42,15 @@ dependencies { developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("org.postgresql:postgresql") + testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.cloud:spring-cloud-stream-test-binder") testImplementation("org.springframework.kafka:spring-kafka-test") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + // Testcontainers PostgreSQL + testImplementation("org.testcontainers:postgresql") } dependencyManagement { diff --git a/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java b/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java index 4016122..3c7c63b 100644 --- a/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java +++ b/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java @@ -1,11 +1,39 @@ package com.worldline.easypay.fraudetect; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class FraudetectServiceApplicationTests { + @LocalServerPort + private Integer port; + + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine"); + + @BeforeAll + static void beforeAll() { + postgres.start(); + } + + @AfterAll + static void afterAll() { + postgres.stop(); + } + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + @Test void contextLoads() { } diff --git a/fraudetect-service/src/test/resources/application.yml b/fraudetect-service/src/test/resources/application.yml new file mode 100644 index 0000000..84a78d0 --- /dev/null +++ b/fraudetect-service/src/test/resources/application.yml @@ -0,0 +1,3 @@ +eureka: + client: + enabled: false diff --git a/merchant-backoffice/build.gradle.kts b/merchant-backoffice/build.gradle.kts index 317a12c..566f0eb 100644 --- a/merchant-backoffice/build.gradle.kts +++ b/merchant-backoffice/build.gradle.kts @@ -50,6 +50,9 @@ dependencies { testImplementation("org.springframework.graphql:spring-graphql-test") testImplementation("org.springframework.kafka:spring-kafka-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + // Testcontainers PostgreSQL + testImplementation("org.testcontainers:postgresql") } dependencyManagement { diff --git a/merchant-backoffice/src/test/java/com/worldline/easypay/merchantbo/MerchantBackofficeApplicationTests.java b/merchant-backoffice/src/test/java/com/worldline/easypay/merchantbo/MerchantBackofficeApplicationTests.java index 3f33bcd..d9fd060 100644 --- a/merchant-backoffice/src/test/java/com/worldline/easypay/merchantbo/MerchantBackofficeApplicationTests.java +++ b/merchant-backoffice/src/test/java/com/worldline/easypay/merchantbo/MerchantBackofficeApplicationTests.java @@ -1,11 +1,39 @@ package com.worldline.easypay.merchantbo; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class MerchantBackofficeApplicationTests { + @LocalServerPort + private Integer port; + + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine"); + + @BeforeAll + static void beforeAll() { + postgres.start(); + } + + @AfterAll + static void afterAll() { + postgres.stop(); + } + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + @Test void contextLoads() { } diff --git a/merchant-backoffice/src/test/resources/application.properties b/merchant-backoffice/src/test/resources/application.properties new file mode 100644 index 0000000..1352832 --- /dev/null +++ b/merchant-backoffice/src/test/resources/application.properties @@ -0,0 +1,2 @@ +spring.cloud.config.enabled=false +eureka.client.enabled=false \ No newline at end of file diff --git a/smartbank-gateway/build.gradle.kts b/smartbank-gateway/build.gradle.kts index 58ddf73..9f9cbbe 100644 --- a/smartbank-gateway/build.gradle.kts +++ b/smartbank-gateway/build.gradle.kts @@ -48,6 +48,9 @@ dependencies { runtimeOnly("com.h2database:h2") runtimeOnly("org.postgresql:postgresql") testImplementation("org.springframework.boot:spring-boot-starter-test") + + // Testcontainers PostgreSQL + testImplementation("org.testcontainers:postgresql") } dependencyManagement { diff --git a/smartbank-gateway/src/test/java/com/worldline/easypay/smartbank/SmartbankGatewayApplicationTests.java b/smartbank-gateway/src/test/java/com/worldline/easypay/smartbank/SmartbankGatewayApplicationTests.java index e2f4637..3c1063a 100644 --- a/smartbank-gateway/src/test/java/com/worldline/easypay/smartbank/SmartbankGatewayApplicationTests.java +++ b/smartbank-gateway/src/test/java/com/worldline/easypay/smartbank/SmartbankGatewayApplicationTests.java @@ -1,11 +1,39 @@ package com.worldline.easypay.smartbank; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class SmartbankGatewayApplicationTests { + @LocalServerPort + private Integer port; + + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine"); + + @BeforeAll + static void beforeAll() { + postgres.start(); + } + + @AfterAll + static void afterAll() { + postgres.stop(); + } + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + @Test void contextLoads() { } diff --git a/smartbank-gateway/src/test/resources/application.yml b/smartbank-gateway/src/test/resources/application.yml new file mode 100644 index 0000000..5fde57f --- /dev/null +++ b/smartbank-gateway/src/test/resources/application.yml @@ -0,0 +1,3 @@ +eureka: + client: + enabled: false \ No newline at end of file From ef974504370d026e34dcdb7fbf7de52ca3d6ef6b Mon Sep 17 00:00:00 2001 From: David Pequegnot Date: Fri, 21 Jun 2024 17:45:29 +0200 Subject: [PATCH 05/10] test: add logback configuration for test and populate instrumentation directory --- .gitpod.yml | 9 +++++++ api-gateway/build.gradle.kts | 4 +-- .../src/test/resources/application.yml | 7 +++++ .../src/test/resources/logback-test.xml | 8 ++++++ config-server/build.gradle.kts | 2 +- .../src/test/resources/logback-test.xml | 8 ++++++ discovery-server/build.gradle.kts | 2 +- .../src/test/resources/application.properties | 1 + .../src/test/resources/logback-test.xml | 8 ++++++ easypay-service/build.gradle.kts | 6 ++--- .../easypay/EasypayServiceApplication.java | 2 -- .../payment/boundary/PaymentResource.java | 4 --- .../control/bank/BankAuthorConfig.java | 27 ------------------- .../control/bank/BankAuthorService.java | 1 - .../src/test/resources/logback-test.xml | 8 ++++++ fraudetect-service/build.gradle.kts | 5 ++-- .../FraudetectServiceApplicationTests.java | 9 +++++++ .../src/test/resources/application.properties | 2 ++ .../src/test/resources/application.yml | 3 --- .../src/test/resources/logback-test.xml | 8 ++++++ merchant-backoffice/build.gradle.kts | 2 +- .../src/test/resources/logback-test.xml | 8 ++++++ smartbank-gateway/build.gradle.kts | 7 +++-- .../src/test/resources/logback-test.xml | 8 ++++++ 24 files changed, 97 insertions(+), 52 deletions(-) create mode 100644 api-gateway/src/test/resources/application.yml create mode 100644 api-gateway/src/test/resources/logback-test.xml create mode 100644 config-server/src/test/resources/logback-test.xml create mode 100644 discovery-server/src/test/resources/application.properties create mode 100644 discovery-server/src/test/resources/logback-test.xml delete mode 100644 easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorConfig.java create mode 100644 easypay-service/src/test/resources/logback-test.xml create mode 100644 fraudetect-service/src/test/resources/application.properties delete mode 100644 fraudetect-service/src/test/resources/application.yml create mode 100644 fraudetect-service/src/test/resources/logback-test.xml create mode 100644 merchant-backoffice/src/test/resources/logback-test.xml create mode 100644 smartbank-gateway/src/test/resources/logback-test.xml diff --git a/.gitpod.yml b/.gitpod.yml index 583318e..55c3a77 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,6 +1,15 @@ image: file: .gitpod.Dockerfile + vscode: extensions: - "redhat.java" - "vscjava.vscode-gradle@3.13.5" + +tasks: + # Download the Grafana OpenTelemetry Java agent and saves it in the instrumentation directory + - name: Download Grafana OpenTelemetry Java agent + before: mkdir -p instrumentation + command: curl -L https://github.com/grafana/grafana-opentelemetry-java/releases/download/v2.4.0-beta.1/grafana-opentelemetry-java.jar -o instrumentation/grafana-opentelemetry-java.jar + - name: Build and start all the infrastructure + command: docker compose up -d --build \ No newline at end of file diff --git a/api-gateway/build.gradle.kts b/api-gateway/build.gradle.kts index 89a6b9f..b845968 100644 --- a/api-gateway/build.gradle.kts +++ b/api-gateway/build.gradle.kts @@ -1,8 +1,6 @@ -import org.springframework.boot.gradle.tasks.run.BootRun - plugins { java - id("org.springframework.boot") version "3.3.0" + id("org.springframework.boot") version "3.3.1" id("io.spring.dependency-management") version "1.1.5" } diff --git a/api-gateway/src/test/resources/application.yml b/api-gateway/src/test/resources/application.yml new file mode 100644 index 0000000..9c01199 --- /dev/null +++ b/api-gateway/src/test/resources/application.yml @@ -0,0 +1,7 @@ +spring: + cloud: + config: + enabled: false +eureka: + client: + enabled: false diff --git a/api-gateway/src/test/resources/logback-test.xml b/api-gateway/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/api-gateway/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/config-server/build.gradle.kts b/config-server/build.gradle.kts index c634485..18b5d6d 100644 --- a/config-server/build.gradle.kts +++ b/config-server/build.gradle.kts @@ -1,6 +1,6 @@ plugins { java - id("org.springframework.boot") version "3.3.0" + id("org.springframework.boot") version "3.3.1" id("io.spring.dependency-management") version "1.1.5" } diff --git a/config-server/src/test/resources/logback-test.xml b/config-server/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/config-server/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/discovery-server/build.gradle.kts b/discovery-server/build.gradle.kts index 12890f9..21476ac 100644 --- a/discovery-server/build.gradle.kts +++ b/discovery-server/build.gradle.kts @@ -1,6 +1,6 @@ plugins { java - id("org.springframework.boot") version "3.3.0" + id("org.springframework.boot") version "3.3.1" id("io.spring.dependency-management") version "1.1.5" } diff --git a/discovery-server/src/test/resources/application.properties b/discovery-server/src/test/resources/application.properties new file mode 100644 index 0000000..18d6df7 --- /dev/null +++ b/discovery-server/src/test/resources/application.properties @@ -0,0 +1 @@ +eureka.client.enabled=false \ No newline at end of file diff --git a/discovery-server/src/test/resources/logback-test.xml b/discovery-server/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/discovery-server/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/easypay-service/build.gradle.kts b/easypay-service/build.gradle.kts index de79f0b..2d3b228 100644 --- a/easypay-service/build.gradle.kts +++ b/easypay-service/build.gradle.kts @@ -1,7 +1,7 @@ plugins { java - id("org.springframework.boot") version "3.2.5" - id("io.spring.dependency-management") version "1.1.4" + id("org.springframework.boot") version "3.3.1" + id("io.spring.dependency-management") version "1.1.5" } group = "com.worldline.easypay" @@ -15,7 +15,7 @@ repositories { mavenCentral() } -extra["springCloudVersion"] = "2023.0.1" +extra["springCloudVersion"] = "2023.0.2" extra["springDocVersion"] = "2.5.0" dependencies { diff --git a/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java b/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java index 1944289..4a6186b 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java +++ b/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java @@ -1,7 +1,5 @@ package com.worldline.easypay; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java b/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java index 7197790..52d3ed4 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java @@ -8,14 +8,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @@ -29,7 +26,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import org.springframework.http.HttpStatus; @RestController @RequestMapping("/payments") diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorConfig.java b/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorConfig.java deleted file mode 100644 index acef6cd..0000000 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.worldline.easypay.payment.control.bank; - -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestClient; -import org.springframework.web.client.support.RestClientAdapter; -import org.springframework.web.service.invoker.HttpServiceProxyFactory; - -// @Configuration -public class BankAuthorConfig { - - //@Bean - //@LoadBalanced - public RestClient.Builder restClient() { - return RestClient.builder(); - } - - - // @Bean - BankAuthorClient produceBankAuthorClient(RestClient.Builder restClient) { - RestClientAdapter adapter = RestClientAdapter.create(restClient.build()); - HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); - return factory.createClient(BankAuthorClient.class); - } - -} diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java b/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java index a39cf03..5c80e94 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/control/bank/BankAuthorService.java @@ -4,7 +4,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; diff --git a/easypay-service/src/test/resources/logback-test.xml b/easypay-service/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/easypay-service/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/fraudetect-service/build.gradle.kts b/fraudetect-service/build.gradle.kts index a18a0ae..194ad69 100644 --- a/fraudetect-service/build.gradle.kts +++ b/fraudetect-service/build.gradle.kts @@ -1,6 +1,6 @@ plugins { java - id("org.springframework.boot") version "3.3.0" + id("org.springframework.boot") version "3.3.1" id("io.spring.dependency-management") version "1.1.5" } @@ -15,7 +15,7 @@ repositories { mavenCentral() } -extra["springCloudVersion"] = "2023.0.1" +extra["springCloudVersion"] = "2023.0.2" dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") @@ -51,6 +51,7 @@ dependencies { // Testcontainers PostgreSQL testImplementation("org.testcontainers:postgresql") + testImplementation("org.testcontainers:kafka") } dependencyManagement { diff --git a/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java b/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java index 3c7c63b..7889ce2 100644 --- a/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java +++ b/fraudetect-service/src/test/java/com/worldline/easypay/fraudetect/FraudetectServiceApplicationTests.java @@ -7,7 +7,9 @@ import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.KafkaContainer; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class FraudetectServiceApplicationTests { @@ -17,14 +19,18 @@ class FraudetectServiceApplicationTests { static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine"); + static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.6.1")); + @BeforeAll static void beforeAll() { postgres.start(); + kafka.start(); } @AfterAll static void afterAll() { postgres.stop(); + kafka.stop(); } @DynamicPropertySource @@ -32,6 +38,9 @@ static void configureProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", postgres::getJdbcUrl); registry.add("spring.datasource.username", postgres::getUsername); registry.add("spring.datasource.password", postgres::getPassword); + + registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers); + registry.add("spring.cloud.stream.kafka.binder.defaultBrokerPort", () -> kafka.getMappedPort(KafkaContainer.KAFKA_PORT)); } @Test diff --git a/fraudetect-service/src/test/resources/application.properties b/fraudetect-service/src/test/resources/application.properties new file mode 100644 index 0000000..9dcf0d9 --- /dev/null +++ b/fraudetect-service/src/test/resources/application.properties @@ -0,0 +1,2 @@ +eureka.client.enabled=false +spring.cloud.config.enabled=false diff --git a/fraudetect-service/src/test/resources/application.yml b/fraudetect-service/src/test/resources/application.yml deleted file mode 100644 index 84a78d0..0000000 --- a/fraudetect-service/src/test/resources/application.yml +++ /dev/null @@ -1,3 +0,0 @@ -eureka: - client: - enabled: false diff --git a/fraudetect-service/src/test/resources/logback-test.xml b/fraudetect-service/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/fraudetect-service/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/merchant-backoffice/build.gradle.kts b/merchant-backoffice/build.gradle.kts index 566f0eb..71d326c 100644 --- a/merchant-backoffice/build.gradle.kts +++ b/merchant-backoffice/build.gradle.kts @@ -1,6 +1,6 @@ plugins { java - id("org.springframework.boot") version "3.3.0" + id("org.springframework.boot") version "3.3.1" id("io.spring.dependency-management") version "1.1.5" } diff --git a/merchant-backoffice/src/test/resources/logback-test.xml b/merchant-backoffice/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/merchant-backoffice/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/smartbank-gateway/build.gradle.kts b/smartbank-gateway/build.gradle.kts index 9f9cbbe..bab2a64 100644 --- a/smartbank-gateway/build.gradle.kts +++ b/smartbank-gateway/build.gradle.kts @@ -1,8 +1,7 @@ plugins { java - id("org.springframework.boot") version "3.2.5" - id("io.spring.dependency-management") version "1.1.4" -// id("org.graalvm.buildtools.native") version "0.9.28" + id("org.springframework.boot") version "3.3.1" + id("io.spring.dependency-management") version "1.1.5" } group = "com.worldline.easypay" @@ -16,7 +15,7 @@ repositories { mavenCentral() } -extra["springCloudVersion"] = "2023.0.1" +extra["springCloudVersion"] = "2023.0.2" extra["springDocVersion"] = "2.5.0" dependencies { diff --git a/smartbank-gateway/src/test/resources/logback-test.xml b/smartbank-gateway/src/test/resources/logback-test.xml new file mode 100644 index 0000000..9b4499d --- /dev/null +++ b/smartbank-gateway/src/test/resources/logback-test.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 0d23283fd85e9f77b2b03f0fb993414d4ce6c06a Mon Sep 17 00:00:00 2001 From: David Pequegnot Date: Fri, 21 Jun 2024 18:07:06 +0200 Subject: [PATCH 06/10] test(discovery-server): fix invalid config environment --- .../discoveryserver/DiscoveryServerApplicationTests.java | 2 +- discovery-server/src/test/resources/application.properties | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/discovery-server/src/test/java/com/worldline/easypay/discoveryserver/DiscoveryServerApplicationTests.java b/discovery-server/src/test/java/com/worldline/easypay/discoveryserver/DiscoveryServerApplicationTests.java index caa35be..6caf9b6 100644 --- a/discovery-server/src/test/java/com/worldline/easypay/discoveryserver/DiscoveryServerApplicationTests.java +++ b/discovery-server/src/test/java/com/worldline/easypay/discoveryserver/DiscoveryServerApplicationTests.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class DiscoveryServerApplicationTests { @Test diff --git a/discovery-server/src/test/resources/application.properties b/discovery-server/src/test/resources/application.properties index 18d6df7..ff97549 100644 --- a/discovery-server/src/test/resources/application.properties +++ b/discovery-server/src/test/resources/application.properties @@ -1 +1,6 @@ -eureka.client.enabled=false \ No newline at end of file +#eureka.client.enabled=false +spring.cloud.config.enabled=false + +eureka.instance.hostname=localhost +eureka.client.registerWithEureka=false +eureka.client.fetchRegistry=false \ No newline at end of file From b24a487631a5b104d91b69085aea538405d378a2 Mon Sep 17 00:00:00 2001 From: Alexandre Touret Date: Sun, 23 Jun 2024 15:33:57 +0200 Subject: [PATCH 07/10] feat: Improve Gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7c00b2c..77e25d3 100644 --- a/.gitignore +++ b/.gitignore @@ -76,5 +76,5 @@ logs/*.gz docker/ca-trust/* -LOG_FILE_IS_UNDEFINED +LOG_FILE_IS_UNDEFINED* From 1bfda8292925f98abc6d91d4a6ac41d7f652df11 Mon Sep 17 00:00:00 2001 From: Alexandre Touret Date: Sun, 23 Jun 2024 16:07:12 +0200 Subject: [PATCH 08/10] fix: Typos --- docs/workshop.md | 54 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/docs/workshop.md b/docs/workshop.md index 219ad27..fe7dd1b 100644 --- a/docs/workshop.md +++ b/docs/workshop.md @@ -114,9 +114,9 @@ If you use the wrapper, you won't have troubles. Otherwise...: ```jshelllanguage $ gradle -version - ------------------------------------------------------------ +------------------------------------------------------------ Gradle 8.7 - ------------------------------------------------------------ +------------------------------------------------------------ Build time: 2024-03-22 15:52:46 UTC Revision: 650af14d7653aa949fce5e886e685efc9cf97c10 @@ -355,7 +355,7 @@ Modify the exception trace to provide contextual information such as the authorI Go to the ``easypay-service/src/main/java/com/worldline/easypay/payment/control/CardValidator.java`` class and modify the following block code in the process method in the same way: ```java - private void process(PaymentProcessingContext context) { +private void process(PaymentProcessingContext context) { [...] if (!cardValidator.checkCardNumber(context.cardNumber)) { @@ -443,12 +443,33 @@ java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" To find the root cause, add first a _smart_ log entry in the ``easypay-service/src/main/java/com/worldline/easypay/payment/control/PosValidator.java`` class. In the ``isActive()`` method, catch the exception and trace the error: -> aside negative -> -> TODO Commande pour le message d'erreur + message d'erreur -> -You can also prevent this issue by simply fixing the SQL import file +```java +public boolean isActive(String posId) { + PosRef probe = new PosRef(); + probe.posId = posId; + try { + List posList = posRefRepository.findAll(Example.of(probe)); + + if (posList.isEmpty()) { + log.warn("checkPosStatus NOK, unknown posId {}", posId); + return false; + } + + boolean result = posList.get(0).active; + + if (!result) { + log.warn("checkPosStatus NOK, inactive posId {}", posId); + } + return result; + } catch (NullPointerException e) { + log.warn("Invalid value for this POS: {}", posId); + throw e; + } +} +``` + +You can also prevent this issue by simply fixing the SQL import file. In the file ``easypay-service/src/main/resources/db/postgresql/data.sql``, Modify the implied line for ``POS-02`` from: @@ -489,9 +510,13 @@ Restart the application activating the ``mdc`` profile and see how the logs look ./gradlew :easypay-service:bootRun -x test --args='--spring.profiles.active=default,mdc' ``` -> aside negative +> aside positive > -> TODO Mettre comment vérifier que le bon profil a été démarré +> You can verify the MDC profile is applied by checking the presence of this log message: +> ```shell +The following 2 profiles are active: "default", "mdc" +``` +> ### Adding more content in our logs @@ -525,10 +550,6 @@ We use then [Promtail to broadcast them to Loki](https://grafana.com/grafana/das Check out the Logging configuration in the ``docker/alloy/config.alloy`` file: -> aside negative -> -> TODO mettre à jour - ```json //////////////////// // LOGS @@ -623,7 +644,7 @@ Check out the logs again, view it now as a table. You can also view traces for the other services (e.g., ``api-gateway``) -Finally, you can search logs absed on the correlation ID +Finally, you can search logs based on the correlation ID > aside negative > @@ -644,7 +665,7 @@ Explore the output Now get the prometheus metrics using this command: ```bash -http :8080/actuator/metrics +http :8080/actuator/prometheus ``` You can also have an overview of all the prometheus endpoints metrics on the Prometheus dashboad . @@ -672,7 +693,6 @@ Explore the dashboard, especially the Garbage collector and CPU statistics. Look around the JDBC dashboard then and see what happens on the database connection pool. - > aside negative > > TODO Détailler From 8c163d6e803891505d6590375057fde7d098a2da Mon Sep 17 00:00:00 2001 From: Alexandre Touret Date: Sun, 23 Jun 2024 16:08:04 +0200 Subject: [PATCH 09/10] fix: Typos --- .gitpod.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 31d912d..649532f 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -2,7 +2,7 @@ FROM gitpod/workspace-full ## Install dependencies RUN sudo apt update && \ - sudo apt install -y curl httpie jq + sudo apt install -y curl httpie jq dos2unix RUN sudo gpg -k && \ sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 && \ From 778fdcc46e861ed975ccbabd3c50ab39d4383986 Mon Sep 17 00:00:00 2001 From: David Pequegnot Date: Sun, 16 Jun 2024 23:49:06 +0200 Subject: [PATCH 10/10] Add the whole observability stack and features --- easypay-service/build.gradle.kts | 2 +- .../java/com/worldline/easypay/EasypayServiceApplication.java | 2 ++ .../worldline/easypay/payment/boundary/PaymentResource.java | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/easypay-service/build.gradle.kts b/easypay-service/build.gradle.kts index 2d3b228..f72eaec 100644 --- a/easypay-service/build.gradle.kts +++ b/easypay-service/build.gradle.kts @@ -64,4 +64,4 @@ dependencyManagement { tasks.withType { useJUnitPlatform() -} \ No newline at end of file +} diff --git a/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java b/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java index 4a6186b..1944289 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java +++ b/easypay-service/src/main/java/com/worldline/easypay/EasypayServiceApplication.java @@ -1,5 +1,7 @@ package com.worldline.easypay; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; diff --git a/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java b/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java index 52d3ed4..7197790 100644 --- a/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java +++ b/easypay-service/src/main/java/com/worldline/easypay/payment/boundary/PaymentResource.java @@ -8,11 +8,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @@ -26,6 +29,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import org.springframework.http.HttpStatus; @RestController @RequestMapping("/payments")