@@ -4,19 +4,21 @@ import { ReactComponent as PlayButton } from '../../../../../../assets/icons/ic-
44import { ReactComponent as StopButton } from '../../../../../../assets/icons/ic-stop-filled.svg'
55import { ReactComponent as Search } from '../../../../../../assets/icons/ic-search.svg'
66import { ReactComponent as Abort } from '../../../../assets/icons/ic-abort.svg'
7+ import { ReactComponent as LinesIcon } from '../../../../../../assets/icons/ic-lines.svg'
8+ import { ReactComponent as Download } from '../../../../../../assets/icons/ic-arrow-line-down.svg'
79import { useParams , useRouteMatch , useLocation } from 'react-router'
810import { NodeDetailTab } from '../nodeDetail.type'
9- import { getLogsURL } from '../nodeDetail.api'
11+ import { downloadLogs , getLogsURL } from '../nodeDetail.api'
1012import IndexStore from '../../../index.store'
1113import WebWorker from '../../../../../app/WebWorker'
1214import sseWorker from '../../../../../app/grepSSEworker'
13- import { Checkbox , CHECKBOX_VALUE , Host } from '@devtron-labs/devtron-fe-common-lib'
15+ import { Checkbox , CHECKBOX_VALUE , Host , Progressing } from '@devtron-labs/devtron-fe-common-lib'
1416import { Subject } from '../../../../../../util/Subject'
1517import LogViewerComponent from './LogViewer.component'
1618import { useKeyDown } from '../../../../../common'
1719import { toast } from 'react-toastify'
1820import Select from 'react-select'
19- import { multiSelectStyles } from '../../../../common/ReactSelectCustomization'
21+ import { multiSelectStyles , podsDropdownStyles } from '../../../../common/ReactSelectCustomization'
2022import { LogsComponentProps , Options } from '../../../appDetails.type'
2123import { ReactComponent as Question } from '../../../../assets/icons/ic-question.svg'
2224import { ReactComponent as CloseImage } from '../../../../assets/icons/ic-cancelled.svg'
@@ -30,10 +32,15 @@ import {
3032 getGroupedContainerOptions ,
3133 getInitialPodContainerSelection ,
3234 getPodContainerOptions ,
35+ getPodLogsOptions ,
3336 getSelectedPodList ,
3437} from '../nodeDetail.util'
3538import './nodeDetailTab.scss'
3639import ReactGA from 'react-ga4'
40+ import { CUSTOM_LOGS_FILTER } from '../../../../../../config'
41+ import { SelectedCustomLogFilterType } from './node.type'
42+ import CustomLogsModal from './CustomLogsModal/CustomLogsModal'
43+
3744
3845const subject : Subject < string > = new Subject ( )
3946const commandLineParser = require ( 'command-line-parser' )
@@ -46,6 +53,15 @@ function LogsComponent({
4653 isResourceBrowserView,
4754 selectedResource,
4855} : LogsComponentProps ) {
56+ const [ logsShownOption , setLogsShownOption ] = useState ( {
57+ prev : getPodLogsOptions ( ) [ 5 ] ,
58+ current : getPodLogsOptions ( ) [ 5 ] ,
59+ } )
60+ const [ selectedCustomLogFilter , setSelectedCustomLogFilter ] = useState < SelectedCustomLogFilterType > ( {
61+ option : 'duration' ,
62+ value : '' ,
63+ unit : 'minutes' ,
64+ } )
4965 const location = useLocation ( )
5066 const { url } = useRouteMatch ( )
5167 const params = useParams < {
@@ -72,6 +88,9 @@ function LogsComponent({
7288 )
7389 const [ prevContainer , setPrevContainer ] = useState ( false )
7490 const [ showNoPrevContainer , setNoPrevContainer ] = useState ( '' )
91+ const [ newFilteredLogs , setNewFilteredLogs ] = useState < boolean > ( false )
92+ const [ showCustomOptionsModal , setShowCustomOptionsMoadal ] = useState ( false )
93+ const [ downloadInProgress , setDownloadInProgress ] = useState ( false )
7594
7695 const getPrevContainerLogs = ( ) => {
7796 setPrevContainer ( ! prevContainer )
@@ -203,6 +222,49 @@ function LogsComponent({
203222 setTimeout ( ( ) => setLogsCleared ( false ) , 100 )
204223 }
205224
225+ const handleDownloadLogs = ( ) => {
226+ const nodeName = podContainerOptions . podOptions [ 0 ] . name
227+ if ( isResourceBrowserView ) {
228+ for ( const _co of podContainerOptions . containerOptions ) {
229+ if ( _co . selected ) {
230+ downloadLogs (
231+ setDownloadInProgress ,
232+ appDetails ,
233+ nodeName ,
234+ _co . name ,
235+ prevContainer ,
236+ logsShownOption . current ,
237+ selectedCustomLogFilter ,
238+ isResourceBrowserView ,
239+ selectedResource . clusterId ,
240+ selectedResource . namespace ,
241+ )
242+ }
243+ }
244+ } else {
245+ const selectedPods = podContainerOptions . podOptions
246+ . filter ( ( _pod ) => _pod . selected )
247+ . flatMap ( ( _pod ) => getSelectedPodList ( _pod . name ) )
248+
249+ const containers = podContainerOptions . containerOptions . filter ( ( _co ) => _co . selected ) . map ( ( _co ) => _co . name )
250+ const podsWithContainers = selectedPods
251+ . flatMap ( ( _pod ) => flatContainers ( _pod ) . map ( ( _container ) => [ _pod . name , _container ] ) )
252+ . filter ( ( _pwc ) => containers . includes ( _pwc [ 1 ] ) )
253+
254+ for ( const _pwc of podsWithContainers ) {
255+ downloadLogs (
256+ setDownloadInProgress ,
257+ appDetails ,
258+ _pwc [ 0 ] ,
259+ _pwc [ 1 ] ,
260+ prevContainer ,
261+ logsShownOption . current ,
262+ selectedCustomLogFilter ,
263+ )
264+ }
265+ }
266+ }
267+
206268 const fetchLogs = ( ) => {
207269 if ( podContainerOptions . podOptions . length == 0 || podContainerOptions . containerOptions . length == 0 ) {
208270 return
@@ -226,6 +288,8 @@ function LogsComponent({
226288 Host ,
227289 _co . name ,
228290 prevContainer ,
291+ logsShownOption . current ,
292+ selectedCustomLogFilter ,
229293 isResourceBrowserView ,
230294 selectedResource . clusterId ,
231295 selectedResource . namespace ,
@@ -245,14 +309,23 @@ function LogsComponent({
245309
246310 for ( const _pwc of podsWithContainers ) {
247311 pods . push ( _pwc [ 0 ] )
248- urls . push ( getLogsURL ( appDetails , _pwc [ 0 ] , Host , _pwc [ 1 ] , prevContainer ) )
312+ urls . push (
313+ getLogsURL (
314+ appDetails ,
315+ _pwc [ 0 ] ,
316+ Host ,
317+ _pwc [ 1 ] ,
318+ prevContainer ,
319+ logsShownOption . current ,
320+ selectedCustomLogFilter ,
321+ ) ,
322+ )
249323 }
250324
251325 if ( urls . length == 0 ) {
252326 return
253327 }
254328 }
255-
256329 workerRef . current [ 'postMessage' as any ] ( {
257330 type : 'start' ,
258331 payload : {
@@ -262,6 +335,7 @@ function LogsComponent({
262335 pods : pods ,
263336 } ,
264337 } )
338+ setNewFilteredLogs ( false )
265339 }
266340
267341 const handleCurrentSearchTerm = ( searchTerm : string ) : void => {
@@ -284,6 +358,18 @@ function LogsComponent({
284358 }
285359 }
286360
361+ const handleLogOptionChange = ( selected ) => {
362+ setLogsShownOption ( {
363+ prev : logsShownOption . current ,
364+ current : selected ,
365+ } )
366+ if ( selected . value !== CUSTOM_LOGS_FILTER . CUSTOM ) {
367+ setNewFilteredLogs ( true )
368+ } else {
369+ setShowCustomOptionsMoadal ( true )
370+ }
371+ }
372+
287373 useEffect ( ( ) => {
288374 logsPausedRef . current = logsPaused
289375 } , [ logsPaused ] )
@@ -333,7 +419,7 @@ function LogsComponent({
333419 fetchLogs ( )
334420
335421 return ( ) => stopWorker ( )
336- } , [ logState , prevContainer ] )
422+ } , [ logState , prevContainer , newFilteredLogs ] )
337423
338424 const podContainerOptions = getPodContainerOptions (
339425 isLogAnalyzer ,
@@ -577,6 +663,40 @@ function LogsComponent({
577663 >
578664 < span className = "fs-12 " > Prev. container</ span >
579665 </ Checkbox >
666+ < div className = "h-16 dc__border-right ml-8 mr-8" > </ div >
667+ < LinesIcon className = "icon-dim-16 mr-8" />
668+ < Select
669+ options = { getPodLogsOptions ( ) }
670+ onChange = { handleLogOptionChange }
671+ value = { logsShownOption . current }
672+ styles = { {
673+ ...multiSelectStyles ,
674+ ...podsDropdownStyles ,
675+ } }
676+ components = { {
677+ IndicatorSeparator : null ,
678+ Option : ( props ) => < Option { ...props } /> ,
679+ } }
680+ />
681+ < div className = "h-16 dc__border-right ml-8 mr-8" > </ div >
682+ { downloadInProgress ? (
683+ < Progressing
684+ size = { 16 }
685+ styles = { { display : 'flex' , justifyContent : 'flex-start' , width : 'max-content' } }
686+ />
687+ ) : (
688+ < Tippy className = "default-tt" arrow = { false } placement = "top" content = { 'Download logs' } >
689+ < Download
690+ className = { `icon-dim-16 mr-8 cursor ${
691+ ( podContainerOptions ?. containerOptions ?? [ ] ) . length === 0 ||
692+ ( prevContainer && showNoPrevContainer != '' )
693+ ? 'cursor-not-allowed dc__opacity-0_5'
694+ : ''
695+ } `}
696+ onClick = { handleDownloadLogs }
697+ />
698+ </ Tippy >
699+ ) }
580700 </ div >
581701 < div className = "dc__border-right " > </ div >
582702 < form
@@ -721,6 +841,16 @@ function LogsComponent({
721841 />
722842 </ div >
723843 ) }
844+
845+ { showCustomOptionsModal && (
846+ < CustomLogsModal
847+ setSelectedCustomLogFilter = { setSelectedCustomLogFilter }
848+ selectedCustomLogFilter = { selectedCustomLogFilter }
849+ setLogsShownOption = { setLogsShownOption }
850+ setNewFilteredLogs = { setNewFilteredLogs }
851+ setShowCustomOptionsMoadal = { setShowCustomOptionsMoadal }
852+ />
853+ ) }
724854 </ React . Fragment >
725855 )
726856}
0 commit comments