@@ -30,8 +30,9 @@ import org.apache.spark.ui._
30
30
import org .apache .spark .ui .UIUtils ._
31
31
32
32
/**
33
- * @param divId the `id` used in the html `div` tag
34
- * @param data the data for the timeline graph
33
+ * @param timelineDivId the timeline `id` used in the html `div` tag
34
+ * @param histogramDivId the timeline `id` used in the html `div` tag
35
+ * @param data the data for the graph
35
36
* @param minX the min value of X axis
36
37
* @param maxX the max value of X axis
37
38
* @param minY the min value of Y axis
@@ -40,52 +41,56 @@ import org.apache.spark.ui.UIUtils._
40
41
* @param batchInterval if `batchInterval` is not None, we will draw a line for `batchInterval` in
41
42
* the graph
42
43
*/
43
- private [ui] class TimelineUIData (divId : String , data : Seq [(Long , _)], minX : Long , maxX : Long ,
44
- minY : Double , maxY : Double , unitY : String , batchInterval : Option [Double ] = None ) {
44
+ private [ui] class GraphUIData (
45
+ timelineDivId : String ,
46
+ histogramDivId : String ,
47
+ data : Seq [(Long , Double )],
48
+ minX : Long ,
49
+ maxX : Long ,
50
+ minY : Double ,
51
+ maxY : Double ,
52
+ unitY : String ,
53
+ batchInterval : Option [Double ] = None ) {
54
+
55
+ private var dataJavaScriptName : String = _
45
56
46
- def toHtml (jsCollector : JsCollector ): Seq [ Node ] = {
57
+ def generateDataJs (jsCollector : JsCollector ): Unit = {
47
58
val jsForData = data.map { case (x, y) =>
48
59
s """ {"x": $x, "y": $y} """
49
60
}.mkString(" [" , " ," , " ]" )
61
+ dataJavaScriptName = jsCollector.nextVariableName
62
+ jsCollector.addPreparedStatement(s " var $dataJavaScriptName = $jsForData; " )
63
+ }
64
+
65
+ def generateTimelineHtml (jsCollector : JsCollector ): Seq [Node ] = {
50
66
jsCollector.addPreparedStatement(s " registerTimeline( $minY, $maxY); " )
51
67
if (batchInterval.isDefined) {
52
68
jsCollector.addStatement(
53
69
" drawTimeline(" +
54
- s " '# $divId', $jsForData, $minX, $maxX, $minY, $maxY, ' $unitY', ${batchInterval.get}" +
70
+ s " '# $timelineDivId', $dataJavaScriptName, $minX, $maxX, $minY, $maxY, ' $unitY', " +
71
+ s " ${batchInterval.get}" +
55
72
" );" )
56
73
} else {
57
74
jsCollector.addStatement(
58
- s " drawTimeline('# $divId', $jsForData, $minX, $maxX, $minY, $maxY, ' $unitY'); " )
75
+ s " drawTimeline('# $timelineDivId', $dataJavaScriptName, $minX, $maxX, $minY, $maxY, " +
76
+ s " ' $unitY'); " )
59
77
}
60
- <div id ={divId }></div >
78
+ <div id ={timelineDivId }></div >
61
79
}
62
- }
63
80
64
- /**
65
- * @param divId the `id` used in the html `div` tag
66
- * @param data the data for the histogram graph
67
- * @param minY the min value of Y axis
68
- * @param maxY the max value of Y axis
69
- * @param unitY the unit of Y axis
70
- * @param batchInterval if `batchInterval` is not None, we will draw a line for `batchInterval` in
71
- * the graph
72
- */
73
- private [ui] class HistogramUIData (
74
- divId : String , data : Seq [_], minY : Double , maxY : Double , unitY : String ,
75
- batchInterval : Option [Double ] = None ) {
76
-
77
- def toHtml (jsCollector : JsCollector ): Seq [Node ] = {
78
- val jsForData = data.mkString(" [" , " ," , " ]" )
79
- jsCollector.addPreparedStatement(s " registerHistogram( $jsForData, $minY, $maxY); " )
81
+ def generateHistogramHtml (jsCollector : JsCollector ): Seq [Node ] = {
82
+ val histogramData = s " $dataJavaScriptName.map(function(d) { return d.y; }) "
83
+ jsCollector.addPreparedStatement(s " registerHistogram( $histogramData, $minY, $maxY); " )
80
84
if (batchInterval.isDefined) {
81
85
jsCollector.addStatement(
82
86
" drawHistogram(" +
83
- s " '# $divId ', $jsForData , $minY, $maxY, ' $unitY', ${batchInterval.get}" +
87
+ s " '# $histogramDivId ', $histogramData , $minY, $maxY, ' $unitY', ${batchInterval.get}" +
84
88
" );" )
85
89
} else {
86
- jsCollector.addStatement(s " drawHistogram('# $divId', $jsForData, $minY, $maxY, ' $unitY'); " )
90
+ jsCollector.addStatement(
91
+ s " drawHistogram('# $histogramDivId', $histogramData, $minY, $maxY, ' $unitY'); " )
87
92
}
88
- <div id ={divId }></div >
93
+ <div id ={histogramDivId }></div >
89
94
}
90
95
}
91
96
@@ -246,77 +251,53 @@ private[ui] class StreamingPage(parent: StreamingTab)
246
251
247
252
val jsCollector = new JsCollector
248
253
249
- val timelineDataForEventRateOfAllReceivers =
250
- new TimelineUIData (
254
+ val graphUIDataForEventRateOfAllReceivers =
255
+ new GraphUIData (
251
256
" all-receiver-events-timeline" ,
257
+ " all-receiver-events-histogram" ,
252
258
eventRateForAllReceivers.data,
253
259
minBatchTime,
254
260
maxBatchTime,
255
261
minEventRate,
256
262
maxEventRate,
257
- " events/sec" ).toHtml(jsCollector)
263
+ " events/sec" )
264
+ graphUIDataForEventRateOfAllReceivers.generateDataJs(jsCollector)
258
265
259
- val histogramDataForEventRateOfAllReceivers =
260
- new HistogramUIData (
261
- " all-receiver-events-histogram" ,
262
- eventRateForAllReceivers.data.map(_._2),
263
- minEventRate,
264
- maxEventRate,
265
- " events/sec" ).toHtml(jsCollector)
266
-
267
- val timelineDataForSchedulingDelay =
268
- new TimelineUIData (
266
+ val graphUIDataForSchedulingDelay =
267
+ new GraphUIData (
269
268
" scheduling-delay-timeline" ,
269
+ " scheduling-delay-histogram" ,
270
270
schedulingDelay.timelineData(normalizedUnit),
271
271
minBatchTime,
272
272
maxBatchTime,
273
273
minTime,
274
274
maxTime,
275
- formattedUnit).toHtml(jsCollector)
276
-
277
- val histogramDataForSchedulingDelay =
278
- new HistogramUIData (
279
- " scheduling-delay-histogram" ,
280
- schedulingDelay.histogramData(normalizedUnit),
281
- minTime,
282
- maxTime,
283
- formattedUnit).toHtml(jsCollector)
275
+ formattedUnit)
276
+ graphUIDataForSchedulingDelay.generateDataJs(jsCollector)
284
277
285
- val timelineDataForProcessingTime =
286
- new TimelineUIData (
278
+ val graphUIDataForProcessingTime =
279
+ new GraphUIData (
287
280
" processing-time-timeline" ,
281
+ " processing-time-histogram" ,
288
282
processingTime.timelineData(normalizedUnit),
289
283
minBatchTime,
290
284
maxBatchTime,
291
285
minTime,
292
286
maxTime,
293
- formattedUnit, Some (batchInterval)).toHtml(jsCollector)
294
-
295
- val histogramDataForProcessingTime =
296
- new HistogramUIData (
297
- " processing-time-histogram" ,
298
- processingTime.histogramData(normalizedUnit),
299
- minTime,
300
- maxTime,
301
- formattedUnit, Some (batchInterval)).toHtml(jsCollector)
287
+ formattedUnit, Some (batchInterval))
288
+ graphUIDataForProcessingTime.generateDataJs(jsCollector)
302
289
303
- val timelineDataForTotalDelay =
304
- new TimelineUIData (
290
+ val graphUIDataForTotalDelay =
291
+ new GraphUIData (
305
292
" total-delay-timeline" ,
293
+ " total-delay-histogram" ,
306
294
totalDelay.timelineData(normalizedUnit),
307
295
minBatchTime,
308
296
maxBatchTime,
309
297
minTime,
310
298
maxTime,
311
- formattedUnit).toHtml(jsCollector)
312
-
313
- val histogramDataForTotalDelay =
314
- new HistogramUIData (
315
- " total-delay-histogram" ,
316
- totalDelay.histogramData(normalizedUnit),
317
- minTime,
318
- maxTime,
319
- formattedUnit).toHtml(jsCollector)
299
+ formattedUnit)
300
+ graphUIDataForTotalDelay.generateDataJs(jsCollector)
320
301
321
302
val hasReceiver = listener.allReceivers.nonEmpty
322
303
@@ -344,8 +325,8 @@ private[ui] class StreamingPage(parent: StreamingTab)
344
325
<div >Avg : {eventRateForAllReceivers.formattedAvg} events/ sec</div >
345
326
</div >
346
327
</td >
347
- <td class =" timeline" >{timelineDataForEventRateOfAllReceivers }</td >
348
- <td class =" histogram" >{histogramDataForEventRateOfAllReceivers }</td >
328
+ <td class =" timeline" >{graphUIDataForEventRateOfAllReceivers.generateTimelineHtml(jsCollector) }</td >
329
+ <td class =" histogram" >{graphUIDataForEventRateOfAllReceivers.generateHistogramHtml(jsCollector) }</td >
349
330
</tr >
350
331
{if (hasReceiver) {
351
332
<tr id =" inputs-table" style =" display: none;" >
@@ -361,8 +342,8 @@ private[ui] class StreamingPage(parent: StreamingTab)
361
342
<div >Avg : {schedulingDelay.formattedAvg}</div >
362
343
</div >
363
344
</td >
364
- <td class =" timeline" >{timelineDataForSchedulingDelay }</td >
365
- <td class =" histogram" >{histogramDataForSchedulingDelay }</td >
345
+ <td class =" timeline" >{graphUIDataForSchedulingDelay.generateTimelineHtml(jsCollector) }</td >
346
+ <td class =" histogram" >{graphUIDataForSchedulingDelay.generateHistogramHtml(jsCollector) }</td >
366
347
</tr >
367
348
<tr >
368
349
<td style =" vertical-align: middle;" >
@@ -371,8 +352,8 @@ private[ui] class StreamingPage(parent: StreamingTab)
371
352
<div >Avg : {processingTime.formattedAvg}</div >
372
353
</div >
373
354
</td >
374
- <td class =" timeline" >{timelineDataForProcessingTime }</td >
375
- <td class =" histogram" >{histogramDataForProcessingTime }</td >
355
+ <td class =" timeline" >{graphUIDataForProcessingTime.generateTimelineHtml(jsCollector) }</td >
356
+ <td class =" histogram" >{graphUIDataForProcessingTime.generateHistogramHtml(jsCollector) }</td >
376
357
</tr >
377
358
<tr >
378
359
<td style =" vertical-align: middle;" >
@@ -381,8 +362,8 @@ private[ui] class StreamingPage(parent: StreamingTab)
381
362
<div >Avg : {totalDelay.formattedAvg}</div >
382
363
</div >
383
364
</td >
384
- <td class =" timeline" >{timelineDataForTotalDelay }</td >
385
- <td class =" histogram" >{histogramDataForTotalDelay }</td >
365
+ <td class =" timeline" >{graphUIDataForTotalDelay.generateTimelineHtml(jsCollector) }</td >
366
+ <td class =" histogram" >{graphUIDataForTotalDelay.generateHistogramHtml(jsCollector) }</td >
386
367
</tr >
387
368
</tbody >
388
369
</table >
@@ -442,23 +423,17 @@ private[ui] class StreamingPage(parent: StreamingTab)
442
423
val receivedRecords =
443
424
new EventRateUIData (listener.receivedEventRateWithBatchTime.get(receiverId).getOrElse(Seq ()))
444
425
445
- val timelineForEventRate =
446
- new TimelineUIData (
426
+ val graphUIDataForEventRate =
427
+ new GraphUIData (
447
428
s " receiver- $receiverId-events-timeline " ,
429
+ s " receiver- $receiverId-events-histogram " ,
448
430
receivedRecords.data,
449
431
minX,
450
432
maxX,
451
433
minY,
452
434
maxY,
453
- " events/sec" ).toHtml(jsCollector)
454
-
455
- val histogramForEventsRate =
456
- new HistogramUIData (
457
- s " receiver- $receiverId-events-histogram " ,
458
- receivedRecords.data.map(_._2),
459
- minY,
460
- maxY,
461
- " events/sec" ).toHtml(jsCollector)
435
+ " events/sec" )
436
+ graphUIDataForEventRate.generateDataJs(jsCollector)
462
437
463
438
<tr >
464
439
<td rowspan =" 2" style =" vertical-align: middle; width: 151px;" >
@@ -474,9 +449,9 @@ private[ui] class StreamingPage(parent: StreamingTab)
474
449
</tr >
475
450
<tr >
476
451
<td colspan =" 3" class =" timeline" >
477
- {timelineForEventRate }
452
+ {graphUIDataForEventRate.generateTimelineHtml(jsCollector) }
478
453
</td >
479
- <td class =" histogram" >{histogramForEventsRate }</td >
454
+ <td class =" histogram" >{graphUIDataForEventRate.generateHistogramHtml(jsCollector) }</td >
480
455
</tr >
481
456
}
482
457
@@ -535,6 +510,17 @@ private[ui] object StreamingPage {
535
510
* DOM has finished loading.
536
511
*/
537
512
private [ui] class JsCollector {
513
+
514
+ private var variableId = 0
515
+
516
+ /**
517
+ * Return the next unused JavaScript variable name
518
+ */
519
+ def nextVariableName : String = {
520
+ variableId += 1
521
+ " v" + variableId
522
+ }
523
+
538
524
/**
539
525
* JavaScript statements that will execute before `statements`
540
526
*/
0 commit comments