feat(server): migrate all logging to pino

- Add pino with pino-pretty for structured logging
- Create root logger in index.ts with child loggers per module
- Support log level via PASEO_LOG env var and config.json
- Support log format (pretty/json) via PASEO_LOG_FORMAT and config.json
- Replace all console.* in runtime code with appropriate log levels
- Scripts use process.stdout/stderr for output
- Error logging uses correct pattern: logger.error({ err }, msg)
This commit is contained in:
Mohamed Boudra
2026-01-15 17:16:01 +07:00
parent ef853914d5
commit b8f056060f
48 changed files with 1275 additions and 887 deletions
@@ -57,9 +57,9 @@ class Pushable<T> implements AsyncIterable<T> {
}
async function investigateCommand(commandName: string): Promise<void> {
console.log(`\n${"=".repeat(60)}`);
console.log(`Investigating: /${commandName}`);
console.log("=".repeat(60));
process.stdout.write(`\n${"=".repeat(60)}\n`);
process.stdout.write(`Investigating: /${commandName}\n`);
process.stdout.write(`${"=".repeat(60)}\n`);
const input = new Pushable<SDKUserMessage>();
@@ -89,70 +89,70 @@ async function investigateCommand(commandName: string): Promise<void> {
let messageCount = 0;
for await (const message of claudeQuery) {
messageCount++;
console.log(`\n--- Message ${messageCount} ---`);
console.log(`Type: ${message.type}`);
process.stdout.write(`\n--- Message ${messageCount} ---\n`);
process.stdout.write(`Type: ${message.type}\n`);
// Log the full structure based on type
switch (message.type) {
case "system":
console.log(`Subtype: ${message.subtype}`);
process.stdout.write(`Subtype: ${message.subtype}\n`);
if (message.subtype === "init") {
console.log(`Session: ${message.session_id}`);
console.log(`Model: ${message.model}`);
process.stdout.write(`Session: ${message.session_id}\n`);
process.stdout.write(`Model: ${message.model}\n`);
}
break;
case "user":
console.log(`User message content:`, JSON.stringify(message.message?.content, null, 2));
process.stdout.write(`User message content: ${JSON.stringify(message.message?.content, null, 2)}\n`);
break;
case "assistant":
console.log(`Assistant message content:`);
process.stdout.write("Assistant message content:\n");
const content = message.message?.content;
if (Array.isArray(content)) {
for (const block of content) {
console.log(` Block type: ${block.type}`);
process.stdout.write(` Block type: ${block.type}\n`);
if (block.type === "text") {
console.log(` Text: ${block.text}`);
process.stdout.write(` Text: ${block.text}\n`);
} else if (block.type === "tool_use") {
console.log(` Tool: ${block.name}`);
console.log(` Input: ${JSON.stringify(block.input, null, 2)}`);
process.stdout.write(` Tool: ${block.name}\n`);
process.stdout.write(` Input: ${JSON.stringify(block.input, null, 2)}\n`);
} else {
console.log(` Full block: ${JSON.stringify(block, null, 2)}`);
process.stdout.write(` Full block: ${JSON.stringify(block, null, 2)}\n`);
}
}
} else {
console.log(` Content: ${JSON.stringify(content, null, 2)}`);
process.stdout.write(` Content: ${JSON.stringify(content, null, 2)}\n`);
}
break;
case "stream_event":
console.log(`Stream event type: ${message.event?.type}`);
process.stdout.write(`Stream event type: ${message.event?.type}\n`);
if (message.event?.type === "content_block_delta") {
const delta = message.event.delta;
if (delta?.type === "text_delta") {
console.log(` Text delta: ${delta.text}`);
process.stdout.write(` Text delta: ${delta.text}\n`);
}
}
break;
case "result":
console.log(`Result subtype: ${message.subtype}`);
process.stdout.write(`Result subtype: ${message.subtype}\n`);
if ("errors" in message && message.errors) {
console.log(`Errors: ${JSON.stringify(message.errors)}`);
process.stdout.write(`Errors: ${JSON.stringify(message.errors)}\n`);
}
// Check for any other properties
const resultKeys = Object.keys(message).filter(k => !["type", "subtype"].includes(k));
if (resultKeys.length > 0) {
console.log(`Other result properties: ${resultKeys.join(", ")}`);
process.stdout.write(`Other result properties: ${resultKeys.join(", ")}\n`);
for (const key of resultKeys) {
console.log(` ${key}: ${JSON.stringify((message as any)[key], null, 2)}`);
process.stdout.write(` ${key}: ${JSON.stringify((message as any)[key], null, 2)}\n`);
}
}
break;
default:
console.log(`Full message: ${JSON.stringify(message, null, 2)}`);
process.stdout.write(`Full message: ${JSON.stringify(message, null, 2)}\n`);
}
if (message.type === "result") {
@@ -160,7 +160,7 @@ async function investigateCommand(commandName: string): Promise<void> {
}
}
console.log(`\nTotal messages received: ${messageCount}`);
process.stdout.write(`\nTotal messages received: ${messageCount}\n`);
} finally {
input.end();
@@ -169,7 +169,7 @@ async function investigateCommand(commandName: string): Promise<void> {
}
async function main() {
console.log("=== Command Output Investigation ===\n");
process.stdout.write("=== Command Output Investigation ===\n\n");
// Test /context - a local command that shows context info
await investigateCommand("context");
@@ -180,10 +180,10 @@ async function main() {
// Test /prompt-engineer - a SKILL (not a local command)
await investigateCommand("prompt-engineer");
console.log("\n=== Investigation Complete ===");
process.stdout.write("\n=== Investigation Complete ===\n");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.stderr.write(`Fatal error: ${error}\n`);
process.exit(1);
});