mirror of
https://github.com/Routstr/routstrd.git
synced 2026-10-05 20:38:22 +00:00
feat(logs): route coco wallet-engine diagnostics to separate coco-logs
Refactor the file logger into a createLogger(logsDir) factory so the daemon can maintain two independent sinks: the main daemon logs at ~/.routstrd/logs/ and the Cashu wallet-engine (coco) diagnostics at ~/.routstrd/coco-logs/. Coco diagnostics were polluting the main daemon logs even though their metadata is already scrubbed through the allowlist. Writing them to a separate directory keeps the main logs clean while retaining startup counts and operation IDs for debugging. - src/utils/logger.ts: add COCO_LOGS_DIR, FileLogger interface, and createLogger(); export logger and cocoLogger - src/daemon/wallet/coco-client.ts: log coco diagnostics via cocoLogger - src/cli.ts: add -c/--coco flag to the logs command to view wallet engine logs; make getLogFileForDate dir-aware - src/start-daemon.ts: drop unused existsSync import - SKILL.md: document the new flag and coco-logs directory
This commit is contained in:
@@ -191,9 +191,10 @@ View daemon logs.
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `-f, --follow` | false | Follow log output (like `tail -f`) |
|
||||
| `-c, --coco` | false | Show Cashu wallet-engine (coco) logs instead of daemon logs |
|
||||
| `-n, --lines <number>` | 50 | Number of lines to show |
|
||||
|
||||
Log files are stored at `~/.routstrd/logs/YYYY-MM-DD.log`.
|
||||
Log files are stored at `~/.routstrd/logs/YYYY-MM-DD.log`. Wallet-engine (Cashu/coco) diagnostics go to a separate `~/.routstrd/coco-logs/YYYY-MM-DD.log` so they don't pollute the main daemon logs.
|
||||
|
||||
## Wallet Commands
|
||||
|
||||
@@ -317,4 +318,5 @@ When `routstrd onboard` runs, it automatically configures a `routstr` provider i
|
||||
| `~/.routstrd/routstr.db` | SQLite database |
|
||||
| `~/.routstrd/routstrd.sock` | IPC socket |
|
||||
| `~/.routstrd/routstrd.pid` | PID file |
|
||||
| `~/.routstrd/logs/YYYY-MM-DD.log` | Daily log files |
|
||||
| `~/.routstrd/logs/YYYY-MM-DD.log` | Daily daemon log files |
|
||||
| `~/.routstrd/coco-logs/YYYY-MM-DD.log` | Daily Cashu wallet-engine (coco) log files |
|
||||
|
||||
+9
-7
@@ -25,7 +25,7 @@ import {
|
||||
LOGS_DIR,
|
||||
type RoutstrdConfig,
|
||||
} from "./utils/config";
|
||||
import { logger } from "./utils/logger";
|
||||
import { COCO_LOGS_DIR, logger } from "./utils/logger";
|
||||
import { setupIntegration, runIntegrationsForClients } from "./integrations";
|
||||
import { stopLegacyCocod } from "./daemon/wallet/coco-client";
|
||||
import { getClientsList } from "./utils/clients";
|
||||
@@ -1948,11 +1948,11 @@ program
|
||||
});
|
||||
|
||||
// Logs
|
||||
function getLogFileForDate(date: Date = new Date()): string {
|
||||
function getLogFileForDate(logsDir: string, date: Date = new Date()): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${LOGS_DIR}/${year}-${month}-${day}.log`;
|
||||
return `${logsDir}/${year}-${month}-${day}.log`;
|
||||
}
|
||||
|
||||
function readLastLines(file: string, lines: number): string {
|
||||
@@ -1993,17 +1993,19 @@ program
|
||||
.command("logs")
|
||||
.description("View daemon logs")
|
||||
.option("-f, --follow", "Follow log output", false)
|
||||
.option("-c, --coco", "Show Cashu wallet-engine (coco) logs instead of daemon logs", false)
|
||||
.option("-n, --lines <number>", "Number of lines to show", "50")
|
||||
.action(async (options: { follow: boolean; lines: string }) => {
|
||||
.action(async (options: { follow: boolean; lines: string; coco: boolean }) => {
|
||||
await requireLocalDaemon();
|
||||
const todayFile = getLogFileForDate();
|
||||
const logsDir = options.coco ? COCO_LOGS_DIR : LOGS_DIR;
|
||||
const todayFile = getLogFileForDate(logsDir);
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const yesterdayFile = getLogFileForDate(yesterday);
|
||||
const yesterdayFile = getLogFileForDate(logsDir, yesterday);
|
||||
|
||||
if (!existsSync(todayFile) && !existsSync(yesterdayFile)) {
|
||||
console.log("No log files found. Daemon may not have started yet.");
|
||||
console.log(`Logs directory: ${LOGS_DIR}`);
|
||||
console.log(`Logs directory: ${logsDir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import type {
|
||||
NpcAddress,
|
||||
NpcUsernameResult,
|
||||
} from "./cocod-client";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { cocoLogger, logger } from "../../utils/logger";
|
||||
|
||||
const NPC_DEFAULT_BASE_URL = "https://npubx.cash";
|
||||
|
||||
@@ -148,9 +148,10 @@ function createCocoLogger(bindings: Record<string, unknown> = {}): CocoLogger {
|
||||
) => {
|
||||
// Coco diagnostics may contain proof secrets or encoded tokens. Keep only
|
||||
// an explicit metadata allowlist; startup counts and operation IDs remain
|
||||
// useful without copying wallet material into routstrd's logs.
|
||||
// useful without copying wallet material into routstrd's logs. Written to
|
||||
// ~/.routstrd/coco-logs/ so wallet-engine noise stays out of the main logs.
|
||||
const metadata = safeCocoMetadata([bindings, ...meta]);
|
||||
logger[level](
|
||||
cocoLogger[level](
|
||||
`[coco] ${message}`,
|
||||
...(Object.keys(metadata).length > 0 ? [metadata] : []),
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ import { logger } from "./utils/logger";
|
||||
import { CONFIG_DIR, LOGS_DIR } from "./utils/config";
|
||||
import { withCrossProcessLock } from "./utils/process-lock";
|
||||
import { fileURLToPath } from "url";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
const DAEMON_STARTUP_LOCK_PATH = `${CONFIG_DIR}/routstrd-startup.lock`;
|
||||
const DEBUG_LOG_PATH = `${CONFIG_DIR}/debug.log`;
|
||||
|
||||
+30
-25
@@ -4,25 +4,27 @@ import { join } from "path";
|
||||
const HOME = process.env.HOME || process.env.USERPROFILE || "";
|
||||
const LOG_DIR = process.env.ROUTSTRD_DIR || `${HOME}/.routstrd`;
|
||||
const LOGS_DIR = join(LOG_DIR, "logs");
|
||||
/** Wallet-engine (coco-core / Cashu) diagnostics land in their own directory. */
|
||||
export const COCO_LOGS_DIR = join(LOG_DIR, "coco-logs");
|
||||
|
||||
function getLogFileForDate(date: Date = new Date()): string {
|
||||
function getLogFileForDate(logsDir: string, date: Date = new Date()): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return join(LOGS_DIR, `${year}-${month}-${day}.log`);
|
||||
return join(logsDir, `${year}-${month}-${day}.log`);
|
||||
}
|
||||
|
||||
function ensureLogDir() {
|
||||
if (!existsSync(LOGS_DIR)) {
|
||||
mkdirSync(LOGS_DIR, { recursive: true });
|
||||
function ensureDir(dir: string) {
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: writes are synchronous on purpose — the daemon calls process.exit()
|
||||
// right after logger.error(...) on fatal paths, and async writes were being
|
||||
// silently dropped, making startup failures invisible.
|
||||
function writeLog(level: string, ...args: unknown[]) {
|
||||
ensureLogDir();
|
||||
function writeLog(logsDir: string, level: string, ...args: unknown[]) {
|
||||
ensureDir(logsDir);
|
||||
const timestamp = new Date().toISOString();
|
||||
const message = args
|
||||
.map((a) => {
|
||||
@@ -40,7 +42,7 @@ function writeLog(level: string, ...args: unknown[]) {
|
||||
})
|
||||
.join(" ");
|
||||
const line = `[${timestamp}] [${level}] ${message}\n`;
|
||||
const logFile = getLogFileForDate(new Date(timestamp));
|
||||
const logFile = getLogFileForDate(logsDir, new Date(timestamp));
|
||||
try {
|
||||
appendFileSync(logFile, line);
|
||||
} catch (error) {
|
||||
@@ -48,20 +50,23 @@ function writeLog(level: string, ...args: unknown[]) {
|
||||
}
|
||||
}
|
||||
|
||||
export const logger = {
|
||||
log: (...args: unknown[]) => {
|
||||
writeLog("INFO", ...args);
|
||||
},
|
||||
debug: (...args: unknown[]) => {
|
||||
writeLog("DEBUG", ...args);
|
||||
},
|
||||
warn: (...args: unknown[]) => {
|
||||
writeLog("WARN", ...args);
|
||||
},
|
||||
error: (...args: unknown[]) => {
|
||||
writeLog("ERROR", ...args);
|
||||
},
|
||||
info: (...args: unknown[]) => {
|
||||
writeLog("INFO", ...args);
|
||||
},
|
||||
};
|
||||
export interface FileLogger {
|
||||
log: (...args: unknown[]) => void;
|
||||
debug: (...args: unknown[]) => void;
|
||||
warn: (...args: unknown[]) => void;
|
||||
error: (...args: unknown[]) => void;
|
||||
info: (...args: unknown[]) => void;
|
||||
}
|
||||
|
||||
function createLogger(logsDir: string): FileLogger {
|
||||
return {
|
||||
log: (...args) => writeLog(logsDir, "INFO", ...args),
|
||||
debug: (...args) => writeLog(logsDir, "DEBUG", ...args),
|
||||
warn: (...args) => writeLog(logsDir, "WARN", ...args),
|
||||
error: (...args) => writeLog(logsDir, "ERROR", ...args),
|
||||
info: (...args) => writeLog(logsDir, "INFO", ...args),
|
||||
};
|
||||
}
|
||||
|
||||
export const logger = createLogger(LOGS_DIR);
|
||||
export const cocoLogger = createLogger(COCO_LOGS_DIR);
|
||||
|
||||
Reference in New Issue
Block a user