-
Notifications
You must be signed in to change notification settings - Fork 4
Add Centralized Logging and Monitoring Stack (Prometheus + Grafana) #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,8 @@ services: | |||||||||
| --reload-dir /app/src | ||||||||||
| depends_on: | ||||||||||
| - qdrant | ||||||||||
| networks: | ||||||||||
| - maicro-network | ||||||||||
| healthcheck: | ||||||||||
| test: | ||||||||||
| ["CMD-SHELL", "curl -f http://localhost:8000/api/v1/health || exit 1"] | ||||||||||
|
|
@@ -44,8 +46,49 @@ services: | |||||||||
| - "6334:6334" | ||||||||||
| volumes: | ||||||||||
| - qdrant_data:/qdrant/storage | ||||||||||
| networks: | ||||||||||
| - maicro-network | ||||||||||
| healthcheck: | ||||||||||
| disable: true | ||||||||||
|
|
||||||||||
| prometheus: | ||||||||||
| image: prom/prometheus:latest | ||||||||||
| container_name: prometheus_dev | ||||||||||
| ports: | ||||||||||
| - "9090:9090" | ||||||||||
| volumes: | ||||||||||
| - ./prometheus.yml:/etc/prometheus/prometheus.yml | ||||||||||
| - prometheus_data:/prometheus | ||||||||||
| command: | ||||||||||
| - '--config.file=/etc/prometheus/prometheus.yml' | ||||||||||
| - '--storage.tsdb.path=/prometheus' | ||||||||||
| networks: | ||||||||||
| - maicro-network | ||||||||||
| depends_on: | ||||||||||
| - maicro | ||||||||||
|
|
||||||||||
| grafana: | ||||||||||
| image: grafana/grafana:latest | ||||||||||
| container_name: grafana_dev | ||||||||||
| ports: | ||||||||||
| - "3000:3000" | ||||||||||
| environment: | ||||||||||
| - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin} | ||||||||||
| - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin} | ||||||||||
|
Comment on lines
+76
to
+77
|
||||||||||
| - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin} | |
| - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin} | |
| - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:?GRAFANA_ADMIN_USER not set} | |
| - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD not set} |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,8 @@ services: | |||||||||
| env_file: | ||||||||||
| - .env | ||||||||||
| restart: unless-stopped | ||||||||||
| networks: | ||||||||||
| - maicro-network | ||||||||||
| healthcheck: | ||||||||||
| test: | ||||||||||
| [ | ||||||||||
|
|
@@ -20,3 +22,46 @@ services: | |||||||||
| interval: 30s | ||||||||||
| timeout: 10s | ||||||||||
| retries: 3 | ||||||||||
|
|
||||||||||
| prometheus: | ||||||||||
| image: prom/prometheus:latest | ||||||||||
| container_name: prometheus | ||||||||||
| ports: | ||||||||||
| - "9090:9090" | ||||||||||
| volumes: | ||||||||||
| - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro | ||||||||||
| - prometheus_data:/prometheus | ||||||||||
| command: | ||||||||||
| - "--config.file=/etc/prometheus/prometheus.yml" | ||||||||||
| - "--storage.tsdb.path=/prometheus" | ||||||||||
| depends_on: | ||||||||||
| - maicro | ||||||||||
| restart: unless-stopped | ||||||||||
| networks: | ||||||||||
| - maicro-network | ||||||||||
|
|
||||||||||
| grafana: | ||||||||||
| image: grafana/grafana:latest | ||||||||||
| container_name: grafana | ||||||||||
| ports: | ||||||||||
| - "3000:3000" | ||||||||||
| environment: | ||||||||||
| GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin} | ||||||||||
| GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} | ||||||||||
|
Comment on lines
+49
to
+50
|
||||||||||
| GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin} | |
| GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} | |
| GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:?GRAFANA_ADMIN_USER must be set} | |
| GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD must be set} |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,197 @@ | ||||||
| { | ||||||
| "annotations": { | ||||||
| "list": [ | ||||||
| { | ||||||
| "builtIn": 1, | ||||||
| "datasource": { | ||||||
| "type": "grafana", | ||||||
| "uid": "-- Grafana --" | ||||||
| }, | ||||||
| "enable": true, | ||||||
| "hide": true, | ||||||
| "iconColor": "rgba(0, 211, 255, 1)", | ||||||
| "name": "Annotations & Alerts", | ||||||
| "type": "dashboard" | ||||||
| } | ||||||
| ] | ||||||
| }, | ||||||
| "editable": true, | ||||||
| "fiscalYearStartMonth": 0, | ||||||
| "graphTooltip": 0, | ||||||
| "links": [], | ||||||
| "panels": [ | ||||||
| { | ||||||
| "datasource": { | ||||||
| "type": "prometheus", | ||||||
| "uid": "${DS_PROMETHEUS}" | ||||||
|
||||||
| "uid": "${DS_PROMETHEUS}" | |
| "uid": "prometheus" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| apiVersion: 1 | ||
|
|
||
| providers: | ||
| - name: mAIcro Dashboards | ||
| orgId: 1 | ||
| folder: mAIcro | ||
| type: file | ||
| disableDeletion: false | ||
| editable: true | ||
| options: | ||
| path: /var/lib/grafana/dashboards |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| apiVersion: 1 | ||
|
|
||
| datasources: | ||
| - name: Prometheus | ||
| type: prometheus | ||
| access: proxy | ||
| url: http://prometheus:9090 | ||
| isDefault: true | ||
| editable: true | ||
|
Comment on lines
+3
to
+9
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||||||||||
| global: | ||||||||||||||
| scrape_interval: 15s | ||||||||||||||
| evaluation_interval: 15s | ||||||||||||||
|
|
||||||||||||||
| scrape_configs: | ||||||||||||||
| - job_name: 'maicro' | ||||||||||||||
| static_configs: | ||||||||||||||
| - targets: ['maicro:8000'] | ||||||||||||||
| metrics_path: '/metrics' | ||||||||||||||
|
|
||||||||||||||
|
Comment on lines
+6
to
+10
|
||||||||||||||
| - job_name: 'maicro' | |
| static_configs: | |
| - targets: ['maicro:8000'] | |
| metrics_path: '/metrics' |
Copilot
AI
Mar 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prometheus.yml includes a scrape job targeting qdrant:6333, but the non-dev docker-compose.yml doesn’t define a qdrant service/network alias. In that environment Prometheus will log repeated DNS/connection failures. Either add qdrant to docker-compose.yml (if intended), or split Prometheus config per environment so prod doesn’t reference a missing target.
| metrics_path: '/metrics' | |
| - job_name: 'qdrant' | |
| static_configs: | |
| - targets: ['qdrant:6333'] | |
| metrics_path: '/metrics' |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,14 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.basicConfig( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| level=logging.INFO, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| format="%(asctime)s | %(levelname)s | %(name)s | %(message)s", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get_logger(name: str) -> logging.Logger: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Get a logger instance with the app's default configuration.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+13
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s | %(levelname)s | %(name)s | %(message)s", | |
| ) | |
| def get_logger(name: str) -> logging.Logger: | |
| """Get a logger instance with the app's default configuration.""" | |
| _LOGGING_CONFIGURED = False | |
| def configure_logging() -> None: | |
| """ | |
| Configure application logging. | |
| This function is safe to call multiple times. If the root logger already | |
| has handlers configured (for example, by uvicorn or another framework), | |
| this function will not override that configuration. | |
| """ | |
| global _LOGGING_CONFIGURED | |
| if _LOGGING_CONFIGURED: | |
| return | |
| root_logger = logging.getLogger() | |
| # If handlers are already present (e.g., installed by uvicorn), respect | |
| # the existing configuration and do not call basicConfig. | |
| if not root_logger.handlers: | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s | %(levelname)s | %(name)s | %(message)s", | |
| ) | |
| _LOGGING_CONFIGURED = True | |
| def get_logger(name: str) -> logging.Logger: | |
| """Get a logger instance with the app's default configuration.""" | |
| configure_logging() |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |||
| from contextlib import asynccontextmanager | ||||
|
|
||||
| from fastapi import FastAPI | ||||
| from core.log import get_logger | ||||
|
|
||||
| from api.error_handlers import register_exception_handlers | ||||
| from api.routes import router | ||||
|
|
@@ -12,13 +13,7 @@ | |||
| from core.ingestion import ingest_from_discord, run_startup_audit | ||||
| import logging | ||||
|
||||
| import logging |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Prometheus config bind-mount isn’t read-only here (unlike
docker-compose.ymlwhich uses:ro). Making it read-only helps prevent accidental in-container edits and keeps behavior consistent across environments.