A production-ready Android library for collecting comprehensive system metrics including CPU, memory, battery, thermal, storage, and network information.
| Language | Link |
|---|---|
| 🇬🇧 English | Full Documentation |
| 🇷🇺 Русский | Полная документация |
- 📊 Comprehensive Metrics - CPU, Memory, Battery, Thermal, Storage, and Network
- 🔄 Real-time Streaming - Flow-based reactive API
- 💪 Health Scoring - Automatic system health assessment
- 🏗️ Clean Architecture - Domain/Data/Infrastructure layers
- 🔒 Thread-safe - Safe concurrent access
- ⚡ High Performance - <5ms latency, <5MB memory
- 🎯 Zero Dependencies - Only Kotlin stdlib, Coroutines, Serialization
- 📤 Data Export - CSV and JSON export with streaming support
- 🖥️ Debug Overlay - In-app HUD for real-time metrics visualization (optional module)
Add the dependency to your build.gradle.kts:
dependencies {
// Core metrics library
implementation("com.sysmetrics:sysmetrics-core:1.0.0")
// Optional: Debug overlay (HUD)
debugImplementation("com.sysmetrics:sysmetrics-overlay:1.0.0")
}class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
SysMetrics.initialize(this)
}
}lifecycleScope.launch {
SysMetrics.getCurrentMetrics()
.onSuccess { metrics ->
println("CPU Usage: ${metrics.cpuMetrics.usagePercent}%")
println("Memory Usage: ${metrics.memoryMetrics.usagePercent}%")
println("Battery Level: ${metrics.batteryMetrics.level}%")
}
.onFailure { error ->
Log.e("SysMetrics", "Failed to get metrics", error)
}
}lifecycleScope.launch {
SysMetrics.observeMetrics(intervalMs = 1000)
.collect { metrics ->
updateUI(metrics)
}
}lifecycleScope.launch {
SysMetrics.observeHealthScore()
.collect { healthScore ->
when (healthScore.status) {
HealthStatus.EXCELLENT -> showGreenIndicator()
HealthStatus.GOOD -> showYellowIndicator()
HealthStatus.WARNING -> showOrangeIndicator()
HealthStatus.CRITICAL -> showRedIndicator()
}
// Display recommendations
healthScore.recommendations.forEach { recommendation ->
showRecommendation(recommendation)
}
}
}override fun onTerminate() {
super.onTerminate()
runBlocking {
SysMetrics.destroy()
}
}Show real-time metrics overlay in your app:
class MainActivity : AppCompatActivity() {
private var overlayHandle: OverlayHandle? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Attach overlay (debug builds only by default)
overlayHandle = SysMetricsOverlay.attach(this)
}
override fun onDestroy() {
overlayHandle?.detach()
super.onDestroy()
}
}The overlay shows FPS, CPU%, RAM%, and network speed. Tap "▼ More" to expand and see all metrics. See OVERLAY_GUIDE.md for full documentation.
| Method | Description |
|---|---|
initialize(context, logger) |
Initialize the library with optional logger |
getCurrentMetrics() |
Get current system metrics snapshot |
observeMetrics(intervalMs) |
Stream metrics at specified interval |
observeHealthScore() |
Stream health score updates |
getMetricsHistory(count) |
Get historical metrics |
getAggregatedMetrics(timeWindow) |
Get aggregated metrics for a time window |
getAggregatedHistory(timeWindow, count) |
Get historical aggregated metrics |
exportMetrics(metrics, format, config) |
Export raw metrics to CSV/JSON |
exportAggregatedMetrics(aggregated, format) |
Export aggregated metrics |
getSupportedExportFormats() |
Get list of supported export formats |
clearHistory() |
Clear metrics history |
destroy() |
Release all resources |
- SystemMetrics - Complete metrics snapshot
- CpuMetrics - CPU usage, cores, frequencies
- MemoryMetrics - RAM usage, available, cached
- BatteryMetrics - Level, temperature, status, health
- ThermalMetrics - CPU/battery temperature, throttling
- StorageMetrics - Storage capacity and usage
- NetworkMetrics - Network traffic and connection info
- HealthScore - Overall system health assessment
- AggregatedMetrics - Aggregated statistics over time window
- TimeWindow - Time window for aggregation (1min, 5min, 30min, 1hour)
- ExportConfig - Base configuration for metrics export
- CsvExportConfig - CSV-specific export configuration (RFC 4180)
- MetricsLogger - Logging interface for diagnostics
- LogLevel - Log level enumeration (DEBUG, INFO, WARN, ERROR)
- HealthStatus - EXCELLENT, GOOD, WARNING, CRITICAL
- BatteryStatus - UNKNOWN, CHARGING, DISCHARGING, NOT_CHARGING, FULL
- BatteryHealth - UNKNOWN, GOOD, OVERHEAT, DEAD, OVER_VOLTAGE, UNSPECIFIED_FAILURE, COLD
- HealthIssue - HIGH_CPU_USAGE, HIGH_MEMORY_USAGE, HIGH_TEMPERATURE, LOW_BATTERY, THERMAL_THROTTLING, LOW_STORAGE, POOR_PERFORMANCE
Aggregate metrics over configurable time windows for trend analysis:
// Get aggregated metrics for the last 5-minute window
lifecycleScope.launch {
SysMetrics.getAggregatedMetrics(TimeWindow.FIVE_MINUTES)
.onSuccess { aggregated ->
println("Avg CPU: ${aggregated.cpuPercentAverage}%")
println("Min CPU: ${aggregated.cpuPercentMin}%")
println("Max CPU: ${aggregated.cpuPercentMax}%")
println("Samples: ${aggregated.sampleCount}")
// Use for chart drawing
drawCpuGauge(aggregated.cpuPercentAverage)
}
}
// Get historical aggregated data (e.g., last hour as 12 five-minute windows)
lifecycleScope.launch {
SysMetrics.getAggregatedHistory(TimeWindow.FIVE_MINUTES, count = 12)
.onSuccess { history ->
val cpuTrend = history.map { it.cpuPercentAverage }
val memoryTrend = history.map { it.memoryPercentAverage }
drawTrendChart(cpuTrend, memoryTrend)
}
}Available time windows: ONE_MINUTE, FIVE_MINUTES, THIRTY_MINUTES, ONE_HOUR
Export metrics to CSV format (RFC 4180 compliant):
// Export raw metrics to CSV
lifecycleScope.launch {
val history = SysMetrics.getMetricsHistory(100).getOrNull() ?: emptyList()
SysMetrics.exportMetrics(
metrics = history,
format = "csv",
config = CsvExportConfig.forExcel() // UTF-8 BOM, comma delimiter
).onSuccess { csv ->
// Save to file
File(context.cacheDir, "metrics.csv").writeText(csv)
// Or share
shareFile("metrics.csv", csv, "text/csv")
}
}
// Export aggregated metrics
lifecycleScope.launch {
val aggregated = SysMetrics.getAggregatedHistory(TimeWindow.FIVE_MINUTES, 12)
.getOrNull() ?: emptyList()
SysMetrics.exportAggregatedMetrics(aggregated, "csv")
.onSuccess { csv ->
saveToDownloads("hourly_report.csv", csv)
}
}
// Custom CSV configuration
val europeanConfig = CsvExportConfig(
delimiter = ';', // Semicolon for European locales
includeUtf8Bom = true, // Excel compatibility
includeHeaders = true,
lineEnding = "\r\n" // RFC 4180 compliant
)
// Export to JSON format
lifecycleScope.launch {
val history = SysMetrics.getMetricsHistory(50).getOrNull() ?: emptyList()
SysMetrics.exportMetrics(
metrics = history,
format = "json" // JSON format
).onSuccess { json ->
// Use for API responses or log aggregation
sendToAnalyticsServer(json)
}
}Configure logging for debugging and monitoring:
// Default logging (INFO level)
SysMetrics.initialize(context)
// Development mode (DEBUG level - verbose logging)
SysMetrics.initialize(
context,
logger = AndroidMetricsLogger.forDevelopment()
)
// Production mode (WARN level - minimal logging)
SysMetrics.initialize(
context,
logger = AndroidMetricsLogger.forProduction()
)
// Custom log level
SysMetrics.initialize(
context,
logger = AndroidMetricsLogger(
tag = "MyApp",
minLevel = LogLevel.DEBUG
)
)
// File logging with rotation
val logFile = File(context.filesDir, "sysmetrics.log")
val fileLogger = FileMetricsLogger(
logFile = logFile,
maxSizeBytes = 5 * 1024 * 1024, // 5 MB
maxBackupFiles = 3
)
SysMetrics.initialize(context, fileLogger)
// Combined logging (Android + File)
val compositeLogger = CompositeMetricsLogger(
AndroidMetricsLogger(),
FileMetricsLogger(logFile)
)
SysMetrics.initialize(context, compositeLogger)
// Disable logging completely
SysMetrics.initialize(context, NoOpLogger)The health score (0-100) is calculated using weighted factors:
| Factor | Weight | Description |
|---|---|---|
| CPU Usage | 30% | Lower usage = higher score |
| Memory Usage | 35% | Lower usage = higher score |
| Temperature | 20% | Lower temp = higher score (max 80°C) |
| Battery Level | 15% | Higher level = higher score |
| Metric | Target | Achieved |
|---|---|---|
| Startup | <100ms | ✅ |
| Per-collection | <5ms (p99) | ✅ |
| Memory | <5MB steady state | ✅ |
| Cache TTL | 500ms | ✅ |
| History | 300 items max | ✅ |
sysmetrics-core/ # Core metrics library (no UI dependencies)
├── domain/
│ ├── model/ # Data classes & enums
│ ├── repository/ # IMetricsRepository interface
│ ├── logger/ # MetricsLogger interface
│ └── export/ # MetricsExporter interface
├── data/
│ ├── repository/ # MetricsRepositoryImpl
│ ├── aggregation/ # MetricsAggregationStrategy
│ ├── export/ # CsvMetricsExporter, JsonMetricsExporter, ExportManager
│ ├── mapper/ # Data transformers
│ └── cache/ # MetricsCache (500ms TTL)
├── infrastructure/
│ ├── proc/ # ProcFileReader (/proc files)
│ ├── android/ # AndroidMetricsProvider
│ ├── logger/ # AndroidMetricsLogger, FileMetricsLogger
│ └── extension/ # Utility extensions
└── SysMetrics.kt # Public API singleton
sysmetrics-overlay/ # Optional debug overlay module
├── SysMetricsOverlay.kt # Public API
├── OverlayConfig.kt # Configuration
├── OverlayHandle.kt # Control interface
├── fps/
│ └── FrameRateMonitor.kt # FPS monitoring (Choreographer)
└── view/
└── MetricsOverlayView.kt # UI component
- Min SDK: 21 (Android 5.0)
- Target SDK: 34 (Android 14)
- Kotlin: 1.9.22+
- Coroutines: 1.8.0+
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2025 Yhtyyar Kadyrow
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
Yhtyyar Kadyrow
- Email: kadyrow1506@gmail.com
- GitHub: @yhtyyar
For issues and feature requests, please use the GitHub issue tracker or contact kadyrow1506@gmail.com
Commercial licenses available for organizations requiring extended support. Contact for details.