@@ -17,6 +17,8 @@ interface MongoLogOptions {
1717 retentionDays : number ;
1818 /** The maximal number of log files which are kept. */
1919 maxLogFileCount ?: number ;
20+ /** The maximal GB of log files which are kept. */
21+ logRetentionGB ?: number ;
2022 /** A handler for warnings related to a specific filesystem path. */
2123 onerror : ( err : Error , path : string ) => unknown | Promise < void > ;
2224 /** A handler for errors related to a specific filesystem path. */
@@ -54,8 +56,15 @@ export class MongoLogManager {
5456 const leastRecentFileHeap = new Heap < {
5557 fileTimestamp : number ;
5658 fullPath : string ;
59+ fileSize ?: number ;
5760 } > ( ( a , b ) => a . fileTimestamp - b . fileTimestamp ) ;
5861
62+ const storageSizeLimit = this . _options . logRetentionGB
63+ ? this . _options . logRetentionGB * 1024 * 1024 * 1024
64+ : Infinity ;
65+ let usedStorageSize = this . _options . logRetentionGB ? 0 : - Infinity ;
66+ // eslint-disable-next-line no-console
67+
5968 for await ( const dirent of dirHandle ) {
6069 // Cap the overall time spent inside this function. Consider situations like
6170 // a large number of machines using a shared network-mounted $HOME directory
@@ -69,23 +78,53 @@ export class MongoLogManager {
6978 if ( ! id ) continue ;
7079 const fileTimestamp = + new ObjectId ( id ) . getTimestamp ( ) ;
7180 const fullPath = path . join ( dir , dirent . name ) ;
72- let toDelete : string | undefined ;
81+ let toDelete :
82+ | {
83+ fullPath : string ;
84+ /** If the file wasn't deleted right away and there is a
85+ * retention size limit, its size should be accounted */
86+ fileSize ?: number ;
87+ }
88+ | undefined ;
7389
7490 // If the file is older than expected, delete it. If the file is recent,
7591 // add it to the list of seen files, and if that list is too large, remove
7692 // the least recent file we've seen so far.
7793 if ( fileTimestamp < deletionCutoffTimestamp ) {
78- toDelete = fullPath ;
79- } else if ( this . _options . maxLogFileCount ) {
80- leastRecentFileHeap . push ( { fullPath, fileTimestamp } ) ;
81- if ( leastRecentFileHeap . size ( ) > this . _options . maxLogFileCount ) {
82- toDelete = leastRecentFileHeap . pop ( ) ?. fullPath ;
94+ toDelete = {
95+ fullPath,
96+ } ;
97+ } else if (
98+ this . _options . logRetentionGB ||
99+ this . _options . maxLogFileCount
100+ ) {
101+ const fileSize = ( await fs . stat ( fullPath ) ) . size ;
102+ if ( this . _options . logRetentionGB ) {
103+ usedStorageSize += fileSize ;
104+ }
105+
106+ leastRecentFileHeap . push ( {
107+ fullPath,
108+ fileTimestamp,
109+ fileSize,
110+ } ) ;
111+
112+ const reachedMaxStorageSize = usedStorageSize > storageSizeLimit ;
113+ const reachedMaxFileCount =
114+ this . _options . maxLogFileCount &&
115+ leastRecentFileHeap . size ( ) > this . _options . maxLogFileCount ;
116+
117+ if ( reachedMaxStorageSize || reachedMaxFileCount ) {
118+ toDelete = leastRecentFileHeap . pop ( ) ;
83119 }
84120 }
85121
86122 if ( ! toDelete ) continue ;
87123 try {
88- await fs . unlink ( toDelete ) ;
124+ await fs . unlink ( toDelete . fullPath ) ;
125+ if ( toDelete . fileSize ) {
126+ usedStorageSize -= toDelete . fileSize ;
127+ }
89128 // eslint-disable-next-line @typescript-eslint/no-explicit-any
90129 } catch ( err : any ) {
91130 if ( err ?. code !== 'ENOENT' ) {
0 commit comments