diff --git a/apps/api/plane/settings/local.py b/apps/api/plane/settings/local.py index a346eca141b..ef6a59ae8f8 100644 --- a/apps/api/plane/settings/local.py +++ b/apps/api/plane/settings/local.py @@ -16,6 +16,11 @@ DEBUG_TOOLBAR_PATCH_SETTINGS = False +# Prometheus metrics settings +INSTALLED_APPS += ("django_prometheus",) +MIDDLEWARE.insert(0, "django_prometheus.middleware.PrometheusBeforeMiddleware") +MIDDLEWARE.append("django_prometheus.middleware.PrometheusAfterMiddleware") + # Only show emails in console don't send it to smtp EMAIL_BACKEND = os.environ.get("EMAIL_BACKEND", "django.core.mail.backends.console.EmailBackend") diff --git a/apps/api/plane/urls.py b/apps/api/plane/urls.py index f5e43408cb8..9b8c7180971 100644 --- a/apps/api/plane/urls.py +++ b/apps/api/plane/urls.py @@ -20,6 +20,7 @@ path("api/instances/", include("plane.license.urls")), path("api/v1/", include("plane.api.urls")), path("auth/", include("plane.authentication.urls")), + path("", include("django_prometheus.urls")), path("", include("plane.web.urls")), ] diff --git a/apps/api/requirements/base.txt b/apps/api/requirements/base.txt index 89b15694f39..7aa8b4b3947 100644 --- a/apps/api/requirements/base.txt +++ b/apps/api/requirements/base.txt @@ -75,3 +75,6 @@ opentelemetry-exporter-otlp-proto-grpc==1.28.1 drf-spectacular==0.28.0 # html sanitizer nh3==0.2.18 + +# prometheus metrics +django-prometheus==2.3.1 diff --git a/deployments/grafana/dashboards/plane-dashboard.json b/deployments/grafana/dashboards/plane-dashboard.json new file mode 100644 index 00000000000..631e03c84e8 --- /dev/null +++ b/deployments/grafana/dashboards/plane-dashboard.json @@ -0,0 +1,118 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "title": "API Request Rate (per Second)", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "sum(rate(django_http_requests_total_by_view_transport_method_total[5m])) by (view)", + "legendFormat": "{{view}}", + "range": true, + "refId": "A" + } + ], + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "lineInterpolation": "smooth" + }, + "unit": "reqps" + } + } + }, + { + "collapsed": false, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "title": "Average API Latency (seconds)", + "type": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "editorMode": "code", + "expr": "sum(rate(django_http_requests_latency_seconds_by_view_method_sum[5m])) by (view) / sum(rate(django_http_requests_latency_seconds_by_view_method_count[5m])) by (view)", + "legendFormat": "{{view}}", + "range": true, + "refId": "A" + } + ], + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "lineInterpolation": "smooth" + }, + "unit": "s" + } + } + } + ], + "refresh": "5s", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Plane API Analytics", + "uid": "plane-api-analytics", + "version": 1, + "weekStart": "" +} diff --git a/deployments/grafana/provisioning/dashboards/dashboard.yml b/deployments/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 00000000000..688f9f3800a --- /dev/null +++ b/deployments/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'Plane Dashboards' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /var/lib/grafana/dashboards diff --git a/deployments/grafana/provisioning/datasources/datasource.yml b/deployments/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 00000000000..86fd3465e14 --- /dev/null +++ b/deployments/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,8 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true diff --git a/deployments/prometheus/prometheus.yml b/deployments/prometheus/prometheus.yml new file mode 100644 index 00000000000..b2643829b7f --- /dev/null +++ b/deployments/prometheus/prometheus.yml @@ -0,0 +1,9 @@ +global: + scrape_interval: 5s + evaluation_interval: 5s + +scrape_configs: + - job_name: "plane-api" + metrics_path: "/metrics" + static_configs: + - targets: ["api:8000"] diff --git a/docker-compose-local.yml b/docker-compose-local.yml index 6bc5ee309f7..aae1db780d2 100644 --- a/docker-compose-local.yml +++ b/docker-compose-local.yml @@ -140,11 +140,44 @@ services: - plane-db - plane-redis + prometheus: + image: prom/prometheus:latest + restart: unless-stopped + volumes: + - ./deployments/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + ports: + - "9091:9090" + networks: + - dev_env + + grafana: + image: grafana/grafana:latest + restart: unless-stopped + ports: + - "3002:3000" + volumes: + - ./deployments/grafana/provisioning:/etc/grafana/provisioning + - ./deployments/grafana/dashboards:/var/lib/grafana/dashboards + - grafana_data:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + networks: + - dev_env + depends_on: + - prometheus + volumes: redisdata: uploads: pgdata: rabbitmq_data: + prometheus_data: + grafana_data: networks: dev_env: