diff --git a/deploy/openshift/templates/che-monitoring.yaml b/deploy/openshift/templates/che-monitoring.yaml index 3ea3d166e7b..5a84960f93e 100644 --- a/deploy/openshift/templates/che-monitoring.yaml +++ b/deploy/openshift/templates/che-monitoring.yaml @@ -3964,6 +3964,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4049,6 +4050,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4134,6 +4136,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4193,13 +4196,107 @@ objects: "alignLevel": null } }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Che", + "description": "", + "fill": 1, + "gridPos": { + "h": 5, + "w": 11, + "x": 0, + "y": 6 + }, + "id": 36, + "interval": "", + "legend": { + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "paceLength": 10, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(che_workspace_failure_total{while=\"STARTING\"} [1h])) /(sum(increase(che_workspace_failure_total{while=\"STARTING\"} [1h])) + sum(increase(che_workspace_started_total [1h])))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "fail", + "refId": "A" + }, + { + "expr": "sum(increase(che_workspace_started_total [1h]))/(sum(increase(che_workspace_failure_total{while=\"STARTING\"} [1h])) + sum(increase(che_workspace_started_total [1h])))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "success", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Workspace start rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 6 + "y": 11 }, "id": 24, "panels": [], @@ -4217,7 +4314,7 @@ objects: "h": 7, "w": 11, "x": 0, - "y": 7 + "y": 12 }, "id": 16, "legend": { @@ -4233,6 +4330,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4298,7 +4396,7 @@ objects: "h": 1, "w": 24, "x": 0, - "y": 14 + "y": 19 }, "id": 22, "panels": [], @@ -4316,7 +4414,7 @@ objects: "h": 7, "w": 9, "x": 0, - "y": 15 + "y": 20 }, "id": 18, "legend": { @@ -4333,6 +4431,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null as zero", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4427,7 +4526,7 @@ objects: "h": 7, "w": 7, "x": 9, - "y": 15 + "y": 20 }, "id": 26, "legend": { @@ -4444,6 +4543,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null as zero", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4523,7 +4623,7 @@ objects: "h": 7, "w": 7, "x": 16, - "y": 15 + "y": 20 }, "id": 32, "legend": { @@ -4542,6 +4642,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4621,7 +4722,7 @@ objects: "h": 8, "w": 9, "x": 0, - "y": 22 + "y": 27 }, "id": 30, "legend": { @@ -4637,6 +4738,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null as zero", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4713,7 +4815,7 @@ objects: "h": 8, "w": 7, "x": 9, - "y": 22 + "y": 27 }, "id": 34, "legend": { @@ -4729,6 +4831,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null as zero", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4807,7 +4910,7 @@ objects: "h": 8, "w": 7, "x": 16, - "y": 22 + "y": 27 }, "id": 28, "legend": { @@ -4823,6 +4926,7 @@ objects: "linewidth": 1, "links": [], "nullPointMode": "null as zero", + "paceLength": 10, "percentage": false, "pointradius": 5, "points": false, @@ -4892,14 +4996,14 @@ objects: } ], "refresh": "5s", - "schemaVersion": 16, + "schemaVersion": 18, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-5m", + "from": "now-1h", "to": "now" }, "timepicker": { diff --git a/wsmaster/che-core-api-metrics/pom.xml b/wsmaster/che-core-api-metrics/pom.xml index 716e4c65445..b9960e18753 100644 --- a/wsmaster/che-core-api-metrics/pom.xml +++ b/wsmaster/che-core-api-metrics/pom.xml @@ -51,6 +51,11 @@ org.eclipse.che.core che-core-api-workspace-shared + + ch.qos.logback + logback-classic + test + org.eclipse.che.core che-core-api-dto diff --git a/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceStartAttemptsMeterBinder.java b/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceStartAttemptsMeterBinder.java new file mode 100644 index 00000000000..20995e91c10 --- /dev/null +++ b/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceStartAttemptsMeterBinder.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.api.metrics; + +import static org.eclipse.che.api.metrics.WorkspaceBinders.workspaceMetric; + +import com.google.inject.Inject; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.binder.MeterBinder; +import javax.inject.Singleton; +import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; + +/** Counts number of attempts to start workspace. */ +@Singleton +public class WorkspaceStartAttemptsMeterBinder implements MeterBinder { + private final EventService eventService; + + private Counter startingCounter; + + @Inject + public WorkspaceStartAttemptsMeterBinder(EventService eventService) { + this.eventService = eventService; + } + + @Override + public void bindTo(MeterRegistry registry) { + startingCounter = + Counter.builder(workspaceMetric("starting_attempts.total")) + .description("The count of workspaces start attempts") + .register(registry); + + // only subscribe to the event once we have the counters ready + eventService.subscribe( + event -> { + if (event.getPrevStatus() == WorkspaceStatus.STOPPED + && event.getStatus() == WorkspaceStatus.STARTING) { + startingCounter.increment(); + } + }, + WorkspaceStatusEvent.class); + } +} diff --git a/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceSuccessfulStartAttemptsMeterBinder.java b/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceSuccessfulStartAttemptsMeterBinder.java new file mode 100644 index 00000000000..6ca70db5ee2 --- /dev/null +++ b/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceSuccessfulStartAttemptsMeterBinder.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.api.metrics; + +import static org.eclipse.che.api.metrics.WorkspaceBinders.workspaceMetric; + +import com.google.inject.Inject; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.binder.MeterBinder; +import javax.inject.Singleton; +import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; + +/** Counts number of successfully started workspaces. */ +@Singleton +public class WorkspaceSuccessfulStartAttemptsMeterBinder implements MeterBinder { + private final EventService eventService; + + private Counter startedCounter; + + @Inject + public WorkspaceSuccessfulStartAttemptsMeterBinder(EventService eventService) { + this.eventService = eventService; + } + + @Override + public void bindTo(MeterRegistry registry) { + startedCounter = + Counter.builder(workspaceMetric("started.total")) + .description("The count of started workspaces") + .register(registry); + + // only subscribe to the event once we have the counters ready + eventService.subscribe( + event -> { + if (event.getPrevStatus() == WorkspaceStatus.STARTING + && event.getStatus() == WorkspaceStatus.RUNNING) { + startedCounter.increment(); + } + }, + WorkspaceStatusEvent.class); + } +} diff --git a/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WsMasterMetricsModule.java b/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WsMasterMetricsModule.java index 502fea2baea..3c5d2c7f832 100644 --- a/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WsMasterMetricsModule.java +++ b/wsmaster/che-core-api-metrics/src/main/java/org/eclipse/che/api/metrics/WsMasterMetricsModule.java @@ -28,5 +28,7 @@ protected void configure() { meterMultibinder.addBinding().to(WorkspaceActivityMeterBinder.class); meterMultibinder.addBinding().to(WorkspaceFailureMeterBinder.class); + meterMultibinder.addBinding().to(WorkspaceSuccessfulStartAttemptsMeterBinder.class); + meterMultibinder.addBinding().to(WorkspaceStartAttemptsMeterBinder.class); } } diff --git a/wsmaster/che-core-api-metrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceStartAttemptsMeterBinderTest.java b/wsmaster/che-core-api-metrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceStartAttemptsMeterBinderTest.java new file mode 100644 index 00000000000..5696aab4f97 --- /dev/null +++ b/wsmaster/che-core-api-metrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceStartAttemptsMeterBinderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.api.metrics; + +import static java.util.Arrays.asList; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.ArrayList; +import java.util.List; +import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; +import org.eclipse.che.dto.server.DtoFactory; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class WorkspaceStartAttemptsMeterBinderTest { + + private EventService eventService; + private MeterRegistry registry; + + @BeforeMethod + public void setUp() { + eventService = new EventService(); + registry = new SimpleMeterRegistry(); + } + + @Test(dataProvider = "allStatusTransitionsWithoutStarting") + public void shouldNotCollectEvents(WorkspaceStatus from, WorkspaceStatus to) { + // given + WorkspaceStartAttemptsMeterBinder meterBinder = + new WorkspaceStartAttemptsMeterBinder(eventService); + meterBinder.bindTo(registry); + + // when + + eventService.publish( + DtoFactory.newDto(WorkspaceStatusEvent.class) + .withPrevStatus(from) + .withStatus(to) + .withWorkspaceId("id1")); + + // then + Counter successful = registry.find("che.workspace.starting_attempts.total").counter(); + Assert.assertEquals(successful.count(), 0.0); + } + + @Test + public void shouldCollectOnlyStart() { + // given + WorkspaceStartAttemptsMeterBinder meterBinder = + new WorkspaceStartAttemptsMeterBinder(eventService); + meterBinder.bindTo(registry); + + // when + eventService.publish( + DtoFactory.newDto(WorkspaceStatusEvent.class) + .withPrevStatus(WorkspaceStatus.STOPPED) + .withStatus(WorkspaceStatus.STARTING) + .withWorkspaceId("id1")); + // then + Counter successful = registry.find("che.workspace.starting_attempts.total").counter(); + Assert.assertEquals(successful.count(), 1.0); + } + + @DataProvider + public Object[][] allStatusTransitionsWithoutStarting() { + List> transitions = new ArrayList<>(9); + + for (WorkspaceStatus from : WorkspaceStatus.values()) { + for (WorkspaceStatus to : WorkspaceStatus.values()) { + if (from == WorkspaceStatus.STOPPED && to == WorkspaceStatus.STARTING) { + continue; + } + + transitions.add(asList(from, to)); + } + } + + return transitions.stream().map(List::toArray).toArray(Object[][]::new); + } +} diff --git a/wsmaster/che-core-api-metrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceSuccessfulStartAttemptsMeterBinderTest.java b/wsmaster/che-core-api-metrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceSuccessfulStartAttemptsMeterBinderTest.java new file mode 100644 index 00000000000..8c55c045b6e --- /dev/null +++ b/wsmaster/che-core-api-metrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceSuccessfulStartAttemptsMeterBinderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.api.metrics; + +import static java.util.Arrays.asList; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.ArrayList; +import java.util.List; +import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; +import org.eclipse.che.dto.server.DtoFactory; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class WorkspaceSuccessfulStartAttemptsMeterBinderTest { + + private EventService eventService; + private MeterRegistry registry; + + @BeforeMethod + public void setUp() { + eventService = new EventService(); + registry = new SimpleMeterRegistry(); + } + + @Test(dataProvider = "allStatusTransitionsWithoutRunning") + public void shouldNotCollectEvents(WorkspaceStatus from, WorkspaceStatus to) { + // given + WorkspaceSuccessfulStartAttemptsMeterBinder meterBinder = + new WorkspaceSuccessfulStartAttemptsMeterBinder(eventService); + meterBinder.bindTo(registry); + + // when + + eventService.publish( + DtoFactory.newDto(WorkspaceStatusEvent.class) + .withPrevStatus(from) + .withStatus(to) + .withWorkspaceId("id1")); + + // then + Counter successful = registry.find("che.workspace.started.total").counter(); + Assert.assertEquals(successful.count(), 0.0); + } + + @Test + public void shouldCollectOnlyStarted() { + // given + WorkspaceSuccessfulStartAttemptsMeterBinder meterBinder = + new WorkspaceSuccessfulStartAttemptsMeterBinder(eventService); + meterBinder.bindTo(registry); + + // when + eventService.publish( + DtoFactory.newDto(WorkspaceStatusEvent.class) + .withPrevStatus(WorkspaceStatus.STARTING) + .withStatus(WorkspaceStatus.RUNNING) + .withWorkspaceId("id1")); + // then + Counter successful = registry.find("che.workspace.started.total").counter(); + Assert.assertEquals(successful.count(), 1.0); + } + + @DataProvider + public Object[][] allStatusTransitionsWithoutRunning() { + List> transitions = new ArrayList<>(9); + + for (WorkspaceStatus from : WorkspaceStatus.values()) { + for (WorkspaceStatus to : WorkspaceStatus.values()) { + if (from == WorkspaceStatus.STARTING && to == WorkspaceStatus.RUNNING) { + continue; + } + + transitions.add(asList(from, to)); + } + } + + return transitions.stream().map(List::toArray).toArray(Object[][]::new); + } +} diff --git a/wsmaster/che-core-api-metrics/src/test/resources/logback-test.xml b/wsmaster/che-core-api-metrics/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..e6abf05f024 --- /dev/null +++ b/wsmaster/che-core-api-metrics/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + + %-41(%date[%.15thread]) %-45([%-5level] [%.30logger{30} %L]) - %msg%n + + + + + + + +