Observability
NeuralCleave exposes 13 Prometheus metrics at /api/v1/metrics and emits structured JSON logs via ContextLogger. Both are available from startup with no extra configuration.
Prometheus metrics
All metrics are available at GET /api/v1/metrics in standard Prometheus text/plain format. Scrape it directly from prometheus.yml:
# prometheus.yml
scrape_configs:
- job_name: neuralcleave
static_configs:
- targets: ["localhost:7432"]
metrics_path: /api/v1/metrics
| Metric | Type | Description |
|---|---|---|
nc_requests_total | Counter | Total chat requests processed, labelled by channel and provider |
nc_request_duration_seconds | Histogram | End-to-end request latency from receive to response delivery |
nc_llm_tokens_input_total | Counter | Total input (prompt) tokens sent to LLM providers |
nc_llm_tokens_output_total | Counter | Total output (completion) tokens received from LLM providers |
nc_llm_errors_total | Counter | LLM call failures, labelled by provider and error_type |
nc_reflection_score | Gauge | Most recent ReflectionEngine quality score (0–100) |
nc_reflection_retries_total | Counter | Responses regenerated due to low reflection score |
nc_memory_rows_total | Gauge | Current SQLite long-term memory row count |
nc_memory_compactions_total | Counter | Session compaction events triggered |
nc_active_sessions | Gauge | WebSocket sessions currently connected |
nc_plugin_tool_calls_total | Counter | Plugin tool invocations, labelled by plugin and tool |
nc_channel_messages_total | Counter | Inbound messages per channel adapter |
nc_orchestrator_routes_total | Counter | Orchestrator routing decisions, labelled by node and task_type |
Example metric output
# HELP nc_requests_total Total chat requests processed
# TYPE nc_requests_total counter
nc_requests_total{channel="telegram",provider="anthropic"} 1432
nc_requests_total{channel="discord",provider="anthropic"} 87
# HELP nc_request_duration_seconds Request duration
# TYPE nc_request_duration_seconds histogram
nc_request_duration_seconds_bucket{le="0.5"} 112
nc_request_duration_seconds_bucket{le="1.0"} 876
nc_request_duration_seconds_bucket{le="2.0"} 1401
nc_request_duration_seconds_sum 2843.7
nc_request_duration_seconds_count 1519
# HELP nc_reflection_score Latest reflection quality score
# TYPE nc_reflection_score gauge
nc_reflection_score 91
Structured logging
The gateway uses ContextLogger, which wraps Python's standard logging with a JsonFormatter. Every log line is a JSON object with standard fields:
{
"time": "2026-07-21T14:23:01.412Z",
"level": "INFO",
"logger": "neuralcleave.gateway",
"message": "Chat request complete",
"session_id": "abc123",
"channel": "telegram",
"provider": "anthropic",
"duration_ms": 847,
"tokens": {"input": 143, "output": 89},
"quality": 91
}
Log level
Set in config.toml under [gateway]:
[gateway]
log_level = "DEBUG" # DEBUG | INFO | WARNING | ERROR
Or pass at launch:
neuralcleave start --log-level DEBUG
Using ContextLogger in plugins
Plugins get a pre-configured logger via the setup() context. Use it to emit structured fields alongside the message:
from neuralcleave.logging import get_logger
logger = get_logger("neuralcleave_weather")
async def execute(self, city: str):
logger.info("weather_lookup", city=city)
result = await fetch_weather(city)
logger.info("weather_result", city=city, temp=result["temp"])
return ToolResult(success=True, data=result)
Grafana dashboard
A pre-built Grafana dashboard JSON is in deploy/grafana/neuralcleave-dashboard.json. Import it into any Grafana instance pointed at the Prometheus scrape target above. The dashboard includes panels for: request rate, p50/p99 latency, token usage, reflection score distribution, and per-channel message volume.