add timestamped log files with automatic cleanup

This commit is contained in:
Tommy D. Rossi
2025-12-19 12:23:42 +01:00
parent 71c8507b55
commit e73c935da0
7 changed files with 64 additions and 25 deletions
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## 0.0.20
### Patch Changes
- **Timestamped log files**: Log files now include timestamps in filename (`relay-server-{timestamp}.log`) instead of overwriting a single file
- **Automatic log cleanup**: Keeps only the 10 most recent log files, deleting older ones automatically
- **Async log writes**: Logger now uses a queue for async file writes instead of blocking sync writes
## 0.0.19
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "playwriter",
"description": "",
"version": "0.0.19",
"version": "0.0.20",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
+38 -8
View File
@@ -1,26 +1,56 @@
import fs from 'node:fs'
import path from 'node:path'
import util from 'node:util'
import { ensureDataDir, getLogFilePath } from './utils.js'
import { LOG_FILE_PATH } from './utils.js'
export type Logger = {
log(...args: unknown[]): void
error(...args: unknown[]): void
log(...args: unknown[]): Promise<void>
error(...args: unknown[]): Promise<void>
logFilePath: string
}
function cleanupOldLogs(logsDir: string): void {
if (!fs.existsSync(logsDir)) {
return
}
const files = fs.readdirSync(logsDir)
.filter((f) => f.startsWith('relay-server-') && f.endsWith('.log'))
.map((f) => ({
name: f,
path: path.join(logsDir, f),
mtime: fs.statSync(path.join(logsDir, f)).mtime.getTime(),
}))
.sort((a, b) => b.mtime - a.mtime)
if (files.length > 10) {
files.slice(10).forEach((f) => {
fs.unlinkSync(f.path)
})
}
}
export function createFileLogger({ logFilePath }: { logFilePath?: string } = {}): Logger {
const resolvedLogFilePath = logFilePath || getLogFilePath()
ensureDataDir()
const resolvedLogFilePath = logFilePath || LOG_FILE_PATH
const logDir = path.dirname(resolvedLogFilePath)
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true })
}
cleanupOldLogs(logDir)
fs.writeFileSync(resolvedLogFilePath, '')
const log = (...args: unknown[]) => {
let queue: Promise<void> = Promise.resolve()
const log = (...args: unknown[]): Promise<void> => {
const message = args.map(arg =>
typeof arg === 'string' ? arg : util.inspect(arg, { depth: null, colors: false })
).join(' ')
fs.appendFileSync(resolvedLogFilePath, message + '\n')
queue = queue.then(() => fs.promises.appendFile(resolvedLogFilePath, message + '\n'))
return queue
}
return {
log,
error: log
error: log,
logFilePath: resolvedLogFilePath,
}
}
+2 -8
View File
@@ -1914,19 +1914,13 @@ describe('CDP Session Tests', () => {
sampleFunctionNames: functionNames,
}).toMatchInlineSnapshot(`
{
"durationMicroseconds": 8646,
"durationMicroseconds": 11057,
"hasNodes": true,
"nodeCount": 20,
"nodeCount": 4,
"sampleFunctionNames": [
"(root)",
"(program)",
"(idle)",
"evaluate",
"fibonacci",
"fibonacci",
"fibonacci",
"fibonacci",
"fibonacci",
"fibonacci",
],
}
+1 -2
View File
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'
import vm from 'node:vm'
import dedent from 'string-dedent'
import { createPatch } from 'diff'
import { getCdpUrl, getLogFilePath } from './utils.js'
import { getCdpUrl, LOG_FILE_PATH } from './utils.js'
import { waitForPageLoad, WaitForPageLoadOptions, WaitForPageLoadResult } from './wait-for-page-load.js'
import { getCDPSessionForPage, CDPSession } from './cdp-session.js'
@@ -92,7 +92,6 @@ const lastSnapshots: WeakMap<Page, string> = new WeakMap()
const cdpSessionCache: WeakMap<Page, CDPSession> = new WeakMap()
const RELAY_PORT = 19988
const LOG_FILE_PATH = getLogFilePath()
const NO_TABS_ERROR = `No browser tabs are connected. Please install and enable the Playwriter extension on at least one tab: https://chromewebstore.google.com/detail/playwriter-mcp/jfeammnjpkecdekppnclgkkffahnhfhe`
+2 -4
View File
@@ -1,11 +1,9 @@
import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js'
import { createFileLogger } from './create-logger.js'
import { getLogFilePath } from './utils.js'
const logFilePath = getLogFilePath()
process.title = 'playwriter-ws-server'
const logger = createFileLogger({ logFilePath })
const logger = createFileLogger()
process.on('uncaughtException', async (err) => {
await logger.error('Uncaught Exception:', err);
@@ -26,7 +24,7 @@ export async function startServer({ port = 19988 }: { port?: number } = {}) {
const server = await startPlayWriterCDPRelayServer({ port, logger })
console.log('CDP Relay Server running. Press Ctrl+C to stop.')
console.log('Logs are being written to:', logFilePath)
console.log('Logs are being written to:', logger.logFilePath)
process.on('SIGINT', () => {
console.log('\nShutting down...')
+12 -2
View File
@@ -19,10 +19,20 @@ export function ensureDataDir(): string {
return dataDir
}
export function getLogFilePath(): string {
return process.env.PLAYWRITER_LOG_PATH || path.join(getDataDir(), 'relay-server.log')
function getLogsDir(): string {
return path.join(getDataDir(), 'logs')
}
function getLogFilePath(): string {
if (process.env.PLAYWRITER_LOG_PATH) {
return process.env.PLAYWRITER_LOG_PATH
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
return path.join(getLogsDir(), `relay-server-${timestamp}.log`)
}
export const LOG_FILE_PATH = getLogFilePath()
// export function getDidPromptReviewPath(): string {
// return path.join(getDataDir(), 'did-prompt-review')
// }