Files
2026-07-10 15:02:09 +01:00

152 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Logging & Configuration
## Logging Setup
Log files are written to `logs/` using `tracing-appender` with daily rotation:
```
logs/skald.log.YYYY-MM-DD
```
A new file is created each day. The non-blocking writer is initialized in `main()` and the `_log_guard` is kept alive for the full process lifetime to ensure all buffered logs are flushed on shutdown.
The subscriber has **two layers** (`main.rs`):
| Layer | Writer | Filter | Format |
|---|---|---|---|
| File | `logs/skald.log` (daily) | `EnvFilter` (`RUST_LOG`, default `info`) | full structured (timestamp, level, target, fields) |
| Stdout | terminal | `boot` target only | minimal — message only, failures in red |
**Runtime output goes only to the file.** Stdout carries just the curated
**bootstrap** lines (see below); once the app is up nothing else is printed there.
---
## Bootstrap output (stdout)
During startup a small, ordered set of human-readable lines is printed to stdout
so you can see at a glance how the app is configured and how it is coming up:
```
skald v0.1.0 — starting
Database ready (schema v16)
MCP servers — connecting to 18 in background
✓ codebase-memory (14 tools)
Plugins — 6 active, 1 failed, 2 available
✓ honcho, telegram, comfyui, elevenlabs, mobile-connector, whisper_local
✗ remote_connectivity — creating tailscale device
○ orpheus_tts_3b, kokoro_tts
✅ Ready — http://localhost:3000
✓ gcal (8 tools)
...
```
These are emitted via the helpers in `src/boot.rs` (`title`, `section`, `ok`,
`off`, `fail`, `ready`) on the `boot` tracing target. They are rendered by a
dedicated stdout layer that:
- shows **only** the `boot` target (it ignores `RUST_LOG`, so bootstrap output
always appears);
- strips timestamps/levels/targets and colours failures red (ANSI only on a TTY);
- still lets the same lines reach the **file** log as a high-level startup trace.
Note the glyph convention: `✓` started/connected, `✗` failed (with reason),
`○` available but disabled, `` phase header, `✅` ready.
**MCP servers connect asynchronously** and do not block startup, so their `✓`/`✗`
lines stream in as each server responds — some may appear *after* the `✅ Ready`
line. The app is usable as soon as `Ready` prints (HTTP listening); MCP tools
become available as their servers connect.
To add a bootstrap line from anywhere in the binary crate, call
`crate::boot::section("…")` (or `ok`/`fail`/`off`). Keep them few and targeted —
this is a curated summary, not a log.
---
## Log Levels and RUST_LOG
Default level: **`info`**
Override with the `RUST_LOG` env var:
| Example | Effect |
|---|---|
| `RUST_LOG=info` | Default: info and above |
| `RUST_LOG=skald=debug,info` | Debug for this crate, info for dependencies |
| `RUST_LOG=trace` | Everything (very verbose) |
Level semantics:
| Level | Use for |
|---|---|
| `ERROR` | Failures requiring a code or config fix (config load failure, DB init error, LLM loop exhausted) |
| `WARN` | Non-critical anomalies to fix eventually (malformed API response, agent skipped) |
| `INFO` | Normal significant events (server started, session opened, job executed, response tokens) |
| `DEBUG` | Per-operation lifecycle (request sent to LLM, tool dispatched, context built) |
| `TRACE` | Fine-grained internals (round counters, full request bodies, message arrays) |
A dropped WebSocket connection, a cancelled request, or a completed session are **INFO** at most — they are expected runtime events, not errors.
---
## config.yml Structure
Loaded by `Config::load()` at startup. Copied from `default.config.yaml` if `config.yml` does not exist. **Never commit `config.yml`** — it may contain API keys.
| Section | Key | Type | Default | Notes |
|---|---|---|---|---|
| `server` | `host` | string | `127.0.0.1` | Bind address |
| `server` | `port` | u16 | `3000` | HTTP/WS port |
| `web` | `static_dir` | string | `./web` | Path to static frontend files |
| `db` | `path` | string | `./database.db` | SQLite file path |
| `llm` | `max_history_messages` | usize | `30` | Max messages kept per context window. Ignored when `compaction` is configured — the compactor manages the token budget instead. |
| `llm` | `max_tool_rounds` | usize? | `20` | Max tool-call rounds per message; falls back to `DEFAULT_MAX_TOOL_ROUNDS` |
| `llm.requests_log` | `enabled` | bool | `false` | Log every LLM call to the `llm_requests` table. **Disabling this also disables home-page LLM statistics.** |
| `llm.requests_log` | `request_payload_save` | bool | `true` | Persist request JSON (can be hundreds of KB per call) |
| `llm.requests_log` | `response_payload_save` | bool | `true` | Persist response JSON |
| `llm.requests_log` | `request_header_save` | bool | `true` | Persist request HTTP headers (api-key always redacted) |
| `llm.requests_log` | `response_header_save` | bool | `true` | Persist response HTTP headers |
| `llm.requests_log` | `cleanup_request_payload_after` | u32? | `null` | Set `request_json = ''` for rows older than N days |
| `llm.requests_log` | `cleanup_response_payload_after` | u32? | `null` | Set `response_json = NULL` for rows older than N days |
| `llm.requests_log` | `cleanup_headers_after` | u32? | `null` | Null out both header columns for rows older than N days |
| `llm.requests_log` | `cleanup_rows_after` | u32? | `null` | Physically delete rows older than N days (`null` = keep forever) |
The `llm.clients` block in `config.yml` is for reference only — actual runtime LLM providers and models are stored in the DB and managed via the UI or API.
---
## LLM Providers in config.yml
| Provider | Required fields | Optional fields |
|---|---|---|
| `lm_studio` | `model` | `base_url` (default: `http://localhost:1234/v1`) |
| `ollama` | `model` | `base_url` (default: `http://localhost:11434`) |
| `openai` | `model`, `api_key` | `base_url`, `strength`, `scope` |
| `anthropic` | `model`, `api_key` | `strength`, `scope` |
| `open_ai` (OpenRouter) | `model`, `api_key`, `base_url` | `strength`, `scope`, `extra_params` |
`extra_params` is merged into the request body top-level (used for provider-specific fields like `reasoning.effort`).
---
## config.yml vs DB
| What | Where | How to change |
|---|---|---|
| Server host/port | `config.yml` | Edit file, restart app |
| DB path | `config.yml` | Edit file, restart app |
| History/round limits | `config.yml` | Edit file, restart app |
| LLM providers | DB (`llm_providers`) | UI or REST API |
| LLM models | DB (`llm_models`) | UI or REST API |
| MCP servers | DB (`mcp_servers`) | `register_mcp` tool or UI |
| Cron jobs | DB (`scheduled_jobs`) | `execute_task` tool (mode=cron) or UI |
---
## When to Update This File
- `Config` struct gains or loses a field
- Log level semantics change (see also `memory/feedback_logging.md`)
- `config.yml` gains a new section or key