Skip to content

Commit dee9d57

Browse files
authored
feat(mongodb-log-writer): add ability to specify a prefix for log files (#505)
This is needed for mongosh to use a `mongosh_` prefix for custom log directories.
1 parent 821875d commit dee9d57

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

packages/mongodb-log-writer/src/mongo-log-manager.spec.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,48 @@ describe('MongoLogManager', function () {
3131
sinon.restore();
3232
});
3333

34+
it('constructor throws with invalid prefixes', function () {
35+
expect(() => {
36+
new MongoLogManager({
37+
directory,
38+
retentionDays,
39+
prefix: '%asdabs/',
40+
onwarn,
41+
onerror,
42+
});
43+
}).to.throw();
44+
45+
expect(() => {
46+
new MongoLogManager({
47+
directory,
48+
retentionDays,
49+
prefix: '$$$$',
50+
onwarn,
51+
onerror,
52+
});
53+
}).to.throw();
54+
55+
expect(() => {
56+
new MongoLogManager({
57+
directory,
58+
retentionDays,
59+
prefix: 'abc_',
60+
onwarn,
61+
onerror,
62+
});
63+
}).not.to.throw();
64+
65+
expect(() => {
66+
new MongoLogManager({
67+
directory,
68+
retentionDays,
69+
prefix: 'something',
70+
onwarn,
71+
onerror,
72+
});
73+
}).not.to.throw();
74+
});
75+
3476
it('allows creating and writing to log files', async function () {
3577
const manager = new MongoLogManager({
3678
directory,
@@ -61,6 +103,19 @@ describe('MongoLogManager', function () {
61103
expect(log[0].t.$date).to.be.a('string');
62104
});
63105

106+
it('can take a custom prefix for log files', async function () {
107+
const manager = new MongoLogManager({
108+
directory,
109+
retentionDays,
110+
prefix: 'custom_',
111+
onwarn,
112+
onerror,
113+
});
114+
115+
const writer = await manager.createLogWriter();
116+
expect(writer.logFilePath as string).to.match(/custom_/);
117+
});
118+
64119
it('cleans up old log files when requested', async function () {
65120
retentionDays = 0.000001; // 86.4 ms
66121
const manager = new MongoLogManager({
@@ -178,7 +233,57 @@ describe('MongoLogManager', function () {
178233
expect(leftoverFiles).deep.equals([faultyFile, ...validFiles.slice(3)]);
179234
});
180235

181-
it('cleans up least recent log files when over a storage limit', async function () {
236+
it('cleanup only applies to files with the prefix, if set', async function () {
237+
const manager = new MongoLogManager({
238+
directory,
239+
retentionDays,
240+
maxLogFileCount: 7,
241+
prefix: 'custom_',
242+
onwarn,
243+
onerror,
244+
});
245+
246+
const paths: string[] = [];
247+
const offset = Math.floor(Date.now() / 1000);
248+
249+
// Create 4 files: 2 with a different prefix and 2 with no prefix
250+
for (let i = 1; i >= 0; i--) {
251+
const withoutPrefix = path.join(
252+
directory,
253+
ObjectId.createFromTime(offset - i).toHexString() + '_log'
254+
);
255+
await fs.writeFile(withoutPrefix, '');
256+
paths.push(withoutPrefix);
257+
258+
const withDifferentPrefix = path.join(
259+
directory,
260+
'different_' +
261+
ObjectId.createFromTime(offset - i).toHexString() +
262+
'_log'
263+
);
264+
await fs.writeFile(withDifferentPrefix, '');
265+
paths.push(withDifferentPrefix);
266+
}
267+
268+
// Create 10 files with the prefix
269+
for (let i = 9; i >= 0; i--) {
270+
const filename = path.join(
271+
directory,
272+
`custom_${ObjectId.createFromTime(offset - i).toHexString()}_log`
273+
);
274+
await fs.writeFile(filename, '');
275+
paths.push(filename);
276+
}
277+
278+
expect(await getFilesState(paths)).to.equal('11111111111111');
279+
await manager.cleanupOldLogFiles();
280+
281+
// The first 4 files without the right prefix should still be there.
282+
// The next (oldest) 3 files with the prefix should be deleted.
283+
expect(await getFilesState(paths)).to.equal('11110001111111');
284+
});
285+
286+
it('cleans up least recent log files when requested with a storage limit', async function () {
182287
const manager = new MongoLogManager({
183288
directory,
184289
retentionDays,

packages/mongodb-log-writer/src/mongo-log-manager.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ interface MongoLogOptions {
1919
maxLogFileCount?: number;
2020
/** The maximal size of log files which are kept. */
2121
retentionGB?: number;
22+
/** Prefix to use for the log files */
23+
prefix?: string;
2224
/** A handler for errors related to a specific filesystem path. */
2325
onerror: (err: Error, path: string) => unknown | Promise<void>;
2426
/** A handler for warnings related to a specific filesystem path. */
@@ -34,6 +36,13 @@ export class MongoLogManager {
3436
_options: MongoLogOptions;
3537

3638
constructor(options: MongoLogOptions) {
39+
if (options.prefix) {
40+
if (!/^[a-z0-9_]+$/i.test(options.prefix)) {
41+
throw new Error(
42+
'Prefix must only contain letters, numbers, and underscores'
43+
);
44+
}
45+
}
3746
this._options = options;
3847
}
3948

@@ -48,6 +57,10 @@ export class MongoLogManager {
4857
}
4958
}
5059

60+
private get prefix() {
61+
return this._options.prefix ?? '';
62+
}
63+
5164
/** Clean up log files older than `retentionDays`. */
5265
async cleanupOldLogFiles(maxDurationMs = 5_000): Promise<void> {
5366
const dir = this._options.directory;
@@ -80,8 +93,11 @@ export class MongoLogManager {
8093
if (Date.now() - deletionStartTimestamp > maxDurationMs) break;
8194

8295
if (!dirent.isFile()) continue;
83-
const { id } =
84-
/^(?<id>[a-f0-9]{24})_log(\.gz)?$/i.exec(dirent.name)?.groups ?? {};
96+
const logRegExp = new RegExp(
97+
`^${this.prefix}(?<id>[a-f0-9]{24})_log(\\.gz)?$`,
98+
'i'
99+
);
100+
const { id } = logRegExp.exec(dirent.name)?.groups ?? {};
85101
if (!id) continue;
86102

87103
const fileTimestamp = +new ObjectId(id).getTimestamp();
@@ -143,7 +159,7 @@ export class MongoLogManager {
143159
const doGzip = !!this._options.gzip;
144160
const logFilePath = path.join(
145161
this._options.directory,
146-
`${logId}_log${doGzip ? '.gz' : ''}`
162+
`${this.prefix}${logId}_log${doGzip ? '.gz' : ''}`
147163
);
148164

149165
let originalTarget: Writable;

0 commit comments

Comments
 (0)