From 92d92282e52f1f9e8226cc3f871d6ba24332a637 Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:03:42 +0300 Subject: [PATCH 01/10] Configure Prometheus metrics and histograms --- src/main/resources/application.properties | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 570ca71..32aa3d9 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,6 +6,7 @@ spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect spring.liquibase.change-log=classpath:db/changelog/changelog-master.xml api.version=api/v1 + # Azure blob storage configuration spring.cloud.azure.storage.account-name=${AZURE_STORAGE_ACCOUNT_NAME} spring.cloud.azure.storage.connection-string=${AZURE_STORAGE_CONNECTION_STRING} @@ -14,14 +15,16 @@ spring.servlet.multipart.enabled=true spring.servlet.multipart.max-file-size=4MB spring.servlet.multipart.max-request-size=40MB spring.cloud.azure.profile.environment.storage-endpoint-suffix=core.windows.net + +# Management endpoints configuration management.server.port=8080 management.endpoints.web.exposure.include=health,info,metrics,prometheus management.endpoint.metrics.enabled=true management.endpoint.prometheus.enabled=true -management.prometheus.metrics.export.enabled=true -# JVM metrics -management.metrics.enable.jvm=true -management.metrics.enable.system=true -management.metrics.enable.web=true management.endpoint.health.probes.enabled=true -management.endpoint.health.show-details=always \ No newline at end of file +management.endpoint.health.show-details=always + +# Micrometer metrics configuration +management.metrics.distribution.percentiles-histogram.http.server.requests=true +# Custom SLO boundaries for http.server.requests histogram in milliseconds +management.metrics.distribution.slo.http.server.requests=50ms,100ms,200ms,500ms,1s,2s From 78174e14fb9edf726ece7f0f9ff6b0c804bf532d Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:05:08 +0300 Subject: [PATCH 02/10] Add custom Grafana dashboard for application metrics --- k8s/logging/grafana/custom-app-dashboard.yaml | 252 ++++++++++++++++++ k8s/logging/grafana/dashboard.yaml | 105 +++++++- 2 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 k8s/logging/grafana/custom-app-dashboard.yaml diff --git a/k8s/logging/grafana/custom-app-dashboard.yaml b/k8s/logging/grafana/custom-app-dashboard.yaml new file mode 100644 index 0000000..cd5a699 --- /dev/null +++ b/k8s/logging/grafana/custom-app-dashboard.yaml @@ -0,0 +1,252 @@ +# k8s/logging/grafana/custom-app-dashboard.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-custom-app-dashboard + namespace: logging + labels: + grafana_dashboard: "1" +data: + custom-app-dashboard.json: | + { + "id": null, + "title": "Custom Application Metrics", + "tags": [ + "spring-boot", + "app-metrics", + "camera-onboarding" + ], + "style": "dark", + "timezone": "browser", + "panels": [ + { + "id": 1, + "title": "Request Latency Percentiles", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", + "legendFormat": "P95 Latency", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", + "legendFormat": "P90 Latency", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", + "legendFormat": "P50 Latency", + "refId": "C" + } + ], + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 0}, + "fieldConfig": { + "defaults": { + "unit": "s", + "min": 0, + "color": { + "mode": "palette-classic"} + } + }, + "options": { + "legend": { + "displayMode": "table", + "values": [ + "last", + "max"]} + } + }, + { + "id": 2, + "title": "Request Rate (req/sec)", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[1m]))", + "legendFormat": "Total RPS", + "refId": "A" + } + ], + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 8}, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "color": { + "mode": "palette-classic" + } + } + } + }, + { + "id": 3, + "title": "Successful Requests (5m)", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m]))", + "legendFormat": "Successful" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 14}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds"}, + "thresholds": { + "steps": [ + { + "color": "green", + "value": null} + ] + } + } + } + }, + { + "id": 4, + "title": "Failed Requests (5m)", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"[45]..\"}[5m]))", + "legendFormat": "Failed" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 14}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds"}, + "thresholds": { + "steps": [ + { + "color": "green", + "value": null}, + { + "color": "red", + "value": 1} + ] + } + } + } + }, + { + "id": 5, + "title": "Success Rate (%)", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "(sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m])) / sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[5m]))) * 100", + "legendFormat": "Success Rate" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 14}, + "fieldConfig": { + "defaults": { + "unit": "percent", + "color": { + "mode": "thresholds"}, + "thresholds": { + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 90}, + { + "color": "green", + "value": 95} + ] + } + } + } + }, + { + "id": 6, + "title": "Request Status Distribution", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "sum by (status) (rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[1m]))", + "legendFormat": "{{status}}" + } + ], + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 20}, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "color": { + "mode": "palette-classic"} + } + } + }, + { + "id": 7, + "title": "DEBUG PANEL: Available HTTP Metrics", + "type": "table", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "group by (__name__) ({__name__=~\".*http.*request.*\", kubernetes_namespace=\"app\"})", + "format": "table" + } + ], + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 20} + } + ], + "time": { + "from": "now-15m", + "to": "now"}, + "refresh": "5s" + } \ No newline at end of file diff --git a/k8s/logging/grafana/dashboard.yaml b/k8s/logging/grafana/dashboard.yaml index e190422..06c70c0 100644 --- a/k8s/logging/grafana/dashboard.yaml +++ b/k8s/logging/grafana/dashboard.yaml @@ -168,4 +168,107 @@ data: "from": "now-1h", "to": "now"}, "refresh": "5s" - } \ No newline at end of file + } + custom-app-dashboard.json: | + { + "id": null, + "title": "Custom Application Metrics", + "tags": [ + "spring-boot", + "app-metrics"], + "style": "dark", + "timezone": "browser", + "panels": [ + { + "id": 1, + "title": "Request Latency (P95)", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum by (le) (rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])))", + "legendFormat": "P95 (http_server_requests)", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket{kubernetes_namespace=\"app\"}[2m])))", + "legendFormat": "P95 (http_request_duration)", + "refId": "B" + } + ], + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 0}, + "fieldConfig": { + "defaults": { + "unit": "s", + "min": 0}} + }, + { + "id": 2, + "title": "Successful Requests (5m)", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m]))", + "legendFormat": "Successful" + } + ], + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 8} + }, + { + "id": 3, + "title": "Failed Requests (5m)", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"[45]..\"}[5m]))", + "legendFormat": "Failed" + } + ], + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 8} + }, + { + "id": 4, + "title": "DEBUG PANEL: Available HTTP Metrics", + "type": "table", + "datasource": { + "type": "prometheus", + "uid": "Prometheus"}, + "targets": [ + { + "expr": "group by (__name__) ({__name__=~\".*http.*request.*\", kubernetes_namespace=\"app\"})", + "format": "table" + } + ], + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 14} + } + ], + "time": { + "from": "now-15m", + "to": "now"}, + "refresh": "5s" + } From a18f7d4833b683e9668f118741543777e724f0d2 Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:05:21 +0300 Subject: [PATCH 03/10] Update image and adjust probes for metrics --- k8s/app/deployment.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/k8s/app/deployment.yaml b/k8s/app/deployment.yaml index 8d51ed2..99a8586 100644 --- a/k8s/app/deployment.yaml +++ b/k8s/app/deployment.yaml @@ -18,7 +18,7 @@ spec: spec: containers: - name: camera-onboarding - image: mrsert/camera-onboarding:monitoring + image: mrsert/camera-onboarding:metrics-v2 resources: requests: cpu: "275m" @@ -31,11 +31,12 @@ spec: envFrom: - secretRef: name: camera-onboarding-secrets + livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 - initialDelaySeconds: 30 + initialDelaySeconds: 180 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 @@ -43,7 +44,7 @@ spec: httpGet: path: /actuator/health/readiness port: 8080 - initialDelaySeconds: 15 + initialDelaySeconds: 180 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 From 55f506d7756cc931d52fae91a85ff8a19ccbbecb Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:24:50 +0300 Subject: [PATCH 04/10] Add MetricsService for application monitoring --- pom.xml | 6 + .../service/MetricsService.java | 96 ++++++++++ .../service/impl/MetricsServiceImpl.java | 177 ++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java diff --git a/pom.xml b/pom.xml index 51566b9..15a0663 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,12 @@ compile + + javax.annotation + javax.annotation-api + 1.3.2 + + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java new file mode 100644 index 0000000..1d409da --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java @@ -0,0 +1,96 @@ +package com.onboarding.camera.cameraonboarding.service; + +public interface MetricsService { + + /** + * Increments the counter for successful camera onboarding. + */ + void incrementCameraOnboardingSuccess(); + + /** + * Increments the counter for failed camera onboarding. + */ + void incrementCameraOnboardingFailure(); + + /** + * Increments the counter for successful camera initialization. + */ + void incrementCameraInitializationSuccess(); + + /** + * Increments the counter for failed camera initialization. + */ + void incrementCameraInitializationFailure(); + + /** + * Increments the counter for successful image uploads. + */ + void incrementImageUploadSuccess(); + + /** + * Increments the counter for failed image uploads. + */ + void incrementImageUploadFailure(); + + /** + * Increments the counter for successful image downloads. + */ + void incrementImageDownloadSuccess(); + + /** + * Increments the counter for failed image downloads. + */ + void incrementImageDownloadFailure(); + + /** + * Increments the counter for successfully adding a location. + */ + void incrementLocationAddSuccess(); + + /** + * Increments the counter for failed attempts at adding a location. + */ + void incrementLocationAddFailure(); + + /** + * Increments the counter for successful sensor creation. + * + * @param sensorType the type of the sensor + */ + void incrementSensorCreateSuccess(String sensorType); + + /** + * Increments the counter for failed sensor creation. + * + * @param sensorType the type of the sensor + */ + void incrementSensorCreateFailure(String sensorType); + + /** + * Increments the counter for successful sensor updates. + * + * @param sensorType the type of the sensor + */ + void incrementSensorUpdateSuccess(String sensorType); + + /** + * Increments the counter for failed sensor updates. + * + * @param sensorType the type of the sensor + */ + void incrementSensorUpdateFailure(String sensorType); + + /** + * Increments the counter for successful sensor deletions. + * + * @param sensorType the type of the sensor + */ + void incrementSensorDeleteSuccess(String sensorType); + + /** + * Increments the counter for failed sensor deletions. + * + * @param sensorType the type of the sensor + */ + void incrementSensorDeleteFailure(String sensorType); +} diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java new file mode 100644 index 0000000..1d74881 --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java @@ -0,0 +1,177 @@ +package com.onboarding.camera.cameraonboarding.service.impl; + +import com.onboarding.camera.cameraonboarding.service.MetricsService; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class MetricsServiceImpl implements MetricsService { + + private final MeterRegistry meterRegistry; + + private Counter cameraOnboardingSuccessCounter; + private Counter cameraOnboardingFailureCounter; + + private Counter cameraInitializationSuccessCounter; + private Counter cameraInitializationFailureCounter; + + private Counter imageUploadSuccessCounter; + private Counter imageUploadFailureCounter; + + private Counter imageDownloadSuccessCounter; + private Counter imageDownloadFailureCounter; + + private Counter locationAddSuccessCounter; + private Counter locationAddFailureCounter; + + private final Map sensorCreateSuccessCounters = new HashMap<>(); + private final Map sensorCreateFailureCounters = new HashMap<>(); + private final Map sensorUpdateSuccessCounters = new HashMap<>(); + private final Map sensorUpdateFailureCounters = new HashMap<>(); + private final Map sensorDeleteSuccessCounters = new HashMap<>(); + private final Map sensorDeleteFailureCounters = new HashMap<>(); + + + @PostConstruct + public void init() { + cameraOnboardingSuccessCounter = Counter.builder("camera.onboarding.success") + .description("Number of successful camera onboarding operations") + .register(meterRegistry); + cameraOnboardingFailureCounter = Counter.builder("camera.onboarding.failure") + .description("Number of failed camera onboarding operations") + .register(meterRegistry); + + cameraInitializationSuccessCounter = Counter.builder("camera.initialization.success") + .description("Number of successful camera initialization operations") + .register(meterRegistry); + cameraInitializationFailureCounter = Counter.builder("camera.initialization.failure") + .description("Number of failed camera initialization operations") + .register(meterRegistry); + + imageUploadSuccessCounter = Counter.builder("image.upload.success") + .description("Number of successful image upload operations") + .register(meterRegistry); + imageUploadFailureCounter = Counter.builder("image.upload.failure") + .description("Number of failed image upload operations") + .register(meterRegistry); + + imageDownloadSuccessCounter = Counter.builder("image.download.success") + .description("Number of successful image download operations") + .register(meterRegistry); + imageDownloadFailureCounter = Counter.builder("image.download.failure") + .description("Number of failed image download operations") + .register(meterRegistry); + + locationAddSuccessCounter = Counter.builder("location.add.success") + .description("Number of successful location add operations") + .register(meterRegistry); + locationAddFailureCounter = Counter.builder("location.add.failure") + .description("Number of failed location add operations") + .register(meterRegistry); + } + + @Override + public void incrementCameraOnboardingSuccess() { + cameraOnboardingSuccessCounter.increment(); + } + + @Override + public void incrementCameraOnboardingFailure() { + cameraOnboardingFailureCounter.increment(); + } + + @Override + public void incrementCameraInitializationSuccess() { + cameraInitializationSuccessCounter.increment(); + } + + @Override + public void incrementCameraInitializationFailure() { + cameraInitializationFailureCounter.increment(); + } + + @Override + public void incrementImageUploadSuccess() { + imageUploadSuccessCounter.increment(); + } + + @Override + public void incrementImageUploadFailure() { + imageUploadFailureCounter.increment(); + } + + @Override + public void incrementImageDownloadSuccess() { + imageDownloadSuccessCounter.increment(); + } + + @Override + public void incrementImageDownloadFailure() { + imageDownloadFailureCounter.increment(); + } + + @Override + public void incrementLocationAddSuccess() { + locationAddSuccessCounter.increment(); + } + + @Override + public void incrementLocationAddFailure() { + locationAddFailureCounter.increment(); + } + + @Override + public void incrementSensorCreateSuccess(String sensorType) { + sensorCreateSuccessCounters.computeIfAbsent(sensorType, k -> Counter.builder("sensor.create.success") + .tag("sensor.type", k) + .description("Number of successful sensor creation operations") + .register(meterRegistry)).increment(); + } + + @Override + public void incrementSensorCreateFailure(String sensorType) { + sensorCreateFailureCounters.computeIfAbsent(sensorType, k -> Counter.builder("sensor.create.failure") + .tag("sensor.type", k) + .description("Number of failed sensor creation operations") + .register(meterRegistry)).increment(); + } + + @Override + public void incrementSensorUpdateSuccess(String sensorType) { + sensorUpdateSuccessCounters.computeIfAbsent(sensorType, k -> Counter.builder("sensor.update.success") + .tag("sensor.type", k) + .description("Number of successful sensor update operations") + .register(meterRegistry)).increment(); + } + + @Override + public void incrementSensorUpdateFailure(String sensorType) { + sensorUpdateFailureCounters.computeIfAbsent(sensorType, k -> Counter.builder("sensor.update.failure") + .tag("sensor.type", k) + .description("Number of failed sensor update operations") + .register(meterRegistry)).increment(); + } + + @Override + public void incrementSensorDeleteSuccess(String sensorType) { + sensorDeleteSuccessCounters.computeIfAbsent(sensorType, k -> Counter.builder("sensor.delete.success") + .tag("sensor.type", k) + .description("Number of successful sensor deletion operations") + .register(meterRegistry)).increment(); + } + + @Override + public void incrementSensorDeleteFailure(String sensorType) { + sensorDeleteFailureCounters.computeIfAbsent(sensorType, k -> Counter.builder("sensor.delete.failure") + .tag("sensor.type", k) + .description("Number of failed sensor deletion operations") + .register(meterRegistry)).increment(); + } +} From f6f12e145884955692171d1ef774deff4fcad6ea Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:26:05 +0300 Subject: [PATCH 05/10] Integrate metrics collection into application services --- .../service/impl/CameraServiceImpl.java | 21 +++++++++++++++++++ .../service/impl/LightSensorService.java | 18 ++++++++++++++++ .../service/impl/MotionSensorService.java | 18 ++++++++++++++++ .../impl/TemperatureSensorService.java | 18 ++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java index efce22b..f0e7101 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java @@ -15,7 +15,9 @@ import com.onboarding.camera.cameraonboarding.repository.CameraRepository; import com.onboarding.camera.cameraonboarding.service.BlobStorageService; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.util.DateTimeFactory; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -36,7 +38,10 @@ public class CameraServiceImpl implements CameraService { private final BlobStorageService blobStorageService; + private final MetricsService metricsService; + @Override + @Timed("camera.onboarding") public Camera handleSaveCamera(Camera camera) { try { @@ -44,14 +49,17 @@ public Camera handleSaveCamera(Camera camera) { camera.setOnboardedAt(dateTimeFactory.now()); Camera savedCamera = cameraRepository.save(camera); log.info("Camera saved with ID: {}", savedCamera.getCamId()); + metricsService.incrementCameraOnboardingSuccess(); return savedCamera; } catch (Exception ex) { log.error("Exception occurred while saving camera: {}", ex.getMessage()); + metricsService.incrementCameraOnboardingFailure(); throw new CameraNotCreatedException(String.format("Error occurred while saving camera: %s", ex.getMessage())); } } @Override + @Timed("camera.initialization") public void handleInitializeCamera(UUID cameraId) { Camera camera = getCameraById(cameraId); if (camera.getInitializedAt() != null && !camera.getInitializedAt().toString().isBlank()) { @@ -62,8 +70,10 @@ public void handleInitializeCamera(UUID cameraId) { camera.setInitializedAt(dateTimeFactory.now()); cameraRepository.save(camera); log.info("Camera initialized with ID: {}", cameraId); + metricsService.incrementCameraInitializationSuccess(); } catch (Exception ex) { log.error("Exception occurred while initializing camera with ID: {}", cameraId, ex); + metricsService.incrementCameraInitializationFailure(); throw new CameraNotInitializedException(String.format("Error occurred while initializing camera: %s", ex.getMessage())); } } @@ -75,6 +85,7 @@ public Camera getCameraById(UUID cameraId) { } @Override + @Timed("image.upload") public void handleUploadImage(UUID cameraId, UUID imageId, byte[] imageData) { Camera camera = getCameraById(cameraId); validateCameraImage(camera); @@ -88,16 +99,20 @@ public void handleUploadImage(UUID cameraId, UUID imageId, byte[] imageData) { log.info("Uploading image with ID: {}", imageId); blobStorageService.uploadFile(blobStorageService.getContainerName(), imageId.toString(), imageData); cameraRepository.save(camera); + metricsService.incrementImageUploadSuccess(); } catch (ImageAlreadyUploadedException ex) { log.error("Exception occurred while uploading image"); + metricsService.incrementImageUploadFailure(); throw new ImageAlreadyUploadedException(String.format("Camera already have image with id: %s", camera.getImageId())); } catch (Exception ex) { log.error("Exception occurred while uploading image:{}:ex:{}", imageId, ex.getMessage()); + metricsService.incrementImageUploadFailure(); throw new ImageNotUploadedException(String.format("Error occurred while uploading image: %s", ex.getMessage())); } } @Override + @Timed("image.download") public byte[] handleDownloadImage(UUID cameraId) { Camera camera = getCameraById(cameraId); validateCameraImage(camera); @@ -111,18 +126,22 @@ public byte[] handleDownloadImage(UUID cameraId) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); blobStorageService.getBlob(outputStream, blobStorageService.getContainerName(), camera.getImageId().toString()); + metricsService.incrementImageDownloadSuccess(); return outputStream.toByteArray(); } catch (ImageNotFoundException ex) { log.error("Image is not found by given cameraId: '{}'", cameraId); + metricsService.incrementImageDownloadFailure(); throw new ImageNotFoundException(String.format("Image is not found by given cameraId: %s", cameraId)); } catch (Exception ex) { log.error("Exception occurred while downloading image, camera:{}:ex:{}", cameraId, ex.getMessage()); + metricsService.incrementImageDownloadFailure(); throw new ImageNotDownloadedException(String.format("Error occurred while downloading image: %s", ex.getMessage())); } } @Override @Transactional + @Timed("location.add") public Camera handleAddLocation(UUID cameraId, LocationDto locationDto) { Camera camera = getCameraById(cameraId); @@ -137,9 +156,11 @@ public Camera handleAddLocation(UUID cameraId, LocationDto locationDto) { cameraRepository.save(camera); log.info("Location added/updated successfully for Camera ID: {}", cameraId); + metricsService.incrementLocationAddSuccess(); return camera; } catch (Exception ex) { log.error("Exception occurred while adding location, camera:{}:ex:{}", cameraId, ex.getMessage()); + metricsService.incrementLocationAddFailure(); throw new LocationNotAddedException(String.format("Error occurred while adding location: %s", ex.getMessage())); } } diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java index 3926a2d..cbe61d9 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java @@ -9,7 +9,9 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.LightSensorRepository; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.service.SensorService; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -27,19 +29,25 @@ public class LightSensorService implements SensorService { private final CameraService cameraService; + private final MetricsService metricsService; + @Override @Transactional + @Timed("sensor.create") public LightSensor handleCreateSensor(UUID cameraId, LightSensor sensor) { try { sensor.setCamera(cameraService.getCameraById(cameraId)); LightSensor createdSensor = lightSensorRepository.save(sensor); log.info("Creating sensor: {}", createdSensor); + metricsService.incrementSensorCreateSuccess(SensorType.LIGHT.name()); return createdSensor; } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorCreateFailure(SensorType.LIGHT.name()); throw ex; } catch (Exception ex) { log.error("Failed to create sensor, camera:{}:ex:{}", cameraId, ex.getMessage()); + metricsService.incrementSensorCreateFailure(SensorType.LIGHT.name()); throw new SensorNotCreatedException(String.format("Failed to create sensor: %s", ex.getMessage())); } } @@ -61,6 +69,7 @@ public List handleGetSensorsByCameraId(UUID cameraId) { @Override @Transactional + @Timed("sensor.update") public LightSensor handleUpdateSensor(UUID cameraId, UUID sensorId, LightSensor sensor) { try { Camera camera = cameraService.getCameraById(cameraId); @@ -74,15 +83,19 @@ public LightSensor handleUpdateSensor(UUID cameraId, UUID sensorId, LightSensor existingSensor.setVersion(sensor.getVersion()); existingSensor.setData(sensor.getData()); log.info("Updating sensor: {}", existingSensor); + metricsService.incrementSensorUpdateSuccess(SensorType.LIGHT.name()); return lightSensorRepository.save(existingSensor); } catch (SensorNotFoundException ex) { log.error("Sensor not found while updating, sensorId: {}", sensorId); + metricsService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); throw ex; } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while updating sensor, sensorId:{}", sensorId); + metricsService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); throw new SensorNotUpdatedException(String.format("Error occurred while updating sensors: %s", ex.getMessage())); } } @@ -95,6 +108,7 @@ public LightSensor getSensorById(UUID sensorId) { } @Override + @Timed("sensor.delete") public void handleDeleteSensor(UUID cameraId, UUID sensorId) { try { Camera camera = cameraService.getCameraById(cameraId); @@ -107,14 +121,18 @@ public void handleDeleteSensor(UUID cameraId, UUID sensorId) { camera.getSensors().remove(sensor); lightSensorRepository.delete(sensor); log.info("Deleted sensor: {}", sensorId); + metricsService.incrementSensorDeleteSuccess(SensorType.LIGHT.name()); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while deleting, sensorId: {}", sensorId); + metricsService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while deleting sensor, sensorId:{}", sensorId); + metricsService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); throw new SensorNotUpdatedException(String.format("Error occurred while deleting sensor: %s", ex.getMessage())); } } diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java index c7be36f..75aba4b 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java @@ -9,7 +9,9 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.MotionSensorRepository; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.service.SensorService; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -27,19 +29,25 @@ public class MotionSensorService implements SensorService { private final CameraService cameraService; + private final MetricsService metricsService; + @Override @Transactional + @Timed("sensor.create") public MotionSensor handleCreateSensor(UUID cameraId, MotionSensor sensor) { try { sensor.setCamera(cameraService.getCameraById(cameraId)); MotionSensor createdSensor = motionSensorRepository.save(sensor); log.info("Creating sensor: {}", createdSensor); + metricsService.incrementSensorCreateSuccess(SensorType.MOTION.name()); return createdSensor; } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorCreateFailure(SensorType.MOTION.name()); throw ex; } catch (Exception ex) { log.error("Failed to create sensor, camera:{}:ex:{}", cameraId, ex.getMessage()); + metricsService.incrementSensorCreateFailure(SensorType.MOTION.name()); throw new SensorNotCreatedException(String.format("Failed to create sensor: %s", ex.getMessage())); } } @@ -61,6 +69,7 @@ public List handleGetSensorsByCameraId(UUID cameraId) { @Override @Transactional + @Timed("sensor.update") public MotionSensor handleUpdateSensor(UUID cameraId, UUID sensorId, MotionSensor sensor) { try { Camera camera = cameraService.getCameraById(cameraId); @@ -74,15 +83,19 @@ public MotionSensor handleUpdateSensor(UUID cameraId, UUID sensorId, MotionSenso existingSensor.setVersion(sensor.getVersion()); existingSensor.setData(sensor.getData()); log.info("Updating sensor: {}", existingSensor); + metricsService.incrementSensorUpdateSuccess(SensorType.MOTION.name()); return motionSensorRepository.save(existingSensor); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorUpdateFailure(SensorType.MOTION.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while updating, sensorId: {}", sensorId); + metricsService.incrementSensorUpdateFailure(SensorType.MOTION.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while updating sensor, sensorId:{}", sensorId); + metricsService.incrementSensorUpdateFailure(SensorType.MOTION.name()); throw new SensorNotUpdatedException(String.format("Error occurred while updating sensors: %s", ex.getMessage())); } } @@ -95,6 +108,7 @@ public MotionSensor getSensorById(UUID sensorId) { } @Override + @Timed("sensor.delete") public void handleDeleteSensor(UUID cameraId, UUID sensorId) { try { Camera camera = cameraService.getCameraById(cameraId); @@ -107,14 +121,18 @@ public void handleDeleteSensor(UUID cameraId, UUID sensorId) { camera.getSensors().remove(sensor); motionSensorRepository.delete(sensor); log.info("Deleted sensor: {}", sensorId); + metricsService.incrementSensorDeleteSuccess(SensorType.MOTION.name()); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorDeleteFailure(SensorType.MOTION.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while deleting, sensorId: {}", sensorId); + metricsService.incrementSensorDeleteFailure(SensorType.MOTION.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while deleting sensor, sensorId:{}", sensorId); + metricsService.incrementSensorDeleteFailure(SensorType.MOTION.name()); throw new SensorNotUpdatedException(String.format("Error occurred while deleting sensor: %s", ex.getMessage())); } } diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java index f1a0835..1accc92 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java @@ -9,7 +9,9 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.TemperatureSensorRepository; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.service.SensorService; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -27,19 +29,25 @@ public class TemperatureSensorService implements SensorService handleGetSensorsByCameraId(UUID cameraId) { @Override @Transactional + @Timed("sensor.update") public TemperatureSensor handleUpdateSensor(UUID cameraId, UUID sensorId, TemperatureSensor sensor) { try { Camera camera = cameraService.getCameraById(cameraId); @@ -74,15 +83,19 @@ public TemperatureSensor handleUpdateSensor(UUID cameraId, UUID sensorId, Temper existingSensor.setVersion(sensor.getVersion()); existingSensor.setData(sensor.getData()); log.info("Updating sensor: {}", existingSensor); + metricsService.incrementSensorUpdateSuccess(SensorType.TEMPERATURE.name()); return temperatureSensorRepository.save(existingSensor); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorUpdateFailure(SensorType.TEMPERATURE.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while updating, sensorId: {}", sensorId); + metricsService.incrementSensorUpdateFailure(SensorType.TEMPERATURE.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while updating sensor, sensorId:{}", sensorId); + metricsService.incrementSensorUpdateFailure(SensorType.TEMPERATURE.name()); throw new SensorNotUpdatedException(String.format("Error occurred while updating sensors: %s", ex.getMessage())); } } @@ -95,6 +108,7 @@ public TemperatureSensor getSensorById(UUID sensorId) { } @Override + @Timed("sensor.delete") public void handleDeleteSensor(UUID cameraId, UUID sensorId) { try { Camera camera = cameraService.getCameraById(cameraId); @@ -107,14 +121,18 @@ public void handleDeleteSensor(UUID cameraId, UUID sensorId) { camera.getSensors().remove(sensor); temperatureSensorRepository.delete(sensor); log.info("Deleted sensor: {}", sensorId); + metricsService.incrementSensorDeleteSuccess(SensorType.TEMPERATURE.name()); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); + metricsService.incrementSensorDeleteFailure(SensorType.TEMPERATURE.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while deleting, sensorId: {}", sensorId); + metricsService.incrementSensorDeleteFailure(SensorType.TEMPERATURE.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while deleting sensor, sensorId:{}", sensorId); + metricsService.incrementSensorDeleteFailure(SensorType.TEMPERATURE.name()); throw new SensorNotUpdatedException(String.format("Error occurred while deleting sensor: %s", ex.getMessage())); } } From 072c6cf2127078335558ec22497de2dcb35383f0 Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:26:58 +0300 Subject: [PATCH 06/10] Update service tests to mock MetricsService --- .../cameraonboarding/service/CameraServiceImplTest.java | 3 +++ .../cameraonboarding/service/impl/LightSensorServiceTest.java | 4 ++++ .../service/impl/MotionSensorServiceTest.java | 4 ++++ .../service/impl/TemperatureSensorServiceTest.java | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java index f8149a0..0840358 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java @@ -45,6 +45,9 @@ class CameraServiceImplTest { @Mock private BlobStorageServiceImpl blobStorageService; + @Mock + private MetricsService metricsService; + @InjectMocks private CameraServiceImpl cameraService; diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorServiceTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorServiceTest.java index aa47bac..b49354b 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorServiceTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorServiceTest.java @@ -8,6 +8,7 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.LightSensorRepository; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import jakarta.transaction.Transactional; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -34,6 +35,9 @@ class LightSensorServiceTest { @Mock private Camera camera; + @Mock + private MetricsService metricsService; + @InjectMocks private LightSensorService lightSensorService; diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorServiceTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorServiceTest.java index 46eaa8e..391ca9b 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorServiceTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorServiceTest.java @@ -8,6 +8,7 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.MotionSensorRepository; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import jakarta.transaction.Transactional; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -34,6 +35,9 @@ class MotionSensorServiceTest { @Mock private Camera camera; + @Mock + private MetricsService metricsService; + @InjectMocks private MotionSensorService motionSensorService; diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorServiceTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorServiceTest.java index dea4b04..a8a8f49 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorServiceTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorServiceTest.java @@ -8,6 +8,7 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.TemperatureSensorRepository; import com.onboarding.camera.cameraonboarding.service.CameraService; +import com.onboarding.camera.cameraonboarding.service.MetricsService; import jakarta.transaction.Transactional; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -34,6 +35,9 @@ class TemperatureSensorServiceTest { @Mock private Camera camera; + @Mock + private MetricsService metricsService; + @InjectMocks private TemperatureSensorService temperatureSensorService; From ab795441bf4b08d3235708e53c71e21d61e281b2 Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:27:54 +0300 Subject: [PATCH 07/10] Enhance Grafana dashboards with application metrics --- k8s/logging/grafana/custom-app-dashboard.yaml | 1143 ++++++++++++++- k8s/logging/grafana/dashboard.yaml | 1281 ++++++++++++++++- 2 files changed, 2373 insertions(+), 51 deletions(-) diff --git a/k8s/logging/grafana/custom-app-dashboard.yaml b/k8s/logging/grafana/custom-app-dashboard.yaml index cd5a699..ec66ced 100644 --- a/k8s/logging/grafana/custom-app-dashboard.yaml +++ b/k8s/logging/grafana/custom-app-dashboard.yaml @@ -25,7 +25,8 @@ data: "type": "timeseries", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", @@ -47,13 +48,15 @@ data: "h": 8, "w": 24, "x": 0, - "y": 0}, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "s", "min": 0, "color": { - "mode": "palette-classic"} + "mode": "palette-classic" + } } }, "options": { @@ -61,7 +64,9 @@ data: "displayMode": "table", "values": [ "last", - "max"]} + "max" + ] + } } }, { @@ -70,7 +75,8 @@ data: "type": "timeseries", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[1m]))", @@ -82,7 +88,8 @@ data: "h": 6, "w": 24, "x": 0, - "y": 8}, + "y": 8 + }, "fieldConfig": { "defaults": { "unit": "reqps", @@ -98,7 +105,8 @@ data: "type": "stat", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m]))", @@ -109,16 +117,19 @@ data: "h": 6, "w": 8, "x": 0, - "y": 14}, + "y": 14 + }, "fieldConfig": { "defaults": { "color": { - "mode": "thresholds"}, + "mode": "thresholds" + }, "thresholds": { "steps": [ { "color": "green", - "value": null} + "value": null + } ] } } @@ -130,7 +141,8 @@ data: "type": "stat", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"[45]..\"}[5m]))", @@ -141,19 +153,23 @@ data: "h": 6, "w": 8, "x": 8, - "y": 14}, + "y": 14 + }, "fieldConfig": { "defaults": { "color": { - "mode": "thresholds"}, + "mode": "thresholds" + }, "thresholds": { "steps": [ { "color": "green", - "value": null}, + "value": null + }, { "color": "red", - "value": 1} + "value": 1 + } ] } } @@ -165,7 +181,8 @@ data: "type": "stat", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "(sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m])) / sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[5m]))) * 100", @@ -176,12 +193,14 @@ data: "h": 6, "w": 8, "x": 16, - "y": 14}, + "y": 14 + }, "fieldConfig": { "defaults": { "unit": "percent", "color": { - "mode": "thresholds"}, + "mode": "thresholds" + }, "thresholds": { "steps": [ { @@ -190,10 +209,12 @@ data: }, { "color": "yellow", - "value": 90}, + "value": 90 + }, { "color": "green", - "value": 95} + "value": 95 + } ] } } @@ -205,7 +226,8 @@ data: "type": "timeseries", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "sum by (status) (rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[1m]))", @@ -216,12 +238,14 @@ data: "h": 8, "w": 12, "x": 0, - "y": 20}, + "y": 20 + }, "fieldConfig": { "defaults": { "unit": "reqps", "color": { - "mode": "palette-classic"} + "mode": "palette-classic" + } } } }, @@ -231,7 +255,8 @@ data: "type": "table", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "group by (__name__) ({__name__=~\".*http.*request.*\", kubernetes_namespace=\"app\"})", @@ -242,11 +267,1077 @@ data: "h": 8, "w": 12, "x": 12, - "y": 20} + "y": 20 + } + }, + { + "id": 8, + "title": "Camera Onboarding Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_onboarding_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 28 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 9, + "title": "Camera Onboarding Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_onboarding_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 28 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 10, + "title": "Camera Initialization Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_initialization_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 28 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 11, + "title": "Camera Initialization Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_initialization_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 34 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 12, + "title": "Image Upload Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_upload_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 34 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 13, + "title": "Image Upload Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_upload_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 34 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 14, + "title": "Image Download Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_download_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 40 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 15, + "title": "Image Download Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_download_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 40 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 16, + "title": "Location Add Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "location_add_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 40 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 17, + "title": "Location Add Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "location_add_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 46 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 18, + "title": "Light Sensor Create Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_success_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 46 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 19, + "title": "Light Sensor Create Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_failure_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 46 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 20, + "title": "Motion Sensor Create Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_success_total{sensor_type=\"MOTION\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 52 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 21, + "title": "Motion Sensor Create Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_failure_total{sensor_type=\"MOTION\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 52 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 22, + "title": "Temperature Sensor Create Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_success_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 52 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 23, + "title": "Temperature Sensor Create Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_failure_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 58 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 24, + "title": "Light Sensor Update Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_success_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 58 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 25, + "title": "Light Sensor Update Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_failure_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 58 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 26, + "title": "Motion Sensor Update Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_success_total{sensor_type=\"MOTION\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 27, + "title": "Motion Sensor Update Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_failure_total{sensor_type=\"MOTION\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 28, + "title": "Temperature Sensor Update Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_success_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 29, + "title": "Temperature Sensor Update Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_failure_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 70 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 30, + "title": "Light Sensor Delete Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_success_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 70 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 31, + "title": "Light Sensor Delete Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_failure_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 70 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 32, + "title": "Motion Sensor Delete Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_success_total{sensor_type=\"MOTION\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 76 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 33, + "title": "Motion Sensor Delete Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_failure_total{sensor_type=\"MOTION\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 76 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 34, + "title": "Temperature Sensor Delete Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_success_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 76 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 35, + "title": "Temperature Sensor Delete Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_failure_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 82 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } } ], "time": { "from": "now-15m", - "to": "now"}, + "to": "now" + }, "refresh": "5s" } \ No newline at end of file diff --git a/k8s/logging/grafana/dashboard.yaml b/k8s/logging/grafana/dashboard.yaml index 06c70c0..8a3a48d 100644 --- a/k8s/logging/grafana/dashboard.yaml +++ b/k8s/logging/grafana/dashboard.yaml @@ -175,46 +175,100 @@ data: "title": "Custom Application Metrics", "tags": [ "spring-boot", - "app-metrics"], + "app-metrics", + "camera-onboarding" + ], "style": "dark", "timezone": "browser", "panels": [ { "id": 1, - "title": "Request Latency (P95)", + "title": "Request Latency Percentiles", "type": "timeseries", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { - "expr": "histogram_quantile(0.95, sum by (le) (rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])))", - "legendFormat": "P95 (http_server_requests)", + "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", + "legendFormat": "P95 Latency", "refId": "A" }, { - "expr": "histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket{kubernetes_namespace=\"app\"}[2m])))", - "legendFormat": "P95 (http_request_duration)", + "expr": "histogram_quantile(0.90, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", + "legendFormat": "P90 Latency", "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(http_server_requests_seconds_bucket{kubernetes_namespace=\"app\"}[2m])) by (le))", + "legendFormat": "P50 Latency", + "refId": "C" } ], "gridPos": { "h": 8, "w": 24, "x": 0, - "y": 0}, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "s", - "min": 0}} + "min": 0, + "color": { + "mode": "palette-classic" + } + } + }, + "options": { + "legend": { + "displayMode": "table", + "values": [ + "last", + "max" + ] + } + } }, { "id": 2, + "title": "Request Rate (req/sec)", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[1m]))", + "legendFormat": "Total RPS", + "refId": "A" + } + ], + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 8 + }, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "color": { + "mode": "palette-classic" + } + } + } + }, + { + "id": 3, "title": "Successful Requests (5m)", "type": "stat", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "sum(increase(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m]))", @@ -223,12 +277,28 @@ data: ], "gridPos": { "h": 6, - "w": 12, + "w": 8, "x": 0, - "y": 8} + "y": 14 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": null + } + ] + } + } + } }, { - "id": 3, + "id": 4, "title": "Failed Requests (5m)", "type": "stat", "datasource": { @@ -243,17 +313,112 @@ data: ], "gridPos": { "h": 6, + "w": 8, + "x": 8, + "y": 14 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 5, + "title": "Success Rate (%)", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "(sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\", status=~\"2..\"}[5m])) / sum(rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[5m]))) * 100", + "legendFormat": "Success Rate" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 14 + }, + "fieldConfig": { + "defaults": { + "unit": "percent", + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 90 + }, + { + "color": "green", + "value": 95 + } + ] + } + } + } + }, + { + "id": 6, + "title": "Request Status Distribution", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sum by (status) (rate(http_server_requests_seconds_count{kubernetes_namespace=\"app\"}[1m]))", + "legendFormat": "{{status}}" + } + ], + "gridPos": { + "h": 8, "w": 12, - "x": 12, - "y": 8} + "x": 0, + "y": 20 + }, + "fieldConfig": { + "defaults": { + "unit": "reqps", + "color": { + "mode": "palette-classic" + } + } + } }, { - "id": 4, + "id": 7, "title": "DEBUG PANEL: Available HTTP Metrics", "type": "table", "datasource": { "type": "prometheus", - "uid": "Prometheus"}, + "uid": "Prometheus" + }, "targets": [ { "expr": "group by (__name__) ({__name__=~\".*http.*request.*\", kubernetes_namespace=\"app\"})", @@ -261,14 +426,1080 @@ data: } ], "gridPos": { - "h": 9, - "w": 24, + "h": 8, + "w": 12, + "x": 12, + "y": 20 + } + }, + { + "id": 8, + "title": "Camera Onboarding Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_onboarding_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, "x": 0, - "y": 14} - } - ], - "time": { - "from": "now-15m", - "to": "now"}, + "y": 28 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 9, + "title": "Camera Onboarding Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_onboarding_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 28 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 10, + "title": "Camera Initialization Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_initialization_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 28 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 11, + "title": "Camera Initialization Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "camera_initialization_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 34 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 12, + "title": "Image Upload Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_upload_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 34 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 13, + "title": "Image Upload Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_upload_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 34 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 14, + "title": "Image Download Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_download_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 40 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 15, + "title": "Image Download Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "image_download_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 40 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 16, + "title": "Location Add Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "location_add_success_total", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 40 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 17, + "title": "Location Add Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "location_add_failure_total", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 46 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 18, + "title": "Light Sensor Create Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_success_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 46 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 19, + "title": "Light Sensor Create Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_failure_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 46 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 20, + "title": "Motion Sensor Create Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_success_total{sensor_type=\"MOTION\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 52 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 21, + "title": "Motion Sensor Create Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_failure_total{sensor_type=\"MOTION\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 52 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 22, + "title": "Temperature Sensor Create Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_success_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 52 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 23, + "title": "Temperature Sensor Create Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_create_failure_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 58 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 24, + "title": "Light Sensor Update Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_success_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 58 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 25, + "title": "Light Sensor Update Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_failure_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 58 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 26, + "title": "Motion Sensor Update Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_success_total{sensor_type=\"MOTION\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 27, + "title": "Motion Sensor Update Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_failure_total{sensor_type=\"MOTION\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 28, + "title": "Temperature Sensor Update Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_success_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 64 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 29, + "title": "Temperature Sensor Update Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_update_failure_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 70 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 30, + "title": "Light Sensor Delete Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_success_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 70 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 31, + "title": "Light Sensor Delete Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_failure_total{sensor_type=\"LIGHT\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 70 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 32, + "title": "Motion Sensor Delete Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_success_total{sensor_type=\"MOTION\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 76 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 33, + "title": "Motion Sensor Delete Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_failure_total{sensor_type=\"MOTION\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 76 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + }, + { + "id": 34, + "title": "Temperature Sensor Delete Success", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_success_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Success" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 76 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + } + ] + } + } + } + }, + { + "id": 35, + "title": "Temperature Sensor Delete Failure", + "type": "stat", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "expr": "sensor_delete_failure_total{sensor_type=\"TEMPERATURE\"}", + "legendFormat": "Failure" + } + ], + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 82 + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 1 + } + ] + } + } + } + } + ], + "time": { + "from": "now-15m", + "to": "now" + }, "refresh": "5s" } From 31f52822966cb342654e9d387b08b7128d24aa7b Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:28:47 +0300 Subject: [PATCH 08/10] Update deployment image and OWASP suppressions --- k8s/app/deployment.yaml | 2 +- owasp-suppressions.xml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/k8s/app/deployment.yaml b/k8s/app/deployment.yaml index 99a8586..16ef0dc 100644 --- a/k8s/app/deployment.yaml +++ b/k8s/app/deployment.yaml @@ -18,7 +18,7 @@ spec: spec: containers: - name: camera-onboarding - image: mrsert/camera-onboarding:metrics-v2 + image: mrsert/camera-onboarding:metrics-v3 resources: requests: cpu: "275m" diff --git a/owasp-suppressions.xml b/owasp-suppressions.xml index 398fcf5..7d12eba 100644 --- a/owasp-suppressions.xml +++ b/owasp-suppressions.xml @@ -27,6 +27,12 @@ CVE-2024-35255 + + + CVE-2025-55163 + CVE-2025-58056 + CVE-2025-58057 + @@ -61,6 +67,8 @@ CVE-2024-38820 + CVE-2025-41249 + CVE-2025-41242 @@ -96,6 +104,8 @@ CVE-2025-49125 CVE-2025-52520 CVE-2025-53506 + CVE-2025-55668 + CVE-2025-48989 From ebee12f83a0e23c5fec28efe05cb92e964e1625d Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Thu, 25 Sep 2025 19:41:13 +0300 Subject: [PATCH 09/10] Update dependencies and suppress CVEs --- owasp-suppressions.xml | 69 ++++-------------------------------------- pom.xml | 9 +++--- 2 files changed, 11 insertions(+), 67 deletions(-) diff --git a/owasp-suppressions.xml b/owasp-suppressions.xml index 7d12eba..410be39 100644 --- a/owasp-suppressions.xml +++ b/owasp-suppressions.xml @@ -1,9 +1,11 @@ - + CVE-2023-36415 + + CVE-2024-35255 @@ -11,85 +13,30 @@ CVE-2025-48924 - - - - CVE-2024-57699 - - - - - CVE-2024-12798 - CVE-2024-12801 - - - - - CVE-2024-35255 - CVE-2025-55163 CVE-2025-58056 CVE-2025-58057 - - - - CVE-2024-47535 - CVE-2025-24970 - - - CVE-2025-53864 - - - - - CVE-2025-22227 - - - + + CVE-2022-30187 CVE-2025-25193 - - - - CVE-2025-22233 - - - - - CVE-2024-38820 - CVE-2025-41249 - CVE-2025-41242 - - - - - CVE-2024-38809 - CVE-2024-38820 - CVE-2025-41234 - - - - - CVE-2024-38816 - CVE-2024-38820 - CVE-2025-26791 - + CVE-2024-50379 CVE-2024-52316 @@ -107,8 +54,4 @@ CVE-2025-55668 CVE-2025-48989 - - - CVE-2025-22235 - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 15a0663..1aff973 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.1 + 3.3.8 com.onboarding.camera @@ -16,7 +16,7 @@ 21 - 5.15.0 + 5.18.0 @@ -50,6 +50,7 @@ org.projectlombok lombok + 1.18.38 true @@ -61,7 +62,6 @@ com.azure azure-storage-blob - 12.27.1 @@ -188,8 +188,9 @@ org.owasp dependency-check-maven - 12.1.0 + 12.1.5 + false ${project.basedir}/owasp-suppressions.xml false 0 From a6bd0ee9cbd39e1bb28ee53aa1ce2c49c150b79d Mon Sep 17 00:00:00 2001 From: Selahattin <98191442+SelahattinSert@users.noreply.github.com> Date: Sun, 28 Sep 2025 16:24:12 +0300 Subject: [PATCH 10/10] Rename metric service as CameraMetricService --- ...sService.java => CameraMetricService.java} | 2 +- ...Impl.java => CameraMetricServiceImpl.java} | 4 +-- .../service/impl/CameraServiceImpl.java | 28 +++++++++---------- .../service/impl/LightSensorService.java | 26 ++++++++--------- .../service/impl/MotionSensorService.java | 26 ++++++++--------- .../impl/TemperatureSensorService.java | 26 ++++++++--------- .../service/CameraServiceImplTest.java | 2 +- .../service/impl/LightSensorServiceTest.java | 4 +-- .../service/impl/MotionSensorServiceTest.java | 4 +-- .../impl/TemperatureSensorServiceTest.java | 4 +-- 10 files changed, 63 insertions(+), 63 deletions(-) rename src/main/java/com/onboarding/camera/cameraonboarding/service/{MetricsService.java => CameraMetricService.java} (98%) rename src/main/java/com/onboarding/camera/cameraonboarding/service/impl/{MetricsServiceImpl.java => CameraMetricServiceImpl.java} (97%) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/CameraMetricService.java similarity index 98% rename from src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java rename to src/main/java/com/onboarding/camera/cameraonboarding/service/CameraMetricService.java index 1d409da..ed5448e 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/MetricsService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/CameraMetricService.java @@ -1,6 +1,6 @@ package com.onboarding.camera.cameraonboarding.service; -public interface MetricsService { +public interface CameraMetricService { /** * Increments the counter for successful camera onboarding. diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraMetricServiceImpl.java similarity index 97% rename from src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java rename to src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraMetricServiceImpl.java index 1d74881..0415e81 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MetricsServiceImpl.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraMetricServiceImpl.java @@ -1,6 +1,6 @@ package com.onboarding.camera.cameraonboarding.service.impl; -import com.onboarding.camera.cameraonboarding.service.MetricsService; +import com.onboarding.camera.cameraonboarding.service.CameraMetricService; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import lombok.RequiredArgsConstructor; @@ -12,7 +12,7 @@ @Service @RequiredArgsConstructor -public class MetricsServiceImpl implements MetricsService { +public class CameraMetricServiceImpl implements CameraMetricService { private final MeterRegistry meterRegistry; diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java index f0e7101..c392d1b 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java @@ -14,8 +14,8 @@ import com.onboarding.camera.cameraonboarding.exception.LocationNotAddedException; import com.onboarding.camera.cameraonboarding.repository.CameraRepository; import com.onboarding.camera.cameraonboarding.service.BlobStorageService; +import com.onboarding.camera.cameraonboarding.service.CameraMetricService; import com.onboarding.camera.cameraonboarding.service.CameraService; -import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.util.DateTimeFactory; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -38,7 +38,7 @@ public class CameraServiceImpl implements CameraService { private final BlobStorageService blobStorageService; - private final MetricsService metricsService; + private final CameraMetricService cameraMetricService; @Override @Timed("camera.onboarding") @@ -49,11 +49,11 @@ public Camera handleSaveCamera(Camera camera) { camera.setOnboardedAt(dateTimeFactory.now()); Camera savedCamera = cameraRepository.save(camera); log.info("Camera saved with ID: {}", savedCamera.getCamId()); - metricsService.incrementCameraOnboardingSuccess(); + cameraMetricService.incrementCameraOnboardingSuccess(); return savedCamera; } catch (Exception ex) { log.error("Exception occurred while saving camera: {}", ex.getMessage()); - metricsService.incrementCameraOnboardingFailure(); + cameraMetricService.incrementCameraOnboardingFailure(); throw new CameraNotCreatedException(String.format("Error occurred while saving camera: %s", ex.getMessage())); } } @@ -70,10 +70,10 @@ public void handleInitializeCamera(UUID cameraId) { camera.setInitializedAt(dateTimeFactory.now()); cameraRepository.save(camera); log.info("Camera initialized with ID: {}", cameraId); - metricsService.incrementCameraInitializationSuccess(); + cameraMetricService.incrementCameraInitializationSuccess(); } catch (Exception ex) { log.error("Exception occurred while initializing camera with ID: {}", cameraId, ex); - metricsService.incrementCameraInitializationFailure(); + cameraMetricService.incrementCameraInitializationFailure(); throw new CameraNotInitializedException(String.format("Error occurred while initializing camera: %s", ex.getMessage())); } } @@ -99,14 +99,14 @@ public void handleUploadImage(UUID cameraId, UUID imageId, byte[] imageData) { log.info("Uploading image with ID: {}", imageId); blobStorageService.uploadFile(blobStorageService.getContainerName(), imageId.toString(), imageData); cameraRepository.save(camera); - metricsService.incrementImageUploadSuccess(); + cameraMetricService.incrementImageUploadSuccess(); } catch (ImageAlreadyUploadedException ex) { log.error("Exception occurred while uploading image"); - metricsService.incrementImageUploadFailure(); + cameraMetricService.incrementImageUploadFailure(); throw new ImageAlreadyUploadedException(String.format("Camera already have image with id: %s", camera.getImageId())); } catch (Exception ex) { log.error("Exception occurred while uploading image:{}:ex:{}", imageId, ex.getMessage()); - metricsService.incrementImageUploadFailure(); + cameraMetricService.incrementImageUploadFailure(); throw new ImageNotUploadedException(String.format("Error occurred while uploading image: %s", ex.getMessage())); } } @@ -126,15 +126,15 @@ public byte[] handleDownloadImage(UUID cameraId) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); blobStorageService.getBlob(outputStream, blobStorageService.getContainerName(), camera.getImageId().toString()); - metricsService.incrementImageDownloadSuccess(); + cameraMetricService.incrementImageDownloadSuccess(); return outputStream.toByteArray(); } catch (ImageNotFoundException ex) { log.error("Image is not found by given cameraId: '{}'", cameraId); - metricsService.incrementImageDownloadFailure(); + cameraMetricService.incrementImageDownloadFailure(); throw new ImageNotFoundException(String.format("Image is not found by given cameraId: %s", cameraId)); } catch (Exception ex) { log.error("Exception occurred while downloading image, camera:{}:ex:{}", cameraId, ex.getMessage()); - metricsService.incrementImageDownloadFailure(); + cameraMetricService.incrementImageDownloadFailure(); throw new ImageNotDownloadedException(String.format("Error occurred while downloading image: %s", ex.getMessage())); } } @@ -156,11 +156,11 @@ public Camera handleAddLocation(UUID cameraId, LocationDto locationDto) { cameraRepository.save(camera); log.info("Location added/updated successfully for Camera ID: {}", cameraId); - metricsService.incrementLocationAddSuccess(); + cameraMetricService.incrementLocationAddSuccess(); return camera; } catch (Exception ex) { log.error("Exception occurred while adding location, camera:{}:ex:{}", cameraId, ex.getMessage()); - metricsService.incrementLocationAddFailure(); + cameraMetricService.incrementLocationAddFailure(); throw new LocationNotAddedException(String.format("Error occurred while adding location: %s", ex.getMessage())); } } diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java index cbe61d9..8865f0c 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/LightSensorService.java @@ -8,8 +8,8 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotFoundException; import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.LightSensorRepository; +import com.onboarding.camera.cameraonboarding.service.CameraMetricService; import com.onboarding.camera.cameraonboarding.service.CameraService; -import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.service.SensorService; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -29,7 +29,7 @@ public class LightSensorService implements SensorService { private final CameraService cameraService; - private final MetricsService metricsService; + private final CameraMetricService cameraMetricService; @Override @Transactional @@ -39,15 +39,15 @@ public LightSensor handleCreateSensor(UUID cameraId, LightSensor sensor) { sensor.setCamera(cameraService.getCameraById(cameraId)); LightSensor createdSensor = lightSensorRepository.save(sensor); log.info("Creating sensor: {}", createdSensor); - metricsService.incrementSensorCreateSuccess(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorCreateSuccess(SensorType.LIGHT.name()); return createdSensor; } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); - metricsService.incrementSensorCreateFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorCreateFailure(SensorType.LIGHT.name()); throw ex; } catch (Exception ex) { log.error("Failed to create sensor, camera:{}:ex:{}", cameraId, ex.getMessage()); - metricsService.incrementSensorCreateFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorCreateFailure(SensorType.LIGHT.name()); throw new SensorNotCreatedException(String.format("Failed to create sensor: %s", ex.getMessage())); } } @@ -83,19 +83,19 @@ public LightSensor handleUpdateSensor(UUID cameraId, UUID sensorId, LightSensor existingSensor.setVersion(sensor.getVersion()); existingSensor.setData(sensor.getData()); log.info("Updating sensor: {}", existingSensor); - metricsService.incrementSensorUpdateSuccess(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorUpdateSuccess(SensorType.LIGHT.name()); return lightSensorRepository.save(existingSensor); } catch (SensorNotFoundException ex) { log.error("Sensor not found while updating, sensorId: {}", sensorId); - metricsService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); throw ex; } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); - metricsService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while updating sensor, sensorId:{}", sensorId); - metricsService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorUpdateFailure(SensorType.LIGHT.name()); throw new SensorNotUpdatedException(String.format("Error occurred while updating sensors: %s", ex.getMessage())); } } @@ -121,18 +121,18 @@ public void handleDeleteSensor(UUID cameraId, UUID sensorId) { camera.getSensors().remove(sensor); lightSensorRepository.delete(sensor); log.info("Deleted sensor: {}", sensorId); - metricsService.incrementSensorDeleteSuccess(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorDeleteSuccess(SensorType.LIGHT.name()); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); - metricsService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while deleting, sensorId: {}", sensorId); - metricsService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while deleting sensor, sensorId:{}", sensorId); - metricsService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); + cameraMetricService.incrementSensorDeleteFailure(SensorType.LIGHT.name()); throw new SensorNotUpdatedException(String.format("Error occurred while deleting sensor: %s", ex.getMessage())); } } diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java index 75aba4b..3c72087 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/MotionSensorService.java @@ -8,8 +8,8 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotFoundException; import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.MotionSensorRepository; +import com.onboarding.camera.cameraonboarding.service.CameraMetricService; import com.onboarding.camera.cameraonboarding.service.CameraService; -import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.service.SensorService; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -29,7 +29,7 @@ public class MotionSensorService implements SensorService { private final CameraService cameraService; - private final MetricsService metricsService; + private final CameraMetricService cameraMetricService; @Override @Transactional @@ -39,15 +39,15 @@ public MotionSensor handleCreateSensor(UUID cameraId, MotionSensor sensor) { sensor.setCamera(cameraService.getCameraById(cameraId)); MotionSensor createdSensor = motionSensorRepository.save(sensor); log.info("Creating sensor: {}", createdSensor); - metricsService.incrementSensorCreateSuccess(SensorType.MOTION.name()); + cameraMetricService.incrementSensorCreateSuccess(SensorType.MOTION.name()); return createdSensor; } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); - metricsService.incrementSensorCreateFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorCreateFailure(SensorType.MOTION.name()); throw ex; } catch (Exception ex) { log.error("Failed to create sensor, camera:{}:ex:{}", cameraId, ex.getMessage()); - metricsService.incrementSensorCreateFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorCreateFailure(SensorType.MOTION.name()); throw new SensorNotCreatedException(String.format("Failed to create sensor: %s", ex.getMessage())); } } @@ -83,19 +83,19 @@ public MotionSensor handleUpdateSensor(UUID cameraId, UUID sensorId, MotionSenso existingSensor.setVersion(sensor.getVersion()); existingSensor.setData(sensor.getData()); log.info("Updating sensor: {}", existingSensor); - metricsService.incrementSensorUpdateSuccess(SensorType.MOTION.name()); + cameraMetricService.incrementSensorUpdateSuccess(SensorType.MOTION.name()); return motionSensorRepository.save(existingSensor); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); - metricsService.incrementSensorUpdateFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorUpdateFailure(SensorType.MOTION.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while updating, sensorId: {}", sensorId); - metricsService.incrementSensorUpdateFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorUpdateFailure(SensorType.MOTION.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while updating sensor, sensorId:{}", sensorId); - metricsService.incrementSensorUpdateFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorUpdateFailure(SensorType.MOTION.name()); throw new SensorNotUpdatedException(String.format("Error occurred while updating sensors: %s", ex.getMessage())); } } @@ -121,18 +121,18 @@ public void handleDeleteSensor(UUID cameraId, UUID sensorId) { camera.getSensors().remove(sensor); motionSensorRepository.delete(sensor); log.info("Deleted sensor: {}", sensorId); - metricsService.incrementSensorDeleteSuccess(SensorType.MOTION.name()); + cameraMetricService.incrementSensorDeleteSuccess(SensorType.MOTION.name()); } catch (CameraNotFoundException ex) { log.error("Camera not found, cameraId:{}", cameraId); - metricsService.incrementSensorDeleteFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorDeleteFailure(SensorType.MOTION.name()); throw ex; } catch (SensorNotFoundException ex) { log.error("Sensor not found while deleting, sensorId: {}", sensorId); - metricsService.incrementSensorDeleteFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorDeleteFailure(SensorType.MOTION.name()); throw ex; } catch (Exception ex) { log.error("Exception occurred while deleting sensor, sensorId:{}", sensorId); - metricsService.incrementSensorDeleteFailure(SensorType.MOTION.name()); + cameraMetricService.incrementSensorDeleteFailure(SensorType.MOTION.name()); throw new SensorNotUpdatedException(String.format("Error occurred while deleting sensor: %s", ex.getMessage())); } } diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java index 1accc92..9da17e9 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/TemperatureSensorService.java @@ -8,8 +8,8 @@ import com.onboarding.camera.cameraonboarding.exception.SensorNotFoundException; import com.onboarding.camera.cameraonboarding.exception.SensorNotUpdatedException; import com.onboarding.camera.cameraonboarding.repository.TemperatureSensorRepository; +import com.onboarding.camera.cameraonboarding.service.CameraMetricService; import com.onboarding.camera.cameraonboarding.service.CameraService; -import com.onboarding.camera.cameraonboarding.service.MetricsService; import com.onboarding.camera.cameraonboarding.service.SensorService; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -29,7 +29,7 @@ public class TemperatureSensorService implements SensorService