MariaDB metrics exporter for Prometheus written in Rust.
- Modular collectors – Enable only what you need; heavy/optional collectors stay off by default.
- Compatibility – Metric names align with Prometheus
mysqld_exporter(prefixedmariadb_). - Lean defaults – Essential availability, InnoDB, and replication metrics enabled by default; optional collectors opt-in.
- Low footprint – Designed to minimize cardinality and avoid expensive scans.
Install via Cargo:
cargo install mariadb_exporterBest practice: Use Unix socket with dedicated user
Create the exporter user with minimal privileges:
-- Create user for local socket connection only with connection limit
CREATE USER 'exporter'@'localhost' IDENTIFIED BY '' WITH MAX_USER_CONNECTIONS 3;
-- Grant minimal required permissions for all collectors
GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'exporter'@'localhost';
FLUSH PRIVILEGES;Run the exporter:
mariadb_exporter --dsn "mysql:///mysql?socket=/var/run/mysqld/mysqld.sock&user=exporter"Why this is secure:
- ✅ No password needed (socket authentication)
- ✅ User restricted to
localhostonly (no network access) - ✅ Minimal privileges (read-only + monitoring)
- ✅ Connection limit prevents resource exhaustion
- ✅ No exposure to network attacks
For remote monitoring or testing:
mariadb_exporter --dsn "mysql://exporter:password@host:3306/mysql"Create user for network access:
CREATE USER 'exporter'@'%' IDENTIFIED BY 'strong_password_here' WITH MAX_USER_CONNECTIONS 3;
GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'exporter'@'%';
FLUSH PRIVILEGES;- Unix socket (recommended):
mysql:///mysql?socket=/var/run/mysqld/mysqld.sock&user=exporter - TCP:
mysql://user:password@host:3306/database - TLS required:
mysql://user:password@host/mysql?ssl-mode=REQUIRED - TLS verify identity:
mysql://user:password@host/mysql?ssl-mode=VERIFY_IDENTITY&ssl-ca=/path/to/ca.pem
Default port is 9306:
mariadb_exporter --dsn "..." --port 9187Collectors are toggled with --collector.<name> or --no-collector.<name>.
--collector.default(enabled) – Core status (uptime, threads, connections, traffic), InnoDB basics, replication basics, binlog stats, config flags, version,mariadb_up, audit log enabled status.--collector.exporter(enabled) – Exporter self-metrics (process, scrape, cardinality).--collector.innodb– Advanced InnoDB metrics fromSHOW ENGINE INNODB STATUS: LSN tracking, checkpoint age, active transactions, semaphore waits, adaptive hash index stats.--collector.tls– TLS session + cipher info.--collector.query_response_time– Buckets fromquery_response_timeplugin.--collector.statements– Statement digest summaries/top latency fromperformance_schema.--collector.schema– Table size/row estimates (largest 20 non-system tables).--collector.replication– Relay log size/pos, binlog file count.--collector.locks– Metadata/table lock waits fromperformance_schema.--collector.metadata–metadata_lock_infotable counts.--collector.userstat– Per-user stats (requires@@userstat=1andUSER_STATISTICS).
defaultexporter
Everything else is opt-in.
To enable all collectors for maximum visibility:
mariadb_exporter \
--dsn "mysql:///mysql?socket=/var/run/mysqld/mysqld.sock&user=exporter" \
--collector.default \
--collector.exporter \
--collector.innodb \
--collector.tls \
--collector.query_response_time \
--collector.statements \
--collector.schema \
--collector.replication \
--collector.locks \
--collector.metadata \
--collector.userstatOr using environment variables:
export MARIADB_EXPORTER_DSN="mysql:///mysql?socket=/var/run/mysqld/mysqld.sock&user=exporter"
export MARIADB_EXPORTER_PORT="9306"
mariadb_exporter \
--collector.default \
--collector.exporter \
--collector.innodb \
--collector.tls \
--collector.query_response_time \
--collector.statements \
--collector.schema \
--collector.replication \
--collector.locks \
--collector.metadata \
--collector.userstatNote: Some collectors require additional privileges or database configuration:
innodb– RequiresPROCESSprivilege (included in recommended setup)tls– Only shows data if TLS/SSL is enabledquery_response_time– Requiresquery_response_timeplugin enabledstatements– Requiresperformance_schemaenabledschema– Queriesinformation_schema(can be slow on large databases)locks,metadata– Requireperformance_schemaenableduserstat– Requires@@userstat=1andUSER_STATISTICSenabled
The --collector.innodb provides deep visibility into InnoDB internals by parsing SHOW ENGINE INNODB STATUS:
Metrics exposed:
mariadb_innodb_lsn_current– Current log sequence numbermariadb_innodb_lsn_flushed– LSN flushed to diskmariadb_innodb_lsn_checkpoint– Last checkpoint LSNmariadb_innodb_checkpoint_age_bytes– Uncheckpointed bytes (LSN current - checkpoint)mariadb_innodb_active_transactions– Count of active InnoDB transactionsmariadb_innodb_semaphore_waits_total– Semaphore wait events (internal contention)mariadb_innodb_semaphore_wait_time_ms_total– Total semaphore wait timemariadb_innodb_adaptive_hash_searches_total– Adaptive hash index hitsmariadb_innodb_adaptive_hash_searches_btree_total– AHI misses requiring B-tree lookup
Use cases:
- Monitor checkpoint age to prevent log file overflow
- Track LSN progression for write workload analysis
- Detect long-running transactions
- Identify internal InnoDB contention (semaphore waits)
- Measure adaptive hash index efficiency
Requirements:
PROCESSprivilege (forSHOW ENGINE INNODB STATUS)
Enable with:
mariadb_exporter --collector.default --collector.innodbmariadb_exporter
├── bin
├── cli
├── collectors
│ ├── config.rs
│ ├── default
│ ├── exporter
│ ├── innodb
│ ├── locks
│ ├── metadata
│ ├── mod.rs
│ ├── query_response_time
│ ├── register_macro.rs
│ ├── registry.rs
│ ├── replication
│ ├── schema
│ ├── statements
│ ├── tls
│ ├── userstat
│ └── util.rs
└── src/lib.rs
Each collector lives in its own subdirectory for clarity and easy extension.
Run tests:
cargo testRun with container-backed integration (requires podman):
just testTest with Unix socket connection (production-like setup):
# Test with combined MariaDB + exporter container (most realistic)
just test-socketLint:
cargo clippy --all-targets --all-featuresFor detailed information on testing with Unix socket connections, see TESTING_SOCKET.md.
Quick start:
# Test with combined MariaDB + exporter container (most realistic)
just test-socketThe project follows a modular collector architecture:
mariadb_exporter/
├── bin/ # Binary entry point
├── cli/ # CLI argument parsing
├── collectors/ # All metric collectors
│ ├── mod.rs # Collector trait and registration
│ ├── registry.rs # Collector orchestration
│ ├── config.rs # Collector enable/disable logic
│ └── */ # Individual collector modules
└── exporter/ # HTTP server (Axum)
- Create a subdirectory under
src/collectors/with amod.rs - Define a struct implementing the
Collectortrait:register_metrics(&self, registry: &Registry)- Register Prometheus metricscollect(&self, pool: &MySqlPool)- Fetch data and update metrics (async)enabled_by_default(&self)- Whether collector runs by default
- Add ONE line to
register_collectors!macro insrc/collectors/mod.rs:register_collectors! { // ... existing collectors ... your_collector => YourCollector, }
The macro automatically generates all registration boilerplate.
This project enforces strict clippy lints (see Cargo.toml):
- DENY:
unwrap_used,expect_used,panic,indexing_slicing,await_holding_lock - Use
?for error propagation, never.unwrap()or.expect() - Use
.get()instead of[index]for slicing - Use pattern matching or
.ok()instead of.unwrap()
Exceptions are allowed only in test code with #[allow(clippy::unwrap_used)].
# Run unit tests
cargo test
# Run with container integration
just test
# Lint (must pass)
cargo clippy --all-targets --all-features
# Validate Grafana dashboard
just validate-dashboardWhen adding metrics to the Grafana dashboard:
- Ensure metrics are exported by collectors
- Add panels following existing structure (clean, professional, no emojis)
- Use template variables (
$job,$instance) - Add clear descriptions (Goal/Action format)
- Validate before committing:
just validate-dashboard
See grafana/README.md for detailed dashboard documentation.
- Run tests before committing:
cargo test - Run clippy:
cargo clippy --all-targets --all-features - Validate dashboard if modified:
just validate-dashboard - Keep commit messages clear and descriptive
- User statistics: enable with
SET GLOBAL userstat=ON;(or@@userstat=1) to exposeuserstatmetrics. - Metadata locks: load
metadata_lock_infoplugin for themetadatacollector. - Performance schema is needed for statements/locks collectors to return data.
- Optional collectors skip gracefully when prerequisites aren't present.