152 lines
6.7 KiB
Markdown
152 lines
6.7 KiB
Markdown
# 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
|