A standalone Docker-based analytics system for ingesting and visualizing conda environment usage telemetry from NCI GADI xp65 logs.
-
Clone/navigate to this directory:
cd /home/romain/PROJECTS/xp65-telemetry -
(Optional) Update the logs source path in
.env:# Edit .env and update LOGS_SOURCE to your actual path # Default is /home/romain/NCI_gadi/xp65_logs
-
Start the system:
docker-compose up
This will:
- Start PostgreSQL database
- Run the ingester (dump + upload)
- Start Grafana
- Provision datasource and dashboard automatically
If you set
DATABASE_URL, the ingester will send data to that managed PostgreSQL instance instead of using the local Compose database. -
Access Grafana:
- Open http://localhost:3000
- Login:
admin/admin - Dashboard "XP65 Conda Telemetry" will be ready to use
- PostgreSQL 16: Database for telemetry storage
- Grafana 11.1.0: Visualization and dashboarding
- Python Ingester: Automatically dumps and uploads telemetry on startup
- Idempotent via event_hash unique key
- Supports partial ingestion with
INGEST_MAX_FILES - Graceful handling of missing source directory
Edit .env to customize:
# Optional: managed PostgreSQL connection string for the ingester
DATABASE_URL=postgresql://USER:PASSWORD@managed-db.example.com:5432/RESOURCES
# Database credentials (used when DATABASE_URL is not set)
DB_PASSWORD=postgres
DB_USER=postgres
DB_NAME=RESOURCES
# Grafana login
GRAFANA_PASSWORD=admin
# Source log directory (critical: update to your path)
LOGS_SOURCE=/home/romain/NCI_gadi/xp65_logs
# Optional: limit files per ingest run (0 = all)
INGEST_MAX_FILES=0- Set
DATABASE_URLin.envif you want the ingester to target a managed PostgreSQL instance. - This is intentionally a minimal ingestion-side change; Grafana provisioning still points at the local Compose PostgreSQL service.
The ingester can restrict tracking to a configurable package allowlist file:
- Edit ingester/tracked_packages.txt using your environment-style package lines.
- During ingestion, only normalized package names from this file are inserted into
conda_env_packages. - To clean old rows that are not in the allowlist, run ingestion with cleanup once:
docker compose run --rm --entrypoint bash ingester -lc 'DBURL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}"; python /app/upload_conda_telemetry.py --db "$DBURL" --schema-file /schema.sql --tracked-packages-file /app/tracked_packages.txt --cleanup-existing-packages /dev/null'Tables:
conda_env_sessions: telemetry events (user sessions, packages, timestamps, etc.)conda_env_packages: package imports per sessionconda_env_ingest_runs: audit log of ingestion runs
Views (pre-aggregated for fast dashboards):
conda_env_daily_usage: daily active users and session counts by environmentconda_env_package_daily_usage: daily package usage and user counts
docker-compose up
# Ingester will process entire logs directory and uploaddocker-compose restart ingester
# Duplicate events are skipped automatically# Edit .env: INGEST_MAX_FILES=50
docker-compose updocker exec xp65-postgres psql -U postgres -d RESOURCES -c \
"SELECT env_name, count(*) FROM conda_env_sessions GROUP BY env_name;"Grafana is provisioned with:
- PostgreSQL datasource at
postgres:5432 - Dashboard "XP65 Conda Telemetry" with 4 panels:
- Active Users by Environment (time series)
- Sessions by Environment (time series)
- Top Users (table)
- Unique Users by Environment (bar chart)
Connection refused
- Ensure
LOGS_SOURCEin.envpoints to a valid directory - Check
docker-compose logs ingesterfor details
No data visible in Grafana
- Verify PostgreSQL is healthy:
docker-compose logs postgres - Check ingester logs:
docker-compose logs ingester - Manually query:
docker exec xp65-postgres psql -U postgres -d RESOURCES -c "SELECT count(*) FROM conda_env_sessions;"
Want to restart everything
docker-compose down -v # Remove volumes
docker-compose up --buildxp65-telemetry/
├── docker-compose.yml # Service orchestration
├── .env # Configuration (edit LOGS_SOURCE here)
├── Dockerfile # Ingester image
├── ingester/
│ ├── dump_conda_telemetry.py # Flatten imports.jsonl to NDJSON
│ ├── upload_conda_telemetry.py # Insert into Postgres
│ ├── entrypoint.sh # Orchestrates dump + upload
│ └── requirements.txt # Python deps (psycopg2-binary)
├── postgres/
│ └── init.sql # Schema, tables, indexes, views
└── grafana/
└── provisioning/
├── datasources/postgres.yaml
└── dashboards/conda-telemetry.json
- 309K events from 215 user files ingests in ~30 seconds
- Idempotent hashing ensures safe re-ingestion
- Daily summary views are materialized for fast panel rendering
- Indexes on (env_name, time), (user_id, time), (package_name)
- Monitor ingestion logs for patterns or errors
- Adjust Grafana dashboard panels for specific use cases
- Plan retention/archival policy if telemetry grows
- Consider scheduling periodic re-ingestion (cron outside container)