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