import { schema } from '@kbn/config-schema' ; import { LogRecord , Layout , DisposableAppender } from '@kbn/logging' ; import { Layouts , LayoutConfigType } from '../../layouts/layouts' ; import { BufferAppender } from '../buffer/buffer_appender' ; import { TriggeringPolicyConfig , createTriggeringPolicy , triggeringPolicyConfigSchema , TriggeringPolicy , } from './policies' ; import { RollingStrategy , createRollingStrategy , RollingStrategyConfig , rollingStrategyConfigSchema , } from './strategies' ; import { RollingFileManager } from './rolling_file_manager' ; import { RollingFileContext } from './rolling_file_context' ; export interface RollingFileAppenderConfig { type : 'rolling-file' ; /**
     * The layout to use when writing log entries
     */ layout : LayoutConfigType ; /**
     * The absolute path of the file to write to.
     */ fileName : string ; /**
     * The  TriggeringPolicy to use to determine if a rollover should occur.
      policy : TriggeringPolicyConfig ; /**
     * The  RollingStrategy to use for rolling.
      strategy : RollingStrategyConfig ; } internal export class RollingFileAppender implements DisposableAppender { public static configSchema = schema . object ( { type : schema . literal ( 'rolling-file' ) , layout : Layouts . configSchema , fileName : schema . string ( ) , policy : triggeringPolicyConfigSchema , strategy : rollingStrategyConfigSchema , } ) ; private isRolling = false ; private disposed = false ; private rollingPromise ? : Promise < void > ; private readonly layout : Layout ; private readonly context : RollingFileContext ; private readonly fileManager : RollingFileManager ; private readonly policy : TriggeringPolicy ; private readonly strategy : RollingStrategy ; private readonly buffer : BufferAppender ; constructor ( config ) { this . context = new RollingFileContext ( config . fileName ) ; this . context . refreshFileInfo ( ) ; this . fileManager = new RollingFileManager ( this . context ) ; this . layout = Layouts . create ( config . layout ) ; this . policy = createTriggeringPolicy ( config . policy , this . context ) ; this . strategy = createRollingStrategy ( config . strategy , this . context ) ; this . buffer = new BufferAppender ( ) ; } /**
     * Formats specified `record` and writes it to the specified file. If the record
     * would trigger a rollover, it will be performed before the effective write operation.
     */ public append ( record ) { if ( this . isRolling ) { this . buffer . append ( record ) ; return ; } if ( this . needRollout ( record ) ) { this . buffer . append ( record ) ; this . rollingPromise = this . performRollout ( ) ; return ; } this . _writeToFile ( record ) ; } private _writeToFile ( record ) { this . fileManager . write ( `${ this . layout . format ( record ) }\n` ) ; } /**
     * Disposes the appender.
     * If a rollout is currently in progress, it will be awaited.
     */ public async dispose ( ) { if ( this . disposed ) { return ; } this . disposed = true ; if ( this . rollingPromise ) { await this . rollingPromise ; } await this . buffer . dispose ( ) ; await this . fileManager . closeStream ( ) ; } private async performRollout ( ) { if ( this . isRolling ) { return ; } this . isRolling = true ; try { await this . strategy . rollout ( ) ; await this . fileManager . closeStream ( ) ; } catch ( e ) { console . error ( '[RollingFileAppender]: error while rolling file: ' , e ) ; } this . rollingPromise = undefined ; this . isRolling = false ; this . flushBuffer ( ) ; } private flushBuffer ( ) { const pendingLogs = this . buffer . flush ( ) ; for ( const log of pendingLogs ) { if ( this . disposed ) { this . _writeToFile ( log ) ; } else { this . append ( log ) ; } } } /**
     * Checks if the current event should trigger a rollout
     */ private needRollout ( record ) { return this . policy . isTriggeringEvent ( record ) ; } }