@orgloop/logger-file 0.1.0
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/LICENSE.md +21 -0
- package/dist/file-logger.d.ts +21 -0
- package/dist/file-logger.d.ts.map +1 -0
- package/dist/file-logger.js +126 -0
- package/dist/file-logger.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/rotation.d.ts +28 -0
- package/dist/rotation.d.ts.map +1 -0
- package/dist/rotation.js +122 -0
- package/dist/rotation.js.map +1 -0
- package/package.json +28 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OrgLoop contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL file logger with buffering and rotation.
|
|
3
|
+
*
|
|
4
|
+
* The default production logger for OrgLoop.
|
|
5
|
+
*/
|
|
6
|
+
import type { LogEntry, Logger } from '@orgloop/sdk';
|
|
7
|
+
export declare class FileLogger implements Logger {
|
|
8
|
+
readonly id = "file";
|
|
9
|
+
private filePath;
|
|
10
|
+
private buffer;
|
|
11
|
+
private bufferSize;
|
|
12
|
+
private flushTimer;
|
|
13
|
+
private rotation;
|
|
14
|
+
private dirEnsured;
|
|
15
|
+
init(config: Record<string, unknown>): Promise<void>;
|
|
16
|
+
log(entry: LogEntry): Promise<void>;
|
|
17
|
+
flush(): Promise<void>;
|
|
18
|
+
shutdown(): Promise<void>;
|
|
19
|
+
private ensureDir;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=file-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-logger.d.ts","sourceRoot":"","sources":["../src/file-logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAmBrD,qBAAa,UAAW,YAAW,MAAM;IACxC,QAAQ,CAAC,EAAE,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAKd;IACF,OAAO,CAAC,UAAU,CAAS;IAErB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA2DpD,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAQjB,SAAS;CAUvB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL file logger with buffering and rotation.
|
|
3
|
+
*
|
|
4
|
+
* The default production logger for OrgLoop.
|
|
5
|
+
*/
|
|
6
|
+
import { appendFile, mkdir } from 'node:fs/promises';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { dirname, resolve } from 'node:path';
|
|
9
|
+
import { parseDuration } from '@orgloop/sdk';
|
|
10
|
+
import { needsRotation, parseSize, rotateFile } from './rotation.js';
|
|
11
|
+
export class FileLogger {
|
|
12
|
+
id = 'file';
|
|
13
|
+
filePath = '';
|
|
14
|
+
buffer = [];
|
|
15
|
+
bufferSize = 100;
|
|
16
|
+
flushTimer = null;
|
|
17
|
+
rotation = {
|
|
18
|
+
maxSize: 100 * 1024 * 1024, // 100MB
|
|
19
|
+
maxAge: 7 * 24 * 60 * 60 * 1000, // 7d
|
|
20
|
+
maxFiles: 10,
|
|
21
|
+
compress: true,
|
|
22
|
+
};
|
|
23
|
+
dirEnsured = false;
|
|
24
|
+
async init(config) {
|
|
25
|
+
const cfg = config;
|
|
26
|
+
// Resolve file path (expand ~ to home directory)
|
|
27
|
+
const rawPath = cfg.path ?? `${homedir()}/.orgloop/logs/orgloop.log`;
|
|
28
|
+
const expanded = rawPath.startsWith('~/') ? rawPath.replace('~', homedir()) : rawPath;
|
|
29
|
+
this.filePath = resolve(expanded);
|
|
30
|
+
// Parse rotation config
|
|
31
|
+
if (cfg.rotation) {
|
|
32
|
+
if (cfg.rotation.max_size) {
|
|
33
|
+
this.rotation.maxSize = parseSize(cfg.rotation.max_size);
|
|
34
|
+
}
|
|
35
|
+
if (cfg.rotation.max_age) {
|
|
36
|
+
this.rotation.maxAge = parseDuration(cfg.rotation.max_age);
|
|
37
|
+
}
|
|
38
|
+
if (cfg.rotation.max_files !== undefined) {
|
|
39
|
+
this.rotation.maxFiles = cfg.rotation.max_files;
|
|
40
|
+
}
|
|
41
|
+
if (cfg.rotation.compress !== undefined) {
|
|
42
|
+
this.rotation.compress = cfg.rotation.compress;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Parse buffer config
|
|
46
|
+
if (cfg.buffer) {
|
|
47
|
+
if (cfg.buffer.size !== undefined) {
|
|
48
|
+
this.bufferSize = cfg.buffer.size;
|
|
49
|
+
}
|
|
50
|
+
if (cfg.buffer.flush_interval) {
|
|
51
|
+
const intervalMs = parseDuration(cfg.buffer.flush_interval);
|
|
52
|
+
this.flushTimer = setInterval(() => {
|
|
53
|
+
void this.flush();
|
|
54
|
+
}, intervalMs);
|
|
55
|
+
if (this.flushTimer.unref) {
|
|
56
|
+
this.flushTimer.unref();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Default flush interval of 1s if not configured
|
|
61
|
+
if (!this.flushTimer) {
|
|
62
|
+
this.flushTimer = setInterval(() => {
|
|
63
|
+
void this.flush();
|
|
64
|
+
}, 1000);
|
|
65
|
+
if (this.flushTimer.unref) {
|
|
66
|
+
this.flushTimer.unref();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Create log file on init so tail -f works immediately
|
|
70
|
+
await this.ensureDir();
|
|
71
|
+
try {
|
|
72
|
+
await appendFile(this.filePath, '', 'utf-8');
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Best-effort — will retry on first flush
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async log(entry) {
|
|
79
|
+
try {
|
|
80
|
+
this.buffer.push(JSON.stringify(entry));
|
|
81
|
+
if (this.buffer.length >= this.bufferSize) {
|
|
82
|
+
await this.flush();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Loggers must not throw
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async flush() {
|
|
90
|
+
if (this.buffer.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
const entries = this.buffer.splice(0);
|
|
93
|
+
const data = entries.map((e) => `${e}\n`).join('');
|
|
94
|
+
try {
|
|
95
|
+
await this.ensureDir();
|
|
96
|
+
await appendFile(this.filePath, data, 'utf-8');
|
|
97
|
+
// Check if rotation is needed
|
|
98
|
+
if (await needsRotation(this.filePath, this.rotation.maxSize)) {
|
|
99
|
+
await rotateFile(this.filePath, this.rotation);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Loggers must not throw — entries are lost on write failure
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async shutdown() {
|
|
107
|
+
if (this.flushTimer) {
|
|
108
|
+
clearInterval(this.flushTimer);
|
|
109
|
+
this.flushTimer = null;
|
|
110
|
+
}
|
|
111
|
+
await this.flush();
|
|
112
|
+
}
|
|
113
|
+
async ensureDir() {
|
|
114
|
+
if (this.dirEnsured)
|
|
115
|
+
return;
|
|
116
|
+
try {
|
|
117
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
118
|
+
this.dirEnsured = true;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Directory may already exist
|
|
122
|
+
this.dirEnsured = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=file-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-logger.js","sourceRoot":"","sources":["../src/file-logger.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @orgloop/logger-file — registration entry point.
|
|
3
|
+
*/
|
|
4
|
+
import type { LoggerRegistration } from '@orgloop/sdk';
|
|
5
|
+
export declare function register(): LoggerRegistration;
|
|
6
|
+
export { FileLogger } from './file-logger.js';
|
|
7
|
+
export { parseSize, rotateFile, needsRotation } from './rotation.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD,wBAAgB,QAAQ,IAAI,kBAAkB,CA+C7C;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @orgloop/logger-file — registration entry point.
|
|
3
|
+
*/
|
|
4
|
+
import { FileLogger } from './file-logger.js';
|
|
5
|
+
export function register() {
|
|
6
|
+
return {
|
|
7
|
+
id: 'file',
|
|
8
|
+
logger: FileLogger,
|
|
9
|
+
configSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
path: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Log file path (default: ~/.orgloop/logs/orgloop.log)',
|
|
15
|
+
},
|
|
16
|
+
format: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: ['jsonl'],
|
|
19
|
+
description: 'Log format (only "jsonl" for MVP).',
|
|
20
|
+
default: 'jsonl',
|
|
21
|
+
},
|
|
22
|
+
rotation: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
max_size: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Rotate when file exceeds this size (e.g., "100MB")',
|
|
28
|
+
},
|
|
29
|
+
max_age: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'Delete rotated files after this duration (e.g., "7d")',
|
|
32
|
+
},
|
|
33
|
+
max_files: { type: 'number', description: 'Keep at most N rotated files', default: 10 },
|
|
34
|
+
compress: { type: 'boolean', description: 'Gzip rotated files', default: true },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
buffer: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
size: { type: 'number', description: 'Buffer N entries before flushing', default: 100 },
|
|
41
|
+
flush_interval: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Flush at least every N (e.g., "1s")',
|
|
44
|
+
default: '1s',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export { FileLogger } from './file-logger.js';
|
|
54
|
+
export { parseSize, rotateFile, needsRotation } from './rotation.js';
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,UAAU,QAAQ;IACvB,OAAO;QACN,EAAE,EAAE,MAAM;QACV,MAAM,EAAE,UAAU;QAClB,YAAY,EAAE;YACb,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACX,IAAI,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,sDAAsD;iBACnE;gBACD,MAAM,EAAE;oBACP,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,OAAO,CAAC;oBACf,WAAW,EAAE,oCAAoC;oBACjD,OAAO,EAAE,OAAO;iBAChB;gBACD,QAAQ,EAAE;oBACT,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACX,QAAQ,EAAE;4BACT,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,oDAAoD;yBACjE;wBACD,OAAO,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,uDAAuD;yBACpE;wBACD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8BAA8B,EAAE,OAAO,EAAE,EAAE,EAAE;wBACvF,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,oBAAoB,EAAE,OAAO,EAAE,IAAI,EAAE;qBAC/E;iBACD;gBACD,MAAM,EAAE;oBACP,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACX,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kCAAkC,EAAE,OAAO,EAAE,GAAG,EAAE;wBACvF,cAAc,EAAE;4BACf,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,qCAAqC;4BAClD,OAAO,EAAE,IAAI;yBACb;qBACD;iBACD;aACD;YACD,oBAAoB,EAAE,KAAK;SAC3B;KACD,CAAC;AACH,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File rotation logic for the JSONL file logger.
|
|
3
|
+
*
|
|
4
|
+
* Handles: renaming current file to timestamped name, optional gzip compression,
|
|
5
|
+
* and cleanup of old rotated files.
|
|
6
|
+
*/
|
|
7
|
+
export interface RotationConfig {
|
|
8
|
+
maxSize: number;
|
|
9
|
+
maxAge: number;
|
|
10
|
+
maxFiles: number;
|
|
11
|
+
compress: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse a size string (e.g., "100MB", "1GB") to bytes.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseSize(size: string): number;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a file exceeds the max size.
|
|
19
|
+
*/
|
|
20
|
+
export declare function needsRotation(filePath: string, maxSize: number): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Rotate the current log file:
|
|
23
|
+
* 1. Rename to timestamped name
|
|
24
|
+
* 2. Optionally gzip
|
|
25
|
+
* 3. Clean up old files
|
|
26
|
+
*/
|
|
27
|
+
export declare function rotateFile(filePath: string, config: RotationConfig): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=rotation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../src/rotation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB9C;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOvF;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxF"}
|
package/dist/rotation.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File rotation logic for the JSONL file logger.
|
|
3
|
+
*
|
|
4
|
+
* Handles: renaming current file to timestamped name, optional gzip compression,
|
|
5
|
+
* and cleanup of old rotated files.
|
|
6
|
+
*/
|
|
7
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
8
|
+
import { mkdir, readdir, rename, stat, unlink } from 'node:fs/promises';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
import { pipeline } from 'node:stream/promises';
|
|
11
|
+
import { createGzip } from 'node:zlib';
|
|
12
|
+
/**
|
|
13
|
+
* Parse a size string (e.g., "100MB", "1GB") to bytes.
|
|
14
|
+
*/
|
|
15
|
+
export function parseSize(size) {
|
|
16
|
+
const match = size.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB)$/i);
|
|
17
|
+
if (!match) {
|
|
18
|
+
throw new Error(`Invalid size format: "${size}". Expected format: <number><unit> (e.g., 100MB, 1GB)`);
|
|
19
|
+
}
|
|
20
|
+
const value = Number.parseFloat(match[1]);
|
|
21
|
+
const unit = match[2].toUpperCase();
|
|
22
|
+
switch (unit) {
|
|
23
|
+
case 'B':
|
|
24
|
+
return value;
|
|
25
|
+
case 'KB':
|
|
26
|
+
return value * 1024;
|
|
27
|
+
case 'MB':
|
|
28
|
+
return value * 1024 * 1024;
|
|
29
|
+
case 'GB':
|
|
30
|
+
return value * 1024 * 1024 * 1024;
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Unknown size unit: ${unit}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if a file exceeds the max size.
|
|
37
|
+
*/
|
|
38
|
+
export async function needsRotation(filePath, maxSize) {
|
|
39
|
+
try {
|
|
40
|
+
const st = await stat(filePath);
|
|
41
|
+
return st.size >= maxSize;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Rotate the current log file:
|
|
49
|
+
* 1. Rename to timestamped name
|
|
50
|
+
* 2. Optionally gzip
|
|
51
|
+
* 3. Clean up old files
|
|
52
|
+
*/
|
|
53
|
+
export async function rotateFile(filePath, config) {
|
|
54
|
+
const dir = dirname(filePath);
|
|
55
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
56
|
+
const baseName = filePath.split('/').pop();
|
|
57
|
+
const rotatedName = `${baseName}.${timestamp}`;
|
|
58
|
+
const rotatedPath = join(dir, rotatedName);
|
|
59
|
+
// Rename current → timestamped
|
|
60
|
+
try {
|
|
61
|
+
await rename(filePath, rotatedPath);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// File may not exist or be locked; skip rotation
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Optionally compress
|
|
68
|
+
if (config.compress) {
|
|
69
|
+
try {
|
|
70
|
+
const gzPath = `${rotatedPath}.gz`;
|
|
71
|
+
await pipeline(createReadStream(rotatedPath), createGzip(), createWriteStream(gzPath));
|
|
72
|
+
await unlink(rotatedPath);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Compression failed; leave the uncompressed file
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Clean up old files
|
|
79
|
+
await cleanupOldFiles(dir, baseName, config);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Remove rotated files that exceed maxFiles or maxAge.
|
|
83
|
+
*/
|
|
84
|
+
async function cleanupOldFiles(dir, baseName, config) {
|
|
85
|
+
try {
|
|
86
|
+
await mkdir(dir, { recursive: true });
|
|
87
|
+
const files = await readdir(dir);
|
|
88
|
+
// Find rotated files matching our base name
|
|
89
|
+
const rotated = [];
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
if (file.startsWith(`${baseName}.`) && file !== baseName) {
|
|
92
|
+
try {
|
|
93
|
+
const st = await stat(join(dir, file));
|
|
94
|
+
rotated.push({ name: file, mtime: st.mtimeMs });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Skip files we can't stat
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Sort by mtime descending (newest first)
|
|
102
|
+
rotated.sort((a, b) => b.mtime - a.mtime);
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
for (let i = 0; i < rotated.length; i++) {
|
|
105
|
+
const entry = rotated[i];
|
|
106
|
+
const isOverLimit = i >= config.maxFiles;
|
|
107
|
+
const isExpired = now - entry.mtime > config.maxAge;
|
|
108
|
+
if (isOverLimit || isExpired) {
|
|
109
|
+
try {
|
|
110
|
+
await unlink(join(dir, entry.name));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Best effort cleanup
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Directory issues — skip cleanup
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=rotation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rotation.js","sourceRoot":"","sources":["../src/rotation.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@orgloop/logger-file",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OrgLoop file logger — JSONL with rotation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@orgloop/sdk": "0.1.0"
|
|
10
|
+
},
|
|
11
|
+
"orgloop": {
|
|
12
|
+
"type": "logger",
|
|
13
|
+
"id": "file"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"clean": "rm -rf dist",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest run"
|
|
27
|
+
}
|
|
28
|
+
}
|