Lesson
Logging and Debugging Cursor Hooks
Learn how to effectively debug and log Cursor hooks, moving from simple console errors to a persistent, rolling JSONL file managed with the help of an AI agent.
- Access
- Included
- Transcript
- Needs source
Debugging hooks can be confusing at first. Using console.log doesn't work because stdout is reserved for communicating with the AI. You need a different approach for both quick debugging and production-ready logging.
This lesson takes you from quick debugging with console.error to building a robust, persistent logging system using JSONL files. You'll learn why certain outputs are silent, how to see immediate debug information, and how to create a production logging solution with automatic log rotation—all with the AI's help.
The Immediate Fix: Use console.error
The simplest way to see debug output from your hooks is to use console.error instead of console.log. This directs your log messages to standard error (stderr), which Cursor displays in the "Hooks" output panel.
// This will be silent in the output panel
console.log(JSON.stringify(output, null, 2));
// This will appear in the STDERR section of the output panel
console.error(JSON.stringify(output, null, 2));
While effective for quick checks, this method's output is transient and can be lost among other messages. For more reliable debugging, a persistent logging solution is necessary.
Building a Persistent Log File
For production hooks, you need a complete history of what your hooks have done. Writing logs to a dedicated file gives you this history, making complex debugging much easier.
The JSON Lines (.jsonl) format is ideal for hook logging. Each line is a complete, valid JSON object, and you can append new entries indefinitely. This format is both human-readable and easy to process with tools.
The implementation is straightforward:
- Create a
logsdirectory in your project - Use Node.js's
fs/promisesandpathmodules to append data - Enrich each log entry with timestamps and the full input payload for context
import { appendFile } from "node:fs/promises";
import { join } from "node:path";
// ... inside your hook logic
const output = {
timestamp: new Date().toISOString(),
...input, // Include the full input payload for context
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
exitCode: result.exitCode,
};
const logFilePath = join(input.workspace_roots[0]!, "logs", "after-file-edit.jsonl");
await appendFile(logFilePath, JSON.stringify(output) + '\n');
Automating Log Management with AI
Log files grow forever if left unchecked, consuming disk space and becoming unwieldy. Instead of manually implementing log rotation, you can ask the AI to build it for you.
Give the AI your logging code and a clear instruction:
Once there are over 500 JSON objects in this file, please remove the first 100 to keep this log rolling.
The AI will generate code to count entries, trim old ones when the threshold is reached, and keep your logs at a manageable size. You can iterate with the AI to refine the logic—like ensuring the counting method correctly handles only top-level objects, not nested ones.
This demonstrates a powerful workflow: using Cursor's AI to build the very automation that manages Cursor's hooks. The AI becomes your co-pilot for creating production-ready hook systems.
Don't Forget: .gitignore
Always add your logs directory to .gitignore to prevent committing large or sensitive log files:
# .gitignore
logs/
With this complete logging strategy, you have both immediate debugging visibility and a persistent, manageable history. Your hooks become production-ready—reliable, observable, and easy to troubleshoot when issues arise.
Prompts
@index.ts Please add some comments.
Add some more comments
remove all the comments
Add the comments back
remove the comments again
Once there are over 500 JSON objects in this file, please remove the first 100 to keep this log rolling.
Make sure this is only checking an opening brace on a new line to avoid matching nested objects.
Code Snippets
// Use console.error for debugging output in hooks
console.error(JSON.stringify(output, null, 2));
// Import Node.js modules for file operations
import { appendFile, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
// Setup for logging to a JSON Lines file
const output = {
timestamp: new Date().toISOString(),
...input,
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
exitCode: result.exitCode,
};
const logFilePath = join(input.workspace_roots[0]!, "logs", "after-file-edit.jsonl");
await appendFile(logFilePath, JSON.stringify(output) + '\n');
// Refined regex to count top-level JSON objects at the start of a line
const entryCount = (logContent.match(/^s*{/gm) || []).length;