- Add Python exporter for ActivityWatch API metrics collection - Configure Prometheus to scrape ActivityWatch exporter - Create Grafana dashboard for ActivityWatch overview - Integrate ActivityWatch monitoring into existing docker-compose stack - Add Prometheus datasource for ActivityWatch metrics Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
143 lines
5.0 KiB
Python
143 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
ActivityWatch Prometheus Exporter
|
|
Собирает метрики из ActivityWatch API и экспонирует их в формате Prometheus.
|
|
"""
|
|
|
|
import time
|
|
import logging
|
|
import requests
|
|
from prometheus_client import start_http_server, Gauge, Counter, Histogram, Info
|
|
from datetime import datetime, timedelta
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Configuration
|
|
AW_SERVER_HOST = "10.10.10.13"
|
|
AW_SERVER_PORT = 5600
|
|
AW_SERVER_SCHEME = "http"
|
|
AW_API_BASE = f"{AW_SERVER_SCHEME}://{AW_SERVER_HOST}:{AW_SERVER_PORT}/api/0"
|
|
EXPORTER_PORT = 9398
|
|
|
|
# Metrics
|
|
aw_buckets_total = Gauge('aw_buckets_total', 'Total number of ActivityWatch buckets')
|
|
aw_events_total = Counter('aw_events_total', 'Total number of ActivityWatch events', ['bucket', 'event_type'])
|
|
aw_events_last_timestamp = Gauge('aw_events_last_timestamp', 'Timestamp of last event in bucket', ['bucket'])
|
|
aw_bucket_events_count = Gauge('aw_bucket_events_count', 'Number of events in bucket', ['bucket'])
|
|
aw_collector_status = Info('aw_collector_status', 'Status of ActivityWatch collectors')
|
|
aw_server_info = Info('aw_server_info', 'ActivityWatch server information')
|
|
|
|
class ActivityWatchExporter:
|
|
def __init__(self, api_base):
|
|
self.api_base = api_base
|
|
self.session = requests.Session()
|
|
self.session.headers.update({'Accept': 'application/json'})
|
|
self.bucket_cache = {}
|
|
|
|
def get_buckets(self):
|
|
"""Get all buckets from ActivityWatch API."""
|
|
try:
|
|
response = self.session.get(f"{self.api_base}/buckets", timeout=10)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except Exception as e:
|
|
logger.error(f"Failed to get buckets: {e}")
|
|
return {}
|
|
|
|
def get_bucket_events(self, bucket_id, limit=1):
|
|
"""Get events from a specific bucket."""
|
|
try:
|
|
response = self.session.get(
|
|
f"{self.api_base}/buckets/{bucket_id}/events",
|
|
params={'limit': limit},
|
|
timeout=10
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except Exception as e:
|
|
logger.error(f"Failed to get events for {bucket_id}: {e}")
|
|
return []
|
|
|
|
def get_bucket_info(self, bucket_id):
|
|
"""Get detailed info about a bucket."""
|
|
try:
|
|
response = self.session.get(f"{self.api_base}/buckets/{bucket_id}", timeout=10)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except Exception as e:
|
|
logger.error(f"Failed to get info for {bucket_id}: {e}")
|
|
return {}
|
|
|
|
def collect_metrics(self):
|
|
"""Collect metrics from ActivityWatch."""
|
|
buckets = self.get_buckets()
|
|
|
|
# Update bucket count
|
|
aw_buckets_total.set(len(buckets))
|
|
|
|
# Server info
|
|
aw_server_info.info({
|
|
'host': AW_SERVER_HOST,
|
|
'port': AW_SERVER_PORT,
|
|
'scheme': AW_SERVER_SCHEME,
|
|
'api_base': self.api_base
|
|
})
|
|
|
|
# Collector status
|
|
collectors = {}
|
|
for bucket_id, bucket_data in buckets.items():
|
|
client = bucket_data.get('client', 'unknown')
|
|
hostname = bucket_data.get('hostname', 'unknown')
|
|
bucket_type = bucket_data.get('type', 'unknown')
|
|
|
|
# Count events
|
|
events = self.get_bucket_events(bucket_id, limit=1000)
|
|
event_count = len(events)
|
|
aw_bucket_events_count.labels(bucket=bucket_id).set(event_count)
|
|
|
|
# Last event timestamp
|
|
if events:
|
|
last_event = events[0]
|
|
timestamp = last_event.get('timestamp', 0)
|
|
try:
|
|
# Convert to Unix timestamp if needed
|
|
if isinstance(timestamp, str):
|
|
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
|
unix_ts = dt.timestamp()
|
|
else:
|
|
unix_ts = timestamp
|
|
aw_events_last_timestamp.labels(bucket=bucket_id).set(unix_ts)
|
|
except:
|
|
pass
|
|
|
|
# Collector status
|
|
collector_key = f"{hostname}_{client}"
|
|
collectors[collector_key] = {
|
|
'status': 'active',
|
|
'bucket': bucket_id,
|
|
'type': bucket_type,
|
|
'events': event_count
|
|
}
|
|
|
|
aw_collector_status.info(collectors)
|
|
|
|
def main():
|
|
exporter = ActivityWatchExporter(AW_API_BASE)
|
|
|
|
# Initial collection
|
|
exporter.collect_metrics()
|
|
|
|
# Start HTTP server
|
|
start_http_server(EXPORTER_PORT)
|
|
logger.info(f"ActivityWatch exporter started on port {EXPORTER_PORT}")
|
|
logger.info(f"Scraping ActivityWatch API at {AW_API_BASE}")
|
|
|
|
# Collect metrics every 30 seconds
|
|
while True:
|
|
time.sleep(30)
|
|
exporter.collect_metrics()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|