diff --git a/2024-11/spring-35-36-spring-cloud/.gitignore b/2024-11/spring-35-36-spring-cloud/.gitignore new file mode 100755 index 00000000..dae2cf99 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/.gitignore @@ -0,0 +1,39 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +!gradle-wrapper.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Ignore Gradle project-specific cache directory +.gradle +/buildSrc/.gradle/ + +# Ignore Gradle build output directory +build +out + +#Idea +*.iml +*.iws +*.ipr +*.idea + diff --git a/2024-11/spring-35-36-spring-cloud/README.md b/2024-11/spring-35-36-spring-cloud/README.md new file mode 100755 index 00000000..387c879c --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/README.md @@ -0,0 +1 @@ +# Spring Cloud example diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/api-gateway/build.gradle.kts new file mode 100755 index 00000000..4030c0d4 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/build.gradle.kts @@ -0,0 +1,14 @@ +dependencies { + implementation(project(":kafka-log-appender")) + implementation("net.logstash.logback:logstash-logback-encoder") + implementation("ch.qos.logback:logback-classic") + + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + + implementation("org.springframework.cloud:spring-cloud-starter-gateway") + implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client") + + implementation("io.micrometer:micrometer-tracing-bridge-otel") // bridges the Micrometer Observation API to OpenTelemetry. + implementation("io.opentelemetry:opentelemetry-exporter-zipkin") // reports traces to Zipkin. +} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/hey b/2024-11/spring-35-36-spring-cloud/api-gateway/hey new file mode 100755 index 00000000..27aec446 Binary files /dev/null and b/2024-11/spring-35-36-spring-cloud/api-gateway/hey differ diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/runLoad.sh b/2024-11/spring-35-36-spring-cloud/api-gateway/runLoad.sh new file mode 100755 index 00000000..7a0a9b20 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/runLoad.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./hey -n=1000000 -c=1 -m GET http://localhost:7777/client/info?name=testClientName + diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/ApiGateway.java b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/ApiGateway.java new file mode 100755 index 00000000..6c3bc85a --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/ApiGateway.java @@ -0,0 +1,22 @@ +package ru.demo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ApiGateway { + private static final Logger log = LoggerFactory.getLogger(ApiGateway.class); + + /* + curl -v http://localhost:7777/client/info?name=testClientName + curl -v http://localhost:7777/order/info?id=testOrderId + + */ + + public static void main(String[] args) { + SpringApplication.run(ApiGateway.class, args); + log.info("SrvApiGateway started"); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApiConfig.java b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApiConfig.java new file mode 100755 index 00000000..c9526422 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApiConfig.java @@ -0,0 +1,34 @@ +package ru.demo.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.demo.controller.XrequestFilter; + +@Configuration +@EnableConfigurationProperties(ApplConfigProperties.class) +@EnableDiscoveryClient +public class ApiConfig { + + @Bean + public XrequestFilter xrequestFilter() { + return new XrequestFilter(); + } + + @Bean + RouteLocator gateway( + RouteLocatorBuilder rlb, ApplConfigProperties applConfigProperties, XrequestFilter xrequestFilter) { + var routesBuilder = rlb.routes(); + for (var route : applConfigProperties.getApiRoutes()) { + routesBuilder.route(route.id(), routeSpec -> routeSpec + .path(String.format("/%s/**", route.from())) + .filters(fs -> fs.filters(xrequestFilter) + .rewritePath(String.format("/%s/(?.*)", route.from()), "/${segment}")) + .uri(String.format("%s/@", route.to()))); + } + return routesBuilder.build(); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApiRoute.java b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApiRoute.java new file mode 100755 index 00000000..85e84ccf --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApiRoute.java @@ -0,0 +1,3 @@ +package ru.demo.config; + +public record ApiRoute(String id, String from, String to) {} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApplConfigProperties.java b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApplConfigProperties.java new file mode 100755 index 00000000..47cc8501 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/config/ApplConfigProperties.java @@ -0,0 +1,30 @@ +package ru.demo.config; + +import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.ConstructorBinding; + +@ConfigurationProperties(prefix = "application") +public class ApplConfigProperties { + private final String authHost; + private final List apiRoutes; + + @ConstructorBinding + public ApplConfigProperties(String authHost, List apiRoutes) { + this.authHost = authHost; + this.apiRoutes = apiRoutes; + } + + public String getAuthHost() { + return authHost; + } + + public List getApiRoutes() { + return apiRoutes; + } + + @Override + public String toString() { + return "ApplConfigProperties{" + "authHost='" + authHost + '\'' + ", apiRoutes=" + apiRoutes + '}'; + } +} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/controller/ErrorHandler.java b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/controller/ErrorHandler.java new file mode 100755 index 00000000..34fb8f5f --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/controller/ErrorHandler.java @@ -0,0 +1,38 @@ +package ru.demo.controller; + +import java.net.ConnectException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import reactor.util.annotation.NonNull; + +@Order(-2) +@Component +public class ErrorHandler implements ErrorWebExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(ErrorHandler.class); + + @Override + public Mono handle(@NonNull ServerWebExchange serverWebExchange, @NonNull Throwable thr) { + var bufferFactory = serverWebExchange.getResponse().bufferFactory(); + var response = serverWebExchange.getResponse(); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + + if (thr instanceof ConnectException) { + log.error("Target host connection error", thr); + response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE); + var dataBuffer = bufferFactory.wrap("Target host connection error".getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } else { + log.error("Unhandled exception", thr); + response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + var dataBuffer = bufferFactory.wrap("Unhandled error".getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } + } +} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/controller/XrequestFilter.java b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/controller/XrequestFilter.java new file mode 100755 index 00000000..0b8a5176 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/java/ru/demo/controller/XrequestFilter.java @@ -0,0 +1,24 @@ +package ru.demo.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class XrequestFilter implements GatewayFilter { + private static final String HEADER_X_REQUEST_ID = "X-Request-Id"; + private static final Logger log = LoggerFactory.getLogger(XrequestFilter.class); + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + var guid = java.util.UUID.randomUUID().toString(); + log.info("requestId:{}", guid); + + var request = + exchange.getRequest().mutate().header(HEADER_X_REQUEST_ID, guid).build(); + + return chain.filter(exchange.mutate().request(request).build()); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/resources/application.yml b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/resources/application.yml new file mode 100755 index 00000000..50e23846 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/resources/application.yml @@ -0,0 +1,52 @@ +spring: + application: + name: api-gateway + + +server: + port: 7777 + +application: + api-routes: + - id: "service-client" + from: "client" + to: "lb://SERVICE-CLIENT" + - id: "service-order" + from: "order" + to: "lb://SERVICE-ORDER" + +eureka: + client: + registerWithEureka: false + fetchRegistry: true + serviceUrl: + defaultZone: http://localhost:9999/eureka/ + instance: + lease-renewal-interval-in-seconds: 30 + metadataMap: + zone: zone1 + +management: + tracing: + sampling: + probability: 1.0 + endpoint: + prometheus: + enabled: true + metrics: + enabled: true + health: + enabled: true + probes: + enabled: true + refresh: + enabled: true + endpoints: + web: + exposure: + include: + - prometheus + - health + - metrics + - refresh + enabled-by-default: false \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/resources/logback.xml b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/resources/logback.xml new file mode 100755 index 00000000..c20eb884 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/api-gateway/src/main/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + localhost:9092 + applLogs + + + [ignore] + + {"appname":"api-gateway"} + + + + + + + + diff --git a/2024-11/spring-35-36-spring-cloud/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/build.gradle.kts new file mode 100755 index 00000000..f9c9d78a --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/build.gradle.kts @@ -0,0 +1,121 @@ +import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension +import name.remal.gradle_plugins.sonarlint.SonarLintExtension +import org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES +import org.gradle.plugins.ide.idea.model.IdeaLanguageLevel +import org.springframework.boot.gradle.plugin.SpringBootPlugin + +plugins { + idea + id("fr.brouillard.oss.gradle.jgitver") + id("io.spring.dependency-management") + id("org.springframework.boot") apply false + id("name.remal.sonarlint") apply false + id("com.diffplug.spotless") apply false +} + +allprojects { + group = "ru.demo" + + repositories { + mavenLocal() + mavenCentral() + } + + val springCloudVersion: String by project + val logbackEncoder: String by project + + apply(plugin = "io.spring.dependency-management") + dependencyManagement { + dependencies { + imports { + mavenBom(BOM_COORDINATES) + mavenBom("org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion") + } + dependency("net.logstash.logback:logstash-logback-encoder:$logbackEncoder") + } + } + + configurations.all { + resolutionStrategy { + failOnVersionConflict() + + force("org.glassfish.hk2.external:aopalliance-repackaged:3.1.1") + force("org.glassfish.hk2:hk2-utils:3.1.1") + force("commons-logging:commons-logging:1.3.1") + force("com.fasterxml.woodstox:woodstox-core:6.6.2") + force("org.glassfish.hk2:hk2-api:3.1.1") + force("org.apache.httpcomponents:httpclient:4.5.14") + force("org.sonarsource.analyzer-commons:sonar-analyzer-commons:2.11.0.2861") + force("org.sonarsource.analyzer-commons:sonar-xml-parsing:2.11.0.2861") + force("org.sonarsource.sslr:sslr-core:1.24.0.633") + force("org.sonarsource.analyzer-commons:sonar-analyzer-recognizers:2.11.0.2861") + force("commons-io:commons-io:2.15.1") + force("com.google.guava:guava:32.1.3-jre") + force("com.google.code.findbugs:jsr305:3.0.2") + force("org.codehaus.woodstox:stax2-api:4.2.2") + force("io.opentelemetry:opentelemetry-api-incubator:1.38.0-alpha") + } + } +} + +subprojects { + plugins.apply(SpringBootPlugin::class.java) + plugins.apply(JavaPlugin::class.java) + extensions.configure { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + + tasks.withType { + options.encoding = "UTF-8" + options.compilerArgs.addAll(listOf("-Xlint:all,-serial,-processing")) + + dependsOn("spotlessApply") + } + + apply() + configure { + nodeJs { + detectNodeJs = false + logNodeJsNotFound = false + } + } + apply() + configure { + java { + palantirJavaFormat("2.38.0") + } + } + + plugins.apply(fr.brouillard.oss.gradle.plugins.JGitverPlugin::class.java) + extensions.configure { + strategy("PATTERN") + nonQualifierBranches("main,master") + tagVersionPattern("\${v}\${ { + useJUnitPlatform() + testLogging.showExceptions = true + reports { + junitXml.required.set(true) + html.required.set(true) + } + } +} + +tasks { + val managedVersions by registering { + doLast { + project.extensions.getByType() + .managedVersions + .toSortedMap() + .map { "${it.key}:${it.value}" } + .forEach(::println) + } + } +} \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/config-server/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/config-server/build.gradle.kts new file mode 100755 index 00000000..3e355df7 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/config-server/build.gradle.kts @@ -0,0 +1,12 @@ +dependencies { + implementation(project(":kafka-log-appender")) + implementation("net.logstash.logback:logstash-logback-encoder") + + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + + implementation ("org.springframework.cloud:spring-cloud-config-server") + + implementation("io.micrometer:micrometer-tracing-bridge-otel") // bridges the Micrometer Observation API to OpenTelemetry. + implementation("io.opentelemetry:opentelemetry-exporter-zipkin") // reports traces to Zipkin. +} diff --git a/2024-11/spring-35-36-spring-cloud/config-server/src/main/java/ru/demo/ConfigServer.java b/2024-11/spring-35-36-spring-cloud/config-server/src/main/java/ru/demo/ConfigServer.java new file mode 100755 index 00000000..bbbb0a42 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/config-server/src/main/java/ru/demo/ConfigServer.java @@ -0,0 +1,19 @@ +package ru.demo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; + +@SpringBootApplication +@EnableConfigServer +public class ConfigServer { + private static final Logger log = LoggerFactory.getLogger(ConfigServer.class); + + public static void main(String[] args) { + + SpringApplication.run(ConfigServer.class, args); + log.info("ConfigServer started"); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/config-server/src/main/resources/application.yml b/2024-11/spring-35-36-spring-cloud/config-server/src/main/resources/application.yml new file mode 100755 index 00000000..7f99933c --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/config-server/src/main/resources/application.yml @@ -0,0 +1,38 @@ +server: + port: 8888 + +spring: + application: + name: config-server + cloud: + config: + server: + git: + uri: "git@github.com:OtusTeam/Spring.git" + search-paths: "2024-11/spring-34-35-spring-cloud/git-config" + ignoreLocalSshSettings: false + strictHostKeyChecking: false +management: + tracing: + sampling: + probability: 1.0 + endpoint: + prometheus: + enabled: true + metrics: + enabled: true + health: + enabled: true + probes: + enabled: true + refresh: + enabled: true + endpoints: + web: + exposure: + include: + - prometheus + - health + - metrics + - refresh + enabled-by-default: false diff --git a/2024-11/spring-35-36-spring-cloud/config-server/src/main/resources/logback.xml b/2024-11/spring-35-36-spring-cloud/config-server/src/main/resources/logback.xml new file mode 100755 index 00000000..85e2f041 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/config-server/src/main/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + localhost:9092 + applLogs + + + [ignore] + + {"appname":"config-server"} + + + + + + + + diff --git a/2024-11/spring-35-36-spring-cloud/elk/JVM (Micrometer)-1733760120676.json b/2024-11/spring-35-36-spring-cloud/elk/JVM (Micrometer)-1733760120676.json new file mode 100644 index 00000000..b0f42ea7 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/elk/JVM (Micrometer)-1733760120676.json @@ -0,0 +1,3880 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.4.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", + "hide": false, + "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, + "graphTooltip": 1, + "id": null, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "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", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 148, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(request_counter_total{application=\"service-client\"}[1m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "{{application}}", + "range": true, + "refId": "A", + "useBackend": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + } + } + ], + "title": "Счетчик запросов", + "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, + "barWidthFactor": 0.6, + "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", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 149, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "request_duration{application=\"service-client\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "{{application}}", + "range": true, + "refId": "A", + "useBackend": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + } + } + ], + "title": "Время выполнения запроса", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 139, + "panels": [], + "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": 9 + }, + "id": 63, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_uptime_seconds{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "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": 9 + }, + "id": 92, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "process_start_time_seconds{application=\"$application\", instance=\"$instance\"}*1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "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" + } + ], + "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": 9 + }, + "id": 65, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "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": "", + "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" + } + ], + "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": 9 + }, + "id": 75, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 140, + "panels": [], + "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, + "barWidthFactor": 0.6, + "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": 13 + }, + "id": 111, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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" + } + } + ] + }, + { + "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": 13 + }, + "id": 112, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 13 + }, + "id": 113, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 13 + }, + "id": 119, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 141, + "panels": [], + "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, + "barWidthFactor": 0.6, + "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": 21 + }, + "id": 24, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 21 + }, + "id": 25, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 21 + }, + "id": 26, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 21 + }, + "id": 86, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 142, + "panels": [], + "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, + "barWidthFactor": 0.6, + "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": 29 + }, + "id": 106, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 29 + }, + "id": 93, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 29 + }, + "id": 32, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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" + } + } + ] + }, + { + "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": 29 + }, + "id": 124, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 36 + }, + "id": 138, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 36 + }, + "id": 91, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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": 36 + }, + "id": 61, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 143, + "panels": [], + "repeat": "persistence_counts", + "title": "JVM Memory Pools (Heap)", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 44 + }, + "id": 3, + "maxPerRow": 3, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_memory_pool_heap", + "repeatDirection": "h", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 51 + }, + "id": 144, + "panels": [], + "title": "JVM Memory Pools (Non-Heap)", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 52 + }, + "id": 78, + "maxPerRow": 3, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_memory_pool_nonheap", + "repeatDirection": "h", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 66 + }, + "id": 145, + "panels": [], + "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, + "barWidthFactor": 0.6, + "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" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 67 + }, + "id": 98, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 67 + }, + "id": 101, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 67 + }, + "id": 99, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 74 + }, + "id": 146, + "panels": [], + "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, + "barWidthFactor": 0.6, + "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" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 75 + }, + "id": 37, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "barWidthFactor": 0.6, + "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" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 75 + }, + "id": 38, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "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, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 82 + }, + "id": 147, + "panels": [], + "title": "Buffer Pools", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": {}, + "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" + } + ] + }, + { + "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" + } + ] + }, + { + "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" + } + ] + }, + { + "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": 24, + "x": 0, + "y": 83 + }, + "id": 131, + "maxPerRow": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_buffer_pool", + "repeatDirection": "h", + "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": "5s", + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "includeAll": false, + "label": "Application", + "name": "application", + "options": [], + "query": "label_values(application)", + "refresh": 2, + "regex": "", + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "includeAll": false, + "label": "Instance", + "name": "instance", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\"}, instance)", + "refresh": 2, + "regex": "", + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "hide": 2, + "includeAll": true, + "label": "JVM Memory Pools Heap", + "name": "jvm_memory_pool_heap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"},id)", + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "hide": 2, + "includeAll": true, + "label": "JVM Memory Pools Non-Heap", + "name": "jvm_memory_pool_nonheap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"},id)", + "refresh": 1, + "regex": "", + "sort": 2, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "hide": 2, + "includeAll": true, + "label": "JVM Buffer Pools", + "name": "jvm_buffer_pool", + "options": [], + "query": "label_values(jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\"},id)", + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "JVM (Micrometer)", + "uid": "fe6atqkegecqoe", + "version": 4, + "weekStart": "" +} \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/elk/docker-compose.yml b/2024-11/spring-35-36-spring-cloud/elk/docker-compose.yml new file mode 100755 index 00000000..34b58852 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/elk/docker-compose.yml @@ -0,0 +1,143 @@ + +services: + opensearch-node1: + image: opensearchproject/opensearch:2.18.0 + container_name: opensearch-node1 + environment: + - cluster.name=opensearch-cluster # Name the cluster + - node.name=opensearch-node1 # Name the node that will run in this container + - discovery.seed_hosts=opensearch-node1,opensearch-node2 # Nodes to look for when discovering the cluster + - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # Nodes eligibile to serve as cluster manager + - bootstrap.memory_lock=true # Disable JVM heap memory swapping + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # Set min and max JVM heap sizes to at least 50% of system RAM + - "DISABLE_INSTALL_DEMO_CONFIG=true" # Prevents execution of bundled demo script which installs demo certificates and security configurations to OpenSearch + - "DISABLE_SECURITY_PLUGIN=true" # Disables Security plugin + ulimits: + memlock: + soft: -1 # Set memlock to unlimited (no soft or hard limit) + hard: -1 + nofile: + soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536 + hard: 65536 + volumes: + - opensearch-data1:/usr/share/opensearch/data # Creates volume called opensearch-data1 and mounts it to the container + ports: + - 9200:9200 # REST API + - 9600:9600 # Performance Analyzer + networks: + - opensearch-net # All of the containers will join the same Docker bridge network + opensearch-node2: + image: opensearchproject/opensearch:2.18.0 + container_name: opensearch-node2 + environment: + - cluster.name=opensearch-cluster # Name the cluster + - node.name=opensearch-node2 # Name the node that will run in this container + - discovery.seed_hosts=opensearch-node1,opensearch-node2 # Nodes to look for when discovering the cluster + - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # Nodes eligibile to serve as cluster manager + - bootstrap.memory_lock=true # Disable JVM heap memory swapping + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # Set min and max JVM heap sizes to at least 50% of system RAM + - "DISABLE_INSTALL_DEMO_CONFIG=true" # Prevents execution of bundled demo script which installs demo certificates and security configurations to OpenSearch + - "DISABLE_SECURITY_PLUGIN=true" # Disables Security plugin + ulimits: + memlock: + soft: -1 # Set memlock to unlimited (no soft or hard limit) + hard: -1 + nofile: + soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536 + hard: 65536 + volumes: + - opensearch-data2:/usr/share/opensearch/data # Creates volume called opensearch-data2 and mounts it to the container + networks: + - opensearch-net # All of the containers will join the same Docker bridge network + opensearch-dashboards: + image: opensearchproject/opensearch-dashboards:2.18.0 + container_name: opensearch-dashboards + ports: + - 5601:5601 # Map host port 5601 to container port 5601 + expose: + - "5601" # Expose port 5601 for web access to OpenSearch Dashboards + environment: + - 'OPENSEARCH_HOSTS=["http://opensearch-node1:9200","http://opensearch-node2:9200"]' + - "DISABLE_SECURITY_DASHBOARDS_PLUGIN=true" # disables security dashboards plugin in OpenSearch Dashboards + networks: + - opensearch-net + +#logstash + + logstash: + image: opensearchproject/logstash-oss-with-opensearch-output-plugin:7.16.2 + container_name: logstash + volumes: + - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf + networks: + - opensearch-net + + zookeeper: + image: confluentinc/cp-zookeeper:6.2.0 + container_name: zookeeper + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + networks: + - opensearch-net + + broker: + image: confluentinc/cp-kafka:7.0.0 + container_name: broker + ports: + - "9092:9092" + depends_on: + - zookeeper + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092,PLAINTEXT_INTERNAL://broker:29092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + networks: + - opensearch-net + + kafka-ui: + container_name: kafka-ui + image: provectuslabs/kafka-ui:latest + network_mode: "host" + environment: + DYNAMIC_CONFIG_ENABLED: 'true' + + zipkin: + container_name: zipkin + image: openzipkin/zipkin:3 + network_mode: "host" + expose: + - "9411" + + prometheus: + image: bitnami/prometheus:3.0.1 + container_name: prometheus + network_mode: "host" + expose: + - "9090" + volumes: + - type: bind + source: ./prometheus.yml + target: /etc/prometheus/prometheus.yml + + grafana: + image: grafana/grafana-enterprise:11.4.0 + container_name: grafana + network_mode: "host" + expose: + - "3000" + environment: + GF_SECURITY_ADMIN_USER: "grafana" + GF_SECURITY_ADMIN_PASSWORD: "grafana" + + +volumes: + opensearch-data1: + opensearch-data2: + +networks: + opensearch-net: \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/elk/logstash.conf b/2024-11/spring-35-36-spring-cloud/elk/logstash.conf new file mode 100644 index 00000000..35bb470e --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/elk/logstash.conf @@ -0,0 +1,24 @@ +input { + http { + id => "test_http_plugin_id" + host => "127.0.0.1" + port => 8080 + } + + kafka { + bootstrap_servers => "broker:29092" + topics => "applLogs" + codec => "json" + } +} + +output { + opensearch { + hosts => "http://opensearch-node1:9200" + user => "admin" + password => "admin" + index => "logstash-logs-%{+YYYY.MM.dd}" + ssl_certificate_verification => false + ssl => false + } +} \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/elk/prometheus.yml b/2024-11/spring-35-36-spring-cloud/elk/prometheus.yml new file mode 100755 index 00000000..efdaf62d --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/elk/prometheus.yml @@ -0,0 +1,11 @@ +# JVM (Micrometer) Grafana dashboard: 4701 +global: + scrape_interval: 10s + evaluation_interval: 15s + +scrape_configs: + - job_name: service-client + metrics_path: '/actuator/prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:8081'] diff --git a/2024-11/spring-35-36-spring-cloud/eureka-server/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/eureka-server/build.gradle.kts new file mode 100755 index 00000000..86f0e5c4 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/eureka-server/build.gradle.kts @@ -0,0 +1,13 @@ +dependencies { + implementation(project(":kafka-log-appender")) + implementation("net.logstash.logback:logstash-logback-encoder") + + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + + implementation ("org.springframework.cloud:spring-cloud-config-server") + implementation ("org.springframework.cloud:spring-cloud-starter-netflix-eureka-server") + + implementation("io.micrometer:micrometer-tracing-bridge-otel") // bridges the Micrometer Observation API to OpenTelemetry. + implementation("io.opentelemetry:opentelemetry-exporter-zipkin") // reports traces to Zipkin. +} diff --git a/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/java/ru/demo/EurekaServer.java b/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/java/ru/demo/EurekaServer.java new file mode 100755 index 00000000..34063ca5 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/java/ru/demo/EurekaServer.java @@ -0,0 +1,19 @@ +package ru.demo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +@EnableEurekaServer +public class EurekaServer { + private static final Logger log = LoggerFactory.getLogger(EurekaServer.class); + + public static void main(String[] args) { + + SpringApplication.run(EurekaServer.class, args); + log.info("EurekaServer started"); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/resources/application.yml b/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/resources/application.yml new file mode 100755 index 00000000..3857bd82 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/resources/application.yml @@ -0,0 +1,42 @@ +spring: + application: + name: eureka-server +server: + port: 9999 + +eureka: + server: + renewal-percent-threshold: 0.5 + instance: + hostname: localhost + lease-expiration-duration-in-seconds: 90 + client: + registerWithEureka: false + fetchRegistry: false + serviceUrl: + defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ + +management: + tracing: + sampling: + probability: 1.0 + endpoint: + prometheus: + enabled: true + metrics: + enabled: true + health: + enabled: true + probes: + enabled: true + refresh: + enabled: true + endpoints: + web: + exposure: + include: + - prometheus + - health + - metrics + - refresh + enabled-by-default: false diff --git a/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/resources/logback.xml b/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/resources/logback.xml new file mode 100755 index 00000000..01075dfc --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/eureka-server/src/main/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + localhost:9092 + applLogs + + + [ignore] + + {"appname":"eureka-server"} + + + + + + + + diff --git a/2024-11/spring-35-36-spring-cloud/git-config/application.yml b/2024-11/spring-35-36-spring-cloud/git-config/application.yml new file mode 100755 index 00000000..a5328e56 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/git-config/application.yml @@ -0,0 +1,28 @@ +management: + tracing: + sampling: + probability: 1.0 + endpoint: + prometheus: + enabled: true + metrics: + enabled: true + health: + enabled: true + probes: + enabled: true + refresh: + enabled: true + endpoints: + web: + exposure: + include: + - prometheus + - health + - metrics + - refresh + enabled-by-default: false + metrics: + tags: + application: ${spring.application.name} + diff --git a/2024-11/spring-35-36-spring-cloud/git-config/service-client-info.yml b/2024-11/spring-35-36-spring-cloud/git-config/service-client-info.yml new file mode 100755 index 00000000..9899989d --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/git-config/service-client-info.yml @@ -0,0 +1,2 @@ +server: + port: 8083 \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/git-config/service-client.yml b/2024-11/spring-35-36-spring-cloud/git-config/service-client.yml new file mode 100755 index 00000000..54b155ff --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/git-config/service-client.yml @@ -0,0 +1,2 @@ +server: + port: 8081 \ No newline at end of file diff --git a/2024-11/spring-35-36-spring-cloud/git-config/service-order.yml b/2024-11/spring-35-36-spring-cloud/git-config/service-order.yml new file mode 100755 index 00000000..4a2daf55 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/git-config/service-order.yml @@ -0,0 +1,2 @@ +server: + port: 8082 diff --git a/2024-11/spring-35-36-spring-cloud/gradle.properties b/2024-11/spring-35-36-spring-cloud/gradle.properties new file mode 100755 index 00000000..ec293240 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/gradle.properties @@ -0,0 +1,15 @@ +# -------Gradle-------- +org.gradle.jvmargs=-Xmx4g +org.gradle.daemon=true +org.gradle.parallel=true +# -------Plugins--------- +jgitver=0.10.0-rc03 +dependencyManagement=1.1.5 +springframeworkBoot=3.3.1 +springCloudVersion=2023.0.3 + +sonarlint=4.2.4 +spotless=6.25.0 +jib=3.4.3 +# -------Versions-------- +logbackEncoder=8.0 diff --git a/2024-11/spring-35-36-spring-cloud/gradle/wrapper/gradle-wrapper.jar b/2024-11/spring-35-36-spring-cloud/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 00000000..d64cd491 Binary files /dev/null and b/2024-11/spring-35-36-spring-cloud/gradle/wrapper/gradle-wrapper.jar differ diff --git a/2024-11/spring-35-36-spring-cloud/gradle/wrapper/gradle-wrapper.properties b/2024-11/spring-35-36-spring-cloud/gradle/wrapper/gradle-wrapper.properties new file mode 100755 index 00000000..df97d72b --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/2024-11/spring-35-36-spring-cloud/gradlew b/2024-11/spring-35-36-spring-cloud/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/2024-11/spring-35-36-spring-cloud/gradlew.bat b/2024-11/spring-35-36-spring-cloud/gradlew.bat new file mode 100755 index 00000000..93e3f59f --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/2024-11/spring-35-36-spring-cloud/kafka-log-appender/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/build.gradle.kts new file mode 100755 index 00000000..93ab7ab5 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/build.gradle.kts @@ -0,0 +1,10 @@ +dependencies { + implementation("ch.qos.logback:logback-classic") + implementation ("org.apache.kafka:kafka-clients") +} + +tasks { + bootJar { + enabled = false + } +} diff --git a/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/AppenderException.java b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/AppenderException.java new file mode 100755 index 00000000..d57ddd0f --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/AppenderException.java @@ -0,0 +1,8 @@ +package ru.appender.kafka; + +public class AppenderException extends RuntimeException { + + public AppenderException(String message) { + super(message); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/ErrorMsgConsumer.java b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/ErrorMsgConsumer.java new file mode 100644 index 00000000..f7dbb53e --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/ErrorMsgConsumer.java @@ -0,0 +1,5 @@ +package ru.appender.kafka; + +import java.util.function.Consumer; + +public interface ErrorMsgConsumer extends Consumer {} diff --git a/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/LogAppender.java b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/LogAppender.java new file mode 100755 index 00000000..a0e30079 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/LogAppender.java @@ -0,0 +1,84 @@ +package ru.appender.kafka; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.UnsynchronizedAppenderBase; +import ch.qos.logback.core.encoder.Encoder; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + +public class LogAppender extends UnsynchronizedAppenderBase { + private static final String MESSAGE_TEMPLATE = "[Kafka appender] %s"; + + private String bootstrapServers; + private String topicName; + + private final Queue eventsQueue = new ArrayBlockingQueue<>(1000); + + private Thread senderThread; + private Encoder encoder; + + private final ErrorMsgConsumer errorMsgConsumer = error -> addError(String.format(MESSAGE_TEMPLATE, error)); + + public void setEncoder(Encoder encoder) { + this.encoder = encoder; + } + + public void setBootstrapServers(String bootstrapServers) { + this.bootstrapServers = bootstrapServers; + addInfo(String.format(MESSAGE_TEMPLATE, "set bootstrapServers:" + bootstrapServers)); + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + addInfo(String.format(MESSAGE_TEMPLATE, "set topicName:" + topicName)); + } + + @Override + public void start() { + if (bootstrapServers == null) { + addError(String.format(MESSAGE_TEMPLATE, "bootstrapServers is null")); + return; + } + + if (topicName == null) { + addError(String.format(MESSAGE_TEMPLATE, "topicName is null")); + return; + } + var logProducer = new LogProducer(bootstrapServers, topicName); + + senderThread = Thread.ofVirtual().name("senderThread").start(() -> sendMessages(logProducer)); + super.start(); + addInfo(String.format(MESSAGE_TEMPLATE, "started")); + } + + @Override + public void stop() { + super.stop(); + if (senderThread != null) { + senderThread.interrupt(); + } + addInfo(String.format(MESSAGE_TEMPLATE, "stopped")); + } + + @Override + public void append(LoggingEvent eventObject) { + var result = eventsQueue.offer(eventObject); + if (!result) { + addWarn(String.format(MESSAGE_TEMPLATE, "eventsQueue is full")); + } + } + + private void sendMessages(LogProducer logProducer) { + while (!Thread.currentThread().isInterrupted()) { + var event = eventsQueue.poll(); + if (event != null) { + try { + var messageAsText = new String(encoder.encode(event)); + logProducer.send(messageAsText, errorMsgConsumer); + } catch (Exception ex) { + addError(String.format(MESSAGE_TEMPLATE, ex.getMessage())); + } + } + } + } +} diff --git a/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/LogProducer.java b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/LogProducer.java new file mode 100755 index 00000000..656a9fff --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/kafka-log-appender/src/main/java/ru/appender/kafka/LogProducer.java @@ -0,0 +1,57 @@ +package ru.appender.kafka; + +import static org.apache.kafka.clients.CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG; +import static org.apache.kafka.clients.CommonClientConfigs.CLIENT_ID_CONFIG; +import static org.apache.kafka.clients.CommonClientConfigs.RETRIES_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.ACKS_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.BATCH_SIZE_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.BUFFER_MEMORY_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.LINGER_MS_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.MAX_BLOCK_MS_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG; + +import java.util.Properties; +import java.util.function.Consumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringSerializer; + +public class LogProducer { + private final KafkaProducer kafkaProducer; + private final String topicName; + private long lastSendKey = System.currentTimeMillis(); + + public LogProducer(String bootstrapServers, String topicName) { + this.topicName = topicName; + Properties props = new Properties(); + props.put(CLIENT_ID_CONFIG, "myKafkaProducer"); + props.put(BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + props.put(ACKS_CONFIG, "1"); + props.put(RETRIES_CONFIG, 1); + props.put(BATCH_SIZE_CONFIG, 16384); + props.put(LINGER_MS_CONFIG, 10); + props.put(BUFFER_MEMORY_CONFIG, 33_554_432); // bytes + props.put(MAX_BLOCK_MS_CONFIG, 1_000); // ms + props.put(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + + kafkaProducer = new KafkaProducer<>(props); + + var shutdownHook = new Thread(this::close); + Runtime.getRuntime().addShutdownHook(shutdownHook); + } + + public void send(String value, Consumer errorCallback) { + var key = lastSendKey++; + kafkaProducer.send(new ProducerRecord<>(topicName, String.valueOf(key), value), (metadata, exception) -> { + if (exception != null) { + errorCallback.accept(String.format(exception.getMessage())); + } + }); + } + + public void close() { + kafkaProducer.close(); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/ServiceClientInfo.java b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/ServiceClientInfo.java new file mode 100755 index 00000000..c5ce2a91 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/ServiceClientInfo.java @@ -0,0 +1,11 @@ +package ru.demo; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ServiceClientInfo { + public static void main(String[] args) { + new SpringApplicationBuilder().sources(ServiceClientInfo.class).run(args); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/config/ApplConf.java b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/config/ApplConf.java new file mode 100755 index 00000000..be04c7bd --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/config/ApplConf.java @@ -0,0 +1,19 @@ +package ru.demo.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.OncePerRequestFilter; +import ru.demo.filter.MdcFilter; + +@Configuration +public class ApplConf { + + @Bean + public FilterRegistrationBean mdcFilterRegistrationBean() { + var registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(new MdcFilter()); + registrationBean.setOrder(1); + return registrationBean; + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/controller/ClientInfoController.java b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/controller/ClientInfoController.java new file mode 100755 index 00000000..273a35f1 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/controller/ClientInfoController.java @@ -0,0 +1,30 @@ +package ru.demo.controller; + +import java.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.demo.model.ClientData; + +@RestController +public class ClientInfoController { + private static final Logger logger = LoggerFactory.getLogger(ClientInfoController.class); + + // curl -v http://localhost:8083/additional-info?name="testClient" + + // docker run -d --rm -p 9411:9411 openzipkin/zipkin + // http://localhost:9411 + + @GetMapping(value = "/additional-info") + public ClientData info(@RequestParam(name = "name") String name) throws InterruptedException { + logger.info("request. name:{}", name); + doJob(); + return new ClientData(String.format("additional ClientInfo name:%s", name)); + } + + private void doJob() throws InterruptedException { + Thread.sleep(Duration.ofMillis(100)); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/filter/MdcFilter.java b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/filter/MdcFilter.java new file mode 100755 index 00000000..5b134b08 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/filter/MdcFilter.java @@ -0,0 +1,39 @@ +package ru.demo.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.web.filter.OncePerRequestFilter; + +public class MdcFilter extends OncePerRequestFilter { + private final Logger log = LoggerFactory.getLogger(MdcFilter.class); + private static final String HEADER_X_REQUEST_ID = "X-Request-Id"; + private static final String MDC_REQUEST_ID = "requestId"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + var xRequestId = request.getHeader(HEADER_X_REQUEST_ID); + log.info("method:{}, xRequestId:{}", request.getMethod(), xRequestId); + if (xRequestId != null) { + MDC.put(MDC_REQUEST_ID, xRequestId); + } + + var headerIterator = request.getHeaderNames().asIterator(); + var headers = new ArrayList(); + while (headerIterator.hasNext()) { + headers.add(headerIterator.next()); + } + log.info("request headers:{}", headers); + + response.addHeader(HEADER_X_REQUEST_ID, xRequestId); + filterChain.doFilter(request, response); + MDC.remove(MDC_REQUEST_ID); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/model/ClientData.java b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/model/ClientData.java new file mode 100755 index 00000000..75a423ed --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/java/ru/demo/model/ClientData.java @@ -0,0 +1,3 @@ +package ru.demo.model; + +public record ClientData(String data) {} diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/resources/application.yml b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/resources/application.yml new file mode 100755 index 00000000..dc3f8248 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + application: + name: service-client-info + cloud: + config: + fail-fast: true + retry: + initial-interval: 5000 + max-attempts: 10 + max-interval: 5000 + multiplier: 1.2 + config: + import: optional:configserver:http://localhost:8888 + codec: + max-in-memory-size: 10MB + +eureka: + client: + fetchRegistry: false + serviceUrl: + defaultZone: http://localhost:9999/eureka/ + instance: + lease-renewal-interval-in-seconds: 30 + metadataMap: + zone: zone1 + + diff --git a/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/resources/logback.xml b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/resources/logback.xml new file mode 100755 index 00000000..5507f6db --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client-info/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{requestId}] %msg%n + + + + + localhost:9092 + applLogs + + + [ignore] + + {"appname":"service-client-info"} + requestId + + + + + + + + diff --git a/2024-11/spring-35-36-spring-cloud/service-client/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/service-client/build.gradle.kts new file mode 100755 index 00000000..3b63f3e7 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/build.gradle.kts @@ -0,0 +1,19 @@ +dependencies { + implementation(project(":kafka-log-appender")) + implementation("net.logstash.logback:logstash-logback-encoder") + + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + + implementation("org.springframework.boot:spring-boot-starter-web") + + implementation("org.springframework.cloud:spring-cloud-starter-config") + implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client") + implementation("org.springframework.cloud:spring-cloud-starter-openfeign") + + implementation("io.micrometer:micrometer-tracing-bridge-otel") // bridges the Micrometer Observation API to OpenTelemetry. + implementation("io.opentelemetry:opentelemetry-exporter-zipkin") // reports traces to Zipkin. + implementation("io.github.openfeign:feign-micrometer") + + implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j") +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/ServiceClient.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/ServiceClient.java new file mode 100755 index 00000000..98e9c7a0 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/ServiceClient.java @@ -0,0 +1,12 @@ +package ru.demo; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ServiceClient { + public static void main(String[] args) { + + new SpringApplicationBuilder().sources(ServiceClient.class).run(args); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/RequestEncoder.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/RequestEncoder.java new file mode 100755 index 00000000..58e6e120 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/RequestEncoder.java @@ -0,0 +1,23 @@ +package ru.demo.config; + +import feign.RequestTemplate; +import feign.codec.EncodeException; +import feign.codec.Encoder; +import java.lang.reflect.Type; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RequestEncoder implements Encoder { + private static final Logger log = LoggerFactory.getLogger(RequestEncoder.class); + private final Encoder defaultEncoder; + + public RequestEncoder(Encoder defaultEncoder) { + this.defaultEncoder = defaultEncoder; + } + + @Override + public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { + log.info("encode value:{}", object); + defaultEncoder.encode(object, bodyType, template); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/ResponseDecoder.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/ResponseDecoder.java new file mode 100755 index 00000000..1ae51572 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/ResponseDecoder.java @@ -0,0 +1,30 @@ +package ru.demo.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.FeignException; +import feign.Response; +import feign.codec.Decoder; +import java.io.IOException; +import java.lang.reflect.Type; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.demo.model.ClientData; + +public class ResponseDecoder implements Decoder { + private static final Logger log = LoggerFactory.getLogger(ResponseDecoder.class); + + private final Decoder defaultDecoder; + private final ObjectMapper mapper; + + public ResponseDecoder(Decoder defaultDecoder, ObjectMapper mapper) { + this.defaultDecoder = defaultDecoder; + this.mapper = mapper; + } + + @Override + public ClientData decode(Response response, Type type) throws IOException, FeignException { + var responseAsString = (String) defaultDecoder.decode(response, String.class); + log.info("response:{}", responseAsString); + return mapper.readValue(responseAsString, ClientData.class); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/ServiceClientApplConf.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/ServiceClientApplConf.java new file mode 100755 index 00000000..4078e0a2 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/config/ServiceClientApplConf.java @@ -0,0 +1,106 @@ +package ru.demo.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import feign.Contract; +import feign.Feign; +import feign.Logger; +import feign.Retryer; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.micrometer.MicrometerCapability; +import feign.micrometer.MicrometerObservationCapability; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.observation.ObservationRegistry; +import java.time.Duration; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.cloud.openfeign.FeignClientsConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.web.filter.OncePerRequestFilter; +import ru.demo.controller.ClientAdditionalInfoClient; +import ru.demo.filter.MdcFilter; +import ru.demo.metrics.MetricsManager; +import ru.demo.metrics.MicrometerMetricsManager; + +@Configuration +@Import(FeignClientsConfiguration.class) +public class ServiceClientApplConf { + + @Bean + public RateLimiterConfig rateLimiterConfig() { + return RateLimiterConfig.custom() + .timeoutDuration(Duration.ofMillis(100)) + .limitRefreshPeriod(Duration.ofSeconds(1)) + .limitForPeriod(1000) + .build(); + } + + @Bean + public RateLimiter rateLimiter(RateLimiterConfig config) { + return RateLimiter.of("defaultRateLimiter", config); + } + + @Bean + public Customizer defaultCustomizer() { + return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) + .timeLimiterConfig(TimeLimiterConfig.custom() + .timeoutDuration(Duration.ofSeconds(5)) + .build()) + .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()) + .build()); + } + + @Bean + public CircuitBreaker circuitBreaker(CircuitBreakerFactory circuitBreakerFactory) { + return circuitBreakerFactory.create("defaultCircuitBreaker"); + } + + @Bean + public FilterRegistrationBean mdcFilterRegistrationBean() { + var registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(new MdcFilter()); + registrationBean.setOrder(1); + return registrationBean; + } + + @Bean + public ObjectMapper objectMapper() { + return JsonMapper.builder().build(); + } + + @Bean + public ClientAdditionalInfoClient clientAdditionalInfoClient( + Decoder decoder, + Encoder encoder, + Contract contract, + ObjectMapper mapper, + MeterRegistry meterRegistry, + ObservationRegistry observationRegistry) { + + return Feign.builder() + .encoder(new RequestEncoder(encoder)) + .decoder(new ResponseDecoder(decoder, mapper)) + .contract(contract) + .logLevel(Logger.Level.FULL) + .addCapability(new MicrometerObservationCapability(observationRegistry)) // <-- THIS IS NEW + .addCapability(new MicrometerCapability(meterRegistry)) // <-- THIS IS NEW + .retryer(new Retryer.Default(500, 5_000, 10)) + .target(ClientAdditionalInfoClient.class, "http"); + } + + @Bean + public MetricsManager micrometerMetricsManager(MeterRegistry meterRegistry) { + return new MicrometerMetricsManager(meterRegistry); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/controller/ClientAdditionalInfoClient.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/controller/ClientAdditionalInfoClient.java new file mode 100755 index 00000000..e9b1470a --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/controller/ClientAdditionalInfoClient.java @@ -0,0 +1,16 @@ +package ru.demo.controller; + +import static ru.demo.filter.MdcFilter.HEADER_X_REQUEST_ID; + +import java.net.URI; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; +import ru.demo.model.ClientData; + +public interface ClientAdditionalInfoClient { + + @GetMapping(value = "/additional-info", consumes = "application/json") + ClientData additionalInfo( + @RequestHeader(HEADER_X_REQUEST_ID) String xRequestId, URI baseUri, @RequestParam("name") String nameVal); +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/controller/ClientController.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/controller/ClientController.java new file mode 100755 index 00000000..e3d996ca --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/controller/ClientController.java @@ -0,0 +1,90 @@ +package ru.demo.controller; + +import static ru.demo.filter.MdcFilter.MDC_REQUEST_ID; +import static ru.demo.metrics.Meter.REQUEST_COUNTER; + +import com.netflix.discovery.EurekaClient; +import io.github.resilience4j.core.functions.CheckedFunction; +import io.github.resilience4j.ratelimiter.RateLimiter; +import java.net.URI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.demo.metrics.Meter; +import ru.demo.metrics.MetricsManager; +import ru.demo.model.RequestForData; + +@RestController +public class ClientController { + private static final Logger log = LoggerFactory.getLogger(ClientController.class); + + private final MetricsManager metricsManager; + private final ClientAdditionalInfoClient clientAdditionalInfoClient; + private final EurekaClient discoveryClient; + private final CheckedFunction getAdditionalInfoFunction; + + // curl -v -H "X-Request-Id: 123" http://localhost:8081/info?name="testClient" + + public ClientController( + MetricsManager metricsManager, + ClientAdditionalInfoClient clientAdditionalInfoClient, + EurekaClient discoveryClient, + CircuitBreaker circuitBreaker, + RateLimiter rateLimiter) { + this.metricsManager = metricsManager; + this.clientAdditionalInfoClient = clientAdditionalInfoClient; + this.discoveryClient = discoveryClient; + + this.getAdditionalInfoFunction = RateLimiter.decorateCheckedFunction( + rateLimiter, + requestForData -> circuitBreaker.run(() -> doRequest(requestForData), t -> { + log.error("delay call failed error:{}", t.getMessage()); + return "unknown info"; + })); + } + + @GetMapping(value = "/info") + public String info(@RequestParam(name = "name") String name) { + var startTime = System.currentTimeMillis(); + metricsManager.incrementValue(REQUEST_COUNTER); + log.info("request. name:{}", name); + String additionalInfo = null; + try { + additionalInfo = getAdditionalInfoFunction.apply(new RequestForData(name, MDC.get(MDC_REQUEST_ID))); + } catch (Throwable ex) { + log.error("can't execute additional info, name:{}, error:{}", name, ex.getMessage()); + } + var requestResult = String.format("ClientInfo name:%s, additional:%s", name, additionalInfo); + + var duration = System.currentTimeMillis() - startTime; + metricsManager.putValue(Meter.REQUEST_DURATION, duration); + return requestResult; + } + + private String doRequest(RequestForData requestForData) { + try { + MDC.put(MDC_REQUEST_ID, requestForData.requestId()); + return getAdditionalInfo(requestForData.name()); + } finally { + MDC.remove(MDC_REQUEST_ID); + } + } + + private String getAdditionalInfo(String name) { + try { + var clientInfo = discoveryClient.getNextServerFromEureka("SERVICE-CLIENT-INFO", false); + log.info("clientInfo from Eureka:{}", clientInfo); + var additionalInfo = clientAdditionalInfoClient.additionalInfo( + MDC.get(MDC_REQUEST_ID), new URI(clientInfo.getHomePageUrl()), name); + log.info("additionalInfo:{}", additionalInfo); + return additionalInfo.data(); + } catch (Exception ex) { + log.error("can't get additional info, name:{}, error:{}", name, ex.getMessage()); + return null; + } + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/filter/MdcFilter.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/filter/MdcFilter.java new file mode 100755 index 00000000..ebd5fcff --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/filter/MdcFilter.java @@ -0,0 +1,38 @@ +package ru.demo.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.web.filter.OncePerRequestFilter; + +public class MdcFilter extends OncePerRequestFilter { + public static final String HEADER_X_REQUEST_ID = "X-Request-Id"; + public static final String MDC_REQUEST_ID = "requestId"; + private final Logger log = LoggerFactory.getLogger(MdcFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + var xRequestId = request.getHeader(HEADER_X_REQUEST_ID); + log.debug("xRequestId:{}", xRequestId); + if (xRequestId != null) { + MDC.put(MDC_REQUEST_ID, xRequestId); + } + var headerIterator = request.getHeaderNames().asIterator(); + var headers = new ArrayList(); + while (headerIterator.hasNext()) { + headers.add(headerIterator.next()); + } + log.debug("request headers:{}", headers); + response.addHeader(HEADER_X_REQUEST_ID, xRequestId); + filterChain.doFilter(request, response); + MDC.remove(MDC_REQUEST_ID); + log.debug("response headers:{}", response.getHeaderNames()); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/Meter.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/Meter.java new file mode 100644 index 00000000..57a79577 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/Meter.java @@ -0,0 +1,16 @@ +package ru.demo.metrics; + +public enum Meter { + REQUEST_COUNTER("request_counter"), + REQUEST_DURATION("request_duration"); + + private final String meterName; + + Meter(String meterName) { + this.meterName = meterName; + } + + public String getMeterName() { + return meterName; + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/MetricsManager.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/MetricsManager.java new file mode 100644 index 00000000..67365084 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/MetricsManager.java @@ -0,0 +1,11 @@ +package ru.demo.metrics; + +import java.util.function.Supplier; + +public interface MetricsManager { + void putValue(Meter meterName, long value); + + void incrementValue(Meter meterName); + + void registerGauge(Meter gaugeName, Supplier gaugeGetter); +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/MicrometerMetricsManager.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/MicrometerMetricsManager.java new file mode 100644 index 00000000..e15242f5 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/metrics/MicrometerMetricsManager.java @@ -0,0 +1,52 @@ +package ru.demo.metrics; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +public class MicrometerMetricsManager implements MetricsManager { + private final MeterRegistry meterRegistry; + private final Map gauges = new ConcurrentHashMap<>(); + private final Map counters = new ConcurrentHashMap<>(); + + public MicrometerMetricsManager(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @Override + public void putValue(Meter meterName, long value) { + var gaugeName = makeMeterName(meterName); + var gauge = gauges.computeIfAbsent(gaugeName, key -> { + var newGauge = new AtomicLong(); + registerGauge(meterName, newGauge::get); + return newGauge; + }); + gauge.set(value); + } + + @Override + public void registerGauge(Meter gaugeName, Supplier gaugeGetter) { + var builder = Gauge.builder(gaugeName.getMeterName(), gaugeGetter); + builder.register(meterRegistry); + } + + @Override + public void incrementValue(Meter meterName) { + var counterName = makeMeterName(meterName); + var counter = counters.computeIfAbsent(counterName, key -> makeCounter(meterName)); + counter.increment(); + } + + private Counter makeCounter(Meter meterName) { + var builder = Counter.builder(meterName.getMeterName()); + return builder.register(meterRegistry); + } + + private String makeMeterName(Meter meterName) { + return String.format("%s--", meterName.getMeterName()); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/model/ClientData.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/model/ClientData.java new file mode 100755 index 00000000..75a423ed --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/model/ClientData.java @@ -0,0 +1,3 @@ +package ru.demo.model; + +public record ClientData(String data) {} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/model/RequestForData.java b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/model/RequestForData.java new file mode 100644 index 00000000..68ba5ff2 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/java/ru/demo/model/RequestForData.java @@ -0,0 +1,3 @@ +package ru.demo.model; + +public record RequestForData(String name, String requestId) {} diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/resources/application.yml b/2024-11/spring-35-36-spring-cloud/service-client/src/main/resources/application.yml new file mode 100755 index 00000000..224652b7 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/resources/application.yml @@ -0,0 +1,27 @@ +spring: + application: + name: service-client + cloud: + config: + fail-fast: true + retry: + initial-interval: 5000 + max-attempts: 10 + max-interval: 5000 + multiplier: 1.2 + config: + import: optional:configserver:http://localhost:8888 + codec: + max-in-memory-size: 10MB + +eureka: + client: + fetchRegistry: true + serviceUrl: + defaultZone: http://localhost:9999/eureka/ + instance: + lease-renewal-interval-in-seconds: 30 + metadataMap: + zone: zone1 + + diff --git a/2024-11/spring-35-36-spring-cloud/service-client/src/main/resources/logback.xml b/2024-11/spring-35-36-spring-cloud/service-client/src/main/resources/logback.xml new file mode 100755 index 00000000..d20f5eca --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-client/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{requestId}] %msg%n + + + + + localhost:9092 + applLogs + + + [ignore] + + {"appname":"service-client"} + requestId + + + + + + + + diff --git a/2024-11/spring-35-36-spring-cloud/service-order/build.gradle.kts b/2024-11/spring-35-36-spring-cloud/service-order/build.gradle.kts new file mode 100755 index 00000000..f3da6edd --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + id("com.google.cloud.tools.jib") +} + +dependencies { + implementation(project(":kafka-log-appender")) + implementation("net.logstash.logback:logstash-logback-encoder") + + implementation ("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + + implementation("org.springframework.cloud:spring-cloud-starter-config") + implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client") + + implementation("io.micrometer:micrometer-tracing-bridge-otel") // bridges the Micrometer Observation API to OpenTelemetry. + implementation("io.opentelemetry:opentelemetry-exporter-zipkin") // reports traces to Zipkin. +} + +jib { + container { + creationTime.set("USE_CURRENT_TIMESTAMP") + } + from { + image = "bellsoft/liberica-openjdk-alpine-musl:21.0.1" + } + + to { + image = "localrun/service-order" + tags = setOf(project.version.toString()) + } +} + +tasks { + build { + dependsOn(spotlessApply) + dependsOn(jibBuildTar) + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-order/runServiceOrder.sh b/2024-11/spring-35-36-spring-cloud/service-order/runServiceOrder.sh new file mode 100755 index 00000000..b6f76415 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/runServiceOrder.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +../gradlew :service-order:build + +docker load --input build/jib-image.tar + +docker stop service-order-1 +docker stop service-order-2 + +docker run --rm -d --name service-order-1 \ +--memory=256m \ +--cpus 1 \ +--network="host" \ +-e JAVA_TOOL_OPTIONS="-XX:InitialRAMPercentage=80 -XX:MaxRAMPercentage=80" \ +-e SPRING_APPLICATION_INSTANCE_ID="i1" \ +-e SERVER_PORT=8091 \ +localrun/service-order:latest + +docker run --rm -d --name service-order-2 \ +--memory=256m \ +--cpus 1 \ +--network="host" \ +-e JAVA_TOOL_OPTIONS="-XX:InitialRAMPercentage=80 -XX:MaxRAMPercentage=80" \ +-e SPRING_APPLICATION_INSTANCE_ID="i2" \ +-e SERVER_PORT=8092 \ +localrun/service-order:latest + diff --git a/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/Config.java b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/Config.java new file mode 100755 index 00000000..ac7f5a38 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/Config.java @@ -0,0 +1,15 @@ +package ru.demo; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.demo.controller.InstanceId; + +@Configuration +public class Config { + + @Bean + InstanceId instanceId(@Value("${spring.application.instance_id}") String id) { + return new InstanceId(id); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/ServiceOrder.java b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/ServiceOrder.java new file mode 100755 index 00000000..106c40b3 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/ServiceOrder.java @@ -0,0 +1,11 @@ +package ru.demo; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ServiceOrder { + public static void main(String[] args) { + new SpringApplicationBuilder().sources(ServiceOrder.class).run(args); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/controller/InstanceId.java b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/controller/InstanceId.java new file mode 100755 index 00000000..73d8c740 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/controller/InstanceId.java @@ -0,0 +1,3 @@ +package ru.demo.controller; + +public record InstanceId(String name) {} diff --git a/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/controller/OrderController.java b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/controller/OrderController.java new file mode 100755 index 00000000..33307da2 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/src/main/java/ru/demo/controller/OrderController.java @@ -0,0 +1,28 @@ +package ru.demo.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class OrderController { + private static final Logger logger = LoggerFactory.getLogger(OrderController.class); + private final InstanceId instanceId; + + public OrderController(InstanceId instanceId) { + this.instanceId = instanceId; + } + + // curl -v http://localhost:8082/info?id="idClient" + + // curl -v http://localhost:8091/info?id="idClient" + // curl -v http://localhost:8092/info?id="idClient" + + @GetMapping(value = "/info") + public String info(@RequestParam(name = "id") String id) { + logger.info("instanceId:{}, request. id:{}", instanceId.name(), id); + return String.format("%s, Order id:%s", instanceId.name(), id); + } +} diff --git a/2024-11/spring-35-36-spring-cloud/service-order/src/main/resources/application.yml b/2024-11/spring-35-36-spring-cloud/service-order/src/main/resources/application.yml new file mode 100755 index 00000000..f67f599c --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/src/main/resources/application.yml @@ -0,0 +1,28 @@ +spring: + application: + name: service-order + instance_id: i0 + cloud: + config: + fail-fast: true + retry: + initial-interval: 5000 + max-attempts: 10 + max-interval: 5000 + multiplier: 1.2 + config: + import: optional:configserver:http://localhost:8888 + codec: + max-in-memory-size: 10MB + +eureka: + client: + fetchRegistry: false + serviceUrl: + defaultZone: http://localhost:9999/eureka/ + instance: + instance-id: service-order:${spring.application.instance_id:0} + lease-renewal-interval-in-seconds: 30 + metadataMap: + zone: zone1 + diff --git a/2024-11/spring-35-36-spring-cloud/service-order/src/main/resources/logback.xml b/2024-11/spring-35-36-spring-cloud/service-order/src/main/resources/logback.xml new file mode 100755 index 00000000..c74d4279 --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/service-order/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{requestId}] %msg%n + + + + + localhost:9092 + applLogs + + + [ignore] + + {"appname":"service-order"} + requestId + + + + + + + + diff --git a/2024-11/spring-35-36-spring-cloud/settings.gradle.kts b/2024-11/spring-35-36-spring-cloud/settings.gradle.kts new file mode 100755 index 00000000..4934408f --- /dev/null +++ b/2024-11/spring-35-36-spring-cloud/settings.gradle.kts @@ -0,0 +1,27 @@ +pluginManagement { + val jgitver: String by settings + val dependencyManagement: String by settings + val springframeworkBoot: String by settings + val sonarlint: String by settings + val spotless: String by settings + val jib: String by settings + + plugins { + id("fr.brouillard.oss.gradle.jgitver") version jgitver + id("io.spring.dependency-management") version dependencyManagement + id("org.springframework.boot") version springframeworkBoot + id("name.remal.sonarlint") version sonarlint + id("com.diffplug.spotless") version spotless + id("com.google.cloud.tools.jib") version jib + } +} +rootProject.name = "spring-cloud" + +include("api-gateway") +include("config-server") +include("eureka-server") +include("service-client") +include("service-client-info") +include("service-order") + +include("kafka-log-appender")