11/* eslint-disable @typescript-eslint/no-unsafe-function-type */
2+ import { readdir , stat , unlink , readFile , writeFile } from 'fs/promises' ;
3+ import { join } from 'path' ;
4+ import { DateTime } from 'luxon' ;
25import pino , { LevelWithSilent , Logger } from 'pino' ;
3- import { ExtensionName } from '../utils/ExtensionConfig' ;
4- import { AwsMetadata , TelemetrySettings } from './TelemetryConfig' ;
6+ import { pathToArtifact } from '../utils/ArtifactsDir' ;
7+ import { ExtensionId , ExtensionName } from '../utils/ExtensionConfig' ;
8+ import { TelemetrySettings , AwsMetadata } from './TelemetryConfig' ;
59
610export const LogLevel : Record < LevelWithSilent , number > = {
711 silent : 0 ,
@@ -14,6 +18,9 @@ export const LogLevel: Record<LevelWithSilent, number> = {
1418} as const ;
1519
1620export class LoggerFactory {
21+ private static readonly LogsDirectory = pathToArtifact ( 'logs' ) ;
22+ private static readonly MaxFileSize = 100 * 1024 * 1024 ; // 100MB
23+
1724 private static readonly _instance : LoggerFactory = new LoggerFactory ( ) ;
1825
1926 private readonly baseLogger : Logger ;
@@ -27,15 +34,56 @@ export class LoggerFactory {
2734 name : ExtensionName ,
2835 level : this . logLevel ,
2936 transport : {
30- target : 'pino-pretty' ,
31- options : {
32- colorize : false ,
33- translateTime : 'SYS:hh:MM:ss TT' ,
34- ignore : 'pid,hostname,name,clazz' ,
35- messageFormat : '[{clazz}] {msg}' ,
36- } ,
37+ targets : [
38+ {
39+ target : 'pino-pretty' ,
40+ options : {
41+ colorize : false ,
42+ translateTime : 'SYS:hh:MM:ss TT' ,
43+ ignore : 'pid,hostname,name,clazz' ,
44+ messageFormat : '[{clazz}] {msg}' ,
45+ } ,
46+ } ,
47+ {
48+ target : 'pino/file' ,
49+ options : {
50+ destination : join (
51+ LoggerFactory . LogsDirectory ,
52+ `${ ExtensionId } -${ DateTime . utc ( ) . toFormat ( 'yyyy-MM-dd' ) } .log` ,
53+ ) ,
54+ mkdir : true ,
55+ } ,
56+ } ,
57+ ] ,
3758 } ,
3859 } ) ;
60+
61+ void this . cleanOldLogs ( ) ;
62+ }
63+
64+ private async cleanOldLogs ( ) {
65+ try {
66+ const files = await readdir ( LoggerFactory . LogsDirectory ) ;
67+ const oneWeekAgo = DateTime . utc ( ) . minus ( { weeks : 1 } ) ;
68+
69+ for ( const file of files ) {
70+ if ( ! file . endsWith ( '.log' ) ) continue ;
71+
72+ const filePath = join ( LoggerFactory . LogsDirectory , file ) ;
73+ const stats = await stat ( filePath ) ;
74+
75+ if ( DateTime . fromJSDate ( stats . mtime ) < oneWeekAgo ) {
76+ await unlink ( filePath ) ;
77+ } else if ( stats . size > LoggerFactory . MaxFileSize ) {
78+ const content = await readFile ( filePath , 'utf8' ) ;
79+ const lines = content . split ( '\n' ) ;
80+ const trimmed = lines . slice ( - Math . floor ( lines . length / 2 ) ) . join ( '\n' ) ;
81+ await writeFile ( filePath , trimmed ) ;
82+ }
83+ }
84+ } catch ( err ) {
85+ this . baseLogger . error ( err , 'Error cleaning up old logs' ) ;
86+ }
3987 }
4088
4189 private reconfigure ( newLevel : LevelWithSilent ) {
0 commit comments