@logtape/file 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ import { StreamSinkOptions } from "@logtape/logtape";
2
+
3
+ //#region dist/filesink.base.d.ts
4
+
5
+ //#region src/filesink.base.d.ts
6
+ /**
7
+ * Options for the {@link getBaseFileSink} function.
8
+ */
9
+ type FileSinkOptions = StreamSinkOptions & {
10
+ /**
11
+ * If `true`, the file is not opened until the first write. Defaults to `false`.
12
+ */
13
+ lazy?: boolean;
14
+ /**
15
+ * The size of the buffer to use when writing to the file. If not specified,
16
+ * a default buffer size will be used. If it is less or equal to 0,
17
+ * the file will be written directly without buffering.
18
+ * @default 8192
19
+ * @since 0.12.0
20
+ */
21
+ bufferSize?: number;
22
+ /**
23
+ * The maximum time interval in milliseconds between flushes. If this time
24
+ * passes since the last flush, the buffer will be flushed regardless of size.
25
+ * This helps prevent log loss during unexpected process termination.
26
+ * @default 5000
27
+ * @since 0.12.0
28
+ */
29
+ flushInterval?: number;
30
+ /**
31
+ * Enable non-blocking mode with background flushing.
32
+ * When enabled, flush operations are performed asynchronously to prevent
33
+ * blocking the main thread during file I/O operations.
34
+ *
35
+ * @default `false`
36
+ * @since 1.0.0
37
+ */
38
+ nonBlocking?: boolean;
39
+ };
40
+ /**
41
+ * A platform-specific file sink driver.
42
+ * @template TFile The type of the file descriptor.
43
+ */
44
+
45
+ /**
46
+ * Get a platform-independent file sink.
47
+ *
48
+ * @template TFile The type of the file descriptor.
49
+ * @param path A path to the file to write to.
50
+ * @param options The options for the sink and the file driver.
51
+ * @returns A sink that writes to the file. The sink is also a disposable
52
+ * object that closes the file when disposed. If `nonBlocking` is enabled,
53
+ * returns a sink that also implements {@link AsyncDisposable}.
54
+ */
55
+
56
+ /**
57
+ * Options for the {@link getBaseRotatingFileSink} function.
58
+ */
59
+ interface RotatingFileSinkOptions extends Omit<FileSinkOptions, "lazy"> {
60
+ /**
61
+ * The maximum bytes of the file before it is rotated. 1 MiB by default.
62
+ */
63
+ maxSize?: number;
64
+ /**
65
+ * The maximum number of files to keep. 5 by default.
66
+ */
67
+ maxFiles?: number;
68
+ }
69
+ /**
70
+ * A platform-specific rotating file sink driver.
71
+ */
72
+ //#endregion
73
+ export { FileSinkOptions, RotatingFileSinkOptions };
74
+ //# sourceMappingURL=filesink.base.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesink.base.d.cts","names":["Sink","StreamSinkOptions","FileSinkOptions","FileSinkDriver","TFile","Uint8Array","AsyncFileSinkDriver","Promise","RotatingFileSinkOptions","Omit","RotatingFileSinkDriver","AsyncRotatingFileSinkDriver"],"sources":["../filesink.base.d.ts"],"sourcesContent":["import { Sink, StreamSinkOptions } from \"@logtape/logtape\";\n\n//#region src/filesink.base.d.ts\n\n/**\n * Options for the {@link getBaseFileSink} function.\n */\ntype FileSinkOptions = StreamSinkOptions & {\n /**\n * If `true`, the file is not opened until the first write. Defaults to `false`.\n */\n lazy?: boolean;\n /**\n * The size of the buffer to use when writing to the file. If not specified,\n * a default buffer size will be used. If it is less or equal to 0,\n * the file will be written directly without buffering.\n * @default 8192\n * @since 0.12.0\n */\n bufferSize?: number;\n /**\n * The maximum time interval in milliseconds between flushes. If this time\n * passes since the last flush, the buffer will be flushed regardless of size.\n * This helps prevent log loss during unexpected process termination.\n * @default 5000\n * @since 0.12.0\n */\n flushInterval?: number;\n /**\n * Enable non-blocking mode with background flushing.\n * When enabled, flush operations are performed asynchronously to prevent\n * blocking the main thread during file I/O operations.\n *\n * @default `false`\n * @since 1.0.0\n */\n nonBlocking?: boolean;\n};\n/**\n * A platform-specific file sink driver.\n * @template TFile The type of the file descriptor.\n */\ninterface FileSinkDriver<TFile> {\n /**\n * Open a file for appending and return a file descriptor.\n * @param path A path to the file to open.\n */\n openSync(path: string): TFile;\n /**\n * Write a chunk of data to the file.\n * @param fd The file descriptor.\n * @param chunk The data to write.\n */\n writeSync(fd: TFile, chunk: Uint8Array): void;\n /**\n * Write multiple chunks of data to the file in a single operation.\n * This is optional - if not implemented, falls back to multiple writeSync calls.\n * @param fd The file descriptor.\n * @param chunks Array of data chunks to write.\n */\n writeManySync?(fd: TFile, chunks: Uint8Array[]): void;\n /**\n * Flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flushSync(fd: TFile): void;\n /**\n * Close the file.\n * @param fd The file descriptor.\n */\n closeSync(fd: TFile): void;\n}\n/**\n * A platform-specific async file sink driver.\n * @template TFile The type of the file descriptor.\n * @since 1.0.0\n */\ninterface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Asynchronously write multiple chunks of data to the file in a single operation.\n * This is optional - if not implemented, falls back to multiple writeSync calls.\n * @param fd The file descriptor.\n * @param chunks Array of data chunks to write.\n */\n writeMany?(fd: TFile, chunks: Uint8Array[]): Promise<void>;\n /**\n * Asynchronously flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flush(fd: TFile): Promise<void>;\n /**\n * Asynchronously close the file.\n * @param fd The file descriptor.\n */\n close(fd: TFile): Promise<void>;\n}\n/**\n * Get a platform-independent file sink.\n *\n * @template TFile The type of the file descriptor.\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\n\n/**\n * Options for the {@link getBaseRotatingFileSink} function.\n */\ninterface RotatingFileSinkOptions extends Omit<FileSinkOptions, \"lazy\"> {\n /**\n * The maximum bytes of the file before it is rotated. 1 MiB by default.\n */\n maxSize?: number;\n /**\n * The maximum number of files to keep. 5 by default.\n */\n maxFiles?: number;\n}\n/**\n * A platform-specific rotating file sink driver.\n */\ninterface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): {\n size: number;\n };\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n/**\n * A platform-specific async rotating file sink driver.\n * @since 1.0.0\n */\ninterface AsyncRotatingFileSinkDriver<TFile> extends AsyncFileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): {\n size: number;\n };\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n/**\n * Get a platform-independent rotating file sink.\n *\n * This sink writes log records to a file, and rotates the file when it reaches\n * the `maxSize`. The rotated files are named with the original file name\n * followed by a dot and a number, starting from 1. The number is incremented\n * for each rotation, and the maximum number of files to keep is `maxFiles`.\n *\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\n//#endregion\nexport { AsyncFileSinkDriver, AsyncRotatingFileSinkDriver, FileSinkDriver, FileSinkOptions, RotatingFileSinkDriver, RotatingFileSinkOptions };\n//# sourceMappingURL=filesink.base.d.ts.map"],"mappings":";;;;;AA6E2D;;;KAtEtDE,eAAAA,GAAkBD,iBAuGmBQ,GAAAA;EAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAApCD,uBAAAA,SAAgCC,KAAKP"}
@@ -0,0 +1,65 @@
1
+ import { FileSinkOptions, RotatingFileSinkOptions } from "./filesink.base.cjs";
2
+ import { TimeRotatingFileSinkOptions } from "./timefilesink.cjs";
3
+ import { Sink } from "@logtape/logtape";
4
+
5
+ //#region dist/filesink.node.d.ts
6
+
7
+ /**
8
+ * Get a file sink.
9
+ *
10
+ * Note that this function is unavailable in the browser.
11
+ *
12
+ * @param path A path to the file to write to.
13
+ * @param options The options for the sink.
14
+ * @returns A sink that writes to the file. The sink is also a disposable
15
+ * object that closes the file when disposed. If `nonBlocking` is enabled,
16
+ * returns a sink that also implements {@link AsyncDisposable}.
17
+ */
18
+ declare function getFileSink(path: string, options?: FileSinkOptions): Sink & Disposable;
19
+ declare function getFileSink(path: string, options: FileSinkOptions & {
20
+ nonBlocking: true;
21
+ }): Sink & AsyncDisposable;
22
+ /**
23
+ * Get a rotating file sink.
24
+ *
25
+ * This sink writes log records to a file, and rotates the file when it reaches
26
+ * the `maxSize`. The rotated files are named with the original file name
27
+ * followed by a dot and a number, starting from 1. The number is incremented
28
+ * for each rotation, and the maximum number of files to keep is `maxFiles`.
29
+ *
30
+ * Note that this function is unavailable in the browser.
31
+ *
32
+ * @param path A path to the file to write to.
33
+ * @param options The options for the sink and the file driver.
34
+ * @returns A sink that writes to the file. The sink is also a disposable
35
+ * object that closes the file when disposed. If `nonBlocking` is enabled,
36
+ * returns a sink that also implements {@link AsyncDisposable}.
37
+ */
38
+ declare function getRotatingFileSink(path: string, options?: RotatingFileSinkOptions): Sink & Disposable;
39
+ declare function getRotatingFileSink(path: string, options: RotatingFileSinkOptions & {
40
+ nonBlocking: true;
41
+ }): Sink & AsyncDisposable;
42
+ /**
43
+ * Get a time-rotating file sink.
44
+ *
45
+ * This sink writes log records to a file in a directory, rotating to a new
46
+ * file based on time intervals. The filename is generated based on the
47
+ * current date/time and the configured interval.
48
+ *
49
+ * Note that this function is unavailable in the browser.
50
+ *
51
+ * @param options The options for the sink.
52
+ * @returns A sink that writes to the file. The sink is also a disposable
53
+ * object that closes the file when disposed. If `nonBlocking` is
54
+ * enabled, returns a sink that also implements {@link AsyncDisposable}.
55
+ * @since 2.0.0
56
+ */
57
+ declare function getTimeRotatingFileSink(options: TimeRotatingFileSinkOptions): Sink & Disposable;
58
+ declare function getTimeRotatingFileSink(options: TimeRotatingFileSinkOptions & {
59
+ nonBlocking: true;
60
+ }): Sink & AsyncDisposable;
61
+ //# sourceMappingURL=filesink.node.d.ts.map
62
+ //#endregion
63
+ //#endregion
64
+ export { getFileSink, getRotatingFileSink, getTimeRotatingFileSink };
65
+ //# sourceMappingURL=filesink.node.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesink.node.d.cts","names":["AsyncRotatingFileSinkDriver","FileSinkOptions","RotatingFileSinkDriver","RotatingFileSinkOptions","AsyncTimeRotatingFileSinkDriver","TimeRotatingFileSinkDriver","TimeRotatingFileSinkOptions","Sink","nodeDriver","nodeAsyncDriver","nodeTimeDriver","nodeAsyncTimeDriver","getFileSink","Disposable","AsyncDisposable","getRotatingFileSink","getTimeRotatingFileSink"],"sources":["../filesink.node.d.ts"],"sourcesContent":["import { AsyncRotatingFileSinkDriver, FileSinkOptions, RotatingFileSinkDriver, RotatingFileSinkOptions } from \"./filesink.base.js\";\nimport { AsyncTimeRotatingFileSinkDriver, TimeRotatingFileSinkDriver, TimeRotatingFileSinkOptions } from \"./timefilesink.js\";\nimport { Sink } from \"@logtape/logtape\";\n\n//#region src/filesink.node.d.ts\n\n/**\n * A Node.js-specific file sink driver.\n */\ndeclare const nodeDriver: RotatingFileSinkDriver<number | void>;\n/**\n * A Node.js-specific async file sink driver.\n * @since 1.0.0\n */\ndeclare const nodeAsyncDriver: AsyncRotatingFileSinkDriver<number | void>;\n/**\n * A Node.js-specific time-rotating file sink driver.\n * @since 2.0.0\n */\ndeclare const nodeTimeDriver: TimeRotatingFileSinkDriver<number | void>;\n/**\n * A Node.js-specific async time-rotating file sink driver.\n * @since 2.0.0\n */\ndeclare const nodeAsyncTimeDriver: AsyncTimeRotatingFileSinkDriver<number | void>;\n/**\n * Get a file sink.\n *\n * Note that this function is unavailable in the browser.\n *\n * @param path A path to the file to write to.\n * @param options The options for the sink.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\ndeclare function getFileSink(path: string, options?: FileSinkOptions): Sink & Disposable;\ndeclare function getFileSink(path: string, options: FileSinkOptions & {\n nonBlocking: true;\n}): Sink & AsyncDisposable;\n/**\n * Get a rotating file sink.\n *\n * This sink writes log records to a file, and rotates the file when it reaches\n * the `maxSize`. The rotated files are named with the original file name\n * followed by a dot and a number, starting from 1. The number is incremented\n * for each rotation, and the maximum number of files to keep is `maxFiles`.\n *\n * Note that this function is unavailable in the browser.\n *\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\ndeclare function getRotatingFileSink(path: string, options?: RotatingFileSinkOptions): Sink & Disposable;\ndeclare function getRotatingFileSink(path: string, options: RotatingFileSinkOptions & {\n nonBlocking: true;\n}): Sink & AsyncDisposable;\n/**\n * Get a time-rotating file sink.\n *\n * This sink writes log records to a file in a directory, rotating to a new\n * file based on time intervals. The filename is generated based on the\n * current date/time and the configured interval.\n *\n * Note that this function is unavailable in the browser.\n *\n * @param options The options for the sink.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is\n * enabled, returns a sink that also implements {@link AsyncDisposable}.\n * @since 2.0.0\n */\ndeclare function getTimeRotatingFileSink(options: TimeRotatingFileSinkOptions): Sink & Disposable;\ndeclare function getTimeRotatingFileSink(options: TimeRotatingFileSinkOptions & {\n nonBlocking: true;\n}): Sink & AsyncDisposable;\n//# sourceMappingURL=filesink.node.d.ts.map\n//#endregion\nexport { getFileSink, getRotatingFileSink, getTimeRotatingFileSink, nodeAsyncDriver, nodeAsyncTimeDriver, nodeDriver, nodeTimeDriver };\n//# sourceMappingURL=filesink.node.d.ts.map"],"mappings":";;;;;;;;;AA2D0B;AAAA;;;;;AAgBuE;AAAA;iBAvChFY,WAAAA,CAwCuB,IAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAxCaX,eAwCb,CAAA,EAxC+BM,IAwC/B,GAxCsCM,UAwCtC;iBAvCvBD,WAAAA,CAuCiCN,IAAAA,EAAAA,MAAAA,EAAAA,OAAAA,EAvCEL,eAuCFK,GAAAA;EAA2B,WAEzEC,EAAAA,IAAAA;CAAI,CAAA,EAvCJA,IAuCOO,GAvCAA,eAuCAA;AAAe;;;;;;;;;;;;;;;;iBAtBTC,mBAAAA,yBAA4CZ,0BAA0BI,OAAOM;iBAC7EE,mBAAAA,wBAA2CZ;;IAExDI,OAAOO;;;;;;;;;;;;;;;;iBAgBME,uBAAAA,UAAiCV,8BAA8BC,OAAOM;iBACtEG,uBAAAA,UAAiCV;;IAE9CC,OAAOO"}
@@ -0,0 +1,41 @@
1
+ import { FileSinkOptions } from "./filesink.base.cjs";
2
+
3
+ //#region dist/timefilesink.d.ts
4
+
5
+ //#region src/timefilesink.d.ts
6
+ /**
7
+ * The rotation interval for time-based file sinks.
8
+ */
9
+ type TimeRotationInterval = "hourly" | "daily" | "weekly";
10
+ /**
11
+ * Options for the {@link getBaseTimeRotatingFileSink} function.
12
+ */
13
+ interface TimeRotatingFileSinkOptions extends Omit<FileSinkOptions, "lazy"> {
14
+ /**
15
+ * The directory to write log files to.
16
+ */
17
+ directory: string;
18
+ /**
19
+ * A function that generates the filename for the log file based on the date.
20
+ * Default depends on `interval`:
21
+ * - `"daily"`: `YYYY-MM-DD.log` (e.g., `2025-01-15.log`)
22
+ * - `"hourly"`: `YYYY-MM-DD-HH.log` (e.g., `2025-01-15-09.log`)
23
+ * - `"weekly"`: `YYYY-WW.log` (e.g., `2025-W03.log`)
24
+ */
25
+ filename?: (date: Date) => string;
26
+ /**
27
+ * The rotation interval. Defaults to `"daily"`.
28
+ */
29
+ interval?: TimeRotationInterval;
30
+ /**
31
+ * The maximum age of log files in milliseconds. Files older than this
32
+ * will be deleted. If not specified, old files are not deleted.
33
+ */
34
+ maxAgeMs?: number;
35
+ }
36
+ /**
37
+ * A platform-specific time-rotating file sink driver.
38
+ */
39
+ //#endregion
40
+ export { TimeRotatingFileSinkOptions };
41
+ //# sourceMappingURL=timefilesink.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timefilesink.d.cts","names":["AsyncFileSinkDriver","FileSinkDriver","FileSinkOptions","Sink","TimeRotationInterval","TimeRotatingFileSinkOptions","Date","Omit","TimeRotatingFileSinkDriver","TFile","AsyncTimeRotatingFileSinkDriver"],"sources":["../timefilesink.d.ts"],"sourcesContent":["import { AsyncFileSinkDriver, FileSinkDriver, FileSinkOptions } from \"./filesink.base.js\";\nimport { Sink } from \"@logtape/logtape\";\n\n//#region src/timefilesink.d.ts\n\n/**\n * The rotation interval for time-based file sinks.\n */\ntype TimeRotationInterval = \"hourly\" | \"daily\" | \"weekly\";\n/**\n * Options for the {@link getBaseTimeRotatingFileSink} function.\n */\ninterface TimeRotatingFileSinkOptions extends Omit<FileSinkOptions, \"lazy\"> {\n /**\n * The directory to write log files to.\n */\n directory: string;\n /**\n * A function that generates the filename for the log file based on the date.\n * Default depends on `interval`:\n * - `\"daily\"`: `YYYY-MM-DD.log` (e.g., `2025-01-15.log`)\n * - `\"hourly\"`: `YYYY-MM-DD-HH.log` (e.g., `2025-01-15-09.log`)\n * - `\"weekly\"`: `YYYY-WW.log` (e.g., `2025-W03.log`)\n */\n filename?: (date: Date) => string;\n /**\n * The rotation interval. Defaults to `\"daily\"`.\n */\n interval?: TimeRotationInterval;\n /**\n * The maximum age of log files in milliseconds. Files older than this\n * will be deleted. If not specified, old files are not deleted.\n */\n maxAgeMs?: number;\n}\n/**\n * A platform-specific time-rotating file sink driver.\n */\ninterface TimeRotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Read the contents of a directory.\n * @param path A path to the directory.\n * @returns An array of filenames in the directory.\n */\n readdirSync(path: string): string[];\n /**\n * Delete a file.\n * @param path A path to the file to delete.\n */\n unlinkSync(path: string): void;\n /**\n * Create a directory if it doesn't exist.\n * @param path A path to the directory to create.\n * @param options Options for directory creation.\n */\n mkdirSync(path: string, options?: {\n recursive?: boolean;\n }): void;\n /**\n * Join path segments.\n * @param paths Path segments to join.\n * @returns The joined path.\n */\n joinPath(...paths: string[]): string;\n}\n/**\n * A platform-specific async time-rotating file sink driver.\n * @since 2.0.0\n */\ninterface AsyncTimeRotatingFileSinkDriver<TFile> extends AsyncFileSinkDriver<TFile> {\n /**\n * Read the contents of a directory.\n * @param path A path to the directory.\n * @returns An array of filenames in the directory.\n */\n readdirSync(path: string): string[];\n /**\n * Delete a file.\n * @param path A path to the file to delete.\n */\n unlinkSync(path: string): void;\n /**\n * Create a directory if it doesn't exist.\n * @param path A path to the directory to create.\n * @param options Options for directory creation.\n */\n mkdirSync(path: string, options?: {\n recursive?: boolean;\n }): void;\n /**\n * Join path segments.\n * @param paths Path segments to join.\n * @returns The joined path.\n */\n joinPath(...paths: string[]): string;\n}\n/**\n * Get the ISO week number of a date.\n * @param date The date to get the week number of.\n * @returns The ISO week number (1-53).\n */\n//#endregion\nexport { AsyncTimeRotatingFileSinkDriver, TimeRotatingFileSinkDriver, TimeRotatingFileSinkOptions, TimeRotationInterval };\n//# sourceMappingURL=timefilesink.d.ts.map"],"mappings":";;;;AACwC;;;;KAOnCI,oBAAAA,GAoBQA,QAAAA,GAAAA,OAAAA,GAAAA,QAAAA;;AAhBqC;;UAAxCC,2BAAAA,SAAoCE,KAAKL;;;;;;;;;;;;oBAY/BI;;;;aAIPF"}
@@ -431,6 +431,7 @@ function getBaseRotatingFileSink(path, options) {
431
431
  if (activeFlush || disposed) return;
432
432
  activeFlush = flushBuffer().finally(() => {
433
433
  activeFlush = null;
434
+ if (!disposed && buffer.length > 0) scheduleFlush();
434
435
  });
435
436
  }
