Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | /**
* LogWriter - Handles writing log entries to JSONL files with daily rotation
*/
import fs from 'fs-extra';
import path from 'path';
import os from 'os';
export class LogWriter {
writeQueue = [];
flushTimeout = null;
isWriting = false;
currentDate = '';
constructor() {
this.currentDate = this.getTodayDate();
}
/**
* Get today's date in YYYY-MM-DD format
*/
getTodayDate() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* Get the log file path for global application logs
*/
getApplicationLogPath(date) {
const logDate = date || this.getTodayDate();
const logsDir = path.join(os.homedir(), 'Quiqr', 'logs');
return path.join(logsDir, `application-${logDate}.jsonl`);
}
/**
* Get the log file path for site-specific logs
*/
getSiteLogPath(siteKey, workspaceKey, date) {
const logDate = date || this.getTodayDate();
const sitesDir = path.join(os.homedir(), 'Quiqr', 'sites');
return path.join(sitesDir, siteKey, `${workspaceKey}-${logDate}.jsonl`);
}
/**
* Write a log entry asynchronously
*/
async write(entry) {
const filePath = entry.type === 'global'
? this.getApplicationLogPath()
: this.getSiteLogPath(entry.siteKey, entry.workspaceKey);
// Add to queue
this.writeQueue.push({ entry, filePath });
// Schedule flush
if (this.flushTimeout) {
clearTimeout(this.flushTimeout);
}
this.flushTimeout = setTimeout(() => {
this.flush().catch(err => {
console.error('Failed to flush log queue:', err);
});
}, 100); // Batch writes every 100ms
}
/**
* Flush the write queue to disk
*/
async flush() {
if (this.isWriting || this.writeQueue.length === 0) {
return;
}
this.isWriting = true;
try {
// Check if date has changed (daily rotation)
const today = this.getTodayDate();
if (today !== this.currentDate) {
this.currentDate = today;
}
// Group entries by file path
const entriesByFile = new Map();
for (const { entry, filePath } of this.writeQueue) {
if (!entriesByFile.has(filePath)) {
entriesByFile.set(filePath, []);
}
entriesByFile.get(filePath).push(entry);
}
// Write to each file
for (const [filePath, entries] of entriesByFile) {
await this.writeToFile(filePath, entries);
}
// Clear the queue
this.writeQueue = [];
}
finally {
this.isWriting = false;
}
}
/**
* Write entries to a specific file
*/
async writeToFile(filePath, entries) {
try {
// Ensure directory exists
await fs.ensureDir(path.dirname(filePath));
// Convert entries to JSONL format
const lines = entries.map(entry => JSON.stringify(entry)).join('\n') + '\n';
// Append to file
await fs.appendFile(filePath, lines, 'utf8');
}
catch (error) {
console.error(`Failed to write to log file ${filePath}:`, error);
throw error;
}
}
/**
* Force flush any pending writes (e.g., on shutdown)
*/
async forceFlush() {
if (this.flushTimeout) {
clearTimeout(this.flushTimeout);
this.flushTimeout = null;
}
await this.flush();
}
}
|