Ctrl K
ComunidadGuías…

docs/src/metrics-and-tracing.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Metrics and Tracing

Moltis includes comprehensive observability support through Prometheus metrics and
tracing integration. This document explains how to enable, configure, and use
these features.

## Overview

The metrics system is built on the [`metrics`](https://docs.rs/metrics) crate
facade, which provides a unified interface similar to the `log` crate. When the
`prometheus` feature is enabled, metrics are exported in Prometheus text format
for scraping by Grafana, Prometheus, or other monitoring tools.

All metrics are **feature-gated** — they add zero overhead when disabled.

## Feature Flags

Metrics are controlled by two feature flags:

| Feature | Description | Default |
|---------|-------------|---------|
| `metrics` | Enables metrics collection and the `/api/metrics` JSON API | Enabled |
| `prometheus` | Enables the `/metrics` Prometheus endpoint (requires `metrics`) | Enabled |

### Compile-Time Configuration

```toml
# Enable only metrics collection (no Prometheus endpoint)
moltis-gateway = { version = "0.1", features = ["metrics"] }

# Enable metrics with Prometheus export (default)
moltis-gateway = { version = "0.1", features = ["metrics", "prometheus"] }

# Enable metrics for specific crates
moltis-agents = { version = "0.1", features = ["metrics"] }
moltis-cron = { version = "0.1", features = ["metrics"] }
```

To build without metrics entirely:

```bash
cargo build --release --no-default-features --features "file-watcher,tailscale,tls,web-ui"
```

## Prometheus Endpoint

When the `prometheus` feature is enabled, the gateway exposes a `/metrics` endpoint:

```
GET http://localhost:18789/metrics
```

This endpoint is **unauthenticated** to allow Prometheus scrapers to access it.
It returns metrics in Prometheus text format:

```
# HELP moltis_http_requests_total Total number of HTTP requests handled
# TYPE moltis_http_requests_total counter
moltis_http_requests_total{method="GET",status="200",endpoint="/api/chat"} 42

# HELP moltis_llm_completion_duration_seconds Duration of LLM completion requests
# TYPE moltis_llm_completion_duration_seconds histogram
moltis_llm_completion_duration_seconds_bucket{provider="anthropic",model="claude-3-opus",le="1.0"} 5
```

### Grafana Integration

To scrape metrics with Prometheus and visualize in Grafana:

1. Add moltis to your `prometheus.yml`:

```yaml
scrape_configs:
  - job_name: 'moltis'
    static_configs:
      - targets: ['localhost:18789']
    metrics_path: /metrics
    scrape_interval: 15s
```

2. Import or create Grafana dashboards using the `moltis_*` metrics.

## JSON API Endpoints

For the web UI dashboard and programmatic access, authenticated JSON endpoints
are available:

| Endpoint | Description |
|----------|-------------|
| `GET /api/metrics` | Full metrics snapshot with aggregates and per-provider breakdown |
| `GET /api/metrics/summary` | Lightweight counts for navigation badges |
| `GET /api/metrics/history` | Time-series data points for charts (last hour, 10s intervals) |

### History Endpoint

The `/api/metrics/history` endpoint returns historical metrics data for rendering
time-series charts:

```json
{
  "enabled": true,
  "interval_seconds": 10,
  "max_points": 60480,
  "points": [
    {
      "timestamp": 1706832000000,
      "llm_completions": 42,
      "llm_input_tokens": 15000,
      "llm_output_tokens": 8000,
      "http_requests": 150,
      "ws_active": 3,
      "tool_executions": 25,
      "mcp_calls": 12,
      "active_sessions": 2
    }
  ]
}
```

## Metrics Persistence

Metrics history is persisted to SQLite, so historical data survives server
restarts. The database is stored at `~/.moltis/metrics.db` (or the configured
data directory).

Key features:

- **7-day retention**: History is kept for 7 days (60,480 data points at
  10-second intervals)
- **Automatic cleanup**: Old data is automatically removed hourly
- **Startup recovery**: History is loaded from the database when the server
  starts

The storage backend uses a trait-based design (`MetricsStore`), allowing
alternative implementations (e.g., TimescaleDB) for larger deployments.

### Storage Architecture

```rust
// The MetricsStore trait defines the storage interface
#[async_trait]
pub trait MetricsStore: Send + Sync {
    async fn save_point(&self, point: &MetricsHistoryPoint) -> Result<()>;
    async fn load_history(&self, since: u64, limit: usize) -> Result<Vec<MetricsHistoryPoint>>;
    async fn cleanup_before(&self, before: u64) -> Result<u64>;
    async fn latest_point(&self) -> Result<Option<MetricsHistoryPoint>>;
}
```

The default `SqliteMetricsStore` implementation stores data in a single table
with an index on the timestamp column for efficient range queries.

## Web UI Dashboard

The gateway includes a built-in metrics dashboard at `/monitoring` in the web UI.
This page displays:

**Overview Tab:**
- System metrics (uptime, connected clients, active sessions)
- LLM usage (completions, tokens, cache statistics)
- Tool execution statistics
- MCP server status
- Provider breakdown table
- Prometheus endpoint (with copy button)

**Charts Tab:**
- Token usage over time (input/output)
- HTTP requests and LLM completions
- WebSocket connections and active sessions
- Tool executions and MCP calls

The dashboard uses [uPlot](https://github.com/leeoniya/uPlot) for lightweight,
high-performance time-series charts. Data updates every 10 seconds for current
metrics and every 30 seconds for history.

## Available Metrics

### HTTP Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_http_requests_total` | Counter | method, status, endpoint | Total HTTP requests |
| `moltis_http_request_duration_seconds` | Histogram | method, status, endpoint | Request latency |
| `moltis_http_requests_in_flight` | Gauge | — | Currently processing requests |

### LLM/Agent Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_llm_completions_total` | Counter | provider, model | Total completions requested |
| `moltis_llm_completion_duration_seconds` | Histogram | provider, model | Completion latency |
| `moltis_llm_input_tokens_total` | Counter | provider, model | Input tokens processed |
| `moltis_llm_output_tokens_total` | Counter | provider, model | Output tokens generated |
| `moltis_llm_completion_errors_total` | Counter | provider, model, error_type | Completion failures |
| `moltis_llm_time_to_first_token_seconds` | Histogram | provider, model | Streaming TTFT |

#### Provider Aliases

When you have multiple instances of the same provider type (e.g., separate API keys
for work and personal use), you can use the `alias` configuration option to
differentiate them in metrics:

```toml
[providers.anthropic]
api_key = "sk-work-..."
alias = "anthropic-work"

# Note: You would need separate config sections for multiple instances
# of the same provider. This is a placeholder for future functionality.
```

The alias appears in the `provider` label of all LLM metrics:

```
moltis_llm_input_tokens_total{provider="anthropic-work", model="claude-3-opus"} 5000
moltis_llm_input_tokens_total{provider="anthropic-personal", model="claude-3-opus"} 3000
```

This allows you to:
- Track token usage separately for billing purposes
- Create separate Grafana dashboards per provider instance
- Monitor rate limits and quotas independently

### MCP (Model Context Protocol) Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_mcp_tool_calls_total` | Counter | server, tool | Tool invocations |
| `moltis_mcp_tool_call_duration_seconds` | Histogram | server, tool | Tool call latency |
| `moltis_mcp_tool_call_errors_total` | Counter | server, tool, error_type | Tool call failures |
| `moltis_mcp_servers_connected` | Gauge | — | Active MCP server connections |

### Tool Execution Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_tool_executions_total` | Counter | tool | Tool executions |
| `moltis_tool_execution_duration_seconds` | Histogram | tool | Execution time |
| `moltis_sandbox_command_executions_total` | Counter | — | Sandbox commands run |

### Session Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_sessions_created_total` | Counter | — | Sessions created |
| `moltis_sessions_active` | Gauge | — | Currently active sessions |
| `moltis_session_messages_total` | Counter | role | Messages by role |

### Cron Job Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_cron_jobs_scheduled` | Gauge | — | Number of scheduled jobs |
| `moltis_cron_executions_total` | Counter | — | Job executions |
| `moltis_cron_execution_duration_seconds` | Histogram | — | Job duration |
| `moltis_cron_errors_total` | Counter | — | Failed jobs |
| `moltis_cron_stuck_jobs_cleared_total` | Counter | — | Jobs exceeding 2h timeout |
| `moltis_cron_input_tokens_total` | Counter | — | Input tokens from cron runs |
| `moltis_cron_output_tokens_total` | Counter | — | Output tokens from cron runs |

### Memory/Search Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_memory_searches_total` | Counter | search_type | Searches performed |
| `moltis_memory_search_duration_seconds` | Histogram | search_type | Search latency |
| `moltis_memory_embeddings_generated_total` | Counter | provider | Embeddings created |

### Channel Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_channels_active` | Gauge | — | Loaded channel plugins |
| `moltis_channel_messages_received_total` | Counter | channel | Inbound messages |
| `moltis_channel_messages_sent_total` | Counter | channel | Outbound messages |
| `moltis_channel_otp_challenges_total` | Counter | channel | OTP challenges issued to non-allowlisted users |
| `moltis_channel_otp_verifications_total` | Counter | channel, result | OTP verification attempts (`approved`, `wrong_code`, `locked_out`, `expired`) |

### Telegram-Specific Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_telegram_messages_received_total` | Counter | — | Messages from Telegram |
| `moltis_telegram_access_control_denials_total` | Counter | — | Access denied events |
| `moltis_telegram_polling_duration_seconds` | Histogram | — | Message handling time |

### OAuth Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_oauth_flow_starts_total` | Counter | — | OAuth flows initiated |
| `moltis_oauth_flow_completions_total` | Counter | — | Successful completions |
| `moltis_oauth_token_refresh_total` | Counter | — | Token refreshes |
| `moltis_oauth_token_refresh_failures_total` | Counter | — | Refresh failures |

### Skills Metrics

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `moltis_skills_installation_attempts_total` | Counter | — | Installation attempts |
| `moltis_skills_installation_duration_seconds` | Histogram | — | Installation time |
| `moltis_skills_git_clone_total` | Counter | — | Successful git clones |
| `moltis_skills_git_clone_fallback_total` | Counter | — | Fallbacks to HTTP tarball |

## Tracing Integration

The `moltis-metrics` crate includes optional tracing integration via the
`tracing` feature. This allows span context to propagate to metric labels.

### Enabling Tracing

```toml
moltis-metrics = { version = "0.1", features = ["prometheus", "tracing"] }
```

### Initialization

```rust
use moltis_metrics::tracing_integration::init_tracing;

fn main() {
    // Initialize tracing with metrics context propagation
    init_tracing();

    // Now spans will add labels to metrics
}
```

### How It Works

When tracing is enabled, span fields are automatically added as metric labels:

```rust
use tracing::instrument;

#[instrument(fields(operation = "fetch_user", component = "api"))]
async fn fetch_user(id: u64) -> User {
    // Metrics recorded here will include:
    // - operation="fetch_user"
    // - component="api"
    counter!("api_calls_total").increment(1);
}
```

### Span Labels

The following span fields are propagated to metrics:

| Field | Description |
|-------|-------------|
| `operation` | The operation being performed |
| `component` | The component/module name |
| `span.name` | The span's target/name |

## Adding Custom Metrics

### In Your Code

Use the `metrics` macros re-exported from `moltis-metrics`:

```rust
use moltis_metrics::{counter, gauge, histogram, labels};

// Simple counter
counter!("my_custom_requests_total").increment(1);

// Counter with labels
counter!(
    "my_custom_requests_total",
    labels::ENDPOINT => "/api/users",
    labels::METHOD => "GET"
).increment(1);

// Gauge (current value)
gauge!("my_queue_size").set(42.0);

// Histogram (distribution)
histogram!("my_operation_duration_seconds").record(0.123);
```

### Feature-Gating

Always gate metrics code to avoid overhead when disabled:

```rust
#[cfg(feature = "metrics")]
use moltis_metrics::{counter, histogram};

pub async fn my_function() {
    #[cfg(feature = "metrics")]
    let start = std::time::Instant::now();

    // ... do work ...

    #[cfg(feature = "metrics")]
    {
        counter!("my_operations_total").increment(1);
        histogram!("my_operation_duration_seconds")
            .record(start.elapsed().as_secs_f64());
    }
}
```

### Adding New Metric Definitions

For consistency, add metric name constants to `crates/metrics/src/definitions.rs`:

```rust
/// My feature metrics
pub mod my_feature {
    /// Total operations performed
    pub const OPERATIONS_TOTAL: &str = "moltis_my_feature_operations_total";
    /// Operation duration in seconds
    pub const OPERATION_DURATION_SECONDS: &str = "moltis_my_feature_operation_duration_seconds";
}
```

Then use them:

```rust
use moltis_metrics::{counter, my_feature};

counter!(my_feature::OPERATIONS_TOTAL).increment(1);
```

## Configuration

Metrics configuration in `moltis.toml`:

```toml
[metrics]
enabled = true              # Enable metrics collection (default: true)
prometheus_endpoint = true  # Expose /metrics endpoint (default: true)
labels = { env = "prod" }   # Add custom labels to all metrics
```

Environment variables:

- `RUST_LOG=moltis_metrics=debug` — Enable debug logging for metrics initialization

## Best Practices

1. **Use consistent naming**: Follow the pattern `moltis_<subsystem>_<metric>_<unit>`
2. **Add units to names**: `_total` for counters, `_seconds` for durations, `_bytes` for sizes
3. **Keep cardinality low**: Avoid high-cardinality labels (like user IDs or request IDs)
4. **Feature-gate everything**: Use `#[cfg(feature = "metrics")]` to ensure zero overhead when disabled
5. **Use predefined buckets**: The `buckets` module has standard histogram buckets for common metric types

## Troubleshooting

### Metrics not appearing

1. Verify the `metrics` feature is enabled at compile time
2. Check that the metrics recorder is initialized (happens automatically in gateway)
3. Ensure you're hitting the correct `/metrics` endpoint
4. Check `moltis.toml` has `[metrics] enabled = true`

### Prometheus endpoint not available

1. Ensure the `prometheus` feature is enabled (it's separate from `metrics`)
2. Check your build: `cargo build --features prometheus`

### High memory usage

- Check for high-cardinality labels (many unique label combinations)
- Consider reducing histogram bucket counts

### Missing labels

- Ensure labels are passed consistently across all metric recordings
- Check that tracing spans include the expected fields