436
437
  function startFlushTimer() {
@@ -430,6 +430,7 @@ function getBaseRotatingFileSink(path, options) {
430
430
  if (activeFlush || disposed) return;
431
431
  activeFlush = flushBuffer().finally(() => {
432
432
  activeFlush = null;
433
+ if (!disposed && buffer.length > 0) scheduleFlush();
433
434
  });
434
435
  }
435
436
  function startFlushTimer() {
@@ -1 +1 @@
1
- {"version":3,"file":"filesink.base.js","names":["record: LogRecord","baseThreshold: number","baseInterval: number","size: number","timeSinceLastFlush: number","currentSize: number","values: number[]","buffer: Uint8Array","bufferPool: BufferPool","data: Uint8Array","path: string","options:\n & FileSinkOptions\n & (FileSinkDriver<TFile> | AsyncFileSinkDriver<TFile>)","lastFlushTimestamp: number","flushBuffer","sink: Sink & Disposable","formattedRecord: string | undefined","encodedRecord: Uint8Array | undefined","activeFlush: Promise<void> | null","flushTimer: ReturnType<typeof setInterval> | null","error: unknown","flushSize: number","suppressErrorReport: boolean","nonBlockingSink: Sink & AsyncDisposable","options:\n & RotatingFileSinkOptions\n & (RotatingFileSinkDriver<TFile> | AsyncRotatingFileSinkDriver<TFile>)","offset: number","buffer: string","bytes: Uint8Array"],"sources":["../src/filesink.base.ts"],"sourcesContent":["import {\n defaultTextFormatter,\n getLogger,\n type LogRecord,\n type Sink,\n type StreamSinkOptions,\n} from \"@logtape/logtape\";\n\nfunction isMetaLoggerRecord(record: LogRecord): boolean {\n return record.category.length === 2 &&\n record.category[0] === \"logtape\" &&\n record.category[1] === \"meta\";\n}\n\n/**\n * Adaptive flush strategy that dynamically adjusts buffer thresholds\n * based on recent flush patterns for optimal performance.\n */\nclass AdaptiveFlushStrategy {\n private recentFlushSizes: number[] = [];\n private recentFlushTimes: number[] = [];\n private avgFlushSize: number;\n private avgFlushInterval: number;\n private readonly maxHistorySize = 10;\n private readonly baseThreshold: number;\n private readonly baseInterval: number;\n\n constructor(baseThreshold: number, baseInterval: number) {\n this.baseThreshold = baseThreshold;\n this.baseInterval = baseInterval;\n this.avgFlushSize = baseThreshold;\n this.avgFlushInterval = baseInterval;\n }\n\n /**\n * Record a flush event for pattern analysis.\n * @param size The size of data flushed in bytes.\n * @param timeSinceLastFlush Time since last flush in milliseconds.\n */\n recordFlush(size: number, timeSinceLastFlush: number): void {\n this.recentFlushSizes.push(size);\n this.recentFlushTimes.push(timeSinceLastFlush);\n\n // Keep only recent history\n if (this.recentFlushSizes.length > this.maxHistorySize) {\n this.recentFlushSizes.shift();\n this.recentFlushTimes.shift();\n }\n\n // Update averages\n this.updateAverages();\n }\n\n /**\n * Determine if buffer should be flushed based on adaptive strategy.\n * @param currentSize Current buffer size in bytes.\n * @param timeSinceLastFlush Time since last flush in milliseconds.\n * @returns True if buffer should be flushed.\n */\n shouldFlush(currentSize: number, timeSinceLastFlush: number): boolean {\n const adaptiveThreshold = this.calculateAdaptiveThreshold();\n const adaptiveInterval = this.calculateAdaptiveInterval();\n\n return currentSize >= adaptiveThreshold ||\n (adaptiveInterval > 0 && timeSinceLastFlush >= adaptiveInterval);\n }\n\n private updateAverages(): void {\n if (this.recentFlushSizes.length === 0) return;\n\n this.avgFlushSize =\n this.recentFlushSizes.reduce((sum, size) => sum + size, 0) /\n this.recentFlushSizes.length;\n\n this.avgFlushInterval =\n this.recentFlushTimes.reduce((sum, time) => sum + time, 0) /\n this.recentFlushTimes.length;\n }\n\n private calculateAdaptiveThreshold(): number {\n // Adjust threshold based on recent patterns\n // Higher average flush sizes suggest larger batches are beneficial\n const adaptiveFactor = Math.min(\n 2.0,\n Math.max(0.5, this.avgFlushSize / this.baseThreshold),\n );\n\n return Math.max(\n Math.min(4096, this.baseThreshold / 2),\n Math.min(64 * 1024, this.baseThreshold * adaptiveFactor),\n );\n }\n\n private calculateAdaptiveInterval(): number {\n // If base interval is 0, time-based flushing is disabled\n if (this.baseInterval <= 0) return 0;\n\n if (this.avgFlushInterval <= 0) return this.baseInterval;\n\n // Adjust interval based on recent flush frequency\n // More frequent flushes suggest lower latency is preferred\n if (this.recentFlushTimes.length < 3) return this.avgFlushInterval;\n\n const variance = this.calculateVariance(this.recentFlushTimes);\n const stabilityFactor = Math.min(2.0, Math.max(0.5, 1000 / variance));\n\n return Math.max(\n 1000,\n Math.min(10000, this.avgFlushInterval * stabilityFactor),\n );\n }\n\n private calculateVariance(values: number[]): number {\n if (values.length < 2) return 1000; // Default variance\n\n const mean = values.reduce((sum, val) => sum + val, 0) / values.length;\n const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));\n return squaredDiffs.reduce((sum, diff) => sum + diff, 0) / values.length;\n }\n}\n\n/**\n * Memory pool for reusing Uint8Array buffers to minimize GC pressure.\n * Maintains a pool of pre-allocated buffers for efficient reuse.\n */\nclass BufferPool {\n private pool: Uint8Array[] = [];\n private readonly maxPoolSize = 50; // Keep a reasonable pool size\n private readonly maxBufferSize = 64 * 1024; // Don't pool very large buffers\n\n /**\n * Acquire a buffer from the pool or create a new one.\n * @param size The minimum size needed for the buffer.\n * @returns A Uint8Array that can be used for encoding.\n */\n acquire(size: number): Uint8Array {\n // Don't pool very large buffers to avoid memory waste\n if (size > this.maxBufferSize) {\n return new Uint8Array(size);\n }\n\n // Try to find a suitable buffer from the pool\n for (let i = this.pool.length - 1; i >= 0; i--) {\n const buffer = this.pool[i];\n if (buffer.length >= size) {\n // Remove from pool and return\n this.pool.splice(i, 1);\n return buffer.subarray(0, size);\n }\n }\n\n // No suitable buffer found, create a new one\n // Create slightly larger buffer to improve reuse chances\n const actualSize = Math.max(size, 1024); // Minimum 1KB\n return new Uint8Array(actualSize);\n }\n\n /**\n * Return a buffer to the pool for future reuse.\n * @param buffer The buffer to return to the pool.\n */\n release(buffer: Uint8Array): void {\n // Don't pool if we're at capacity or buffer is too large\n if (\n this.pool.length >= this.maxPoolSize || buffer.length > this.maxBufferSize\n ) {\n return;\n }\n\n // Don't pool very small buffers as they're cheap to allocate\n if (buffer.length < 256) {\n return;\n }\n\n // Add to pool for reuse\n this.pool.push(buffer);\n }\n\n /**\n * Clear the pool to free memory. Useful for cleanup.\n */\n clear(): void {\n this.pool.length = 0;\n }\n\n /**\n * Get current pool statistics for monitoring.\n * @returns Object with pool size and buffer count.\n */\n getStats(): { poolSize: number; totalBuffers: number } {\n return {\n poolSize: this.pool.reduce((sum, buf) => sum + buf.length, 0),\n totalBuffers: this.pool.length,\n };\n }\n}\n\n/**\n * High-performance byte buffer for batching log records.\n * Eliminates string concatenation overhead by storing pre-encoded bytes.\n * Uses memory pooling to reduce GC pressure.\n */\nclass ByteRingBuffer {\n private buffers: Uint8Array[] = [];\n private totalSize: number = 0;\n private bufferPool: BufferPool;\n\n constructor(bufferPool: BufferPool) {\n this.bufferPool = bufferPool;\n }\n\n /**\n * Append pre-encoded log record bytes to the buffer.\n * @param data The encoded log record as bytes.\n */\n append(data: Uint8Array): void {\n this.buffers.push(data);\n this.totalSize += data.length;\n }\n\n /**\n * Get the current total size of buffered data in bytes.\n * @returns The total size in bytes.\n */\n size(): number {\n return this.totalSize;\n }\n\n /**\n * Get the number of buffered records.\n * @returns The number of records in the buffer.\n */\n count(): number {\n return this.buffers.length;\n }\n\n /**\n * Flush all buffered data and return it as an array of byte arrays.\n * This clears the internal buffer and returns used buffers to the pool.\n * @returns Array of buffered byte arrays ready for writev() operations.\n */\n flush(): Uint8Array[] {\n const result = [...this.buffers];\n this.clear();\n return result;\n }\n\n /**\n * Clear the buffer without returning data.\n * Returns buffers to the pool for reuse.\n */\n clear(): void {\n // Return buffers to pool for reuse\n for (const buffer of this.buffers) {\n this.bufferPool.release(buffer);\n }\n this.buffers.length = 0;\n this.totalSize = 0;\n }\n\n /**\n * Check if the buffer is empty.\n * @returns True if the buffer contains no data.\n */\n isEmpty(): boolean {\n return this.buffers.length === 0;\n }\n}\n\n/**\n * Options for the {@link getBaseFileSink} function.\n */\nexport type FileSinkOptions = StreamSinkOptions & {\n /**\n * If `true`, the file is not opened until the first write. Defaults to `false`.\n */\n lazy?: boolean;\n\n /**\n * The size of the buffer to use when writing to the file. If not specified,\n * a default buffer size will be used. If it is less or equal to 0,\n * the file will be written directly without buffering.\n * @default 8192\n * @since 0.12.0\n */\n bufferSize?: number;\n\n /**\n * The maximum time interval in milliseconds between flushes. If this time\n * passes since the last flush, the buffer will be flushed regardless of size.\n * This helps prevent log loss during unexpected process termination.\n * @default 5000\n * @since 0.12.0\n */\n flushInterval?: number;\n\n /**\n * Enable non-blocking mode with background flushing.\n * When enabled, flush operations are performed asynchronously to prevent\n * blocking the main thread during file I/O operations.\n *\n * @default `false`\n * @since 1.0.0\n */\n nonBlocking?: boolean;\n};\n\n/**\n * A platform-specific file sink driver.\n * @template TFile The type of the file descriptor.\n */\nexport interface FileSinkDriver<TFile> {\n /**\n * Open a file for appending and return a file descriptor.\n * @param path A path to the file to open.\n */\n openSync(path: string): TFile;\n\n /**\n * Write a chunk of data to the file.\n * @param fd The file descriptor.\n * @param chunk The data to write.\n */\n writeSync(fd: TFile, chunk: Uint8Array): void;\n\n /**\n * Write multiple chunks of data to the file in a single operation.\n * This is optional - if not implemented, falls back to multiple writeSync calls.\n * @param fd The file descriptor.\n * @param chunks Array of data chunks to write.\n */\n writeManySync?(fd: TFile, chunks: Uint8Array[]): void;\n\n /**\n * Flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flushSync(fd: TFile): void;\n\n /**\n * Close the file.\n * @param fd The file descriptor.\n */\n closeSync(fd: TFile): void;\n}\n\n/**\n * A platform-specific async file sink driver.\n * @template TFile The type of the file descriptor.\n * @since 1.0.0\n */\nexport interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Asynchronously write multiple chunks of data to the file in a single operation.\n * This is optional - if not implemented, falls back to multiple writeSync calls.\n * @param fd The file descriptor.\n * @param chunks Array of data chunks to write.\n */\n writeMany?(fd: TFile, chunks: Uint8Array[]): Promise<void>;\n\n /**\n * Asynchronously flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flush(fd: TFile): Promise<void>;\n\n /**\n * Asynchronously close the file.\n * @param fd The file descriptor.\n */\n close(fd: TFile): Promise<void>;\n}\n\n/**\n * Get a platform-independent file sink.\n *\n * @template TFile The type of the file descriptor.\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\nexport function getBaseFileSink<TFile>(\n path: string,\n options: FileSinkOptions & FileSinkDriver<TFile>,\n): Sink & Disposable;\nexport function getBaseFileSink<TFile>(\n path: string,\n options: FileSinkOptions & AsyncFileSinkDriver<TFile>,\n): Sink & AsyncDisposable;\nexport function getBaseFileSink<TFile>(\n path: string,\n options:\n & FileSinkOptions\n & (FileSinkDriver<TFile> | AsyncFileSinkDriver<TFile>),\n): Sink & (Disposable | AsyncDisposable) {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 bytes\n const flushInterval = options.flushInterval ?? 5000; // Default flush interval of 5 seconds\n let fd = options.lazy ? null : options.openSync(path);\n\n // Initialize memory pool and buffer systems\n const bufferPool = new BufferPool();\n const byteBuffer = new ByteRingBuffer(bufferPool);\n const adaptiveStrategy = new AdaptiveFlushStrategy(bufferSize, flushInterval);\n let lastFlushTimestamp: number = Date.now();\n\n if (!options.nonBlocking) {\n // Blocking mode implementation\n // deno-lint-ignore no-inner-declarations\n function flushBuffer(): void {\n if (fd == null || byteBuffer.isEmpty()) return;\n\n const flushSize = byteBuffer.size();\n const currentTime = Date.now();\n const timeSinceLastFlush = currentTime - lastFlushTimestamp;\n\n const chunks = byteBuffer.flush();\n if (options.writeManySync && chunks.length > 1) {\n // Use batch write if available\n options.writeManySync(fd, chunks);\n } else {\n // Fallback to individual writes\n for (const chunk of chunks) {\n options.writeSync(fd, chunk);\n }\n }\n options.flushSync(fd);\n\n // Record flush for adaptive strategy\n adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);\n lastFlushTimestamp = currentTime;\n }\n\n const sink: Sink & Disposable = (record: LogRecord) => {\n if (fd == null) fd = options.openSync(path);\n let formattedRecord: string | undefined;\n let encodedRecord: Uint8Array | undefined;\n\n // ULTRA FAST PATH: Direct write when buffer is empty\n if (byteBuffer.isEmpty()) {\n // Inline everything for maximum speed - avoid all function calls\n formattedRecord = formatter(record);\n encodedRecord = encoder.encode(formattedRecord);\n\n // Only use fast path for typical log sizes to avoid breaking edge cases\n if (encodedRecord.length < 200) {\n // Write directly for small logs - no complex buffering logic\n options.writeSync(fd, encodedRecord);\n options.flushSync(fd);\n const currentTime = Date.now();\n adaptiveStrategy.recordFlush(\n encodedRecord.length,\n currentTime - lastFlushTimestamp,\n );\n lastFlushTimestamp = currentTime;\n return;\n }\n }\n\n // STANDARD PATH: Complex logic for edge cases\n formattedRecord ??= formatter(record);\n encodedRecord ??= encoder.encode(formattedRecord);\n byteBuffer.append(encodedRecord);\n\n // Check for immediate flush conditions\n if (bufferSize <= 0) {\n // No buffering - flush immediately\n flushBuffer();\n } else {\n // Use adaptive strategy for intelligent flushing\n const timeSinceLastFlush = record.timestamp - lastFlushTimestamp;\n const shouldFlush = adaptiveStrategy.shouldFlush(\n byteBuffer.size(),\n timeSinceLastFlush,\n );\n\n if (shouldFlush) {\n flushBuffer();\n }\n }\n };\n sink[Symbol.dispose] = () => {\n if (fd !== null) {\n flushBuffer();\n options.closeSync(fd);\n }\n // Clean up buffer pool\n bufferPool.clear();\n };\n return sink;\n }\n\n // Non-blocking mode implementation\n const asyncOptions = options as AsyncFileSinkDriver<TFile>;\n let disposed = false;\n let activeFlush: Promise<void> | null = null;\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n let bufferedNonMetaRecord = false;\n\n function reportFlushError(error: unknown): void {\n try {\n getLogger([\"logtape\", \"meta\"]).warn(\n \"Non-blocking file sink flush failed for {path}: {error}\",\n { error, path },\n );\n } catch {\n // Last resort: keep non-blocking file sinks from throwing.\n }\n }\n\n async function flushBuffer(): Promise<void> {\n if (fd == null || byteBuffer.isEmpty()) return;\n\n const flushSize = byteBuffer.size();\n const currentTime = Date.now();\n const timeSinceLastFlush = currentTime - lastFlushTimestamp;\n\n const chunks = byteBuffer.flush();\n const suppressErrorReport = !bufferedNonMetaRecord;\n bufferedNonMetaRecord = false;\n try {\n if (asyncOptions.writeMany && chunks.length > 1) {\n // Use async batch write if available\n await asyncOptions.writeMany(fd, chunks);\n } else {\n // Fallback to individual writes\n for (const chunk of chunks) {\n asyncOptions.writeSync(fd, chunk);\n }\n }\n await asyncOptions.flush(fd);\n\n // Record flush for adaptive strategy\n adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);\n lastFlushTimestamp = currentTime;\n } catch (error) {\n if (!suppressErrorReport) reportFlushError(error);\n }\n }\n\n function scheduleFlush(): void {\n if (activeFlush || disposed) return;\n\n activeFlush = flushBuffer().finally(() => {\n activeFlush = null;\n if (!disposed && !byteBuffer.isEmpty()) scheduleFlush();\n });\n }\n\n function scheduleDirectFlush(\n flushSize: number,\n suppressErrorReport: boolean,\n ): void {\n if (activeFlush || disposed || fd == null) return;\n\n const flushFd = fd;\n const startedAt = Date.now();\n const timeSinceLastFlush = startedAt - lastFlushTimestamp;\n activeFlush = Promise.resolve()\n .then(() => asyncOptions.flush(flushFd))\n .then(() => {\n adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);\n lastFlushTimestamp = Date.now();\n })\n .catch((error) => {\n if (!suppressErrorReport) reportFlushError(error);\n })\n .finally(() => {\n activeFlush = null;\n if (!disposed && !byteBuffer.isEmpty()) scheduleFlush();\n });\n }\n\n function startFlushTimer(): void {\n if (flushTimer !== null || disposed) return;\n\n flushTimer = setInterval(() => {\n scheduleFlush();\n }, flushInterval);\n }\n\n const nonBlockingSink: Sink & AsyncDisposable = (record: LogRecord) => {\n if (disposed) return;\n if (fd == null) fd = asyncOptions.openSync(path);\n let formattedRecord: string | undefined;\n let encodedRecord: Uint8Array | undefined;\n\n // ULTRA FAST PATH: Direct write when buffer is empty\n if (byteBuffer.isEmpty() && !activeFlush) {\n // Inline everything for maximum speed - avoid all function calls\n formattedRecord = formatter(record);\n encodedRecord = encoder.encode(formattedRecord);\n\n // Only use fast path for typical log sizes to avoid breaking edge cases\n if (encodedRecord.length < 200) {\n // Write directly for small logs - no complex buffering logic\n asyncOptions.writeSync(fd, encodedRecord);\n scheduleDirectFlush(encodedRecord.length, isMetaLoggerRecord(record));\n return;\n }\n }\n\n // STANDARD PATH: Complex logic for edge cases\n formattedRecord ??= formatter(record);\n encodedRecord ??= encoder.encode(formattedRecord);\n byteBuffer.append(encodedRecord);\n bufferedNonMetaRecord ||= !isMetaLoggerRecord(record);\n\n // Check for immediate flush conditions\n if (bufferSize <= 0) {\n // No buffering - flush immediately\n scheduleFlush();\n } else {\n // Use adaptive strategy for intelligent flushing\n const timeSinceLastFlush = record.timestamp - lastFlushTimestamp;\n const shouldFlush = adaptiveStrategy.shouldFlush(\n byteBuffer.size(),\n timeSinceLastFlush,\n );\n\n if (shouldFlush) {\n scheduleFlush();\n } else if (flushTimer === null && flushInterval > 0) {\n startFlushTimer();\n }\n }\n };\n\n nonBlockingSink[Symbol.asyncDispose] = async () => {\n disposed = true;\n if (flushTimer !== null) {\n clearInterval(flushTimer);\n flushTimer = null;\n }\n if (activeFlush !== null) await activeFlush;\n await flushBuffer();\n if (fd !== null) {\n try {\n await asyncOptions.close(fd);\n } catch {\n // Writer might already be closed or errored\n }\n }\n // Clean up buffer pool\n bufferPool.clear();\n };\n\n return nonBlockingSink;\n}\n\n/**\n * Options for the {@link getBaseRotatingFileSink} function.\n */\nexport interface RotatingFileSinkOptions extends Omit<FileSinkOptions, \"lazy\"> {\n /**\n * The maximum bytes of the file before it is rotated. 1 MiB by default.\n */\n maxSize?: number;\n\n /**\n * The maximum number of files to keep. 5 by default.\n */\n maxFiles?: number;\n}\n\n/**\n * A platform-specific rotating file sink driver.\n */\nexport interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): { size: number };\n\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n\n/**\n * A platform-specific async rotating file sink driver.\n * @since 1.0.0\n */\nexport interface AsyncRotatingFileSinkDriver<TFile>\n extends AsyncFileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): { size: number };\n\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n\n/**\n * Get a platform-independent rotating file sink.\n *\n * This sink writes log records to a file, and rotates the file when it reaches\n * the `maxSize`. The rotated files are named with the original file name\n * followed by a dot and a number, starting from 1. The number is incremented\n * for each rotation, and the maximum number of files to keep is `maxFiles`.\n *\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options: RotatingFileSinkOptions & RotatingFileSinkDriver<TFile>,\n): Sink & Disposable;\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options: RotatingFileSinkOptions & AsyncRotatingFileSinkDriver<TFile>,\n): Sink & AsyncDisposable;\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options:\n & RotatingFileSinkOptions\n & (RotatingFileSinkDriver<TFile> | AsyncRotatingFileSinkDriver<TFile>),\n): Sink & (Disposable | AsyncDisposable) {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n const maxSize = options.maxSize ?? 1024 * 1024;\n const maxFiles = options.maxFiles ?? 5;\n const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars\n const flushInterval = options.flushInterval ?? 5000; // Default flush interval of 5 seconds\n let offset: number = 0;\n try {\n const stat = options.statSync(path);\n offset = stat.size;\n } catch {\n // Continue as the offset is already 0.\n }\n let fd = options.openSync(path);\n let lastFlushTimestamp: number = Date.now();\n let buffer: string = \"\";\n\n function shouldRollover(bytes: Uint8Array): boolean {\n return offset + bytes.length > maxSize;\n }\n function performRollover(): void {\n options.closeSync(fd);\n for (let i = maxFiles - 1; i > 0; i--) {\n const oldPath = `${path}.${i}`;\n const newPath = `${path}.${i + 1}`;\n try {\n options.renameSync(oldPath, newPath);\n } catch (_) {\n // Continue if the file does not exist.\n }\n }\n options.renameSync(path, `${path}.1`);\n offset = 0;\n fd = options.openSync(path);\n }\n\n if (!options.nonBlocking) {\n // Blocking mode implementation\n // deno-lint-ignore no-inner-declarations\n function flushBuffer(): void {\n if (buffer.length > 0) {\n const bytes = encoder.encode(buffer);\n buffer = \"\";\n if (shouldRollover(bytes)) performRollover();\n options.writeSync(fd, bytes);\n options.flushSync(fd);\n offset += bytes.length;\n lastFlushTimestamp = Date.now();\n }\n }\n\n const sink: Sink & Disposable = (record: LogRecord) => {\n buffer += formatter(record);\n\n const shouldFlushBySize = buffer.length >= bufferSize;\n const shouldFlushByTime = flushInterval > 0 &&\n (record.timestamp - lastFlushTimestamp) >= flushInterval;\n\n if (shouldFlushBySize || shouldFlushByTime) {\n flushBuffer();\n }\n };\n sink[Symbol.dispose] = () => {\n flushBuffer();\n options.closeSync(fd);\n };\n return sink;\n }\n\n // Non-blocking mode implementation\n const asyncOptions = options as AsyncRotatingFileSinkDriver<TFile>;\n let disposed = false;\n let activeFlush: Promise<void> | null = null;\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n let bufferedNonMetaRecord = false;\n\n function reportFlushError(error: unknown): void {\n try {\n getLogger([\"logtape\", \"meta\"]).warn(\n \"Non-blocking rotating file sink flush failed for {path}: {error}\",\n { error, path },\n );\n } catch {\n // Last resort: keep non-blocking file sinks from throwing.\n }\n }\n\n async function flushBuffer(): Promise<void> {\n if (buffer.length === 0) return;\n\n const data = buffer;\n buffer = \"\";\n const suppressErrorReport = !bufferedNonMetaRecord;\n bufferedNonMetaRecord = false;\n try {\n const bytes = encoder.encode(data);\n if (shouldRollover(bytes)) performRollover();\n asyncOptions.writeSync(fd, bytes);\n await asyncOptions.flush(fd);\n offset += bytes.length;\n lastFlushTimestamp = Date.now();\n } catch (error) {\n if (!suppressErrorReport) reportFlushError(error);\n }\n }\n\n function scheduleFlush(): void {\n if (activeFlush || disposed) return;\n\n activeFlush = flushBuffer().finally(() => {\n activeFlush = null;\n });\n }\n\n function startFlushTimer(): void {\n if (flushTimer !== null || disposed) return;\n\n flushTimer = setInterval(() => {\n scheduleFlush();\n }, flushInterval);\n }\n\n const nonBlockingSink: Sink & AsyncDisposable = (record: LogRecord) => {\n if (disposed) return;\n buffer += formatter(record);\n bufferedNonMetaRecord ||= !isMetaLoggerRecord(record);\n\n const shouldFlushBySize = buffer.length >= bufferSize;\n const shouldFlushByTime = flushInterval > 0 &&\n (record.timestamp - lastFlushTimestamp) >= flushInterval;\n\n if (shouldFlushBySize || shouldFlushByTime) {\n scheduleFlush();\n } else if (flushTimer === null && flushInterval > 0) {\n startFlushTimer();\n }\n };\n\n nonBlockingSink[Symbol.asyncDispose] = async () => {\n disposed = true;\n if (flushTimer !== null) {\n clearInterval(flushTimer);\n flushTimer = null;\n }\n if (activeFlush !== null) await activeFlush;\n await flushBuffer();\n try {\n await asyncOptions.close(fd);\n } catch {\n // Writer might already be closed or errored\n }\n };\n\n return nonBlockingSink;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"filesink.base.js","names":["record: LogRecord","baseThreshold: number","baseInterval: number","size: number","timeSinceLastFlush: number","currentSize: number","values: number[]","buffer: Uint8Array","bufferPool: BufferPool","data: Uint8Array","path: string","options:\n & FileSinkOptions\n & (FileSinkDriver<TFile> | AsyncFileSinkDriver<TFile>)","lastFlushTimestamp: number","flushBuffer","sink: Sink & Disposable","formattedRecord: string | undefined","encodedRecord: Uint8Array | undefined","activeFlush: Promise<void> | null","flushTimer: ReturnType<typeof setInterval> | null","error: unknown","flushSize: number","suppressErrorReport: boolean","nonBlockingSink: Sink & AsyncDisposable","options:\n & RotatingFileSinkOptions\n & (RotatingFileSinkDriver<TFile> | AsyncRotatingFileSinkDriver<TFile>)","offset: number","buffer: string","bytes: Uint8Array"],"sources":["../src/filesink.base.ts"],"sourcesContent":["import {\n defaultTextFormatter,\n getLogger,\n type LogRecord,\n type Sink,\n type StreamSinkOptions,\n} from \"@logtape/logtape\";\n\nfunction isMetaLoggerRecord(record: LogRecord): boolean {\n return record.category.length === 2 &&\n record.category[0] === \"logtape\" &&\n record.category[1] === \"meta\";\n}\n\n/**\n * Adaptive flush strategy that dynamically adjusts buffer thresholds\n * based on recent flush patterns for optimal performance.\n */\nclass AdaptiveFlushStrategy {\n private recentFlushSizes: number[] = [];\n private recentFlushTimes: number[] = [];\n private avgFlushSize: number;\n private avgFlushInterval: number;\n private readonly maxHistorySize = 10;\n private readonly baseThreshold: number;\n private readonly baseInterval: number;\n\n constructor(baseThreshold: number, baseInterval: number) {\n this.baseThreshold = baseThreshold;\n this.baseInterval = baseInterval;\n this.avgFlushSize = baseThreshold;\n this.avgFlushInterval = baseInterval;\n }\n\n /**\n * Record a flush event for pattern analysis.\n * @param size The size of data flushed in bytes.\n * @param timeSinceLastFlush Time since last flush in milliseconds.\n */\n recordFlush(size: number, timeSinceLastFlush: number): void {\n this.recentFlushSizes.push(size);\n this.recentFlushTimes.push(timeSinceLastFlush);\n\n // Keep only recent history\n if (this.recentFlushSizes.length > this.maxHistorySize) {\n this.recentFlushSizes.shift();\n this.recentFlushTimes.shift();\n }\n\n // Update averages\n this.updateAverages();\n }\n\n /**\n * Determine if buffer should be flushed based on adaptive strategy.\n * @param currentSize Current buffer size in bytes.\n * @param timeSinceLastFlush Time since last flush in milliseconds.\n * @returns True if buffer should be flushed.\n */\n shouldFlush(currentSize: number, timeSinceLastFlush: number): boolean {\n const adaptiveThreshold = this.calculateAdaptiveThreshold();\n const adaptiveInterval = this.calculateAdaptiveInterval();\n\n return currentSize >= adaptiveThreshold ||\n (adaptiveInterval > 0 && timeSinceLastFlush >= adaptiveInterval);\n }\n\n private updateAverages(): void {\n if (this.recentFlushSizes.length === 0) return;\n\n this.avgFlushSize =\n this.recentFlushSizes.reduce((sum, size) => sum + size, 0) /\n this.recentFlushSizes.length;\n\n this.avgFlushInterval =\n this.recentFlushTimes.reduce((sum, time) => sum + time, 0) /\n this.recentFlushTimes.length;\n }\n\n private calculateAdaptiveThreshold(): number {\n // Adjust threshold based on recent patterns\n // Higher average flush sizes suggest larger batches are beneficial\n const adaptiveFactor = Math.min(\n 2.0,\n Math.max(0.5, this.avgFlushSize / this.baseThreshold),\n );\n\n return Math.max(\n Math.min(4096, this.baseThreshold / 2),\n Math.min(64 * 1024, this.baseThreshold * adaptiveFactor),\n );\n }\n\n private calculateAdaptiveInterval(): number {\n // If base interval is 0, time-based flushing is disabled\n if (this.baseInterval <= 0) return 0;\n\n if (this.avgFlushInterval <= 0) return this.baseInterval;\n\n // Adjust interval based on recent flush frequency\n // More frequent flushes suggest lower latency is preferred\n if (this.recentFlushTimes.length < 3) return this.avgFlushInterval;\n\n const variance = this.calculateVariance(this.recentFlushTimes);\n const stabilityFactor = Math.min(2.0, Math.max(0.5, 1000 / variance));\n\n return Math.max(\n 1000,\n Math.min(10000, this.avgFlushInterval * stabilityFactor),\n );\n }\n\n private calculateVariance(values: number[]): number {\n if (values.length < 2) return 1000; // Default variance\n\n const mean = values.reduce((sum, val) => sum + val, 0) / values.length;\n const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));\n return squaredDiffs.reduce((sum, diff) => sum + diff, 0) / values.length;\n }\n}\n\n/**\n * Memory pool for reusing Uint8Array buffers to minimize GC pressure.\n * Maintains a pool of pre-allocated buffers for efficient reuse.\n */\nclass BufferPool {\n private pool: Uint8Array[] = [];\n private readonly maxPoolSize = 50; // Keep a reasonable pool size\n private readonly maxBufferSize = 64 * 1024; // Don't pool very large buffers\n\n /**\n * Acquire a buffer from the pool or create a new one.\n * @param size The minimum size needed for the buffer.\n * @returns A Uint8Array that can be used for encoding.\n */\n acquire(size: number): Uint8Array {\n // Don't pool very large buffers to avoid memory waste\n if (size > this.maxBufferSize) {\n return new Uint8Array(size);\n }\n\n // Try to find a suitable buffer from the pool\n for (let i = this.pool.length - 1; i >= 0; i--) {\n const buffer = this.pool[i];\n if (buffer.length >= size) {\n // Remove from pool and return\n this.pool.splice(i, 1);\n return buffer.subarray(0, size);\n }\n }\n\n // No suitable buffer found, create a new one\n // Create slightly larger buffer to improve reuse chances\n const actualSize = Math.max(size, 1024); // Minimum 1KB\n return new Uint8Array(actualSize);\n }\n\n /**\n * Return a buffer to the pool for future reuse.\n * @param buffer The buffer to return to the pool.\n */\n release(buffer: Uint8Array): void {\n // Don't pool if we're at capacity or buffer is too large\n if (\n this.pool.length >= this.maxPoolSize || buffer.length > this.maxBufferSize\n ) {\n return;\n }\n\n // Don't pool very small buffers as they're cheap to allocate\n if (buffer.length < 256) {\n return;\n }\n\n // Add to pool for reuse\n this.pool.push(buffer);\n }\n\n /**\n * Clear the pool to free memory. Useful for cleanup.\n */\n clear(): void {\n this.pool.length = 0;\n }\n\n /**\n * Get current pool statistics for monitoring.\n * @returns Object with pool size and buffer count.\n */\n getStats(): { poolSize: number; totalBuffers: number } {\n return {\n poolSize: this.pool.reduce((sum, buf) => sum + buf.length, 0),\n totalBuffers: this.pool.length,\n };\n }\n}\n\n/**\n * High-performance byte buffer for batching log records.\n * Eliminates string concatenation overhead by storing pre-encoded bytes.\n * Uses memory pooling to reduce GC pressure.\n */\nclass ByteRingBuffer {\n private buffers: Uint8Array[] = [];\n private totalSize: number = 0;\n private bufferPool: BufferPool;\n\n constructor(bufferPool: BufferPool) {\n this.bufferPool = bufferPool;\n }\n\n /**\n * Append pre-encoded log record bytes to the buffer.\n * @param data The encoded log record as bytes.\n */\n append(data: Uint8Array): void {\n this.buffers.push(data);\n this.totalSize += data.length;\n }\n\n /**\n * Get the current total size of buffered data in bytes.\n * @returns The total size in bytes.\n */\n size(): number {\n return this.totalSize;\n }\n\n /**\n * Get the number of buffered records.\n * @returns The number of records in the buffer.\n */\n count(): number {\n return this.buffers.length;\n }\n\n /**\n * Flush all buffered data and return it as an array of byte arrays.\n * This clears the internal buffer and returns used buffers to the pool.\n * @returns Array of buffered byte arrays ready for writev() operations.\n */\n flush(): Uint8Array[] {\n const result = [...this.buffers];\n this.clear();\n return result;\n }\n\n /**\n * Clear the buffer without returning data.\n * Returns buffers to the pool for reuse.\n */\n clear(): void {\n // Return buffers to pool for reuse\n for (const buffer of this.buffers) {\n this.bufferPool.release(buffer);\n }\n this.buffers.length = 0;\n this.totalSize = 0;\n }\n\n /**\n * Check if the buffer is empty.\n * @returns True if the buffer contains no data.\n */\n isEmpty(): boolean {\n return this.buffers.length === 0;\n }\n}\n\n/**\n * Options for the {@link getBaseFileSink} function.\n */\nexport type FileSinkOptions = StreamSinkOptions & {\n /**\n * If `true`, the file is not opened until the first write. Defaults to `false`.\n */\n lazy?: boolean;\n\n /**\n * The size of the buffer to use when writing to the file. If not specified,\n * a default buffer size will be used. If it is less or equal to 0,\n * the file will be written directly without buffering.\n * @default 8192\n * @since 0.12.0\n */\n bufferSize?: number;\n\n /**\n * The maximum time interval in milliseconds between flushes. If this time\n * passes since the last flush, the buffer will be flushed regardless of size.\n * This helps prevent log loss during unexpected process termination.\n * @default 5000\n * @since 0.12.0\n */\n flushInterval?: number;\n\n /**\n * Enable non-blocking mode with background flushing.\n * When enabled, flush operations are performed asynchronously to prevent\n * blocking the main thread during file I/O operations.\n *\n * @default `false`\n * @since 1.0.0\n */\n nonBlocking?: boolean;\n};\n\n/**\n * A platform-specific file sink driver.\n * @template TFile The type of the file descriptor.\n */\nexport interface FileSinkDriver<TFile> {\n /**\n * Open a file for appending and return a file descriptor.\n * @param path A path to the file to open.\n */\n openSync(path: string): TFile;\n\n /**\n * Write a chunk of data to the file.\n * @param fd The file descriptor.\n * @param chunk The data to write.\n */\n writeSync(fd: TFile, chunk: Uint8Array): void;\n\n /**\n * Write multiple chunks of data to the file in a single operation.\n * This is optional - if not implemented, falls back to multiple writeSync calls.\n * @param fd The file descriptor.\n * @param chunks Array of data chunks to write.\n */\n writeManySync?(fd: TFile, chunks: Uint8Array[]): void;\n\n /**\n * Flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flushSync(fd: TFile): void;\n\n /**\n * Close the file.\n * @param fd The file descriptor.\n */\n closeSync(fd: TFile): void;\n}\n\n/**\n * A platform-specific async file sink driver.\n * @template TFile The type of the file descriptor.\n * @since 1.0.0\n */\nexport interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Asynchronously write multiple chunks of data to the file in a single operation.\n * This is optional - if not implemented, falls back to multiple writeSync calls.\n * @param fd The file descriptor.\n * @param chunks Array of data chunks to write.\n */\n writeMany?(fd: TFile, chunks: Uint8Array[]): Promise<void>;\n\n /**\n * Asynchronously flush the file to ensure that all data is written to the disk.\n * @param fd The file descriptor.\n */\n flush(fd: TFile): Promise<void>;\n\n /**\n * Asynchronously close the file.\n * @param fd The file descriptor.\n */\n close(fd: TFile): Promise<void>;\n}\n\n/**\n * Get a platform-independent file sink.\n *\n * @template TFile The type of the file descriptor.\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\nexport function getBaseFileSink<TFile>(\n path: string,\n options: FileSinkOptions & FileSinkDriver<TFile>,\n): Sink & Disposable;\nexport function getBaseFileSink<TFile>(\n path: string,\n options: FileSinkOptions & AsyncFileSinkDriver<TFile>,\n): Sink & AsyncDisposable;\nexport function getBaseFileSink<TFile>(\n path: string,\n options:\n & FileSinkOptions\n & (FileSinkDriver<TFile> | AsyncFileSinkDriver<TFile>),\n): Sink & (Disposable | AsyncDisposable) {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 bytes\n const flushInterval = options.flushInterval ?? 5000; // Default flush interval of 5 seconds\n let fd = options.lazy ? null : options.openSync(path);\n\n // Initialize memory pool and buffer systems\n const bufferPool = new BufferPool();\n const byteBuffer = new ByteRingBuffer(bufferPool);\n const adaptiveStrategy = new AdaptiveFlushStrategy(bufferSize, flushInterval);\n let lastFlushTimestamp: number = Date.now();\n\n if (!options.nonBlocking) {\n // Blocking mode implementation\n // deno-lint-ignore no-inner-declarations\n function flushBuffer(): void {\n if (fd == null || byteBuffer.isEmpty()) return;\n\n const flushSize = byteBuffer.size();\n const currentTime = Date.now();\n const timeSinceLastFlush = currentTime - lastFlushTimestamp;\n\n const chunks = byteBuffer.flush();\n if (options.writeManySync && chunks.length > 1) {\n // Use batch write if available\n options.writeManySync(fd, chunks);\n } else {\n // Fallback to individual writes\n for (const chunk of chunks) {\n options.writeSync(fd, chunk);\n }\n }\n options.flushSync(fd);\n\n // Record flush for adaptive strategy\n adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);\n lastFlushTimestamp = currentTime;\n }\n\n const sink: Sink & Disposable = (record: LogRecord) => {\n if (fd == null) fd = options.openSync(path);\n let formattedRecord: string | undefined;\n let encodedRecord: Uint8Array | undefined;\n\n // ULTRA FAST PATH: Direct write when buffer is empty\n if (byteBuffer.isEmpty()) {\n // Inline everything for maximum speed - avoid all function calls\n formattedRecord = formatter(record);\n encodedRecord = encoder.encode(formattedRecord);\n\n // Only use fast path for typical log sizes to avoid breaking edge cases\n if (encodedRecord.length < 200) {\n // Write directly for small logs - no complex buffering logic\n options.writeSync(fd, encodedRecord);\n options.flushSync(fd);\n const currentTime = Date.now();\n adaptiveStrategy.recordFlush(\n encodedRecord.length,\n currentTime - lastFlushTimestamp,\n );\n lastFlushTimestamp = currentTime;\n return;\n }\n }\n\n // STANDARD PATH: Complex logic for edge cases\n formattedRecord ??= formatter(record);\n encodedRecord ??= encoder.encode(formattedRecord);\n byteBuffer.append(encodedRecord);\n\n // Check for immediate flush conditions\n if (bufferSize <= 0) {\n // No buffering - flush immediately\n flushBuffer();\n } else {\n // Use adaptive strategy for intelligent flushing\n const timeSinceLastFlush = record.timestamp - lastFlushTimestamp;\n const shouldFlush = adaptiveStrategy.shouldFlush(\n byteBuffer.size(),\n timeSinceLastFlush,\n );\n\n if (shouldFlush) {\n flushBuffer();\n }\n }\n };\n sink[Symbol.dispose] = () => {\n if (fd !== null) {\n flushBuffer();\n options.closeSync(fd);\n }\n // Clean up buffer pool\n bufferPool.clear();\n };\n return sink;\n }\n\n // Non-blocking mode implementation\n const asyncOptions = options as AsyncFileSinkDriver<TFile>;\n let disposed = false;\n let activeFlush: Promise<void> | null = null;\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n let bufferedNonMetaRecord = false;\n\n function reportFlushError(error: unknown): void {\n try {\n getLogger([\"logtape\", \"meta\"]).warn(\n \"Non-blocking file sink flush failed for {path}: {error}\",\n { error, path },\n );\n } catch {\n // Last resort: keep non-blocking file sinks from throwing.\n }\n }\n\n async function flushBuffer(): Promise<void> {\n if (fd == null || byteBuffer.isEmpty()) return;\n\n const flushSize = byteBuffer.size();\n const currentTime = Date.now();\n const timeSinceLastFlush = currentTime - lastFlushTimestamp;\n\n const chunks = byteBuffer.flush();\n const suppressErrorReport = !bufferedNonMetaRecord;\n bufferedNonMetaRecord = false;\n try {\n if (asyncOptions.writeMany && chunks.length > 1) {\n // Use async batch write if available\n await asyncOptions.writeMany(fd, chunks);\n } else {\n // Fallback to individual writes\n for (const chunk of chunks) {\n asyncOptions.writeSync(fd, chunk);\n }\n }\n await asyncOptions.flush(fd);\n\n // Record flush for adaptive strategy\n adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);\n lastFlushTimestamp = currentTime;\n } catch (error) {\n if (!suppressErrorReport) reportFlushError(error);\n }\n }\n\n function scheduleFlush(): void {\n if (activeFlush || disposed) return;\n\n activeFlush = flushBuffer().finally(() => {\n activeFlush = null;\n if (!disposed && !byteBuffer.isEmpty()) scheduleFlush();\n });\n }\n\n function scheduleDirectFlush(\n flushSize: number,\n suppressErrorReport: boolean,\n ): void {\n if (activeFlush || disposed || fd == null) return;\n\n const flushFd = fd;\n const startedAt = Date.now();\n const timeSinceLastFlush = startedAt - lastFlushTimestamp;\n activeFlush = Promise.resolve()\n .then(() => asyncOptions.flush(flushFd))\n .then(() => {\n adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);\n lastFlushTimestamp = Date.now();\n })\n .catch((error) => {\n if (!suppressErrorReport) reportFlushError(error);\n })\n .finally(() => {\n activeFlush = null;\n if (!disposed && !byteBuffer.isEmpty()) scheduleFlush();\n });\n }\n\n function startFlushTimer(): void {\n if (flushTimer !== null || disposed) return;\n\n flushTimer = setInterval(() => {\n scheduleFlush();\n }, flushInterval);\n }\n\n const nonBlockingSink: Sink & AsyncDisposable = (record: LogRecord) => {\n if (disposed) return;\n if (fd == null) fd = asyncOptions.openSync(path);\n let formattedRecord: string | undefined;\n let encodedRecord: Uint8Array | undefined;\n\n // ULTRA FAST PATH: Direct write when buffer is empty\n if (byteBuffer.isEmpty() && !activeFlush) {\n // Inline everything for maximum speed - avoid all function calls\n formattedRecord = formatter(record);\n encodedRecord = encoder.encode(formattedRecord);\n\n // Only use fast path for typical log sizes to avoid breaking edge cases\n if (encodedRecord.length < 200) {\n // Write directly for small logs - no complex buffering logic\n asyncOptions.writeSync(fd, encodedRecord);\n scheduleDirectFlush(encodedRecord.length, isMetaLoggerRecord(record));\n return;\n }\n }\n\n // STANDARD PATH: Complex logic for edge cases\n formattedRecord ??= formatter(record);\n encodedRecord ??= encoder.encode(formattedRecord);\n byteBuffer.append(encodedRecord);\n bufferedNonMetaRecord ||= !isMetaLoggerRecord(record);\n\n // Check for immediate flush conditions\n if (bufferSize <= 0) {\n // No buffering - flush immediately\n scheduleFlush();\n } else {\n // Use adaptive strategy for intelligent flushing\n const timeSinceLastFlush = record.timestamp - lastFlushTimestamp;\n const shouldFlush = adaptiveStrategy.shouldFlush(\n byteBuffer.size(),\n timeSinceLastFlush,\n );\n\n if (shouldFlush) {\n scheduleFlush();\n } else if (flushTimer === null && flushInterval > 0) {\n startFlushTimer();\n }\n }\n };\n\n nonBlockingSink[Symbol.asyncDispose] = async () => {\n disposed = true;\n if (flushTimer !== null) {\n clearInterval(flushTimer);\n flushTimer = null;\n }\n if (activeFlush !== null) await activeFlush;\n await flushBuffer();\n if (fd !== null) {\n try {\n await asyncOptions.close(fd);\n } catch {\n // Writer might already be closed or errored\n }\n }\n // Clean up buffer pool\n bufferPool.clear();\n };\n\n return nonBlockingSink;\n}\n\n/**\n * Options for the {@link getBaseRotatingFileSink} function.\n */\nexport interface RotatingFileSinkOptions extends Omit<FileSinkOptions, \"lazy\"> {\n /**\n * The maximum bytes of the file before it is rotated. 1 MiB by default.\n */\n maxSize?: number;\n\n /**\n * The maximum number of files to keep. 5 by default.\n */\n maxFiles?: number;\n}\n\n/**\n * A platform-specific rotating file sink driver.\n */\nexport interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): { size: number };\n\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n\n/**\n * A platform-specific async rotating file sink driver.\n * @since 1.0.0\n */\nexport interface AsyncRotatingFileSinkDriver<TFile>\n extends AsyncFileSinkDriver<TFile> {\n /**\n * Get the size of the file.\n * @param path A path to the file.\n * @returns The `size` of the file in bytes, in an object.\n */\n statSync(path: string): { size: number };\n\n /**\n * Rename a file.\n * @param oldPath A path to the file to rename.\n * @param newPath A path to be renamed to.\n */\n renameSync(oldPath: string, newPath: string): void;\n}\n\n/**\n * Get a platform-independent rotating file sink.\n *\n * This sink writes log records to a file, and rotates the file when it reaches\n * the `maxSize`. The rotated files are named with the original file name\n * followed by a dot and a number, starting from 1. The number is incremented\n * for each rotation, and the maximum number of files to keep is `maxFiles`.\n *\n * @param path A path to the file to write to.\n * @param options The options for the sink and the file driver.\n * @returns A sink that writes to the file. The sink is also a disposable\n * object that closes the file when disposed. If `nonBlocking` is enabled,\n * returns a sink that also implements {@link AsyncDisposable}.\n */\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options: RotatingFileSinkOptions & RotatingFileSinkDriver<TFile>,\n): Sink & Disposable;\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options: RotatingFileSinkOptions & AsyncRotatingFileSinkDriver<TFile>,\n): Sink & AsyncDisposable;\nexport function getBaseRotatingFileSink<TFile>(\n path: string,\n options:\n & RotatingFileSinkOptions\n & (RotatingFileSinkDriver<TFile> | AsyncRotatingFileSinkDriver<TFile>),\n): Sink & (Disposable | AsyncDisposable) {\n const formatter = options.formatter ?? defaultTextFormatter;\n const encoder = options.encoder ?? new TextEncoder();\n const maxSize = options.maxSize ?? 1024 * 1024;\n const maxFiles = options.maxFiles ?? 5;\n const bufferSize = options.bufferSize ?? 1024 * 8; // Default buffer size of 8192 chars\n const flushInterval = options.flushInterval ?? 5000; // Default flush interval of 5 seconds\n let offset: number = 0;\n try {\n const stat = options.statSync(path);\n offset = stat.size;\n } catch {\n // Continue as the offset is already 0.\n }\n let fd = options.openSync(path);\n let lastFlushTimestamp: number = Date.now();\n let buffer: string = \"\";\n\n function shouldRollover(bytes: Uint8Array): boolean {\n return offset + bytes.length > maxSize;\n }\n function performRollover(): void {\n options.closeSync(fd);\n for (let i = maxFiles - 1; i > 0; i--) {\n const oldPath = `${path}.${i}`;\n const newPath = `${path}.${i + 1}`;\n try {\n options.renameSync(oldPath, newPath);\n } catch (_) {\n // Continue if the file does not exist.\n }\n }\n options.renameSync(path, `${path}.1`);\n offset = 0;\n fd = options.openSync(path);\n }\n\n if (!options.nonBlocking) {\n // Blocking mode implementation\n // deno-lint-ignore no-inner-declarations\n function flushBuffer(): void {\n if (buffer.length > 0) {\n const bytes = encoder.encode(buffer);\n buffer = \"\";\n if (shouldRollover(bytes)) performRollover();\n options.writeSync(fd, bytes);\n options.flushSync(fd);\n offset += bytes.length;\n lastFlushTimestamp = Date.now();\n }\n }\n\n const sink: Sink & Disposable = (record: LogRecord) => {\n buffer += formatter(record);\n\n const shouldFlushBySize = buffer.length >= bufferSize;\n const shouldFlushByTime = flushInterval > 0 &&\n (record.timestamp - lastFlushTimestamp) >= flushInterval;\n\n if (shouldFlushBySize || shouldFlushByTime) {\n flushBuffer();\n }\n };\n sink[Symbol.dispose] = () => {\n flushBuffer();\n options.closeSync(fd);\n };\n return sink;\n }\n\n // Non-blocking mode implementation\n const asyncOptions = options as AsyncRotatingFileSinkDriver<TFile>;\n let disposed = false;\n let activeFlush: Promise<void> | null = null;\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n let bufferedNonMetaRecord = false;\n\n function reportFlushError(error: unknown): void {\n try {\n getLogger([\"logtape\", \"meta\"]).warn(\n \"Non-blocking rotating file sink flush failed for {path}: {error}\",\n { error, path },\n );\n } catch {\n // Last resort: keep non-blocking file sinks from throwing.\n }\n }\n\n async function flushBuffer(): Promise<void> {\n if (buffer.length === 0) return;\n\n const data = buffer;\n buffer = \"\";\n const suppressErrorReport = !bufferedNonMetaRecord;\n bufferedNonMetaRecord = false;\n try {\n const bytes = encoder.encode(data);\n if (shouldRollover(bytes)) performRollover();\n asyncOptions.writeSync(fd, bytes);\n await asyncOptions.flush(fd);\n offset += bytes.length;\n lastFlushTimestamp = Date.now();\n } catch (error) {\n if (!suppressErrorReport) reportFlushError(error);\n }\n }\n\n function scheduleFlush(): void {\n if (activeFlush || disposed) return;\n\n activeFlush = flushBuffer().finally(() => {\n activeFlush = null;\n if (!disposed && buffer.length > 0) scheduleFlush();\n });\n }\n\n function startFlushTimer(): void {\n if (flushTimer !== null || disposed) return;\n\n flushTimer = setInterval(() => {\n scheduleFlush();\n }, flushInterval);\n }\n\n const nonBlockingSink: Sink & AsyncDisposable = (record: LogRecord) => {\n if (disposed) return;\n buffer += formatter(record);\n bufferedNonMetaRecord ||= !isMetaLoggerRecord(record);\n\n const shouldFlushBySize = buffer.length >= bufferSize;\n const shouldFlushByTime = flushInterval > 0 &&\n (record.timestamp - lastFlushTimestamp) >= flushInterval;\n\n if (shouldFlushBySize || shouldFlushByTime) {\n scheduleFlush();\n } else if (flushTimer === null && flushInterval > 0) {\n startFlushTimer();\n }\n };\n\n nonBlockingSink[Symbol.asyncDispose] = async () => {\n disposed = true;\n if (flushTimer !== null) {\n clearInterval(flushTimer);\n flushTimer = null;\n }\n if (activeFlush !== null) await activeFlush;\n await flushBuffer();\n try {\n await asyncOptions.close(fd);\n } catch {\n // Writer might already be closed or errored\n }\n };\n\n return nonBlockingSink;\n}\n"],"mappings":"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"}
package/dist/mod.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FileSinkDriver, FileSinkOptions, RotatingFileSinkDriver, RotatingFileSinkOptions } from "./filesink.base.cjs";
2
2
  import { TimeRotatingFileSinkOptions, TimeRotationInterval } from "./timefilesink.cjs";
3
3
  import { StreamFileSinkOptions, getStreamFileSink } from "./streamfilesink.cjs";
4
- import { getFileSink, getRotatingFileSink, getTimeRotatingFileSink } from "#filesink";
4
+ import { getFileSink, getRotatingFileSink, getTimeRotatingFileSink } from "./dist/filesink.node.cjs";
5
5
  export { FileSinkDriver, FileSinkOptions, RotatingFileSinkDriver, RotatingFileSinkOptions, StreamFileSinkOptions, TimeRotatingFileSinkOptions, TimeRotationInterval, getFileSink, getRotatingFileSink, getStreamFileSink, getTimeRotatingFileSink };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logtape/file",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "File sink and rotating file sink for LogTape",
5
5
  "keywords": [
6
6
  "logging",
@@ -60,7 +60,7 @@
60
60
  "dist/"
61
61
  ],
62
62
  "peerDependencies": {
63
- "@logtape/logtape": "^2.1.3"
63
+ "@logtape/logtape": "^2.1.5"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@alinea/suite": "^0.6.3",