@@ -4,19 +4,21 @@ import { ReactComponent as PlayButton } from '../../../../../../assets/icons/ic-
4
4
import { ReactComponent as StopButton } from '../../../../../../assets/icons/ic-stop-filled.svg'
5
5
import { ReactComponent as Search } from '../../../../../../assets/icons/ic-search.svg'
6
6
import { 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'
7
9
import { useParams , useRouteMatch , useLocation } from 'react-router'
8
10
import { NodeDetailTab } from '../nodeDetail.type'
9
- import { getLogsURL } from '../nodeDetail.api'
11
+ import { downloadLogs , getLogsURL } from '../nodeDetail.api'
10
12
import IndexStore from '../../../index.store'
11
13
import WebWorker from '../../../../../app/WebWorker'
12
14
import 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'
14
16
import { Subject } from '../../../../../../util/Subject'
15
17
import LogViewerComponent from './LogViewer.component'
16
18
import { useKeyDown } from '../../../../../common'
17
19
import { toast } from 'react-toastify'
18
20
import Select from 'react-select'
19
- import { multiSelectStyles } from '../../../../common/ReactSelectCustomization'
21
+ import { multiSelectStyles , podsDropdownStyles } from '../../../../common/ReactSelectCustomization'
20
22
import { LogsComponentProps , Options } from '../../../appDetails.type'
21
23
import { ReactComponent as Question } from '../../../../assets/icons/ic-question.svg'
22
24
import { ReactComponent as CloseImage } from '../../../../assets/icons/ic-cancelled.svg'
@@ -30,10 +32,15 @@ import {
30
32
getGroupedContainerOptions ,
31
33
getInitialPodContainerSelection ,
32
34
getPodContainerOptions ,
35
+ getPodLogsOptions ,
33
36
getSelectedPodList ,
34
37
} from '../nodeDetail.util'
35
38
import './nodeDetailTab.scss'
36
39
import ReactGA from 'react-ga4'
40
+ import { CUSTOM_LOGS_FILTER } from '../../../../../../config'
41
+ import { SelectedCustomLogFilterType } from './node.type'
42
+ import CustomLogsModal from './CustomLogsModal/CustomLogsModal'
43
+
37
44
38
45
const subject : Subject < string > = new Subject ( )
39
46
const commandLineParser = require ( 'command-line-parser' )
@@ -46,6 +53,15 @@ function LogsComponent({
46
53
isResourceBrowserView,
47
54
selectedResource,
48
55
} : 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
+ } )
49
65
const location = useLocation ( )
50
66
const { url } = useRouteMatch ( )
51
67
const params = useParams < {
@@ -72,6 +88,9 @@ function LogsComponent({
72
88
)
73
89
const [ prevContainer , setPrevContainer ] = useState ( false )
74
90
const [ showNoPrevContainer , setNoPrevContainer ] = useState ( '' )
91
+ const [ newFilteredLogs , setNewFilteredLogs ] = useState < boolean > ( false )
92
+ const [ showCustomOptionsModal , setShowCustomOptionsMoadal ] = useState ( false )
93
+ const [ downloadInProgress , setDownloadInProgress ] = useState ( false )
75
94
76
95
const getPrevContainerLogs = ( ) => {
77
96
setPrevContainer ( ! prevContainer )
@@ -203,6 +222,49 @@ function LogsComponent({
203
222
setTimeout ( ( ) => setLogsCleared ( false ) , 100 )
204
223
}
205
224
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
+
206
268
const fetchLogs = ( ) => {
207
269
if ( podContainerOptions . podOptions . length == 0 || podContainerOptions . containerOptions . length == 0 ) {
208
270
return
@@ -226,6 +288,8 @@ function LogsComponent({
226
288
Host ,
227
289
_co . name ,
228
290
prevContainer ,
291
+ logsShownOption . current ,
292
+ selectedCustomLogFilter ,
229
293
isResourceBrowserView ,
230
294
selectedResource . clusterId ,
231
295
selectedResource . namespace ,
@@ -245,14 +309,23 @@ function LogsComponent({
245
309
246
310
for ( const _pwc of podsWithContainers ) {
247
311
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
+ )
249
323
}
250
324
251
325
if ( urls . length == 0 ) {
252
326
return
253
327
}
254
328
}
255
-
256
329
workerRef . current [ 'postMessage' as any ] ( {
257
330
type : 'start' ,
258
331
payload : {
@@ -262,6 +335,7 @@ function LogsComponent({
262
335
pods : pods ,
263
336
} ,
264
337
} )
338
+ setNewFilteredLogs ( false )
265
339
}
266
340
267
341
const handleCurrentSearchTerm = ( searchTerm : string ) : void => {
@@ -284,6 +358,18 @@ function LogsComponent({
284
358
}
285
359
}
286
360
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
+
287
373
useEffect ( ( ) => {
288
374
logsPausedRef . current = logsPaused
289
375
} , [ logsPaused ] )
@@ -333,7 +419,7 @@ function LogsComponent({
333
419
fetchLogs ( )
334
420
335
421
return ( ) => stopWorker ( )
336
- } , [ logState , prevContainer ] )
422
+ } , [ logState , prevContainer , newFilteredLogs ] )
337
423
338
424
const podContainerOptions = getPodContainerOptions (
339
425
isLogAnalyzer ,
@@ -577,6 +663,40 @@ function LogsComponent({
577
663
>
578
664
< span className = "fs-12 " > Prev. container</ span >
579
665
</ 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
+ ) }
580
700
</ div >
581
701
< div className = "dc__border-right " > </ div >
582
702
< form
@@ -721,6 +841,16 @@ function LogsComponent({
721
841
/>
722
842
</ div >
723
843
) }
844
+
845
+ { showCustomOptionsModal && (
846
+ < CustomLogsModal
847
+ setSelectedCustomLogFilter = { setSelectedCustomLogFilter }
848
+ selectedCustomLogFilter = { selectedCustomLogFilter }
849
+ setLogsShownOption = { setLogsShownOption }
850
+ setNewFilteredLogs = { setNewFilteredLogs }
851
+ setShowCustomOptionsMoadal = { setShowCustomOptionsMoadal }
852
+ />
853
+ ) }
724
854
</ React . Fragment >
725
855
)
726
856
}
0 commit comments