Skip to content

Instantly share code, notes, and snippets.

@mortenscheel
Created September 12, 2025 08:53
Show Gist options
  • Save mortenscheel/6c70a5f65bd89dce828643a46e4c702e to your computer and use it in GitHub Desktop.
Save mortenscheel/6c70a5f65bd89dce828643a46e4c702e to your computer and use it in GitHub Desktop.
Tail laravel logs, skipping stack traces
#!/usr/bin/env node
const fs = require("fs");
const readline = require("readline");
const { spawn } = require("child_process");
const path = require("path");
// Parse command line arguments
const args = process.argv.slice(2);
const options = {
reset: args.includes("--reset"),
all: args.includes("--all"),
skipVendor: args.includes("--skip-vendor"),
};
console.clear();
// Remove options from args to get potential filepath
const filepath =
args.filter((arg) => !arg.startsWith("--"))[0] ||
path.join(process.cwd(), "storage", "logs", "laravel.log");
// Default skip patterns
const skippable = [/^#/, /^\[stacktrace\]/, /^"} {"uid"/];
const isSkippable = (line) => {
// Don't skip anything if --all is specified
if (options.all) return false;
// Special handling for --skip-vendor
if (options.skipVendor) {
if (line.startsWith("#") && line.includes("/vendor")) {
return true;
}
}
if (["[", "#"].indexOf(line[0]) === -1) {
return true;
}
// Regular skip patterns
return skippable.some((regex) => regex.test(line));
};
// Reset log file if requested
if (options.reset && fs.existsSync(filepath)) {
try {
fs.unlinkSync(filepath);
fs.writeFileSync(filepath, "", { flag: "w" });
console.log(`Log file reset: ${filepath}`);
} catch (error) {
console.error(`Error resetting log file: ${error.message}`);
process.exit(1);
}
}
// Validate the file exists
if (!fs.existsSync(filepath)) {
console.error(`Error: File not found: ${filepath}`);
console.error(
"Usage: node script.js [filepath] [--reset] [--all] [--skip-vendor]"
);
process.exit(1);
}
// Print startup information
console.log(`Monitoring: ${filepath}`);
if (options.all) {
console.log("Mode: Showing all log entries (--all)");
} else if (options.skipVendor) {
console.log("Mode: Skipping vendor paths (--skip-vendor)");
} else {
console.log("Mode: Standard filtering");
}
console.log("Waiting for new log entries...");
// Use tail -f -n 0 to only show new lines (skip existing content)
const tail = spawn("tail", ["-f", "-n", "0", filepath]);
const rl = readline.createInterface({
input: tail.stdout,
crlfDelay: Infinity,
});
let skipped = 0;
let timeoutId = null;
const checkTimeout = () => {
if (skipped > 0) {
console.log(`${skipped} lines skipped`);
skipped = 0;
}
};
rl.on("line", (line) => {
// Clear any existing timeout
if (timeoutId) {
clearTimeout(timeoutId);
}
// Set a new timeout
timeoutId = setTimeout(checkTimeout, 100);
if (isSkippable(line)) {
skipped++;
} else {
if (skipped > 0) {
console.log(`${skipped} lines skipped`);
skipped = 0;
}
console.log(line);
}
});
// Handle errors
tail.stderr.on("data", (data) => {
console.error(`Error: ${data}`);
});
// Handle process exit
process.on("SIGINT", () => {
if (skipped > 0) {
console.log(`${skipped} lines skipped`);
}
console.log("Monitoring stopped");
tail.kill();
process.exit(0);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment