enya-analyzer 0.1.2

Metrics instrumentation analyzer for source code
Documentation

enya-analyzer

Metrics instrumentation indexer for source code repositories.

This crate scans codebases to discover metric instrumentation points and alert rule definitions, building an in-memory index for fast lookups.

Features

Metric Scanning

Discovers metrics defined in source code using tree-sitter parsing:

Language Library Patterns
Rust metrics-rs counter!(), gauge!(), histogram!()
Python prometheus_client Counter('name', 'help'), Gauge(...), Histogram(...)
Go client_golang prometheus.NewCounter(CounterOpts{...}), promauto.NewGauge(...)
JavaScript prom-client new client.Counter({name: '...'}), new Gauge({...})
TypeScript prom-client Same as JavaScript, supports .ts and .tsx files

For each metric, the scanner extracts:

  • Metric name (e.g., http_requests_total)
  • Metric kind (counter, gauge, histogram)
  • Label keys (e.g., ["method", "status"])
  • File location (path, line, column)
  • Function context (containing function and impl type)

Metric Usage Tracking

Beyond definitions, the scanner also tracks where metrics are used ("hot paths"):

Language Usage Patterns
Python counter.inc(), gauge.set(value), histogram.observe(value), gauge.dec()
Go counter.Inc(), counter.Add(n), gauge.Set(value), histogram.Observe(value)
JavaScript counter.inc(), gauge.set(value), histogram.observe(value), histogram.startTimer()
TypeScript Same as JavaScript

For each usage, the scanner extracts:

  • Usage kind (increment, set, add, sub, observe, time, etc.)
  • Variable name holding the metric
  • Label values (if statically determinable)
  • File location (path, line, column)
  • Function context (containing function and class/impl type)

Alert Scanning

Discovers Prometheus alerting rules in YAML files:

groups:
  - name: example
    rules:
      - alert: HighErrorRate
        expr: rate(errors_total[5m]) > 0.1
        labels:
          severity: critical
        annotations:
          message: "Error rate is high"

For each alert, the scanner extracts:

  • Alert name
  • PromQL expression
  • Primary metric name (extracted from expression)
  • Severity and message
  • File location

Lookup Features

  • Exact matching: Find metrics by exact name
  • Suffix matching: Find http_requests_total when querying myapp_http_requests_total (handles runtime metric prefixes)
  • Fuzzy search: Case-insensitive substring matching
  • Alert lookup: Find alerts that reference a specific metric

Architecture

crates/analyzer/
├── src/
│   ├── lib.rs          # Public API exports
│   ├── index.rs        # CodebaseIndex - builds and queries the index
│   ├── parser.rs       # Tree-sitter parsing utilities
│   ├── repo.rs         # Git operations (clone, fetch)
│   └── scanner/
│       ├── mod.rs      # Scanner trait and registry
│       ├── rust.rs     # Rust metrics-rs scanner
│       ├── python.rs   # Python prometheus_client scanner
│       ├── go.rs       # Go client_golang scanner
│       ├── javascript.rs # JavaScript prom-client scanner
│       ├── typescript.rs # TypeScript prom-client scanner
│       └── yaml.rs     # YAML alert rule scanner

Scanner Trait

Add support for new languages by implementing the Scanner trait:

pub trait Scanner: Send + Sync {
    /// File extensions this scanner handles (e.g., `["rs"]`).
    fn extensions(&self) -> &[&str];

    /// Scan a source file for metric instrumentation points (definitions).
    fn scan_file(&self, path: &Path) -> Result<Vec<MetricInstrumentation>, ParseError>;

    /// Scan a source file for metric usage points (where metrics are recorded).
    /// Default implementation returns empty vec for backward compatibility.
    fn scan_usages(&self, path: &Path) -> Result<Vec<MetricUsage>, ParseError> {
        Ok(Vec::new())
    }
}

Usage

use enya_analyzer::{CodebaseIndex, build_index_with_progress, IndexProgress};
use std::path::Path;

// Build an index from a local repository
let progress = IndexProgress::new();
let index = build_index_with_progress(
    "https://github.com/org/repo.git",
    Path::new("/path/to/repo"),
    &progress,
)?;

// Find metrics by name (supports suffix matching for prefixed metrics)
let metrics = index.find_by_name("myapp_http_requests_total");

// Search for metrics containing a substring
let results = index.search("requests");

// Find alerts referencing a metric
let alerts = index.find_alerts_by_metric("http_requests_total");

Excluded Directories

The scanner automatically excludes:

  • target/ (Rust build output)
  • .git/
  • vendor/
  • node_modules/