add timestamped log files with automatic cleanup
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# 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
|
## 0.0.19
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "playwriter",
|
"name": "playwriter",
|
||||||
"description": "",
|
"description": "",
|
||||||
"version": "0.0.19",
|
"version": "0.0.20",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -1,26 +1,56 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
import util from 'node:util'
|
import util from 'node:util'
|
||||||
import { ensureDataDir, getLogFilePath } from './utils.js'
|
import { LOG_FILE_PATH } from './utils.js'
|
||||||
|
|
||||||
export type Logger = {
|
export type Logger = {
|
||||||
log(...args: unknown[]): void
|
log(...args: unknown[]): Promise<void>
|
||||||
error(...args: unknown[]): 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 {
|
export function createFileLogger({ logFilePath }: { logFilePath?: string } = {}): Logger {
|
||||||
const resolvedLogFilePath = logFilePath || getLogFilePath()
|
const resolvedLogFilePath = logFilePath || LOG_FILE_PATH
|
||||||
ensureDataDir()
|
const logDir = path.dirname(resolvedLogFilePath)
|
||||||
|
if (!fs.existsSync(logDir)) {
|
||||||
|
fs.mkdirSync(logDir, { recursive: true })
|
||||||
|
}
|
||||||
|
cleanupOldLogs(logDir)
|
||||||
fs.writeFileSync(resolvedLogFilePath, '')
|
fs.writeFileSync(resolvedLogFilePath, '')
|
||||||
|
|
||||||
const log = (...args: unknown[]) => {
|
let queue: Promise<void> = Promise.resolve()
|
||||||
|
|
||||||
|
const log = (...args: unknown[]): Promise<void> => {
|
||||||
const message = args.map(arg =>
|
const message = args.map(arg =>
|
||||||
typeof arg === 'string' ? arg : util.inspect(arg, { depth: null, colors: false })
|
typeof arg === 'string' ? arg : util.inspect(arg, { depth: null, colors: false })
|
||||||
).join(' ')
|
).join(' ')
|
||||||
fs.appendFileSync(resolvedLogFilePath, message + '\n')
|
queue = queue.then(() => fs.promises.appendFile(resolvedLogFilePath, message + '\n'))
|
||||||
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
log,
|
log,
|
||||||
error: log
|
error: log,
|
||||||
|
logFilePath: resolvedLogFilePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1914,19 +1914,13 @@ describe('CDP Session Tests', () => {
|
|||||||
sampleFunctionNames: functionNames,
|
sampleFunctionNames: functionNames,
|
||||||
}).toMatchInlineSnapshot(`
|
}).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"durationMicroseconds": 8646,
|
"durationMicroseconds": 11057,
|
||||||
"hasNodes": true,
|
"hasNodes": true,
|
||||||
"nodeCount": 20,
|
"nodeCount": 4,
|
||||||
"sampleFunctionNames": [
|
"sampleFunctionNames": [
|
||||||
"(root)",
|
"(root)",
|
||||||
"(program)",
|
"(program)",
|
||||||
"(idle)",
|
"(idle)",
|
||||||
"evaluate",
|
|
||||||
"fibonacci",
|
|
||||||
"fibonacci",
|
|
||||||
"fibonacci",
|
|
||||||
"fibonacci",
|
|
||||||
"fibonacci",
|
|
||||||
"fibonacci",
|
"fibonacci",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'
|
|||||||
import vm from 'node:vm'
|
import vm from 'node:vm'
|
||||||
import dedent from 'string-dedent'
|
import dedent from 'string-dedent'
|
||||||
import { createPatch } from 'diff'
|
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 { waitForPageLoad, WaitForPageLoadOptions, WaitForPageLoadResult } from './wait-for-page-load.js'
|
||||||
import { getCDPSessionForPage, CDPSession } from './cdp-session.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 cdpSessionCache: WeakMap<Page, CDPSession> = new WeakMap()
|
||||||
|
|
||||||
const RELAY_PORT = 19988
|
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`
|
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`
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js'
|
import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js'
|
||||||
import { createFileLogger } from './create-logger.js'
|
import { createFileLogger } from './create-logger.js'
|
||||||
import { getLogFilePath } from './utils.js'
|
|
||||||
|
|
||||||
const logFilePath = getLogFilePath()
|
|
||||||
process.title = 'playwriter-ws-server'
|
process.title = 'playwriter-ws-server'
|
||||||
|
|
||||||
const logger = createFileLogger({ logFilePath })
|
const logger = createFileLogger()
|
||||||
|
|
||||||
process.on('uncaughtException', async (err) => {
|
process.on('uncaughtException', async (err) => {
|
||||||
await logger.error('Uncaught Exception:', 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 })
|
const server = await startPlayWriterCDPRelayServer({ port, logger })
|
||||||
|
|
||||||
console.log('CDP Relay Server running. Press Ctrl+C to stop.')
|
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', () => {
|
process.on('SIGINT', () => {
|
||||||
console.log('\nShutting down...')
|
console.log('\nShutting down...')
|
||||||
|
|||||||
+12
-2
@@ -19,10 +19,20 @@ export function ensureDataDir(): string {
|
|||||||
return dataDir
|
return dataDir
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLogFilePath(): string {
|
function getLogsDir(): string {
|
||||||
return process.env.PLAYWRITER_LOG_PATH || path.join(getDataDir(), 'relay-server.log')
|
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 {
|
// export function getDidPromptReviewPath(): string {
|
||||||
// return path.join(getDataDir(), 'did-prompt-review')
|
// return path.join(getDataDir(), 'did-prompt-review')
|
||||||
// }
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user