feat: unify runtime logging (rotating files, log root, scripts, desktop)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-14 10:11:51 +08:00
parent 86d4bad107
commit 1490bd5f24
25 changed files with 541 additions and 53 deletions

View file

@ -5,6 +5,8 @@ import os
from pathlib import Path
from typing import Any, Iterable
from svc.config.log_paths import oclaw_hooks_log_dir
def _resolve_state_dir() -> Path:
override = os.environ.get("OCLAW_STATE_DIR") or os.environ.get("OCLAW_HOME")
@ -49,9 +51,7 @@ def handle(event: Any) -> None:
if getattr(event, "type", None) != "gateway" or getattr(event, "action", None) != "startup":
return
state = _resolve_state_dir()
log_dir = state / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
log_dir = oclaw_hooks_log_dir(state_dir_if_legacy=_resolve_state_dir())
out_log = log_dir / "boot-md.log"
now = getattr(event, "timestamp", None)

View file

@ -5,6 +5,8 @@ import os
from pathlib import Path
from typing import Any, Dict
from svc.config.log_paths import oclaw_hooks_log_dir
def _resolve_state_dir() -> Path:
# Keep this compatible with typical Oclaw layouts.
@ -18,9 +20,7 @@ def handle(event) -> None:
if getattr(event, "type", None) != "command":
return
state_dir = _resolve_state_dir()
log_dir = state_dir / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
log_dir = oclaw_hooks_log_dir(state_dir_if_legacy=_resolve_state_dir())
payload: Dict[str, Any] = {
"timestamp": getattr(getattr(event, "timestamp", None), "isoformat", lambda: None)(),

View file

@ -9,6 +9,7 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Any
from svc.config.log_paths import oclaw_log_root
from svc.config.paths import db_path
@ -17,10 +18,7 @@ def _runtime_file() -> Path:
def _runtime_log_dir() -> Path:
p = str(os.getenv("AIA_RUNTIME_LOG_DIR") or "").strip()
if p:
return Path(p).expanduser().resolve()
return Path(db_path()).resolve().parent / "logs"
return oclaw_log_root()
def assistant_runtime_log_dir() -> Path:
@ -29,7 +27,7 @@ def assistant_runtime_log_dir() -> Path:
Same rule as ``AIA_RUNTIME_LOG_DIR`` or ``<parent of sqlite db_path>)/logs``.
Use this from shell scripts so paths match Python ``stack up`` / ``start_service``.
"""
return _runtime_log_dir()
return oclaw_log_root()
def _read_state() -> dict[str, Any]:

View file

@ -0,0 +1,23 @@
# Shared: resolve the same directory as Python ``assistant_runtime_log_dir()`` / ``AIA_RUNTIME_LOG_DIR``.
function Get-OclawRuntimeLogDir {
param(
[Parameter(Mandatory = $true)][string]$RepoRoot
)
$venvPython = Join-Path $RepoRoot ".venv\Scripts\python.exe"
$pythonExe = if (Test-Path $venvPython) { $venvPython } else { "python" }
$savedPyPath = $env:PYTHONPATH
$env:PYTHONPATH = $RepoRoot
$dir = $null
try {
$dir = (& $pythonExe -c "from runtime.operations.runtime import assistant_runtime_log_dir; print(str(assistant_runtime_log_dir()))" 2>$null | Select-Object -Last 1).Trim()
} catch { }
if ($null -ne $savedPyPath) {
$env:PYTHONPATH = $savedPyPath
} else {
Remove-Item Env:PYTHONPATH -ErrorAction SilentlyContinue
}
if (-not $dir) {
$dir = Join-Path $RepoRoot "data\logs"
}
return $dir
}

View file

@ -12,8 +12,6 @@ $desktopDir = Join-Path $repoRoot "desktop"
$runDir = Join-Path $PSScriptRoot ".run"
New-Item -ItemType Directory -Force -Path $runDir | Out-Null
$pidFile = Join-Path $runDir "desktop.pid"
$outLog = Join-Path $runDir "desktop.out.log"
$errLog = Join-Path $runDir "desktop.err.log"
if (-not (Test-Path $desktopDir)) {
Write-Host "[ERROR] desktop directory not found: $desktopDir" -ForegroundColor Red
@ -27,6 +25,12 @@ $env:AIA_WORKSPACE_ROOT = $repoRoot
$env:OPS_WORKSPACE_ROOT = $repoRoot
$env:OCLAW_WORKSPACE = $repoRoot
. (Join-Path $PSScriptRoot "lib\ResolveRuntimeLogDir.ps1")
$runtimeLogDir = Get-OclawRuntimeLogDir -RepoRoot $repoRoot
$env:AIA_RUNTIME_LOG_DIR = $runtimeLogDir
$env:OCLAW_DESKTOP_LOG_ROOT = $runtimeLogDir
New-Item -ItemType Directory -Force -Path $runtimeLogDir | Out-Null
# Do not inherit attach-mode env from a parent shell when launching standalone desktop.
Remove-Item Env:OCLAW_DESKTOP_EMBED_BACKEND -ErrorAction SilentlyContinue
Remove-Item Env:OCLAW_DESKTOP_GATEWAY_BASE_URL -ErrorAction SilentlyContinue
@ -73,7 +77,7 @@ if ($Background) {
}
Set-Content -Path $pidFile -Value "$($p.Id)" -Encoding ascii
Write-Host "desktop.pid = $pidFile" -ForegroundColor DarkGray
Write-Host "Embedded backend/chat logs (Electron): see %APPDATA%\oclaw\logs\desktop.log / backend.log" -ForegroundColor DarkGray
Write-Host "Desktop / embedded gateway file logs: $runtimeLogDir\desktop.log , backend.log , channel-wecom.log" -ForegroundColor DarkGray
Write-Host "PID = $($p.Id)" -ForegroundColor Green
Write-Host "==> Desktop launched in background (this script exits now)." -ForegroundColor Cyan
exit 0

View file

@ -77,14 +77,8 @@ if (-not $SkipInstall) {
Write-Step "Skip dependency install"
}
# Same directory as runtime.operations.runtime.start_service (db_path parent / logs, or AIA_RUNTIME_LOG_DIR).
$logDir = $null
try {
$logDir = (& $pythonExe -c "from runtime.operations.runtime import assistant_runtime_log_dir; print(str(assistant_runtime_log_dir()))" 2>$null | Select-Object -Last 1).Trim()
} catch { }
if (-not $logDir) {
$logDir = Join-Path $repoRoot "data\logs"
}
. (Join-Path $PSScriptRoot "lib\ResolveRuntimeLogDir.ps1")
$logDir = Get-OclawRuntimeLogDir -RepoRoot $repoRoot
Write-Step "Runtime log dir: $logDir"
Write-Host " (stack up / start_service: gateway.err.log + gateway.out.log here)" -ForegroundColor DarkGray
Write-Host " (this script foreground: gateway.foreground.log when log mirror on)" -ForegroundColor DarkGray

View file

@ -13,8 +13,6 @@ $repoRoot = Resolve-RepoRoot
$runDir = Join-Path $PSScriptRoot ".run"
$null = New-Item -ItemType Directory -Force -Path $runDir -ErrorAction SilentlyContinue
$pidFile = Join-Path $runDir "wiki_worker.pid"
$outLog = Join-Path $runDir "wiki_worker.out.log"
$errLog = Join-Path $runDir "wiki_worker.err.log"
function Test-AlivePid([int]$procId) {
try {
@ -51,6 +49,12 @@ $env:AIA_WORKSPACE_ROOT = $repoRoot
$env:OPS_WORKSPACE_ROOT = $repoRoot
$env:OCLAW_WORKSPACE = $repoRoot
. (Join-Path $PSScriptRoot "lib\ResolveRuntimeLogDir.ps1")
$logDir = Get-OclawRuntimeLogDir -RepoRoot $repoRoot
$null = New-Item -ItemType Directory -Force -Path $logDir -ErrorAction SilentlyContinue
$outLog = Join-Path $logDir "wiki_worker.out.log"
$errLog = Join-Path $logDir "wiki_worker.err.log"
if (-not $Background) {
& $pythonExe -m runtime.workers.wiki.main
exit 0

View file

@ -6,10 +6,16 @@ function Warn([string]$msg) {
Write-Host "[WARN] $msg" -ForegroundColor Yellow
}
$repoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
$env:PYTHONPATH = $repoRoot
. (Join-Path $PSScriptRoot "lib\ResolveRuntimeLogDir.ps1")
$logDir = Get-OclawRuntimeLogDir -RepoRoot $repoRoot
$runDir = Join-Path $PSScriptRoot ".run"
$pidFile = Join-Path $runDir "desktop.pid"
$outLog = Join-Path $runDir "desktop.out.log"
$errLog = Join-Path $runDir "desktop.err.log"
$desktopLog = Join-Path $logDir "desktop.log"
$backendLog = Join-Path $logDir "backend.log"
$channelWecomLog = Join-Path $logDir "channel-wecom.log"
if (-not (Test-Path $pidFile)) {
Warn "desktop.pid not found: $pidFile"
@ -29,8 +35,9 @@ try {
Write-Host "desktop_running=1" -ForegroundColor Green
Write-Host "pid=$procId"
Write-Host "name=$($p.ProcessName)"
if (Test-Path $outLog) { Write-Host "out_log=$outLog" }
if (Test-Path $errLog) { Write-Host "err_log=$errLog" }
if (Test-Path $desktopLog) { Write-Host "desktop_log=$desktopLog" }
if (Test-Path $backendLog) { Write-Host "backend_log=$backendLog" }
if (Test-Path $channelWecomLog) { Write-Host "channel_wecom_log=$channelWecomLog" }
exit 0
} catch {
Warn "desktop process not found: PID=$procId"

View file

@ -25,7 +25,6 @@ function Sync-WeixinBridgeRunners {
}
New-Item -ItemType Directory -Force -Path $sidecarRoot | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $sidecarRoot "logs") | Out-Null
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
function Ensure-OfficialPluginRuntimeDeps {

View file

@ -14,11 +14,14 @@ function Resolve-RepoRoot {
$oclawRoot = Resolve-RepoRoot
$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId"
$stateDir = Join-Path $sidecarRoot "state"
$logDir = Join-Path $sidecarRoot "logs"
$pidFile = Join-Path $sidecarRoot "pid.txt"
$bridgeSrc = Join-Path $oclawRoot "runtime\\operations\\weixin_bridge"
$pluginRoot = Join-Path $env:USERPROFILE ".openclaw\\extensions\\openclaw-weixin"
$env:PYTHONPATH = $oclawRoot
. (Join-Path $PSScriptRoot "lib\ResolveRuntimeLogDir.ps1")
$runtimeLogDir = Get-OclawRuntimeLogDir -RepoRoot $oclawRoot
function Get-SidecarProcesses {
$escapedSidecarRoot = $sidecarRoot.Replace("\", "\\")
$patterns = @(
@ -74,8 +77,8 @@ function Ensure-OfficialPluginRuntimeDeps {
if (-not (Test-Path $sidecarRoot)) {
New-Item -ItemType Directory -Force -Path $sidecarRoot | Out-Null
}
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
New-Item -ItemType Directory -Force -Path $runtimeLogDir | Out-Null
$cleaned = Stop-SidecarProcesses
Remove-Item -Force $pidFile -ErrorAction SilentlyContinue
@ -89,8 +92,8 @@ if (Test-Path $bridgeSrc) {
}
}
$logPath = Join-Path $logDir "weixin_sidecar.log"
$errPath = Join-Path $logDir "weixin_sidecar.err.log"
$logPath = Join-Path $runtimeLogDir "weixin_sidecar.log"
$errPath = Join-Path $runtimeLogDir "weixin_sidecar.err.log"
if (Test-Path (Join-Path $sidecarRoot "official_runner.ts")) {
Ensure-OfficialPluginRuntimeDeps
$cmd = "cmd.exe"

View file

@ -17,7 +17,6 @@ function Resolve-RepoRoot {
$oclawRoot = Resolve-RepoRoot
$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId"
$stateDir = Join-Path $sidecarRoot "state"
$logDir = Join-Path $sidecarRoot "logs"
$bridgeSrc = Join-Path $oclawRoot "runtime\\operations\\whatsapp_bridge"
# Pin known-good versions to avoid breakage from upstream latest releases.
@ -29,7 +28,6 @@ $typescriptVersion = "5.8.2"
New-Item -ItemType Directory -Force -Path $sidecarRoot | Out-Null
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
Push-Location $sidecarRoot
try {

View file

@ -14,15 +14,18 @@ function Resolve-RepoRoot {
$oclawRoot = Resolve-RepoRoot
$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId"
$stateDir = Join-Path $sidecarRoot "state"
$logDir = Join-Path $sidecarRoot "logs"
$sidecarPidFile = Join-Path $sidecarRoot "pid.txt"
$env:PYTHONPATH = $oclawRoot
. (Join-Path $PSScriptRoot "lib\ResolveRuntimeLogDir.ps1")
$runtimeLogDir = Get-OclawRuntimeLogDir -RepoRoot $oclawRoot
New-Item -ItemType Directory -Force -Path $sidecarRoot | Out-Null
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
New-Item -ItemType Directory -Force -Path $runtimeLogDir | Out-Null
$logPath = Join-Path $logDir "whatsapp_sidecar.log"
$errPath = Join-Path $logDir "whatsapp_sidecar.err.log"
$logPath = Join-Path $runtimeLogDir "whatsapp_sidecar.log"
$errPath = Join-Path $runtimeLogDir "whatsapp_sidecar.err.log"
$systemNodeDir = "C:\\Program Files\\nodejs"
if (Test-Path (Join-Path $systemNodeDir "node.exe")) {
$env:PATH = "$systemNodeDir;$env:PATH"

View file

@ -337,4 +337,9 @@ def run_worker() -> int:
if __name__ == "__main__":
from svc.config.bootstrap_env import load_system_env
from svc.observability.logging_setup import configure_oclaw_logging
load_system_env()
configure_oclaw_logging(service_name="wiki-worker", include_uvicorn_formatters=False)
raise SystemExit(run_worker())