diff --git a/build.gradle b/build.gradle index af69a1c6..d742c96c 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,9 @@ dependencies { implementation 'net.logstash.logback:logstash-logback-encoder:8.1' implementation 'com.github.napstr:logback-discord-appender:1.0.0' + // Metrics + implementation 'io.micrometer:micrometer-registry-prometheus' + // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/docker-compose.monitoring.yml b/docker-compose.monitoring.yml new file mode 100644 index 00000000..1d9d5c97 --- /dev/null +++ b/docker-compose.monitoring.yml @@ -0,0 +1,53 @@ +services: + prometheus: + image: prom/prometheus:v3.5.1 + container_name: cherrish-prometheus + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--storage.tsdb.retention.time=15d' + - '--web.enable-lifecycle' + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + extra_hosts: + - "host.docker.internal:host-gateway" + networks: + - monitoring + restart: unless-stopped + + grafana: + image: grafana/grafana:11.6.9 + container_name: cherrish-grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin} + - GF_USERS_ALLOW_SIGN_UP=false + - DISCORD_MONITORING_WEBHOOK_URL=${DISCORD_MONITORING_WEBHOOK_URL} + volumes: + - ./monitoring/grafana/provisioning:/etc/grafana/provisioning + - grafana_data:/var/lib/grafana + networks: + - monitoring + depends_on: + prometheus: + condition: service_healthy + restart: unless-stopped + +volumes: + prometheus_data: + grafana_data: + +networks: + monitoring: + driver: bridge diff --git a/monitoring/grafana/provisioning/alerting/contactpoints.yml b/monitoring/grafana/provisioning/alerting/contactpoints.yml new file mode 100644 index 00000000..46bfcf6e --- /dev/null +++ b/monitoring/grafana/provisioning/alerting/contactpoints.yml @@ -0,0 +1,23 @@ +apiVersion: 1 + +contactPoints: + - orgId: 1 + name: discord-monitoring + receivers: + - uid: discord-monitoring + type: discord + settings: + url: ${DISCORD_MONITORING_WEBHOOK_URL} + use_discord_username: true + avatar_url: "https://grafana.com/static/assets/img/fav32.png" + title: '{{ if eq .Status "firing" }}:rotating_light: ALERT{{ else }}:white_check_mark: RESOLVED{{ end }} ({{ len .Alerts }} alerts)' + message: | + {{ range .Alerts }} + {{ if eq $.Status "firing" }}:red_circle:{{ else }}:green_circle:{{ end }} **{{ .Labels.rulename }}** + :warning: **Severity:** {{ .Labels.severity | toUpper }} + :clipboard: **Summary:** {{ .Annotations.summary }} + :memo: **Detail:** {{ .Annotations.description }} + :link: **View:** {{ .GeneratorURL }} + + {{ end }} + disableResolveMessage: false diff --git a/monitoring/grafana/provisioning/alerting/policies.yml b/monitoring/grafana/provisioning/alerting/policies.yml new file mode 100644 index 00000000..1e881943 --- /dev/null +++ b/monitoring/grafana/provisioning/alerting/policies.yml @@ -0,0 +1,10 @@ +apiVersion: 1 + +policies: + - orgId: 1 + receiver: discord-monitoring + group_by: + - grafana_folder + group_wait: 60s + group_interval: 5m + repeat_interval: 4h diff --git a/monitoring/grafana/provisioning/alerting/rules.yml b/monitoring/grafana/provisioning/alerting/rules.yml new file mode 100644 index 00000000..6a527a80 --- /dev/null +++ b/monitoring/grafana/provisioning/alerting/rules.yml @@ -0,0 +1,161 @@ +apiVersion: 1 + +groups: + - orgId: 1 + name: cherrish-alerts + folder: Cherrish + interval: 1m + rules: + - uid: high-error-rate + title: High Error Rate + condition: C + data: + - refId: A + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: prometheus + model: + expr: sum(rate(http_server_requests_seconds_count{application="cherrish", status=~"5.."}[5m])) or vector(0) + intervalMs: 1000 + maxDataPoints: 43200 + refId: A + - refId: B + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: prometheus + model: + expr: sum(rate(http_server_requests_seconds_count{application="cherrish"}[5m])) or vector(1) + intervalMs: 1000 + maxDataPoints: 43200 + refId: B + - refId: C + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: __expr__ + model: + conditions: + - evaluator: + params: + - 0.05 + type: gt + operator: + type: and + query: + params: + - C + reducer: + type: last + expression: $A / $B + intervalMs: 1000 + maxDataPoints: 43200 + refId: C + type: math + noDataState: NoData + execErrState: Error + for: 2m + annotations: + summary: "High error rate detected on Cherrish Server" + description: "Error rate is above 5% for the last 2 minutes" + labels: + severity: critical + rulename: "High Error Rate" + isPaused: false + + - uid: high-latency + title: High Latency (P95) + condition: B + data: + - refId: A + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: prometheus + model: + expr: histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{application="cherrish"}[5m])) by (le)) + intervalMs: 1000 + maxDataPoints: 43200 + refId: A + - refId: B + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: __expr__ + model: + conditions: + - evaluator: + params: + - 1 + type: gt + operator: + type: and + query: + params: + - B + reducer: + type: last + expression: A + intervalMs: 1000 + maxDataPoints: 43200 + refId: B + type: threshold + noDataState: NoData + execErrState: Error + for: 5m + annotations: + summary: "High latency detected on Cherrish Server" + description: "P95 response time is above 1 second for the last 5 minutes" + labels: + severity: warning + rulename: "High Latency (P95)" + isPaused: false + + - uid: metrics-collection-health + title: Metrics Collection Down + condition: B + data: + - refId: A + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: prometheus + model: + expr: up{job="cherrish-server"} + intervalMs: 1000 + maxDataPoints: 43200 + refId: A + - refId: B + relativeTimeRange: + from: 300 + to: 0 + datasourceUid: __expr__ + model: + conditions: + - evaluator: + params: + - 1 + type: lt + operator: + type: and + query: + params: + - B + reducer: + type: last + expression: A + intervalMs: 1000 + maxDataPoints: 43200 + refId: B + type: threshold + noDataState: NoData + execErrState: Error + for: 2m + annotations: + summary: "Cherrish Server metrics collection is down" + description: "Prometheus cannot scrape metrics from Cherrish Server. The application may be down or unreachable." + labels: + severity: critical + rulename: "Metrics Collection Down" + isPaused: false diff --git a/monitoring/grafana/provisioning/dashboards/dashboard.yml b/monitoring/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 00000000..6cbbcfb3 --- /dev/null +++ b/monitoring/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'Cherrish Dashboards' + orgId: 1 + folder: 'Cherrish' + type: file + disableDeletion: true + updateIntervalSeconds: 120 + options: + path: /etc/grafana/provisioning/dashboards/json diff --git a/monitoring/grafana/provisioning/dashboards/json/cherrish-overview.json b/monitoring/grafana/provisioning/dashboards/json/cherrish-overview.json new file mode 100644 index 00000000..a77b93db --- /dev/null +++ b/monitoring/grafana/provisioning/dashboards/json/cherrish-overview.json @@ -0,0 +1,514 @@ +{ + "annotations": { + "list": [] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, + "id": 1, + "panels": [], + "title": "JVM Metrics", + "type": "row" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 0.8 } + ] + }, + "unit": "percentunit", + "min": 0, + "max": 1 + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, + "id": 2, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "sum(jvm_memory_used_bytes{application=\"cherrish\", area=\"heap\"}) / sum(jvm_memory_max_bytes{application=\"cherrish\", area=\"heap\"})", + "legendFormat": "Heap Usage", + "refId": "A" + } + ], + "title": "JVM Heap Memory Usage (%)", + "type": "timeseries" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 0.5 } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, + "id": 3, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "rate(jvm_gc_pause_seconds_sum{application=\"cherrish\"}[5m])", + "legendFormat": "{{cause}} - {{action}}", + "refId": "A" + } + ], + "title": "GC Pause Time (Rate)", + "type": "timeseries" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 80 } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, + "id": 4, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "jvm_threads_live_threads{application=\"cherrish\"}", + "legendFormat": "Live Threads", + "refId": "A" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "jvm_threads_daemon_threads{application=\"cherrish\"}", + "legendFormat": "Daemon Threads", + "refId": "B" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "jvm_threads_peak_threads{application=\"cherrish\"}", + "legendFormat": "Peak Threads", + "refId": "C" + } + ], + "title": "JVM Threads", + "type": "timeseries" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 0.8 } + ] + }, + "unit": "percentunit", + "min": 0, + "max": 1 + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, + "id": 5, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "process_cpu_usage{application=\"cherrish\"}", + "legendFormat": "Process CPU", + "refId": "A" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "system_cpu_usage{application=\"cherrish\"}", + "legendFormat": "System CPU", + "refId": "B" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { "h": 1, "w": 24, "x": 0, "y": 17 }, + "id": 6, + "panels": [], + "title": "HTTP Requests", + "type": "row" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 80 } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 18 }, + "id": 7, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"cherrish\"}[5m])) by (uri)", + "legendFormat": "{{uri}}", + "refId": "A" + } + ], + "title": "Request Rate (per URI)", + "type": "timeseries" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 80 } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 18 }, + "id": 8, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"cherrish\", status=~\"4..|5..\"}[5m])) by (status)", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Error Rate (4xx, 5xx)", + "type": "timeseries" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 80 } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 26 }, + "id": 9, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "histogram_quantile(0.50, sum(rate(http_server_requests_seconds_bucket{application=\"cherrish\"}[5m])) by (le))", + "legendFormat": "P50", + "refId": "A" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{application=\"cherrish\"}[5m])) by (le))", + "legendFormat": "P95", + "refId": "B" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{application=\"cherrish\"}[5m])) by (le))", + "legendFormat": "P99", + "refId": "C" + } + ], + "title": "Response Time Percentiles", + "type": "timeseries" + }, + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "fieldConfig": { + "defaults": { + "color": { "mode": "palette-classic" }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { "legend": false, "tooltip": false, "viz": false }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { "type": "linear" }, + "showPoints": "never", + "spanNulls": false, + "stacking": { "group": "A", "mode": "none" }, + "thresholdsStyle": { "mode": "off" } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "red", "value": 80 } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 26 }, + "id": 10, + "options": { + "legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom", "showLegend": true }, + "tooltip": { "mode": "multi", "sort": "none" } + }, + "targets": [ + { + "datasource": { "type": "prometheus", "uid": "prometheus" }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"cherrish\"}[5m]))", + "legendFormat": "Total Throughput", + "refId": "A" + } + ], + "title": "Total Throughput", + "type": "timeseries" + } + ], + "refresh": "30s", + "schemaVersion": 38, + "tags": ["cherrish", "spring-boot"], + "templating": { "list": [] }, + "time": { "from": "now-1h", "to": "now" }, + "timepicker": {}, + "timezone": "browser", + "title": "Cherrish Overview", + "uid": "cherrish-overview", + "version": 1, + "weekStart": "" +} diff --git a/monitoring/grafana/provisioning/datasources/datasource.yml b/monitoring/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 00000000..8704d9e5 --- /dev/null +++ b/monitoring/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,10 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false diff --git a/monitoring/prometheus/prometheus.prod.yml b/monitoring/prometheus/prometheus.prod.yml new file mode 100644 index 00000000..f2e34033 --- /dev/null +++ b/monitoring/prometheus/prometheus.prod.yml @@ -0,0 +1,13 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'cherrish-server' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['cherrish-server:8081'] diff --git a/monitoring/prometheus/prometheus.yml b/monitoring/prometheus/prometheus.yml new file mode 100644 index 00000000..56a1cee4 --- /dev/null +++ b/monitoring/prometheus/prometheus.yml @@ -0,0 +1,13 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'cherrish-server' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['host.docker.internal:8080'] diff --git a/src/main/java/com/sopt/cherrish/global/monitoring/MonitoringTestController.java b/src/main/java/com/sopt/cherrish/global/monitoring/MonitoringTestController.java new file mode 100644 index 00000000..a0098192 --- /dev/null +++ b/src/main/java/com/sopt/cherrish/global/monitoring/MonitoringTestController.java @@ -0,0 +1,23 @@ +package com.sopt.cherrish.global.monitoring; + +import org.springframework.context.annotation.Profile; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/monitoring/test") +@Profile({"local", "dev"}) +public class MonitoringTestController { + + @GetMapping("/error") + public void testError() { + throw new RuntimeException("Test error for monitoring alert"); + } + + @GetMapping("/slow") + public String testSlow() throws InterruptedException { + Thread.sleep(2000); + return "slow response"; + } +} diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index eb402da1..f36034ae 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -10,10 +10,6 @@ spring: base-url: ${SERVER_URL:http://localhost:8080} management: - endpoints: - web: - exposure: - include: health endpoint: health: show-details: always diff --git a/src/main/resources/application-monitoring.yaml b/src/main/resources/application-monitoring.yaml new file mode 100644 index 00000000..236f1c4c --- /dev/null +++ b/src/main/resources/application-monitoring.yaml @@ -0,0 +1,16 @@ +management: + endpoints: + web: + exposure: + include: health,prometheus,metrics,info + endpoint: + health: + show-details: when_authorized + metrics: + tags: + application: cherrish + distribution: + percentiles-histogram: + http.server.requests: true + percentiles: + http.server.requests: 0.5, 0.95, 0.99 diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml index 2ddca3fa..101c2cbf 100644 --- a/src/main/resources/application-prod.yaml +++ b/src/main/resources/application-prod.yaml @@ -10,10 +10,8 @@ spring: base-url: ${SERVER_URL:http://localhost:8080} management: - endpoints: - web: - exposure: - include: health + server: + port: 8081 endpoint: health: show-details: never diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index e5483e7a..83c301c5 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -7,6 +7,7 @@ spring: include: - db - openai + - monitoring config: import: optional:file:.env[.properties]