camera.ui
Version:
camera.ui launcher
107 lines • 4 kB
JavaScript
import { Logger } from '@camera.ui/common';
import { pathExists } from 'fs-extra/esm';
import { createWriteStream, ftruncate, read } from 'node:fs';
import { open, stat } from 'node:fs/promises';
import { promisify } from 'node:util';
import ora from 'ora';
const truncatePromise = promisify(ftruncate);
const readPromise = promisify(read);
export class CLILogger {
logPath;
action;
truncateInterval;
truncateIntervalMs = 1000 * 60 * 60 * 2;
logger;
fileStream;
constructor(logPath, action = '') {
this.logPath = logPath;
this.action = action;
this.logger = new Logger({ prefix: 'camera.ui', suffix: 'CLI', debugEnabled: true, traceEnabled: true });
}
init() {
this.fileStream = createWriteStream(this.logPath, { flags: 'a' });
// Forward logger events to file stream
this.logger.onLog((entry) => {
if (this.fileStream?.writable) {
const text = Logger.formatAsText(entry);
this.fileStream.write(text + '\n');
}
});
this.truncateInterval = setInterval(() => this.truncateLog(), this.truncateIntervalMs);
this.truncateLog();
}
log(msg, level = 'info', write) {
if (this.action === 'run' || write) {
// For 'raw' level (typically server output), pass through directly without re-formatting
// Server already outputs formatted logs, so we just need to display them
// Note: We don't write to file here because the server's FileTransport already
// writes structured logs to the same file. Writing here would create duplicates.
if (level === 'raw') {
// Write to console only (server FileTransport handles file writing)
console.log(msg);
}
else {
const loggingLevel = this.getLoggingLevel(level);
this.logger[loggingLevel](msg);
}
}
else {
const oraLevel = level === 'debug' || level === 'raw' ? 'info' : level;
ora()[oraLevel](msg);
}
}
close() {
clearInterval(this.truncateInterval);
this.fileStream?.end();
this.fileStream = undefined;
}
async truncateLog() {
if (!(await pathExists(this.logPath))) {
return;
}
const maxSize = 1000000; // ~1 MB
const truncateSize = 200000; // ~0.2 MB
try {
const logStats = await stat(this.logPath);
if (logStats.size < maxSize) {
this.log(`Log file size is less than ${maxSize} bytes, skipping truncate`, 'debug');
return;
}
this.log(`Truncating log file to ${truncateSize} bytes...`, 'debug');
const logStartPosition = logStats.size - truncateSize;
const logBuffer = Buffer.alloc(truncateSize);
const fileHandle = await open(this.logPath, 'r+');
try {
await readPromise(fileHandle.fd, logBuffer, 0, truncateSize, logStartPosition);
await truncatePromise(fileHandle.fd, 0);
await fileHandle.write(logBuffer, 0, truncateSize, 0);
}
finally {
await fileHandle.close();
}
this.log('Log file truncated successfully', 'debug');
}
catch (e) {
this.log(`Failed to truncate log file: ${e.message}`, 'fail');
}
}
getLoggingLevel(level) {
switch (level) {
case 'info':
return 'log';
case 'succeed':
return 'success';
case 'fail':
return 'error';
case 'warn':
return 'warn';
case 'debug':
return 'debug';
case 'raw':
return 'raw';
default:
return 'log';
}
}
}
//# sourceMappingURL=logger.js.map