diff --git a/.gitignore b/.gitignore index 9a4af6d92a..99e245988a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,7 @@ app/client/yalc.lock .idea .fleet/* app/client/.fleet/* + +# Observability related local storage +utils/observability/tempo-data/* + diff --git a/app/server/appsmith-interfaces/pom.xml b/app/server/appsmith-interfaces/pom.xml index 23a28b9a1f..24b91ad3e5 100644 --- a/app/server/appsmith-interfaces/pom.xml +++ b/app/server/appsmith-interfaces/pom.xml @@ -236,6 +236,10 @@ spring-test test + + io.projectreactor + reactor-core-micrometer + diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java new file mode 100644 index 0000000000..3badfd9a93 --- /dev/null +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/spans/ActionSpans.java @@ -0,0 +1,17 @@ +package com.appsmith.external.constants.spans; + +public final class ActionSpans { + + // Action execution spans + public static final String ACTION_EXECUTION_REQUEST_PARSING = "request.parsing"; + public static final String ACTION_EXECUTION_CACHED_ACTION = "get.action.cached"; + public static final String ACTION_EXECUTION_CACHED_DATASOURCE = "get.datasource.cached"; + public static final String ACTION_EXECUTION_CACHED_PLUGIN = "get.plugin.cached"; + public static final String ACTION_EXECUTION_DATASOURCE_CONTEXT = "get.datasource.context"; + public static final String ACTION_EXECUTION_DATASOURCE_CONTEXT_REMOTE = "get.datasource.context.remote"; + public static final String ACTION_EXECUTION_EDITOR_CONFIG = "get.editorConfig.cached"; + public static final String ACTION_EXECUTION_VALIDATE_AUTHENTICATION = "validate.authentication"; + public static final String ACTION_EXECUTION_PLUGIN_EXECUTION = "total.plugin.execution"; + public static final String ACTION_EXECUTION_SERVER_EXECUTION = "total.server.execution"; + +} diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java index cde55ffa22..6fe9640901 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java @@ -12,8 +12,10 @@ import com.appsmith.external.models.Param; import com.appsmith.external.models.Property; import com.appsmith.external.models.TriggerRequestDTO; import com.appsmith.external.models.TriggerResultDTO; +import io.micrometer.observation.ObservationRegistry; import org.pf4j.ExtensionPoint; import org.springframework.util.CollectionUtils; +import reactor.core.observability.micrometer.Micrometer; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.util.function.Tuple2; @@ -24,6 +26,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import static com.appsmith.external.constants.spans.ActionSpans.ACTION_EXECUTION_PLUGIN_EXECUTION; import static com.appsmith.external.helpers.PluginUtils.getHintMessageForLocalhostUrl; public interface PluginExecutor extends ExtensionPoint, CrudTemplateService { @@ -171,6 +174,17 @@ public interface PluginExecutor extends ExtensionPoint, CrudTemplateService { return this.execute(connection, datasourceConfiguration, actionConfiguration); } + default Mono executeParameterizedWithMetrics(C connection, + ExecuteActionDTO executeActionDTO, + DatasourceConfiguration datasourceConfiguration, + ActionConfiguration actionConfiguration, + ObservationRegistry observationRegistry) { + return this.executeParameterized(connection, executeActionDTO, datasourceConfiguration, actionConfiguration) + .tag("plugin", this.getClass().getName()) + .name(ACTION_EXECUTION_PLUGIN_EXECUTION) + .tap(Micrometer.observation(observationRegistry)); + } + /** * This function is responsible for preparing the action and datasource configurations to be ready for execution. * diff --git a/app/server/appsmith-server/pom.xml b/app/server/appsmith-server/pom.xml index 70ac59b0af..3099d3a79d 100644 --- a/app/server/appsmith-server/pom.xml +++ b/app/server/appsmith-server/pom.xml @@ -203,10 +203,15 @@ 1.0.0 - io.micrometer - context-propagation - 1.0.0 + io.zipkin.reporter2 + zipkin-reporter-brave + + + + + + org.junit.jupiter diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/InstanceConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/InstanceConfig.java index 648dcfe486..63a0705d4b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/InstanceConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/InstanceConfig.java @@ -101,7 +101,7 @@ public class InstanceConfig implements ApplicationListener) - pluginExecutor.executeParameterized(resourceContext.getConnection(), + pluginExecutor.executeParameterizedWithMetrics(resourceContext.getConnection(), executeActionDTO, validatedDatasource.getDatasourceConfiguration(), - actionDTO.getActionConfiguration())) + actionDTO.getActionConfiguration(), + observationRegistry)) .map(actionExecutionResult -> { ActionExecutionRequest actionExecutionRequest = actionExecutionResult.getRequest(); if (actionExecutionRequest == null) { @@ -853,7 +869,10 @@ public class NewActionServiceCEImpl extends BaseService getValidatedDatasourceForActionExecution(Datasource datasource, String environmentId) { // the environmentName argument is not consumed over here // See EE override for usage of variable - return authenticationValidator.validateAuthentication(datasource, environmentId).cache(); + return authenticationValidator.validateAuthentication(datasource, environmentId) + .name(ACTION_EXECUTION_VALIDATE_AUTHENTICATION) + .tap(Micrometer.observation(observationRegistry)) + .cache(); } /** @@ -869,9 +888,15 @@ public class NewActionServiceCEImpl extends BaseService environmentMap) { if (plugin.isRemotePlugin()) { - return datasourceContextService.getRemoteDatasourceContext(plugin, validatedDatasource); + return datasourceContextService.getRemoteDatasourceContext(plugin, validatedDatasource) + .tag("plugin", plugin.getPackageName()) + .name(ACTION_EXECUTION_DATASOURCE_CONTEXT_REMOTE) + .tap(Micrometer.observation(observationRegistry)); } - return datasourceContextService.getDatasourceContext(validatedDatasource, datasourceContextIdentifier, environmentMap); + return datasourceContextService.getDatasourceContext(validatedDatasource, datasourceContextIdentifier, environmentMap) + .tag("plugin", plugin.getPackageName()) + .name(ACTION_EXECUTION_DATASOURCE_CONTEXT) + .tap(Micrometer.observation(observationRegistry)); } /** @@ -1188,7 +1213,9 @@ public class NewActionServiceCEImpl extends BaseService this.executeAction(executeActionDTO, environmentName)); + .flatMap(executeActionDTO -> this.executeAction(executeActionDTO, environmentName)) + .name(ACTION_EXECUTION_SERVER_EXECUTION) + .tap(Micrometer.observation(observationRegistry)); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PageLoadActionsUtilCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PageLoadActionsUtilCEImpl.java index 6bf45f4f70..f665fd9dae 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PageLoadActionsUtilCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PageLoadActionsUtilCEImpl.java @@ -788,7 +788,8 @@ public class PageLoadActionsUtilCEImpl implements PageLoadActionsUtilCE { Set bindingPaths = actionBindingMap.keySet(); - return Flux.fromIterable(bindingPaths).flatMap(bindingPath -> { + return Flux.fromIterable(bindingPaths) + .flatMap(bindingPath -> { EntityDependencyNode actionDependencyNode = new EntityDependencyNode(entityDependencyNode.getEntityReferenceType(), entityDependencyNode.getValidEntityName(), bindingPath, null, false, action); return getPossibleEntityReferences(actionNameToActionMapMono, actionBindingMap.get(bindingPath), evalVersion, bindingsInDsl) .flatMapMany(Flux::fromIterable) diff --git a/app/server/appsmith-server/src/main/resources/application.properties b/app/server/appsmith-server/src/main/resources/application.properties index fd55deb063..037dc04c82 100644 --- a/app/server/appsmith-server/src/main/resources/application.properties +++ b/app/server/appsmith-server/src/main/resources/application.properties @@ -50,7 +50,7 @@ segment.ce.key = ${APPSMITH_SEGMENT_CE_KEY:} # Sentry sentry.dsn=${APPSMITH_SENTRY_DSN:} sentry.send-default-pii=true -sentry.debug=off +sentry.debug=false sentry.environment=${APPSMITH_SENTRY_ENVIRONMENT:} # Redis Properties @@ -87,13 +87,12 @@ encrypt.password=${APPSMITH_ENCRYPTION_PASSWORD:} encrypt.salt=${APPSMITH_ENCRYPTION_SALT:} # The following configurations are to help support prometheus scraping for monitoring -management.endpoints.web.exposure.include=prometheus -management.metrics.web.server.request.autotime.enabled=true +management.endpoints.web.exposure.include=prometheus,metrics +management.tracing.enabled=${APPSMITH_TRACING_ENABLED:false} +management.zipkin.tracing.endpoint=${APPSMITH_TRACING_ENDPOINT:http://localhost:9411}/api/v2/spans +management.tracing.sampling.probability=${APPSMITH_SAMPLING_PROBABILITY:0.1} management.prometheus.metrics.export.descriptions=true -management.metrics.web.server.request.ignore-trailing-slash=true -management.metrics.web.server.request.autotime.percentiles=0.5, 0.9, 0.95, 0.99 -management.metrics.web.server.request.autotime.percentiles-histogram=true -management.metrics.distribution.sla.[http.server.requests]=1s +management.metrics.distribution.percentiles-histogram.http.server.requests=true # Support disabling signup with an environment variable signup.disabled = ${APPSMITH_SIGNUP_DISABLED:false} diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java index 4834df3b98..025748307d 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java @@ -144,15 +144,14 @@ public class ApplicationTemplateServiceTest { // make sure we've received the response returned by the mockCloudServices StepVerifier.create(applicationTemplateService.getRecentlyUsedTemplates()) - .assertNext(applicationTemplates -> assertThat(applicationTemplates.size()).isEqualTo(1)) + .assertNext(applicationTemplates -> assertThat(applicationTemplates).hasSize(1)) .verifyComplete(); // verify that mockCloudServices was called with the query param id i.e. id=id-one&id=id-two RecordedRequest recordedRequest = mockCloudServices.takeRequest(); + assert recordedRequest.getRequestUrl() != null; List queryParameterValues = recordedRequest.getRequestUrl().queryParameterValues("id"); - assertThat(queryParameterValues).contains("id-one"); - assertThat(queryParameterValues).contains("id-two"); - assertThat(queryParameterValues.size()).isEqualTo(2); + assertThat(queryParameterValues).containsExactly("id-one", "id-two"); } @Test diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java index 810ad3a920..5f00f48b81 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java @@ -839,7 +839,7 @@ public class ActionServiceCE_Test { AppsmithPluginException pluginException = new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR); Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); - Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); + Mockito.when(pluginExecutor.executeParameterizedWithMetrics(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); @@ -889,7 +889,7 @@ public class ActionServiceCE_Test { AppsmithPluginException pluginException = new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR); Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); - Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); + Mockito.when(pluginExecutor.executeParameterizedWithMetrics(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); @@ -932,7 +932,7 @@ public class ActionServiceCE_Test { executeActionDTO.setViewMode(false); Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); - Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + Mockito.when(pluginExecutor.executeParameterizedWithMetrics(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Mono.error(new StaleConnectionException())).thenReturn(Mono.error(new StaleConnectionException())); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); @@ -976,7 +976,7 @@ public class ActionServiceCE_Test { executeActionDTO.setViewMode(false); Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); - Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + Mockito.when(pluginExecutor.executeParameterizedWithMetrics(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenAnswer(x -> Mono.delay(Duration.ofMillis(1000)).ofType(ActionExecutionResult.class)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); @@ -1061,7 +1061,7 @@ public class ActionServiceCE_Test { mockResult.setBody("response-body"); Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); - Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + Mockito.when(pluginExecutor.executeParameterizedWithMetrics(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenThrow(new StaleConnectionException()) .thenReturn(Mono.just(mockResult)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); @@ -1114,7 +1114,7 @@ public class ActionServiceCE_Test { private Mono executeAction(ExecuteActionDTO executeActionDTO, ActionConfiguration actionConfiguration, ActionExecutionResult mockResult) { Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); - Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.just(mockResult)); + Mockito.when(pluginExecutor.executeParameterizedWithMetrics(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.just(mockResult)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO, null); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java index 007b12c58a..9d140a1545 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java @@ -165,7 +165,11 @@ public class NewActionServiceCEImplTest { datasourcePermission, applicationPermission, pagePermission, - actionPermission); + actionPermission, + observationRegistry); + + ObservationRegistry.ObservationConfig mockObservationConfig = Mockito.mock(ObservationRegistry.ObservationConfig.class); + Mockito.when(observationRegistry.observationConfig()).thenReturn(mockObservationConfig); } @BeforeEach @@ -349,8 +353,8 @@ public class NewActionServiceCEImplTest { StepVerifier .create(actionExecutionResultMono) .assertNext(response -> { - assertTrue(response.getIsExecutionSuccess()); assertTrue(response instanceof ActionExecutionResult); + assertTrue(response.getIsExecutionSuccess()); assertEquals(mockResult.getBody().toString(), response.getBody().toString()); }) .verifyComplete(); @@ -399,8 +403,8 @@ public class NewActionServiceCEImplTest { StepVerifier .create(actionExecutionResultMono) .assertNext(response -> { - assertTrue(response.getIsExecutionSuccess()); assertTrue(response instanceof ActionExecutionResult); + assertTrue(response.getIsExecutionSuccess()); assertEquals(mockResult.getBody().toString(), response.getBody().toString()); }) .verifyComplete(); diff --git a/utils/observability/docker-compose.yml b/utils/observability/docker-compose.yml new file mode 100644 index 0000000000..c1b99a9486 --- /dev/null +++ b/utils/observability/docker-compose.yml @@ -0,0 +1,54 @@ +networks: + default: + name: operations-dc + +services: + tempo: + image: grafana/tempo + extra_hosts: ['host.docker.internal:host-gateway'] + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./docker/tempo/tempo-local.yaml:/etc/tempo.yaml:ro + - ./tempo-data:/tmp/tempo + ports: + - "14268" # jaeger ingest + - "9411:9411" # zipkin + - "3200:3200" + + # loki: + # image: grafana/loki + # extra_hosts: ['host.docker.internal:host-gateway'] + # command: [ "-config.file=/etc/loki/local-config.yaml" ] + # ports: + # - "3100:3100" # loki needs to be exposed so it receives logs + # environment: + # - JAEGER_AGENT_HOST=tempo + # - JAEGER_ENDPOINT=http://tempo:14268/api/traces # send traces to Tempo + # - JAEGER_SAMPLER_TYPE=const + # - JAEGER_SAMPLER_PARAM=1 + + prometheus: + image: prom/prometheus + extra_hosts: ['host.docker.internal:host-gateway'] + command: + - --enable-feature=exemplar-storage + - --config.file=/etc/prometheus/prometheus.yml + volumes: + - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro + ports: + - "9090:9090" + + grafana: + image: grafana/grafana + extra_hosts: ['host.docker.internal:host-gateway'] + volumes: + - ./docker/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro + - ./docker/grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards:ro + environment: + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_DISABLE_LOGIN_FORM=true + ports: + - "3001:3000" +# Prometheus: http://localhost:9090/ +# Grafana: http://localhost:3000/ diff --git a/utils/observability/docker/grafana/provisioning/dashboards/dashboard.yml b/utils/observability/docker/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 0000000000..31d21dbf56 --- /dev/null +++ b/utils/observability/docker/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,10 @@ +apiVersion: 1 + +providers: + - name: dashboards + type: file + disableDeletion: true + editable: true + options: + path: /etc/grafana/provisioning/dashboards + foldersFromFilesStructure: true diff --git a/utils/observability/docker/grafana/provisioning/dashboards/logs_traces_metrics.json b/utils/observability/docker/grafana/provisioning/dashboards/logs_traces_metrics.json new file mode 100644 index 0000000000..21d6233f45 --- /dev/null +++ b/utils/observability/docker/grafana/provisioning/dashboards/logs_traces_metrics.json @@ -0,0 +1,294 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 6, + "iteration": 1654517000502, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "loki" + }, + "description": "", + "gridPos": { + "h": 10, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": true, + "showCommonLabels": true, + "showLabels": true, + "showTime": true, + "sortOrder": "Ascending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "loki" + }, + "editorMode": "builder", + "expr": "{traceID=\"$traceID\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "Logs with trace ID $traceID", + "type": "logs" + }, + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "description": "", + "gridPos": { + "h": 15, + "w": 23, + "x": 0, + "y": 10 + }, + "id": 6, + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "tempo" + }, + "query": "$traceID", + "queryType": "traceId", + "refId": "A" + } + ], + "title": "Trace View for trace with id $traceID", + "type": "traces" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": 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": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 23, + "x": 0, + "y": 25 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(1.00, sum(rate(http_server_requests_seconds_bucket{uri=~\".*\"}[$__rate_interval])) by (le))", + "legendFormat": "max", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{uri=~\".*\"}[$__rate_interval])) by (le))", + "hide": false, + "legendFormat": "tp99", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{uri=~\".*\"}[$__rate_interval])) by (le))", + "hide": false, + "legendFormat": "tp95", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(1.00, sum(rate(server_job_seconds_bucket[$__rate_interval])) by (le))", + "legendFormat": "max", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(server_job_seconds_bucket[$__rate_interval])) by (le))", + "hide": false, + "legendFormat": "tp99", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.95, sum(rate(server_job_seconds_bucket[$__rate_interval])) by (le))", + "hide": false, + "legendFormat": "tp95", + "range": true, + "refId": "F" + } + ], + "title": "latency for All", + "type": "timeseries" + } + ], + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "0003776c79e02b6c", + "value": "0003776c79e02b6c" + }, + "datasource": { + "type": "loki", + "uid": "loki" + }, + "definition": "label_values(traceID)", + "hide": 0, + "includeAll": false, + "label": "Trace ID", + "multi": false, + "name": "traceID", + "options": [], + "query": "label_values(traceID)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Logs, Traces, Metrics", + "uid": "szVLMe97z", + "version": 7, + "weekStart": "" +} diff --git a/utils/observability/docker/grafana/provisioning/datasources/datasource.yml b/utils/observability/docker/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 0000000000..6d95a8c9af --- /dev/null +++ b/utils/observability/docker/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,45 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://host.docker.internal:9090 + editable: false + jsonData: + httpMethod: POST + exemplarTraceIdDestinations: + - name: trace_id + datasourceUid: 'tempo' + - name: Tempo + type: tempo + access: proxy + orgId: 1 + url: http://tempo:3200 + basicAuth: false + isDefault: true + version: 1 + editable: false + apiVersion: 1 + uid: tempo + jsonData: + httpMethod: GET + tracesToLogs: + datasourceUid: 'loki' + - name: Loki + type: loki + uid: loki + access: proxy + orgId: 1 + url: http://loki:3100 + basicAuth: false + isDefault: false + version: 1 + editable: false + apiVersion: 1 + jsonData: + derivedFields: + - datasourceUid: 'tempo' + matcherRegex: \[.+,(.+?), + name: TraceID + url: $${__value.raw} diff --git a/utils/observability/docker/prometheus/prometheus.yml b/utils/observability/docker/prometheus/prometheus.yml new file mode 100644 index 0000000000..fa40d133a5 --- /dev/null +++ b/utils/observability/docker/prometheus/prometheus.yml @@ -0,0 +1,12 @@ +global: + scrape_interval: 2s + evaluation_interval: 2s + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['host.docker.internal:9090'] + - job_name: 'apps' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['host.docker.internal:8080','host.docker.internal:8989'] diff --git a/utils/observability/docker/tempo/tempo-local.yaml b/utils/observability/docker/tempo/tempo-local.yaml new file mode 100644 index 0000000000..1d23b51c89 --- /dev/null +++ b/utils/observability/docker/tempo/tempo-local.yaml @@ -0,0 +1,12 @@ +server: + http_listen_port: 3200 + +distributor: + receivers: + zipkin: + +storage: + trace: + backend: local + local: + path: /tmp/tempo/blocks