///
///
/** Everything a log file's NAME says about it (plus its on-disk size). Times bound the entries roughly: from is when its process started writing it, to is when it was compressed. */
export type LogFileInfo = {
name: string;
/** Bytes on disk (compressed bytes for compressed files) */
size: number;
pid: number;
processStartTime: number;
threadId: string;
/** When the process started writing this file */
startTime: number;
/** When the file was compressed - absent while it is still being streamed to */
endTime?: number;
/** How many entries it holds - only counted at compression, so absent on live files */
entryCount?: number;
compressed: boolean;
/** Whether the writing process is still alive (always false for compressed files) */
active: boolean;
};
/**
* A searcher over one log file (as readFileCompressed returns it - always LZ4) that never decodes
* the whole thing: JSON escapes line breaks inside strings, so the only ACTUAL line breaks in the
* file are the separators between log statements - a plain substring search over the raw text,
* bounded out to the surrounding line breaks, finds exactly the matching statements, and only THOSE
* are decoded and returned as objects. Every search string must appear in a statement's raw JSON for
* it to match (so search for values as they are encoded - e.g. quoted).
*/
export declare function createLogSearcher(data: Buffer): (searches: string[]) => unknown[];
/** Decodes a log file's bytes (as readFileCompressed returns them - always LZ4) back into the logged objects. A torn final line (the writer crashed mid-append) is skipped. */
export declare function decodeLogFile(data: Buffer): unknown[];
export declare class StreamingLogs {
private config;
constructor(config: {
folder: string;
/** Included in every file name - see misc/https/certs.ts getOwnThreadId. Processes without one write "none". */
threadId?: string;
maxFileBytes?: number;
totalLimitBytes?: number;
});
private processStartTime;
private threadId;
private pending;
private flushTimer;
private maintenanceTimer;
private writeChain;
private currentPath;
private currentBytes;
private disposed;
/** Queues one entry (anything JSON-serializable). Never throws - logging must not take down the caller. */
log(entry: unknown): void;
private scheduleFlush;
flush(): Promise;
private writePending;
/**
* Compresses one finished log file: LZ4 into a temp SUBFOLDER (never a temp name in the log
* folder itself - a crashed write must not leave a corrupt file where the maintenance scans are),
* renamed into place (same drive, so the rename is atomic), verified, and only THEN is the
* original deleted - at no point is the data in fewer than one complete file.
*/
private compressFile;
listFiles(): Promise;
/** One file's bytes, ALWAYS LZ4-compressed: compressed files are sent as-is, a still-streaming file is flushed and compressed in memory (smaller over the network; decodeLogFile handles both identically). */
readFileCompressed(name: string): Promise;
private scheduleMaintenance;
/**
* Shared upkeep of the folder - every process using it runs this, so nothing depends on any one
* process surviving: dead processes' uncompressed files are compressed for them; duplicate
* compressions of one stream (two maintainers racing) are resolved by keeping the OLDEST copy;
* an uncompressed original whose compression already exists is deleted (its compressor died
* between rename and unlink); and the folder's total size is brought under its budget by
* deleting the oldest files.
*/
runMaintenance(): Promise;
private enforceTotalLimit;
dispose(): void;
}