@logtape/file 1.0.0-dev.241 → 1.0.0-dev.246
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.
- package/deno.json +1 -1
- package/dist/filesink.base.cjs +52 -347
- package/dist/filesink.base.d.cts +3 -65
- package/dist/filesink.base.d.cts.map +1 -1
- package/dist/filesink.base.d.ts +3 -65
- package/dist/filesink.base.d.ts.map +1 -1
- package/dist/filesink.base.js +52 -347
- package/dist/filesink.base.js.map +1 -1
- package/dist/filesink.deno.cjs +23 -26
- package/dist/filesink.deno.d.cts +4 -17
- package/dist/filesink.deno.d.cts.map +1 -1
- package/dist/filesink.deno.d.ts +4 -17
- package/dist/filesink.deno.d.ts.map +1 -1
- package/dist/filesink.deno.js +24 -26
- package/dist/filesink.deno.js.map +1 -1
- package/dist/filesink.node.cjs +23 -33
- package/dist/filesink.node.d.cts +4 -17
- package/dist/filesink.node.d.cts.map +1 -1
- package/dist/filesink.node.d.ts +4 -17
- package/dist/filesink.node.d.ts.map +1 -1
- package/dist/filesink.node.js +24 -33
- package/dist/filesink.node.js.map +1 -1
- package/dist/mod.cjs +1 -3
- package/dist/mod.d.cts +1 -2
- package/dist/mod.d.ts +1 -2
- package/dist/mod.js +1 -2
- package/filesink.base.ts +37 -618
- package/filesink.deno.ts +4 -57
- package/filesink.jsr.ts +7 -32
- package/filesink.node.ts +4 -58
- package/filesink.test.ts +0 -120
- package/mod.ts +0 -2
- package/package.json +12 -7
- package/tsdown.config.ts +2 -2
- package/dist/streamfilesink.cjs +0 -84
- package/dist/streamfilesink.d.cts +0 -95
- package/dist/streamfilesink.d.cts.map +0 -1
- package/dist/streamfilesink.d.ts +0 -95
- package/dist/streamfilesink.d.ts.map +0 -1
- package/dist/streamfilesink.js +0 -84
- package/dist/streamfilesink.js.map +0 -1
- package/streamfilesink.test.ts +0 -336
- package/streamfilesink.ts +0 -136
package/deno.json
CHANGED
package/dist/filesink.base.cjs
CHANGED
|
@@ -3,303 +3,59 @@ const __logtape_logtape = require_rolldown_runtime.__toESM(require("@logtape/log
|
|
|
3
3
|
|
|
4
4
|
//#region filesink.base.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Get a platform-independent file sink.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam TFile The type of the file descriptor.
|
|
9
|
+
* @param path A path to the file to write to.
|
|
10
|
+
* @param options The options for the sink and the file driver.
|
|
11
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
12
|
+
* object that closes the file when disposed.
|
|
8
13
|
*/
|
|
9
|
-
var AdaptiveFlushStrategy = class {
|
|
10
|
-
recentFlushSizes = [];
|
|
11
|
-
recentFlushTimes = [];
|
|
12
|
-
avgFlushSize;
|
|
13
|
-
avgFlushInterval;
|
|
14
|
-
maxHistorySize = 10;
|
|
15
|
-
baseThreshold;
|
|
16
|
-
constructor(baseThreshold, baseInterval) {
|
|
17
|
-
this.baseThreshold = baseThreshold;
|
|
18
|
-
this.avgFlushSize = baseThreshold;
|
|
19
|
-
this.avgFlushInterval = baseInterval;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Record a flush event for pattern analysis.
|
|
23
|
-
* @param size The size of data flushed in bytes.
|
|
24
|
-
* @param timeSinceLastFlush Time since last flush in milliseconds.
|
|
25
|
-
*/
|
|
26
|
-
recordFlush(size, timeSinceLastFlush) {
|
|
27
|
-
this.recentFlushSizes.push(size);
|
|
28
|
-
this.recentFlushTimes.push(timeSinceLastFlush);
|
|
29
|
-
if (this.recentFlushSizes.length > this.maxHistorySize) {
|
|
30
|
-
this.recentFlushSizes.shift();
|
|
31
|
-
this.recentFlushTimes.shift();
|
|
32
|
-
}
|
|
33
|
-
this.updateAverages();
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Determine if buffer should be flushed based on adaptive strategy.
|
|
37
|
-
* @param currentSize Current buffer size in bytes.
|
|
38
|
-
* @param timeSinceLastFlush Time since last flush in milliseconds.
|
|
39
|
-
* @returns True if buffer should be flushed.
|
|
40
|
-
*/
|
|
41
|
-
shouldFlush(currentSize, timeSinceLastFlush) {
|
|
42
|
-
const adaptiveThreshold = this.calculateAdaptiveThreshold();
|
|
43
|
-
const adaptiveInterval = this.calculateAdaptiveInterval();
|
|
44
|
-
return currentSize >= adaptiveThreshold || adaptiveInterval > 0 && timeSinceLastFlush >= adaptiveInterval;
|
|
45
|
-
}
|
|
46
|
-
updateAverages() {
|
|
47
|
-
if (this.recentFlushSizes.length === 0) return;
|
|
48
|
-
this.avgFlushSize = this.recentFlushSizes.reduce((sum, size) => sum + size, 0) / this.recentFlushSizes.length;
|
|
49
|
-
this.avgFlushInterval = this.recentFlushTimes.reduce((sum, time) => sum + time, 0) / this.recentFlushTimes.length;
|
|
50
|
-
}
|
|
51
|
-
calculateAdaptiveThreshold() {
|
|
52
|
-
const adaptiveFactor = Math.min(2, Math.max(.5, this.avgFlushSize / this.baseThreshold));
|
|
53
|
-
return Math.max(Math.min(4096, this.baseThreshold / 2), Math.min(64 * 1024, this.baseThreshold * adaptiveFactor));
|
|
54
|
-
}
|
|
55
|
-
calculateAdaptiveInterval() {
|
|
56
|
-
if (this.avgFlushInterval <= 0) return 0;
|
|
57
|
-
if (this.recentFlushTimes.length < 3) return this.avgFlushInterval;
|
|
58
|
-
const variance = this.calculateVariance(this.recentFlushTimes);
|
|
59
|
-
const stabilityFactor = Math.min(2, Math.max(.5, 1e3 / variance));
|
|
60
|
-
return Math.max(1e3, Math.min(1e4, this.avgFlushInterval * stabilityFactor));
|
|
61
|
-
}
|
|
62
|
-
calculateVariance(values) {
|
|
63
|
-
if (values.length < 2) return 1e3;
|
|
64
|
-
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
65
|
-
const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));
|
|
66
|
-
return squaredDiffs.reduce((sum, diff) => sum + diff, 0) / values.length;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* Memory pool for reusing Uint8Array buffers to minimize GC pressure.
|
|
71
|
-
* Maintains a pool of pre-allocated buffers for efficient reuse.
|
|
72
|
-
*/
|
|
73
|
-
var BufferPool = class {
|
|
74
|
-
pool = [];
|
|
75
|
-
maxPoolSize = 50;
|
|
76
|
-
maxBufferSize = 64 * 1024;
|
|
77
|
-
/**
|
|
78
|
-
* Acquire a buffer from the pool or create a new one.
|
|
79
|
-
* @param size The minimum size needed for the buffer.
|
|
80
|
-
* @returns A Uint8Array that can be used for encoding.
|
|
81
|
-
*/
|
|
82
|
-
acquire(size) {
|
|
83
|
-
if (size > this.maxBufferSize) return new Uint8Array(size);
|
|
84
|
-
for (let i = this.pool.length - 1; i >= 0; i--) {
|
|
85
|
-
const buffer = this.pool[i];
|
|
86
|
-
if (buffer.length >= size) {
|
|
87
|
-
this.pool.splice(i, 1);
|
|
88
|
-
return buffer.subarray(0, size);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const actualSize = Math.max(size, 1024);
|
|
92
|
-
return new Uint8Array(actualSize);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Return a buffer to the pool for future reuse.
|
|
96
|
-
* @param buffer The buffer to return to the pool.
|
|
97
|
-
*/
|
|
98
|
-
release(buffer) {
|
|
99
|
-
if (this.pool.length >= this.maxPoolSize || buffer.length > this.maxBufferSize) return;
|
|
100
|
-
if (buffer.length < 256) return;
|
|
101
|
-
this.pool.push(buffer);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Clear the pool to free memory. Useful for cleanup.
|
|
105
|
-
*/
|
|
106
|
-
clear() {
|
|
107
|
-
this.pool.length = 0;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Get current pool statistics for monitoring.
|
|
111
|
-
* @returns Object with pool size and buffer count.
|
|
112
|
-
*/
|
|
113
|
-
getStats() {
|
|
114
|
-
return {
|
|
115
|
-
poolSize: this.pool.reduce((sum, buf) => sum + buf.length, 0),
|
|
116
|
-
totalBuffers: this.pool.length
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
/**
|
|
121
|
-
* High-performance byte buffer for batching log records.
|
|
122
|
-
* Eliminates string concatenation overhead by storing pre-encoded bytes.
|
|
123
|
-
* Uses memory pooling to reduce GC pressure.
|
|
124
|
-
*/
|
|
125
|
-
var ByteRingBuffer = class {
|
|
126
|
-
buffers = [];
|
|
127
|
-
totalSize = 0;
|
|
128
|
-
bufferPool;
|
|
129
|
-
constructor(bufferPool) {
|
|
130
|
-
this.bufferPool = bufferPool;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Append pre-encoded log record bytes to the buffer.
|
|
134
|
-
* @param data The encoded log record as bytes.
|
|
135
|
-
*/
|
|
136
|
-
append(data) {
|
|
137
|
-
this.buffers.push(data);
|
|
138
|
-
this.totalSize += data.length;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Get the current total size of buffered data in bytes.
|
|
142
|
-
* @returns The total size in bytes.
|
|
143
|
-
*/
|
|
144
|
-
size() {
|
|
145
|
-
return this.totalSize;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Get the number of buffered records.
|
|
149
|
-
* @returns The number of records in the buffer.
|
|
150
|
-
*/
|
|
151
|
-
count() {
|
|
152
|
-
return this.buffers.length;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Flush all buffered data and return it as an array of byte arrays.
|
|
156
|
-
* This clears the internal buffer and returns used buffers to the pool.
|
|
157
|
-
* @returns Array of buffered byte arrays ready for writev() operations.
|
|
158
|
-
*/
|
|
159
|
-
flush() {
|
|
160
|
-
const result = [...this.buffers];
|
|
161
|
-
this.clear();
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Clear the buffer without returning data.
|
|
166
|
-
* Returns buffers to the pool for reuse.
|
|
167
|
-
*/
|
|
168
|
-
clear() {
|
|
169
|
-
for (const buffer of this.buffers) this.bufferPool.release(buffer);
|
|
170
|
-
this.buffers.length = 0;
|
|
171
|
-
this.totalSize = 0;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Check if the buffer is empty.
|
|
175
|
-
* @returns True if the buffer contains no data.
|
|
176
|
-
*/
|
|
177
|
-
isEmpty() {
|
|
178
|
-
return this.buffers.length === 0;
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
14
|
function getBaseFileSink(path, options) {
|
|
182
15
|
const formatter = options.formatter ?? __logtape_logtape.defaultTextFormatter;
|
|
183
16
|
const encoder = options.encoder ?? new TextEncoder();
|
|
184
17
|
const bufferSize = options.bufferSize ?? 1024 * 8;
|
|
185
18
|
const flushInterval = options.flushInterval ?? 5e3;
|
|
186
19
|
let fd = options.lazy ? null : options.openSync(path);
|
|
187
|
-
|
|
188
|
-
const byteBuffer = new ByteRingBuffer(bufferPool);
|
|
189
|
-
const adaptiveStrategy = new AdaptiveFlushStrategy(bufferSize, flushInterval);
|
|
20
|
+
let buffer = "";
|
|
190
21
|
let lastFlushTimestamp = Date.now();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const timeSinceLastFlush = currentTime - lastFlushTimestamp;
|
|
197
|
-
const chunks = byteBuffer.flush();
|
|
198
|
-
if (options.writeManySync && chunks.length > 1) options.writeManySync(fd, chunks);
|
|
199
|
-
else for (const chunk of chunks) options.writeSync(fd, chunk);
|
|
22
|
+
function flushBuffer() {
|
|
23
|
+
if (fd == null) return;
|
|
24
|
+
if (buffer.length > 0) {
|
|
25
|
+
options.writeSync(fd, encoder.encode(buffer));
|
|
26
|
+
buffer = "";
|
|
200
27
|
options.flushSync(fd);
|
|
201
|
-
|
|
202
|
-
lastFlushTimestamp = currentTime;
|
|
28
|
+
lastFlushTimestamp = Date.now();
|
|
203
29
|
}
|
|
204
|
-
const sink = (record) => {
|
|
205
|
-
if (fd == null) fd = options.openSync(path);
|
|
206
|
-
if (byteBuffer.isEmpty() && bufferSize === 8192) {
|
|
207
|
-
const formattedRecord$1 = formatter(record);
|
|
208
|
-
const encodedRecord$1 = encoder.encode(formattedRecord$1);
|
|
209
|
-
if (encodedRecord$1.length < 200) {
|
|
210
|
-
options.writeSync(fd, encodedRecord$1);
|
|
211
|
-
options.flushSync(fd);
|
|
212
|
-
lastFlushTimestamp = Date.now();
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
const formattedRecord = formatter(record);
|
|
217
|
-
const encodedRecord = encoder.encode(formattedRecord);
|
|
218
|
-
byteBuffer.append(encodedRecord);
|
|
219
|
-
if (bufferSize <= 0) flushBuffer$1();
|
|
220
|
-
else {
|
|
221
|
-
const timeSinceLastFlush = record.timestamp - lastFlushTimestamp;
|
|
222
|
-
const shouldFlush = adaptiveStrategy.shouldFlush(byteBuffer.size(), timeSinceLastFlush);
|
|
223
|
-
if (shouldFlush) flushBuffer$1();
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
sink[Symbol.dispose] = () => {
|
|
227
|
-
if (fd !== null) {
|
|
228
|
-
flushBuffer$1();
|
|
229
|
-
options.closeSync(fd);
|
|
230
|
-
}
|
|
231
|
-
bufferPool.clear();
|
|
232
|
-
};
|
|
233
|
-
return sink;
|
|
234
|
-
}
|
|
235
|
-
const asyncOptions = options;
|
|
236
|
-
let disposed = false;
|
|
237
|
-
let activeFlush = null;
|
|
238
|
-
let flushTimer = null;
|
|
239
|
-
async function flushBuffer() {
|
|
240
|
-
if (fd == null || byteBuffer.isEmpty()) return;
|
|
241
|
-
const flushSize = byteBuffer.size();
|
|
242
|
-
const currentTime = Date.now();
|
|
243
|
-
const timeSinceLastFlush = currentTime - lastFlushTimestamp;
|
|
244
|
-
const chunks = byteBuffer.flush();
|
|
245
|
-
try {
|
|
246
|
-
if (asyncOptions.writeMany && chunks.length > 1) await asyncOptions.writeMany(fd, chunks);
|
|
247
|
-
else for (const chunk of chunks) asyncOptions.writeSync(fd, chunk);
|
|
248
|
-
await asyncOptions.flush(fd);
|
|
249
|
-
adaptiveStrategy.recordFlush(flushSize, timeSinceLastFlush);
|
|
250
|
-
lastFlushTimestamp = currentTime;
|
|
251
|
-
} catch {}
|
|
252
30
|
}
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
function startFlushTimer() {
|
|
260
|
-
if (flushTimer !== null || disposed) return;
|
|
261
|
-
flushTimer = setInterval(() => {
|
|
262
|
-
scheduleFlush();
|
|
263
|
-
}, flushInterval);
|
|
264
|
-
}
|
|
265
|
-
const nonBlockingSink = (record) => {
|
|
266
|
-
if (disposed) return;
|
|
267
|
-
if (fd == null) fd = asyncOptions.openSync(path);
|
|
268
|
-
if (byteBuffer.isEmpty() && !activeFlush && bufferSize === 8192) {
|
|
269
|
-
const formattedRecord$1 = formatter(record);
|
|
270
|
-
const encodedRecord$1 = encoder.encode(formattedRecord$1);
|
|
271
|
-
if (encodedRecord$1.length < 200) {
|
|
272
|
-
asyncOptions.writeSync(fd, encodedRecord$1);
|
|
273
|
-
scheduleFlush();
|
|
274
|
-
lastFlushTimestamp = Date.now();
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
const formattedRecord = formatter(record);
|
|
279
|
-
const encodedRecord = encoder.encode(formattedRecord);
|
|
280
|
-
byteBuffer.append(encodedRecord);
|
|
281
|
-
if (bufferSize <= 0) scheduleFlush();
|
|
282
|
-
else {
|
|
283
|
-
const timeSinceLastFlush = record.timestamp - lastFlushTimestamp;
|
|
284
|
-
const shouldFlush = adaptiveStrategy.shouldFlush(byteBuffer.size(), timeSinceLastFlush);
|
|
285
|
-
if (shouldFlush) scheduleFlush();
|
|
286
|
-
else if (flushTimer === null && flushInterval > 0) startFlushTimer();
|
|
287
|
-
}
|
|
31
|
+
const sink = (record) => {
|
|
32
|
+
if (fd == null) fd = options.openSync(path);
|
|
33
|
+
buffer += formatter(record);
|
|
34
|
+
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
35
|
+
const shouldFlushByTime = flushInterval > 0 && record.timestamp - lastFlushTimestamp >= flushInterval;
|
|
36
|
+
if (shouldFlushBySize || shouldFlushByTime) flushBuffer();
|
|
288
37
|
};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
flushTimer = null;
|
|
38
|
+
sink[Symbol.dispose] = () => {
|
|
39
|
+
if (fd !== null) {
|
|
40
|
+
flushBuffer();
|
|
41
|
+
options.closeSync(fd);
|
|
294
42
|
}
|
|
295
|
-
await flushBuffer();
|
|
296
|
-
if (fd !== null) try {
|
|
297
|
-
await asyncOptions.close(fd);
|
|
298
|
-
} catch {}
|
|
299
|
-
bufferPool.clear();
|
|
300
43
|
};
|
|
301
|
-
return
|
|
44
|
+
return sink;
|
|
302
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Get a platform-independent rotating file sink.
|
|
48
|
+
*
|
|
49
|
+
* This sink writes log records to a file, and rotates the file when it reaches
|
|
50
|
+
* the `maxSize`. The rotated files are named with the original file name
|
|
51
|
+
* followed by a dot and a number, starting from 1. The number is incremented
|
|
52
|
+
* for each rotation, and the maximum number of files to keep is `maxFiles`.
|
|
53
|
+
*
|
|
54
|
+
* @param path A path to the file to write to.
|
|
55
|
+
* @param options The options for the sink and the file driver.
|
|
56
|
+
* @returns A sink that writes to the file. The sink is also a disposable
|
|
57
|
+
* object that closes the file when disposed.
|
|
58
|
+
*/
|
|
303
59
|
function getBaseRotatingFileSink(path, options) {
|
|
304
60
|
const formatter = options.formatter ?? __logtape_logtape.defaultTextFormatter;
|
|
305
61
|
const encoder = options.encoder ?? new TextEncoder();
|
|
@@ -314,7 +70,6 @@ function getBaseRotatingFileSink(path, options) {
|
|
|
314
70
|
} catch {}
|
|
315
71
|
let fd = options.openSync(path);
|
|
316
72
|
let lastFlushTimestamp = Date.now();
|
|
317
|
-
let buffer = "";
|
|
318
73
|
function shouldRollover(bytes) {
|
|
319
74
|
return offset + bytes.length > maxSize;
|
|
320
75
|
}
|
|
@@ -331,79 +86,29 @@ function getBaseRotatingFileSink(path, options) {
|
|
|
331
86
|
offset = 0;
|
|
332
87
|
fd = options.openSync(path);
|
|
333
88
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
buffer = "";
|
|
339
|
-
if (shouldRollover(bytes)) performRollover();
|
|
340
|
-
options.writeSync(fd, bytes);
|
|
341
|
-
options.flushSync(fd);
|
|
342
|
-
offset += bytes.length;
|
|
343
|
-
lastFlushTimestamp = Date.now();
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
const sink = (record) => {
|
|
347
|
-
buffer += formatter(record);
|
|
348
|
-
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
349
|
-
const shouldFlushByTime = flushInterval > 0 && record.timestamp - lastFlushTimestamp >= flushInterval;
|
|
350
|
-
if (shouldFlushBySize || shouldFlushByTime) flushBuffer$1();
|
|
351
|
-
};
|
|
352
|
-
sink[Symbol.dispose] = () => {
|
|
353
|
-
flushBuffer$1();
|
|
354
|
-
options.closeSync(fd);
|
|
355
|
-
};
|
|
356
|
-
return sink;
|
|
357
|
-
}
|
|
358
|
-
const asyncOptions = options;
|
|
359
|
-
let disposed = false;
|
|
360
|
-
let activeFlush = null;
|
|
361
|
-
let flushTimer = null;
|
|
362
|
-
async function flushBuffer() {
|
|
363
|
-
if (buffer.length === 0) return;
|
|
364
|
-
const data = buffer;
|
|
365
|
-
buffer = "";
|
|
366
|
-
try {
|
|
367
|
-
const bytes = encoder.encode(data);
|
|
89
|
+
function flushBuffer() {
|
|
90
|
+
if (buffer.length > 0) {
|
|
91
|
+
const bytes = encoder.encode(buffer);
|
|
92
|
+
buffer = "";
|
|
368
93
|
if (shouldRollover(bytes)) performRollover();
|
|
369
|
-
|
|
370
|
-
|
|
94
|
+
options.writeSync(fd, bytes);
|
|
95
|
+
options.flushSync(fd);
|
|
371
96
|
offset += bytes.length;
|
|
372
97
|
lastFlushTimestamp = Date.now();
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
function scheduleFlush() {
|
|
376
|
-
if (activeFlush || disposed) return;
|
|
377
|
-
activeFlush = flushBuffer().finally(() => {
|
|
378
|
-
activeFlush = null;
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
function startFlushTimer() {
|
|
382
|
-
if (flushTimer !== null || disposed) return;
|
|
383
|
-
flushTimer = setInterval(() => {
|
|
384
|
-
scheduleFlush();
|
|
385
|
-
}, flushInterval);
|
|
98
|
+
}
|
|
386
99
|
}
|
|
387
|
-
|
|
388
|
-
|
|
100
|
+
let buffer = "";
|
|
101
|
+
const sink = (record) => {
|
|
389
102
|
buffer += formatter(record);
|
|
390
103
|
const shouldFlushBySize = buffer.length >= bufferSize;
|
|
391
104
|
const shouldFlushByTime = flushInterval > 0 && record.timestamp - lastFlushTimestamp >= flushInterval;
|
|
392
|
-
if (shouldFlushBySize || shouldFlushByTime)
|
|
393
|
-
else if (flushTimer === null && flushInterval > 0) startFlushTimer();
|
|
105
|
+
if (shouldFlushBySize || shouldFlushByTime) flushBuffer();
|
|
394
106
|
};
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
clearInterval(flushTimer);
|
|
399
|
-
flushTimer = null;
|
|
400
|
-
}
|
|
401
|
-
await flushBuffer();
|
|
402
|
-
try {
|
|
403
|
-
await asyncOptions.close(fd);
|
|
404
|
-
} catch {}
|
|
107
|
+
sink[Symbol.dispose] = () => {
|
|
108
|
+
flushBuffer();
|
|
109
|
+
options.closeSync(fd);
|
|
405
110
|
};
|
|
406
|
-
return
|
|
111
|
+
return sink;
|
|
407
112
|
}
|
|
408
113
|
|
|
409
114
|
//#endregion
|
package/dist/filesink.base.d.cts
CHANGED
|
@@ -26,15 +26,6 @@ type FileSinkOptions = StreamSinkOptions & {
|
|
|
26
26
|
* @since 0.12.0
|
|
27
27
|
*/
|
|
28
28
|
flushInterval?: number;
|
|
29
|
-
/**
|
|
30
|
-
* Enable non-blocking mode with background flushing.
|
|
31
|
-
* When enabled, flush operations are performed asynchronously to prevent
|
|
32
|
-
* blocking the main thread during file I/O operations.
|
|
33
|
-
*
|
|
34
|
-
* @default `false`
|
|
35
|
-
* @since 1.0.0
|
|
36
|
-
*/
|
|
37
|
-
nonBlocking?: boolean;
|
|
38
29
|
};
|
|
39
30
|
/**
|
|
40
31
|
* A platform-specific file sink driver.
|
|
@@ -52,13 +43,6 @@ interface FileSinkDriver<TFile> {
|
|
|
52
43
|
* @param chunk The data to write.
|
|
53
44
|
*/
|
|
54
45
|
writeSync(fd: TFile, chunk: Uint8Array): void;
|
|
55
|
-
/**
|
|
56
|
-
* Write multiple chunks of data to the file in a single operation.
|
|
57
|
-
* This is optional - if not implemented, falls back to multiple writeSync calls.
|
|
58
|
-
* @param fd The file descriptor.
|
|
59
|
-
* @param chunks Array of data chunks to write.
|
|
60
|
-
*/
|
|
61
|
-
writeManySync?(fd: TFile, chunks: Uint8Array[]): void;
|
|
62
46
|
/**
|
|
63
47
|
* Flush the file to ensure that all data is written to the disk.
|
|
64
48
|
* @param fd The file descriptor.
|
|
@@ -70,30 +54,6 @@ interface FileSinkDriver<TFile> {
|
|
|
70
54
|
*/
|
|
71
55
|
closeSync(fd: TFile): void;
|
|
72
56
|
}
|
|
73
|
-
/**
|
|
74
|
-
* A platform-specific async file sink driver.
|
|
75
|
-
* @typeParam TFile The type of the file descriptor.
|
|
76
|
-
* @since 1.0.0
|
|
77
|
-
*/
|
|
78
|
-
interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
79
|
-
/**
|
|
80
|
-
* Asynchronously write multiple chunks of data to the file in a single operation.
|
|
81
|
-
* This is optional - if not implemented, falls back to multiple writeSync calls.
|
|
82
|
-
* @param fd The file descriptor.
|
|
83
|
-
* @param chunks Array of data chunks to write.
|
|
84
|
-
*/
|
|
85
|
-
writeMany?(fd: TFile, chunks: Uint8Array[]): Promise<void>;
|
|
86
|
-
/**
|
|
87
|
-
* Asynchronously flush the file to ensure that all data is written to the disk.
|
|
88
|
-
* @param fd The file descriptor.
|
|
89
|
-
*/
|
|
90
|
-
flush(fd: TFile): Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Asynchronously close the file.
|
|
93
|
-
* @param fd The file descriptor.
|
|
94
|
-
*/
|
|
95
|
-
close(fd: TFile): Promise<void>;
|
|
96
|
-
}
|
|
97
57
|
/**
|
|
98
58
|
* Get a platform-independent file sink.
|
|
99
59
|
*
|
|
@@ -101,8 +61,7 @@ interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
|
101
61
|
* @param path A path to the file to write to.
|
|
102
62
|
* @param options The options for the sink and the file driver.
|
|
103
63
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
104
|
-
* object that closes the file when disposed.
|
|
105
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
64
|
+
* object that closes the file when disposed.
|
|
106
65
|
*/
|
|
107
66
|
|
|
108
67
|
/**
|
|
@@ -137,26 +96,6 @@ interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
|
137
96
|
*/
|
|
138
97
|
renameSync(oldPath: string, newPath: string): void;
|
|
139
98
|
}
|
|
140
|
-
/**
|
|
141
|
-
* A platform-specific async rotating file sink driver.
|
|
142
|
-
* @since 1.0.0
|
|
143
|
-
*/
|
|
144
|
-
interface AsyncRotatingFileSinkDriver<TFile> extends AsyncFileSinkDriver<TFile> {
|
|
145
|
-
/**
|
|
146
|
-
* Get the size of the file.
|
|
147
|
-
* @param path A path to the file.
|
|
148
|
-
* @returns The `size` of the file in bytes, in an object.
|
|
149
|
-
*/
|
|
150
|
-
statSync(path: string): {
|
|
151
|
-
size: number;
|
|
152
|
-
};
|
|
153
|
-
/**
|
|
154
|
-
* Rename a file.
|
|
155
|
-
* @param oldPath A path to the file to rename.
|
|
156
|
-
* @param newPath A path to be renamed to.
|
|
157
|
-
*/
|
|
158
|
-
renameSync(oldPath: string, newPath: string): void;
|
|
159
|
-
}
|
|
160
99
|
/**
|
|
161
100
|
* Get a platform-independent rotating file sink.
|
|
162
101
|
*
|
|
@@ -168,9 +107,8 @@ interface AsyncRotatingFileSinkDriver<TFile> extends AsyncFileSinkDriver<TFile>
|
|
|
168
107
|
* @param path A path to the file to write to.
|
|
169
108
|
* @param options The options for the sink and the file driver.
|
|
170
109
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
171
|
-
* object that closes the file when disposed.
|
|
172
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
110
|
+
* object that closes the file when disposed.
|
|
173
111
|
*/
|
|
174
112
|
//#endregion
|
|
175
|
-
export {
|
|
113
|
+
export { FileSinkDriver, FileSinkOptions, RotatingFileSinkDriver, RotatingFileSinkOptions };
|
|
176
114
|
//# sourceMappingURL=filesink.base.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesink.base.d.cts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"filesink.base.d.cts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;AA6BiB,KA7BL,eAAA,GAAkB,iBA6BC,GAAA;EAAA;;;EAYV,IAAS,CAAA,EAAA,OAAA;EAAU;;AAYnB;AA0DrB;;;;EAAqD,UAAA,CAAA,EAAA,MAAA;EAepC;;;;AAAoD;;;;;;;;;UAjGpD;;;;;0BAKS;;;;;;gBAOV,cAAc;;;;;gBAMd;;;;;gBAMA;;;;;;;;;;;;;;;UA0DC,uBAAA,SAAgC,KAAK;;;;;;;;;;;;;UAerC,sCAAsC,eAAe"}
|
package/dist/filesink.base.d.ts
CHANGED
|
@@ -26,15 +26,6 @@ type FileSinkOptions = StreamSinkOptions & {
|
|
|
26
26
|
* @since 0.12.0
|
|
27
27
|
*/
|
|
28
28
|
flushInterval?: number;
|
|
29
|
-
/**
|
|
30
|
-
* Enable non-blocking mode with background flushing.
|
|
31
|
-
* When enabled, flush operations are performed asynchronously to prevent
|
|
32
|
-
* blocking the main thread during file I/O operations.
|
|
33
|
-
*
|
|
34
|
-
* @default `false`
|
|
35
|
-
* @since 1.0.0
|
|
36
|
-
*/
|
|
37
|
-
nonBlocking?: boolean;
|
|
38
29
|
};
|
|
39
30
|
/**
|
|
40
31
|
* A platform-specific file sink driver.
|
|
@@ -52,13 +43,6 @@ interface FileSinkDriver<TFile> {
|
|
|
52
43
|
* @param chunk The data to write.
|
|
53
44
|
*/
|
|
54
45
|
writeSync(fd: TFile, chunk: Uint8Array): void;
|
|
55
|
-
/**
|
|
56
|
-
* Write multiple chunks of data to the file in a single operation.
|
|
57
|
-
* This is optional - if not implemented, falls back to multiple writeSync calls.
|
|
58
|
-
* @param fd The file descriptor.
|
|
59
|
-
* @param chunks Array of data chunks to write.
|
|
60
|
-
*/
|
|
61
|
-
writeManySync?(fd: TFile, chunks: Uint8Array[]): void;
|
|
62
46
|
/**
|
|
63
47
|
* Flush the file to ensure that all data is written to the disk.
|
|
64
48
|
* @param fd The file descriptor.
|
|
@@ -70,30 +54,6 @@ interface FileSinkDriver<TFile> {
|
|
|
70
54
|
*/
|
|
71
55
|
closeSync(fd: TFile): void;
|
|
72
56
|
}
|
|
73
|
-
/**
|
|
74
|
-
* A platform-specific async file sink driver.
|
|
75
|
-
* @typeParam TFile The type of the file descriptor.
|
|
76
|
-
* @since 1.0.0
|
|
77
|
-
*/
|
|
78
|
-
interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
79
|
-
/**
|
|
80
|
-
* Asynchronously write multiple chunks of data to the file in a single operation.
|
|
81
|
-
* This is optional - if not implemented, falls back to multiple writeSync calls.
|
|
82
|
-
* @param fd The file descriptor.
|
|
83
|
-
* @param chunks Array of data chunks to write.
|
|
84
|
-
*/
|
|
85
|
-
writeMany?(fd: TFile, chunks: Uint8Array[]): Promise<void>;
|
|
86
|
-
/**
|
|
87
|
-
* Asynchronously flush the file to ensure that all data is written to the disk.
|
|
88
|
-
* @param fd The file descriptor.
|
|
89
|
-
*/
|
|
90
|
-
flush(fd: TFile): Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Asynchronously close the file.
|
|
93
|
-
* @param fd The file descriptor.
|
|
94
|
-
*/
|
|
95
|
-
close(fd: TFile): Promise<void>;
|
|
96
|
-
}
|
|
97
57
|
/**
|
|
98
58
|
* Get a platform-independent file sink.
|
|
99
59
|
*
|
|
@@ -101,8 +61,7 @@ interface AsyncFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
|
101
61
|
* @param path A path to the file to write to.
|
|
102
62
|
* @param options The options for the sink and the file driver.
|
|
103
63
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
104
|
-
* object that closes the file when disposed.
|
|
105
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
64
|
+
* object that closes the file when disposed.
|
|
106
65
|
*/
|
|
107
66
|
|
|
108
67
|
/**
|
|
@@ -137,26 +96,6 @@ interface RotatingFileSinkDriver<TFile> extends FileSinkDriver<TFile> {
|
|
|
137
96
|
*/
|
|
138
97
|
renameSync(oldPath: string, newPath: string): void;
|
|
139
98
|
}
|
|
140
|
-
/**
|
|
141
|
-
* A platform-specific async rotating file sink driver.
|
|
142
|
-
* @since 1.0.0
|
|
143
|
-
*/
|
|
144
|
-
interface AsyncRotatingFileSinkDriver<TFile> extends AsyncFileSinkDriver<TFile> {
|
|
145
|
-
/**
|
|
146
|
-
* Get the size of the file.
|
|
147
|
-
* @param path A path to the file.
|
|
148
|
-
* @returns The `size` of the file in bytes, in an object.
|
|
149
|
-
*/
|
|
150
|
-
statSync(path: string): {
|
|
151
|
-
size: number;
|
|
152
|
-
};
|
|
153
|
-
/**
|
|
154
|
-
* Rename a file.
|
|
155
|
-
* @param oldPath A path to the file to rename.
|
|
156
|
-
* @param newPath A path to be renamed to.
|
|
157
|
-
*/
|
|
158
|
-
renameSync(oldPath: string, newPath: string): void;
|
|
159
|
-
}
|
|
160
99
|
/**
|
|
161
100
|
* Get a platform-independent rotating file sink.
|
|
162
101
|
*
|
|
@@ -168,9 +107,8 @@ interface AsyncRotatingFileSinkDriver<TFile> extends AsyncFileSinkDriver<TFile>
|
|
|
168
107
|
* @param path A path to the file to write to.
|
|
169
108
|
* @param options The options for the sink and the file driver.
|
|
170
109
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
171
|
-
* object that closes the file when disposed.
|
|
172
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
110
|
+
* object that closes the file when disposed.
|
|
173
111
|
*/
|
|
174
112
|
//#endregion
|
|
175
|
-
export {
|
|
113
|
+
export { FileSinkDriver, FileSinkOptions, RotatingFileSinkDriver, RotatingFileSinkOptions };
|
|
176
114
|
//# sourceMappingURL=filesink.base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesink.base.d.ts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"filesink.base.d.ts","names":[],"sources":["../filesink.base.ts"],"sourcesContent":[],"mappings":";;;;;;AAUA;AA6BiB,KA7BL,eAAA,GAAkB,iBA6BC,GAAA;EAAA;;;EAYV,IAAS,CAAA,EAAA,OAAA;EAAU;;AAYnB;AA0DrB;;;;EAAqD,UAAA,CAAA,EAAA,MAAA;EAepC;;;;AAAoD;;;;;;;;;UAjGpD;;;;;0BAKS;;;;;;gBAOV,cAAc;;;;;gBAMd;;;;;gBAMA;;;;;;;;;;;;;;;UA0DC,uBAAA,SAAgC,KAAK;;;;;;;;;;;;;UAerC,sCAAsC,eAAe"}
|