import { AbortController } from '@azure/abort-controller' ; import type { WebSiteManagementModels } from '@azure/arm-appservice' ; import { BasicAuthenticationCredentials , HttpOperationResponse , ServiceClient } from '@azure/ms-rest-js' ; import { setInterval } from 'timers' ; import * as vscode from 'vscode' ; import { callWithTelemetryAndErrorHandling , createGenericClient , IActionContext , parseError } from 'vscode-azureextensionui' ; import { ext } from './extensionVariables' ; import { localize } from './localize' ; import { pingFunctionApp } from './pingFunctionApp' ; import { ParsedSite } from './SiteClient' ; import { nonNullProp } from './utils/nonNull' ; export interface ILogStream extends vscode . Disposable { isConnected : boolean ; outputChannel : vscode . OutputChannel ; } const logStreams = new Map < string , ILogStream > ( ) ; function getLogStreamId ( site , logsPath ) { return `${ site . id }${ logsPath }` ; } export async function startStreamingLogs ( context , site , verifyLoggingEnabled , logStreamLabel , logsPath = '' ) { const logStreamId = getLogStreamId ( site , logsPath ) ; const logStream = logStreams . get ( logStreamId ) ; if ( logStream && logStream . isConnected ) { logStream . outputChannel . show ( ) ; void context . ui . showWarningMessage ( localize ( 'logStreamAlreadyActive' , 'The log-streaming service for "{0}" is already active.' , logStreamLabel ) ) ; return logStream ; } else { await verifyLoggingEnabled ( ) ; const outputChannel = logStream ? logStream . outputChannel : vscode . window . createOutputChannel ( localize ( 'logStreamLabel' , '{0} - Log Stream' , logStreamLabel ) ) ; ext . context . subscriptions . push ( outputChannel ) ; outputChannel . show ( ) ; outputChannel . appendLine ( localize ( 'connectingToLogStream' , 'Connecting to log stream...' ) ) ; const client = await site . createClient ( context ) ; const creds = await client . getWebAppPublishCredential ( ) ; return await new Promise ( ( onLogStreamCreated ) : void => { void callWithTelemetryAndErrorHandling ( 'appService.streamingLogs' , async ( streamContext ) => { streamContext . errorHandling . suppressDisplay = true ; let timerId ; if ( site . isFunctionApp ) { await pingFunctionApp ( streamContext , site ) ; timerId = setInterval ( async ( ) => await pingFunctionApp ( streamContext , site ) , 60 * 1000 ) ; } const genericClient = await createGenericClient ( streamContext , new BasicAuthenticationCredentials ( creds . publishingUserName , nonNullProp ( creds , 'publishingPassword' ) ) ) ; const abortController = new AbortController ( ) ; const logsResponse = await genericClient . sendRequest ( { method : 'GET' , url : `${ site . kuduUrl }/api/logstream/${ logsPath }` , streamResponseBody : true , abortSignal : abortController . signal } ) ; await new Promise < void > ( ( onLogStreamEnded , reject ) : void => { const newLogStream = { dispose : ( ) : void => { logsResponse . readableStreamBody ?. removeAllListeners ( ) ; abortController . abort ( ) ; outputChannel . show ( ) ; if ( timerId ) { clearInterval ( timerId ) ; } outputChannel . appendLine ( localize ( 'logStreamDisconnected' , 'Disconnected from log-streaming service.' ) ) ; newLogStream . isConnected = false ; void onLogStreamEnded ( ) ; } , isConnected : true , outputChannel : outputChannel } ; logsResponse . readableStreamBody ?. on ( 'data' , ( chunk ) => { outputChannel . append ( chunk . toString ( ) ) ; } ) . on ( 'error' , ( err ) => { if ( timerId ) { clearInterval ( timerId ) ; } newLogStream . isConnected = false ; outputChannel . show ( ) ; outputChannel . appendLine ( localize ( 'logStreamError' , 'Error connecting to log-streaming service:' ) ) ; outputChannel . appendLine ( parseError ( err ) . message ) ; reject ( err ) ; } ) . on ( 'complete' , ( ) => { newLogStream . dispose ( ) ; } ) ; logStreams . set ( logStreamId , newLogStream ) ; onLogStreamCreated ( newLogStream ) ; } ) ; } ) ; } ) ; } } export async function stopStreamingLogs ( site , logsPath = '' ) { const logStreamId = getLogStreamId ( site , logsPath ) ; const logStream = logStreams . get ( logStreamId ) ; if ( logStream && logStream . isConnected ) { logStream . dispose ( ) ; } else { await vscode . window . showWarningMessage ( localize ( 'alreadyDisconnected' , 'The log-streaming service is already disconnected.' ) ) ; } }