@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/filesink.deno.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Sink } from "@logtape/logtape";
|
|
2
2
|
import {
|
|
3
|
-
type AsyncRotatingFileSinkDriver,
|
|
4
3
|
type FileSinkOptions,
|
|
5
4
|
getBaseFileSink,
|
|
6
5
|
getBaseRotatingFileSink,
|
|
@@ -18,13 +17,6 @@ export const denoDriver: RotatingFileSinkDriver<Deno.FsFile> = {
|
|
|
18
17
|
writeSync(fd, chunk) {
|
|
19
18
|
fd.writeSync(chunk);
|
|
20
19
|
},
|
|
21
|
-
writeManySync(fd: Deno.FsFile, chunks: Uint8Array[]): void {
|
|
22
|
-
// Deno doesn't have writev, but we can optimize by writing all chunks
|
|
23
|
-
// then doing a single sync operation
|
|
24
|
-
for (const chunk of chunks) {
|
|
25
|
-
fd.writeSync(chunk);
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
20
|
flushSync(fd) {
|
|
29
21
|
fd.syncSync();
|
|
30
22
|
},
|
|
@@ -35,27 +27,6 @@ export const denoDriver: RotatingFileSinkDriver<Deno.FsFile> = {
|
|
|
35
27
|
renameSync: globalThis?.Deno.renameSync,
|
|
36
28
|
};
|
|
37
29
|
|
|
38
|
-
/**
|
|
39
|
-
* A Deno-specific async file sink driver.
|
|
40
|
-
* @since 1.0.0
|
|
41
|
-
*/
|
|
42
|
-
export const denoAsyncDriver: AsyncRotatingFileSinkDriver<Deno.FsFile> = {
|
|
43
|
-
...denoDriver,
|
|
44
|
-
async writeMany(fd: Deno.FsFile, chunks: Uint8Array[]): Promise<void> {
|
|
45
|
-
// Deno doesn't have async writev, but we can write all chunks
|
|
46
|
-
// then do a single async sync
|
|
47
|
-
for (const chunk of chunks) {
|
|
48
|
-
await fd.write(chunk);
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
async flush(fd) {
|
|
52
|
-
await fd.sync();
|
|
53
|
-
},
|
|
54
|
-
close(fd) {
|
|
55
|
-
return Promise.resolve(fd.close());
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
30
|
/**
|
|
60
31
|
* Get a file sink.
|
|
61
32
|
*
|
|
@@ -64,24 +35,12 @@ export const denoAsyncDriver: AsyncRotatingFileSinkDriver<Deno.FsFile> = {
|
|
|
64
35
|
* @param path A path to the file to write to.
|
|
65
36
|
* @param options The options for the sink.
|
|
66
37
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
67
|
-
* object that closes the file when disposed.
|
|
68
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
38
|
+
* object that closes the file when disposed.
|
|
69
39
|
*/
|
|
70
|
-
export function getFileSink(
|
|
71
|
-
path: string,
|
|
72
|
-
options?: FileSinkOptions,
|
|
73
|
-
): Sink & Disposable;
|
|
74
|
-
export function getFileSink(
|
|
75
|
-
path: string,
|
|
76
|
-
options: FileSinkOptions & { nonBlocking: true },
|
|
77
|
-
): Sink & AsyncDisposable;
|
|
78
40
|
export function getFileSink(
|
|
79
41
|
path: string,
|
|
80
42
|
options: FileSinkOptions = {},
|
|
81
|
-
): Sink &
|
|
82
|
-
if (options.nonBlocking) {
|
|
83
|
-
return getBaseFileSink(path, { ...options, ...denoAsyncDriver });
|
|
84
|
-
}
|
|
43
|
+
): Sink & Disposable {
|
|
85
44
|
return getBaseFileSink(path, { ...options, ...denoDriver });
|
|
86
45
|
}
|
|
87
46
|
|
|
@@ -98,24 +57,12 @@ export function getFileSink(
|
|
|
98
57
|
* @param path A path to the file to write to.
|
|
99
58
|
* @param options The options for the sink and the file driver.
|
|
100
59
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
101
|
-
* object that closes the file when disposed.
|
|
102
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
60
|
+
* object that closes the file when disposed.
|
|
103
61
|
*/
|
|
104
|
-
export function getRotatingFileSink(
|
|
105
|
-
path: string,
|
|
106
|
-
options?: RotatingFileSinkOptions,
|
|
107
|
-
): Sink & Disposable;
|
|
108
|
-
export function getRotatingFileSink(
|
|
109
|
-
path: string,
|
|
110
|
-
options: RotatingFileSinkOptions & { nonBlocking: true },
|
|
111
|
-
): Sink & AsyncDisposable;
|
|
112
62
|
export function getRotatingFileSink(
|
|
113
63
|
path: string,
|
|
114
64
|
options: RotatingFileSinkOptions = {},
|
|
115
|
-
): Sink &
|
|
116
|
-
if (options.nonBlocking) {
|
|
117
|
-
return getBaseRotatingFileSink(path, { ...options, ...denoAsyncDriver });
|
|
118
|
-
}
|
|
65
|
+
): Sink & Disposable {
|
|
119
66
|
return getBaseRotatingFileSink(path, { ...options, ...denoDriver });
|
|
120
67
|
}
|
|
121
68
|
|
package/filesink.jsr.ts
CHANGED
|
@@ -4,10 +4,7 @@ import type {
|
|
|
4
4
|
RotatingFileSinkOptions,
|
|
5
5
|
} from "./filesink.base.ts";
|
|
6
6
|
|
|
7
|
-
const filesink: Omit<
|
|
8
|
-
typeof import("./filesink.deno.ts"),
|
|
9
|
-
"denoDriver" | "denoAsyncDriver"
|
|
10
|
-
> =
|
|
7
|
+
const filesink: Omit<typeof import("./filesink.deno.ts"), "denoDriver"> =
|
|
11
8
|
// dnt-shim-ignore
|
|
12
9
|
await ("Deno" in globalThis
|
|
13
10
|
? import("./filesink.deno.ts")
|
|
@@ -21,24 +18,13 @@ const filesink: Omit<
|
|
|
21
18
|
* @param path A path to the file to write to.
|
|
22
19
|
* @param options The options for the sink.
|
|
23
20
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
24
|
-
* object that closes the file when disposed.
|
|
25
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
21
|
+
* object that closes the file when disposed.
|
|
26
22
|
*/
|
|
27
|
-
export function getFileSink(
|
|
28
|
-
path: string,
|
|
29
|
-
options?: FileSinkOptions,
|
|
30
|
-
): Sink & Disposable;
|
|
31
|
-
export function getFileSink(
|
|
32
|
-
path: string,
|
|
33
|
-
options: FileSinkOptions & { nonBlocking: true },
|
|
34
|
-
): Sink & AsyncDisposable;
|
|
35
23
|
export function getFileSink(
|
|
36
24
|
path: string,
|
|
37
25
|
options: FileSinkOptions = {},
|
|
38
|
-
): Sink &
|
|
39
|
-
return filesink.getFileSink(path, options)
|
|
40
|
-
& Sink
|
|
41
|
-
& (Disposable | AsyncDisposable);
|
|
26
|
+
): Sink & Disposable {
|
|
27
|
+
return filesink.getFileSink(path, options);
|
|
42
28
|
}
|
|
43
29
|
|
|
44
30
|
/**
|
|
@@ -54,24 +40,13 @@ export function getFileSink(
|
|
|
54
40
|
* @param path A path to the file to write to.
|
|
55
41
|
* @param options The options for the sink and the file driver.
|
|
56
42
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
57
|
-
* object that closes the file when disposed.
|
|
58
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
43
|
+
* object that closes the file when disposed.
|
|
59
44
|
*/
|
|
60
|
-
export function getRotatingFileSink(
|
|
61
|
-
path: string,
|
|
62
|
-
options?: RotatingFileSinkOptions,
|
|
63
|
-
): Sink & Disposable;
|
|
64
|
-
export function getRotatingFileSink(
|
|
65
|
-
path: string,
|
|
66
|
-
options: RotatingFileSinkOptions & { nonBlocking: true },
|
|
67
|
-
): Sink & AsyncDisposable;
|
|
68
45
|
export function getRotatingFileSink(
|
|
69
46
|
path: string,
|
|
70
47
|
options: RotatingFileSinkOptions = {},
|
|
71
|
-
): Sink &
|
|
72
|
-
return filesink.getRotatingFileSink(path, options)
|
|
73
|
-
& Sink
|
|
74
|
-
& (Disposable | AsyncDisposable);
|
|
48
|
+
): Sink & Disposable {
|
|
49
|
+
return filesink.getRotatingFileSink(path, options);
|
|
75
50
|
}
|
|
76
51
|
|
|
77
52
|
// cSpell: ignore filesink
|
package/filesink.node.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { Sink } from "@logtape/logtape";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import { promisify } from "node:util";
|
|
4
3
|
import {
|
|
5
|
-
type AsyncRotatingFileSinkDriver,
|
|
6
4
|
type FileSinkOptions,
|
|
7
5
|
getBaseFileSink,
|
|
8
6
|
getBaseRotatingFileSink,
|
|
@@ -18,40 +16,12 @@ export const nodeDriver: RotatingFileSinkDriver<number | void> = {
|
|
|
18
16
|
return fs.openSync(path, "a");
|
|
19
17
|
},
|
|
20
18
|
writeSync: fs.writeSync,
|
|
21
|
-
writeManySync(fd: number, chunks: Uint8Array[]): void {
|
|
22
|
-
if (chunks.length === 0) return;
|
|
23
|
-
if (chunks.length === 1) {
|
|
24
|
-
fs.writeSync(fd, chunks[0]);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
// Use writev for multiple chunks
|
|
28
|
-
fs.writevSync(fd, chunks);
|
|
29
|
-
},
|
|
30
19
|
flushSync: fs.fsyncSync,
|
|
31
20
|
closeSync: fs.closeSync,
|
|
32
21
|
statSync: fs.statSync,
|
|
33
22
|
renameSync: fs.renameSync,
|
|
34
23
|
};
|
|
35
24
|
|
|
36
|
-
/**
|
|
37
|
-
* A Node.js-specific async file sink driver.
|
|
38
|
-
* @since 1.0.0
|
|
39
|
-
*/
|
|
40
|
-
export const nodeAsyncDriver: AsyncRotatingFileSinkDriver<number | void> = {
|
|
41
|
-
...nodeDriver,
|
|
42
|
-
async writeMany(fd: number, chunks: Uint8Array[]): Promise<void> {
|
|
43
|
-
if (chunks.length === 0) return;
|
|
44
|
-
if (chunks.length === 1) {
|
|
45
|
-
await promisify(fs.write)(fd, chunks[0]);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
// Use async writev for multiple chunks
|
|
49
|
-
await promisify(fs.writev)(fd, chunks);
|
|
50
|
-
},
|
|
51
|
-
flush: promisify(fs.fsync),
|
|
52
|
-
close: promisify(fs.close),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
25
|
/**
|
|
56
26
|
* Get a file sink.
|
|
57
27
|
*
|
|
@@ -60,24 +30,12 @@ export const nodeAsyncDriver: AsyncRotatingFileSinkDriver<number | void> = {
|
|
|
60
30
|
* @param path A path to the file to write to.
|
|
61
31
|
* @param options The options for the sink.
|
|
62
32
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
63
|
-
* object that closes the file when disposed.
|
|
64
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
33
|
+
* object that closes the file when disposed.
|
|
65
34
|
*/
|
|
66
|
-
export function getFileSink(
|
|
67
|
-
path: string,
|
|
68
|
-
options?: FileSinkOptions,
|
|
69
|
-
): Sink & Disposable;
|
|
70
|
-
export function getFileSink(
|
|
71
|
-
path: string,
|
|
72
|
-
options: FileSinkOptions & { nonBlocking: true },
|
|
73
|
-
): Sink & AsyncDisposable;
|
|
74
35
|
export function getFileSink(
|
|
75
36
|
path: string,
|
|
76
37
|
options: FileSinkOptions = {},
|
|
77
|
-
): Sink &
|
|
78
|
-
if (options.nonBlocking) {
|
|
79
|
-
return getBaseFileSink(path, { ...options, ...nodeAsyncDriver });
|
|
80
|
-
}
|
|
38
|
+
): Sink & Disposable {
|
|
81
39
|
return getBaseFileSink(path, { ...options, ...nodeDriver });
|
|
82
40
|
}
|
|
83
41
|
|
|
@@ -94,24 +52,12 @@ export function getFileSink(
|
|
|
94
52
|
* @param path A path to the file to write to.
|
|
95
53
|
* @param options The options for the sink and the file driver.
|
|
96
54
|
* @returns A sink that writes to the file. The sink is also a disposable
|
|
97
|
-
* object that closes the file when disposed.
|
|
98
|
-
* returns a sink that also implements {@link AsyncDisposable}.
|
|
55
|
+
* object that closes the file when disposed.
|
|
99
56
|
*/
|
|
100
|
-
export function getRotatingFileSink(
|
|
101
|
-
path: string,
|
|
102
|
-
options?: RotatingFileSinkOptions,
|
|
103
|
-
): Sink & Disposable;
|
|
104
|
-
export function getRotatingFileSink(
|
|
105
|
-
path: string,
|
|
106
|
-
options: RotatingFileSinkOptions & { nonBlocking: true },
|
|
107
|
-
): Sink & AsyncDisposable;
|
|
108
57
|
export function getRotatingFileSink(
|
|
109
58
|
path: string,
|
|
110
59
|
options: RotatingFileSinkOptions = {},
|
|
111
|
-
): Sink &
|
|
112
|
-
if (options.nonBlocking) {
|
|
113
|
-
return getBaseRotatingFileSink(path, { ...options, ...nodeAsyncDriver });
|
|
114
|
-
}
|
|
60
|
+
): Sink & Disposable {
|
|
115
61
|
return getBaseRotatingFileSink(path, { ...options, ...nodeDriver });
|
|
116
62
|
}
|
|
117
63
|
|
package/filesink.test.ts
CHANGED
|
@@ -2,10 +2,8 @@ import { getFileSink, getRotatingFileSink } from "#filesink";
|
|
|
2
2
|
import { suite } from "@alinea/suite";
|
|
3
3
|
import { isDeno } from "@david/which-runtime";
|
|
4
4
|
import type { Sink } from "@logtape/logtape";
|
|
5
|
-
import { assert } from "@std/assert/assert";
|
|
6
5
|
import { assertEquals } from "@std/assert/equals";
|
|
7
6
|
import { assertThrows } from "@std/assert/throws";
|
|
8
|
-
import { delay } from "@std/async/delay";
|
|
9
7
|
import { join } from "@std/path/join";
|
|
10
8
|
import fs from "node:fs";
|
|
11
9
|
import { tmpdir } from "node:os";
|
|
@@ -693,122 +691,4 @@ test("getBaseFileSink() with flushInterval disabled", () => {
|
|
|
693
691
|
assertEquals(content.includes("Hello, 123 & 456!"), true);
|
|
694
692
|
});
|
|
695
693
|
|
|
696
|
-
test("getFileSink() with nonBlocking mode", async () => {
|
|
697
|
-
const path = makeTempFileSync();
|
|
698
|
-
const sink = getFileSink(path, {
|
|
699
|
-
nonBlocking: true,
|
|
700
|
-
bufferSize: 50, // Small buffer to trigger flush by size
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
// Check that it returns AsyncDisposable
|
|
704
|
-
assert(typeof sink === "function");
|
|
705
|
-
assert(Symbol.asyncDispose in sink);
|
|
706
|
-
|
|
707
|
-
// Add enough records to trigger buffer flush
|
|
708
|
-
sink(debug);
|
|
709
|
-
sink(info);
|
|
710
|
-
|
|
711
|
-
// Wait for async flush to complete
|
|
712
|
-
await delay(50);
|
|
713
|
-
const content = fs.readFileSync(path, { encoding: "utf-8" });
|
|
714
|
-
assert(content.includes("Hello, 123 & 456!"));
|
|
715
|
-
|
|
716
|
-
await (sink as Sink & AsyncDisposable)[Symbol.asyncDispose]();
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
test("getRotatingFileSink() with nonBlocking mode", async () => {
|
|
720
|
-
const path = makeTempFileSync();
|
|
721
|
-
const sink = getRotatingFileSink(path, {
|
|
722
|
-
maxSize: 200,
|
|
723
|
-
nonBlocking: true,
|
|
724
|
-
bufferSize: 1000, // Large buffer to prevent immediate flush
|
|
725
|
-
flushInterval: 50, // Short interval for testing
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
// Check that it returns AsyncDisposable
|
|
729
|
-
assert(typeof sink === "function");
|
|
730
|
-
assert(Symbol.asyncDispose in sink);
|
|
731
|
-
|
|
732
|
-
// Add records with current timestamp
|
|
733
|
-
const record1 = { ...debug, timestamp: Date.now() };
|
|
734
|
-
const record2 = { ...info, timestamp: Date.now() };
|
|
735
|
-
sink(record1);
|
|
736
|
-
sink(record2);
|
|
737
|
-
assertEquals(fs.readFileSync(path, { encoding: "utf-8" }), ""); // Not written yet
|
|
738
|
-
|
|
739
|
-
// Wait for flush interval to pass
|
|
740
|
-
await delay(100);
|
|
741
|
-
const content = fs.readFileSync(path, { encoding: "utf-8" });
|
|
742
|
-
assert(content.includes("Hello, 123 & 456!"));
|
|
743
|
-
|
|
744
|
-
await (sink as Sink & AsyncDisposable)[Symbol.asyncDispose]();
|
|
745
|
-
});
|
|
746
|
-
|
|
747
|
-
test("getFileSink() with nonBlocking high-volume logging", async () => {
|
|
748
|
-
const path = makeTempFileSync();
|
|
749
|
-
const sink = getFileSink(path, {
|
|
750
|
-
nonBlocking: true,
|
|
751
|
-
bufferSize: 50, // Small buffer to trigger flush
|
|
752
|
-
flushInterval: 0, // Disable time-based flushing for this test
|
|
753
|
-
}) as unknown as Sink & AsyncDisposable;
|
|
754
|
-
|
|
755
|
-
// Add enough records to trigger buffer flush (50 chars per record roughly)
|
|
756
|
-
let totalChars = 0;
|
|
757
|
-
let recordCount = 0;
|
|
758
|
-
while (totalChars < 100) { // Exceed buffer size
|
|
759
|
-
sink(debug);
|
|
760
|
-
totalChars += 67; // Approximate length of each debug record
|
|
761
|
-
recordCount++;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// Wait for async flush to complete
|
|
765
|
-
await delay(50);
|
|
766
|
-
const content = fs.readFileSync(path, { encoding: "utf-8" });
|
|
767
|
-
|
|
768
|
-
// Should have some records written by now
|
|
769
|
-
const writtenCount = (content.match(/Hello, 123 & 456!/g) || []).length;
|
|
770
|
-
assert(
|
|
771
|
-
writtenCount > 0,
|
|
772
|
-
`Expected some records to be written, but got ${writtenCount}`,
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
await sink[Symbol.asyncDispose]();
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
test("getRotatingFileSink() with nonBlocking rotation", async () => {
|
|
779
|
-
const path = makeTempFileSync();
|
|
780
|
-
const sink = getRotatingFileSink(path, {
|
|
781
|
-
maxSize: 150, // Small size to trigger rotation
|
|
782
|
-
nonBlocking: true,
|
|
783
|
-
bufferSize: 100,
|
|
784
|
-
flushInterval: 10,
|
|
785
|
-
}) as unknown as Sink & AsyncDisposable;
|
|
786
|
-
|
|
787
|
-
// Add enough records to trigger rotation
|
|
788
|
-
sink(debug);
|
|
789
|
-
sink(info);
|
|
790
|
-
sink(warning);
|
|
791
|
-
sink(error);
|
|
792
|
-
|
|
793
|
-
// Wait for all flushes and rotation to complete
|
|
794
|
-
await delay(200);
|
|
795
|
-
|
|
796
|
-
// Check that rotation occurred
|
|
797
|
-
const mainContent = fs.readFileSync(path, { encoding: "utf-8" });
|
|
798
|
-
let rotatedContent = "";
|
|
799
|
-
try {
|
|
800
|
-
rotatedContent = fs.readFileSync(`${path}.1`, { encoding: "utf-8" });
|
|
801
|
-
} catch {
|
|
802
|
-
// No rotation occurred
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
const allContent = mainContent + rotatedContent;
|
|
806
|
-
|
|
807
|
-
// Should have all 4 records somewhere
|
|
808
|
-
const recordCount = (allContent.match(/Hello, 123 & 456!/g) || []).length;
|
|
809
|
-
assertEquals(recordCount, 4);
|
|
810
|
-
|
|
811
|
-
await sink[Symbol.asyncDispose]();
|
|
812
|
-
});
|
|
813
|
-
|
|
814
694
|
// cSpell: ignore filesink
|
package/mod.ts
CHANGED
|
@@ -4,6 +4,4 @@ export type {
|
|
|
4
4
|
RotatingFileSinkDriver,
|
|
5
5
|
RotatingFileSinkOptions,
|
|
6
6
|
} from "./filesink.base.ts";
|
|
7
|
-
export type { StreamFileSinkOptions } from "./streamfilesink.ts";
|
|
8
7
|
export { getFileSink, getRotatingFileSink } from "#filesink";
|
|
9
|
-
export { getStreamFileSink } from "./streamfilesink.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/file",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.246+c7630de7",
|
|
4
4
|
"description": "File sink and rotating file sink for LogTape",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
@@ -34,29 +34,34 @@
|
|
|
34
34
|
"types": "./dist/mod.d.ts",
|
|
35
35
|
"exports": {
|
|
36
36
|
".": {
|
|
37
|
+
"types": {
|
|
38
|
+
"import": "./dist/mod.d.ts",
|
|
39
|
+
"require": "./dist/mod.d.cts"
|
|
40
|
+
},
|
|
37
41
|
"import": "./dist/mod.js",
|
|
38
|
-
"require": "./dist/mod.cjs"
|
|
39
|
-
"types": "./dist/mod.d.ts"
|
|
42
|
+
"require": "./dist/mod.cjs"
|
|
40
43
|
},
|
|
41
44
|
"./package.json": "./package.json"
|
|
42
45
|
},
|
|
43
46
|
"imports": {
|
|
44
47
|
"#filesink": {
|
|
48
|
+
"types": {
|
|
49
|
+
"import": "./dist/filesink.node.d.ts",
|
|
50
|
+
"require": "./dist/filesink.node.d.cts"
|
|
51
|
+
},
|
|
45
52
|
"bun": "./dist/filesink.node.js",
|
|
46
53
|
"deno": "./dist/filesink.deno.js",
|
|
47
54
|
"import": "./dist/filesink.node.js",
|
|
48
|
-
"require": "./dist/filesink.node.cjs"
|
|
49
|
-
"types": "./dist/filesink.node.d.ts"
|
|
55
|
+
"require": "./dist/filesink.node.cjs"
|
|
50
56
|
}
|
|
51
57
|
},
|
|
52
58
|
"peerDependencies": {
|
|
53
|
-
"@logtape/logtape": "1.0.0-dev.
|
|
59
|
+
"@logtape/logtape": "1.0.0-dev.246+c7630de7"
|
|
54
60
|
},
|
|
55
61
|
"devDependencies": {
|
|
56
62
|
"@alinea/suite": "^0.6.3",
|
|
57
63
|
"@david/which-runtime": "npm:@jsr/david__which-runtime@^0.2.1",
|
|
58
64
|
"@std/assert": "npm:@jsr/std__assert@^1.0.13",
|
|
59
|
-
"@std/async": "npm:@jsr/std__async@^1.0.13",
|
|
60
65
|
"@std/path": "npm:@jsr/std__path@^1.1.0",
|
|
61
66
|
"tsdown": "^0.12.7",
|
|
62
67
|
"typescript": "^5.8.3"
|
package/tsdown.config.ts
CHANGED
|
@@ -6,13 +6,13 @@ export default defineConfig({
|
|
|
6
6
|
sourcemap: true,
|
|
7
7
|
},
|
|
8
8
|
format: ["esm", "cjs"],
|
|
9
|
-
platform: "
|
|
9
|
+
platform: "neutral",
|
|
10
10
|
unbundle: true,
|
|
11
11
|
inputOptions: {
|
|
12
12
|
onLog(level, log, defaultHandler) {
|
|
13
13
|
if (
|
|
14
14
|
level === "warn" && log.code === "UNRESOLVED_IMPORT" &&
|
|
15
|
-
log.exporter
|
|
15
|
+
["node:fs", "#filesink"].includes(log.exporter ?? "")
|
|
16
16
|
) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
package/dist/streamfilesink.cjs
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
-
const __logtape_logtape = require_rolldown_runtime.__toESM(require("@logtape/logtape"));
|
|
3
|
-
const node_fs = require_rolldown_runtime.__toESM(require("node:fs"));
|
|
4
|
-
const node_stream = require_rolldown_runtime.__toESM(require("node:stream"));
|
|
5
|
-
|
|
6
|
-
//#region streamfilesink.ts
|
|
7
|
-
/**
|
|
8
|
-
* Create a high-performance stream-based file sink that writes log records to a file.
|
|
9
|
-
*
|
|
10
|
-
* This sink uses Node.js PassThrough streams piped to WriteStreams for optimal
|
|
11
|
-
* I/O performance. It leverages the Node.js stream infrastructure to provide
|
|
12
|
-
* automatic backpressure management, efficient buffering, and asynchronous writes
|
|
13
|
-
* without blocking the main thread.
|
|
14
|
-
*
|
|
15
|
-
* ## Performance Characteristics
|
|
16
|
-
*
|
|
17
|
-
* - **High Performance**: Optimized for high-volume logging scenarios
|
|
18
|
-
* - **Non-blocking**: Uses asynchronous I/O that doesn't block the main thread
|
|
19
|
-
* - **Memory Efficient**: Automatic backpressure prevents memory buildup
|
|
20
|
-
* - **Stream-based**: Leverages Node.js native stream optimizations
|
|
21
|
-
*
|
|
22
|
-
* ## When to Use
|
|
23
|
-
*
|
|
24
|
-
* Use this sink when you need:
|
|
25
|
-
* - High-performance file logging for production applications
|
|
26
|
-
* - Non-blocking I/O behavior for real-time applications
|
|
27
|
-
* - Automatic backpressure handling for high-volume scenarios
|
|
28
|
-
* - Simple file output without complex buffering configuration
|
|
29
|
-
*
|
|
30
|
-
* For more control over buffering behavior, consider using {@link getFileSink}
|
|
31
|
-
* instead, which provides options for buffer size, flush intervals, and
|
|
32
|
-
* non-blocking modes.
|
|
33
|
-
*
|
|
34
|
-
* ## Example
|
|
35
|
-
*
|
|
36
|
-
* ```typescript
|
|
37
|
-
* import { configure } from "@logtape/logtape";
|
|
38
|
-
* import { getStreamFileSink } from "@logtape/file";
|
|
39
|
-
*
|
|
40
|
-
* await configure({
|
|
41
|
-
* sinks: {
|
|
42
|
-
* file: getStreamFileSink("app.log", {
|
|
43
|
-
* highWaterMark: 32768 // 32KB buffer for high-volume logging
|
|
44
|
-
* })
|
|
45
|
-
* },
|
|
46
|
-
* loggers: [
|
|
47
|
-
* { category: ["myapp"], sinks: ["file"] }
|
|
48
|
-
* ]
|
|
49
|
-
* });
|
|
50
|
-
* ```
|
|
51
|
-
*
|
|
52
|
-
* @param path The path to the file to write logs to. The file will be created
|
|
53
|
-
* if it doesn't exist, or appended to if it does exist.
|
|
54
|
-
* @param options Configuration options for the stream-based sink.
|
|
55
|
-
* @returns A sink that writes formatted log records to the specified file.
|
|
56
|
-
* The returned sink implements `Disposable` for proper resource cleanup.
|
|
57
|
-
*
|
|
58
|
-
* @since 1.0.0
|
|
59
|
-
*/
|
|
60
|
-
function getStreamFileSink(path, options = {}) {
|
|
61
|
-
const highWaterMark = options.highWaterMark ?? 16384;
|
|
62
|
-
const formatter = options.formatter ?? __logtape_logtape.defaultTextFormatter;
|
|
63
|
-
const passThrough = new node_stream.PassThrough({
|
|
64
|
-
highWaterMark,
|
|
65
|
-
objectMode: false
|
|
66
|
-
});
|
|
67
|
-
const writeStream = (0, node_fs.createWriteStream)(path, { flags: "a" });
|
|
68
|
-
passThrough.pipe(writeStream);
|
|
69
|
-
let disposed = false;
|
|
70
|
-
const sink = (record) => {
|
|
71
|
-
if (disposed) return;
|
|
72
|
-
passThrough.write(formatter(record));
|
|
73
|
-
};
|
|
74
|
-
sink[Symbol.dispose] = () => {
|
|
75
|
-
if (disposed) return;
|
|
76
|
-
disposed = true;
|
|
77
|
-
passThrough.end();
|
|
78
|
-
writeStream.end();
|
|
79
|
-
};
|
|
80
|
-
return sink;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
//#endregion
|
|
84
|
-
exports.getStreamFileSink = getStreamFileSink;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { Sink, TextFormatter } from "@logtape/logtape";
|
|
2
|
-
|
|
3
|
-
//#region streamfilesink.d.ts
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Options for the {@link getStreamFileSink} function.
|
|
7
|
-
*
|
|
8
|
-
* This interface configures the high-performance stream-based file sink that
|
|
9
|
-
* uses Node.js PassThrough streams for optimal I/O performance with automatic
|
|
10
|
-
* backpressure management.
|
|
11
|
-
*
|
|
12
|
-
* @since 1.0.0
|
|
13
|
-
*/
|
|
14
|
-
interface StreamFileSinkOptions {
|
|
15
|
-
/**
|
|
16
|
-
* High water mark for the PassThrough stream buffer in bytes.
|
|
17
|
-
*
|
|
18
|
-
* This controls the internal buffer size of the PassThrough stream.
|
|
19
|
-
* Higher values can improve performance for high-volume logging but use
|
|
20
|
-
* more memory. Lower values reduce memory usage but may impact performance.
|
|
21
|
-
*
|
|
22
|
-
* @default 16384
|
|
23
|
-
* @since 1.0.0
|
|
24
|
-
*/
|
|
25
|
-
readonly highWaterMark?: number;
|
|
26
|
-
/**
|
|
27
|
-
* A custom formatter for log records.
|
|
28
|
-
*
|
|
29
|
-
* If not specified, the default text formatter will be used, which formats
|
|
30
|
-
* records in the standard LogTape format with timestamp, level, category,
|
|
31
|
-
* and message.
|
|
32
|
-
*
|
|
33
|
-
* @default defaultTextFormatter
|
|
34
|
-
* @since 1.0.0
|
|
35
|
-
*/
|
|
36
|
-
readonly formatter?: TextFormatter;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Create a high-performance stream-based file sink that writes log records to a file.
|
|
40
|
-
*
|
|
41
|
-
* This sink uses Node.js PassThrough streams piped to WriteStreams for optimal
|
|
42
|
-
* I/O performance. It leverages the Node.js stream infrastructure to provide
|
|
43
|
-
* automatic backpressure management, efficient buffering, and asynchronous writes
|
|
44
|
-
* without blocking the main thread.
|
|
45
|
-
*
|
|
46
|
-
* ## Performance Characteristics
|
|
47
|
-
*
|
|
48
|
-
* - **High Performance**: Optimized for high-volume logging scenarios
|
|
49
|
-
* - **Non-blocking**: Uses asynchronous I/O that doesn't block the main thread
|
|
50
|
-
* - **Memory Efficient**: Automatic backpressure prevents memory buildup
|
|
51
|
-
* - **Stream-based**: Leverages Node.js native stream optimizations
|
|
52
|
-
*
|
|
53
|
-
* ## When to Use
|
|
54
|
-
*
|
|
55
|
-
* Use this sink when you need:
|
|
56
|
-
* - High-performance file logging for production applications
|
|
57
|
-
* - Non-blocking I/O behavior for real-time applications
|
|
58
|
-
* - Automatic backpressure handling for high-volume scenarios
|
|
59
|
-
* - Simple file output without complex buffering configuration
|
|
60
|
-
*
|
|
61
|
-
* For more control over buffering behavior, consider using {@link getFileSink}
|
|
62
|
-
* instead, which provides options for buffer size, flush intervals, and
|
|
63
|
-
* non-blocking modes.
|
|
64
|
-
*
|
|
65
|
-
* ## Example
|
|
66
|
-
*
|
|
67
|
-
* ```typescript
|
|
68
|
-
* import { configure } from "@logtape/logtape";
|
|
69
|
-
* import { getStreamFileSink } from "@logtape/file";
|
|
70
|
-
*
|
|
71
|
-
* await configure({
|
|
72
|
-
* sinks: {
|
|
73
|
-
* file: getStreamFileSink("app.log", {
|
|
74
|
-
* highWaterMark: 32768 // 32KB buffer for high-volume logging
|
|
75
|
-
* })
|
|
76
|
-
* },
|
|
77
|
-
* loggers: [
|
|
78
|
-
* { category: ["myapp"], sinks: ["file"] }
|
|
79
|
-
* ]
|
|
80
|
-
* });
|
|
81
|
-
* ```
|
|
82
|
-
*
|
|
83
|
-
* @param path The path to the file to write logs to. The file will be created
|
|
84
|
-
* if it doesn't exist, or appended to if it does exist.
|
|
85
|
-
* @param options Configuration options for the stream-based sink.
|
|
86
|
-
* @returns A sink that writes formatted log records to the specified file.
|
|
87
|
-
* The returned sink implements `Disposable` for proper resource cleanup.
|
|
88
|
-
*
|
|
89
|
-
* @since 1.0.0
|
|
90
|
-
*/
|
|
91
|
-
declare function getStreamFileSink(path: string, options?: StreamFileSinkOptions): Sink & Disposable;
|
|
92
|
-
//# sourceMappingURL=streamfilesink.d.ts.map
|
|
93
|
-
//#endregion
|
|
94
|
-
export { StreamFileSinkOptions, getStreamFileSink };
|
|
95
|
-
//# sourceMappingURL=streamfilesink.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streamfilesink.d.cts","names":[],"sources":["../streamfilesink.ts"],"sourcesContent":[],"mappings":";;;;;;AAkBA;AA+EA;;;;;AAGoB;UAlFH,qBAAA;;;;;;;;;;;;;;;;;;;;;;uBAuBM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwDP,iBAAA,yBAEL,wBACR,OAAO"}
|