1212 *******************************************************************************/
1313package io .openliberty .microprofile .metrics .internal .monitor .computed .internal ;
1414
15+ import java .lang .management .GarbageCollectorMXBean ;
16+ import java .lang .management .ManagementFactory ;
17+ import java .time .Duration ;
1518import java .util .HashSet ;
1619import java .util .List ;
1720import java .util .Map ;
@@ -138,16 +141,14 @@ public void createComputedBaseMetrics() {
138141
139142 // Add the duration metrics to the computation set, no appName for base metrics.
140143 cmm = new ComputedMonitorMetrics (
141- MetricRegistry .BASE_SCOPE , mid ,
142- Constants .DURATION , null );
144+ MetricRegistry .BASE_SCOPE , mid , Constants .DURATION , null );
143145 computedMonitorMetricsSet .add (cmm );
144146
145147 // Add the total count metric to the computation set, no appName for base metrics.
146148 MetricID totalCountMetricID = new MetricID (
147149 metricTotalCountName , metricTagNames );
148150 cmm = new ComputedMonitorMetrics (
149- MetricRegistry .BASE_SCOPE , totalCountMetricID ,
150- Constants .TOTAL , null );
151+ MetricRegistry .BASE_SCOPE , totalCountMetricID , Constants .TOTAL , null );
151152 computedMonitorMetricsSet .add (cmm );
152153
153154 // Once the existing metrics needed for calculation are cached, register new computed metric in the Metric Registry.
@@ -385,8 +386,8 @@ public Double getComputedValue(MetricID metricId) {
385386 }
386387
387388 public void calculateMetricValue () {
388- double diffDuration = 0.0 , diffTotalCount = 0.0 ;
389389 MetricRegistry mr ;
390+ Double metricVal = null , diffDuration = null , diffTotalCount = null ;
390391
391392 for (Map .Entry <MetricID , Set <ComputedMonitorMetrics >> entry : computationMetricsMap .entrySet ()) {
392393 MetricID computedMetricID = entry .getKey ();
@@ -397,7 +398,6 @@ public void calculateMetricValue() {
397398 }
398399
399400 String computedMetricName = computedMetricID .getName ();
400-
401401 if (computedMetricName .equals ("memory.heapUtilization" )) {
402402 // Do not need to use the EWMA for the heap utilization calculation
403403 // memory utilization = usedHeap / maxHeap;
@@ -406,21 +406,64 @@ public void calculateMetricValue() {
406406 // Do not need to use the EWMA for the cpu utilization calculation
407407 // Can get it directly from com.ibm.ws.kernel.service.util.CpuInfo.getJavaCpuUsage()
408408 calculateProcessCpuUsage (computedMetricID );
409+ } else if (computedMetricName .equals ("gc.time.per.cycle" )) {
410+ // Need to retrieve the gc.time and gc.total from the GarbageCollectionMXBean directly,
411+ // instead of the mpMetrics-5.x API, since there is a known bug, where the gc.time from
412+ // the mpMetrics-5.x API returns as a Counter, which drops the decimal in the returned float value,
413+ // making the value not useful for computation.
414+ calculateEWMAValueForGC (computedMetricID , monitorMetrics );
409415 } else {
410416 for (ComputedMonitorMetrics cmm : monitorMetrics ) {
411417 mr = getMetricRegistry (cmm .getMonitorMetricScope ());
412-
418+ // Get the metric value.
419+ metricVal = getMetricValue (mr , cmm );
420+ if (metricVal != null ) {
421+ if (cmm .getComputationType ().equals (Constants .DURATION )) {
422+ diffDuration = cmm .getDifference (metricVal .doubleValue ());
423+ } else if (cmm .getComputationType ().equals (Constants .TOTAL )) {
424+ diffTotalCount = cmm .getDifference (metricVal .doubleValue ());
425+ }
426+ }
427+ }
428+ if (diffDuration != null && diffTotalCount != null ) {
429+ // Only compute the EWMA if we are able to retrieve the metrics for both Duration and Total count.
430+ calculateEWMAValue (computedMetricID , diffDuration , diffTotalCount );
431+ }
432+ }
433+ }
434+ }
435+
436+ private void calculateEWMAValueForGC (MetricID computedMetricID , Set <ComputedMonitorMetrics > monitorMetrics ) {
437+ double currValue = 0.0 , diffDuration = 0.0 , diffTotalCount = 0.0 ;
438+
439+ // Get the collection of Garbage Collection MXBeans
440+ List <GarbageCollectorMXBean > gcMXBeansList = ManagementFactory .getGarbageCollectorMXBeans ();
441+
442+ for (GarbageCollectorMXBean gcMXBean : gcMXBeansList ) {
443+ for (ComputedMonitorMetrics cmm : monitorMetrics ) {
444+ String gcName = gcMXBean .getName ();
445+ String gcMetricsName = cmm .getMonitorMetricID ().getTags ().get ("name" );
446+ if (gcName .equals (gcMetricsName )) {
413447 if (cmm .getComputationType ().equals (Constants .DURATION )) {
414- diffDuration = getMetricValueDifference (mr , cmm );
448+ currValue = gcMXBean .getCollectionTime () * Constants .MILLISECONDCONVERSION ;
449+ if (TraceComponent .isAnyTracingEnabled () && tc .isDebugEnabled ()) {
450+ Tr .debug (tc , "GarbageCollectionTime for + " + computedMetricID + " = " + currValue );
451+ }
452+ diffDuration = currValue < 0 ? -1.0 : cmm .getDifference (currValue );
415453 } else if (cmm .getComputationType ().equals (Constants .TOTAL )) {
416- diffTotalCount = getMetricValueDifference (mr , cmm );
454+ currValue = gcMXBean .getCollectionCount ();
455+ if (TraceComponent .isAnyTracingEnabled () && tc .isDebugEnabled ()) {
456+ Tr .debug (tc , "GarbageCollectionCount for + " + computedMetricID + " = " + currValue );
457+ }
458+ diffTotalCount = currValue < 0 ? -1.0 : cmm .getDifference (currValue );
417459 }
418460 }
419- calculateEWMAValue (computedMetricID , diffDuration , diffTotalCount );
420461 }
462+ calculateEWMAValue (computedMetricID , diffDuration , diffTotalCount );
421463 }
422464 }
423465
466+
424467 public void calculateHeapUsage (MetricID computedMetricID , Set <ComputedMonitorMetrics > monitorMetrics ) {
425468 double usedHeap = 0.0 , maxHeap = 0.0 , heapUsage = 0.0 ;
426469 MetricRegistry mr = getMetricRegistry (MetricRegistry .BASE_SCOPE );
@@ -459,31 +502,37 @@ public void calculateProcessCpuUsage(MetricID computedMetricID) {
459502 finalComputedMetricsMap .put (computedMetricID , processCpuUsage );
460503 }
461504
462- public double getMetricValueDifference (MetricRegistry mr , ComputedMonitorMetrics cmm ) {
463- double currentValue = 0.0 , diff = 0.0 ;
505+ public Double getMetricValue (MetricRegistry mr , ComputedMonitorMetrics cmm ) {
506+ Number metricNum = null ;
507+ Double currentValue = null ;
464508
465509 Metric metricValue = mr .getMetric (cmm .getMonitorMetricID ());
466510 if (metricValue instanceof Gauge ) {
467511 Gauge <?> currentGauge = (Gauge <?>) metricValue ;
468- currentValue = currentGauge .getValue (). doubleValue ();
512+ metricNum = currentGauge .getValue ();
469513 } else if (metricValue instanceof Counter ) {
470514 Counter currentCount = (Counter ) metricValue ;
471- Long currentLongValue = currentCount .getCount ();
472- currentValue = currentLongValue .doubleValue ();
515+ metricNum = currentCount .getCount ();
473516 } else if (metricValue instanceof org .eclipse .microprofile .metrics .Timer ) {
474517 org .eclipse .microprofile .metrics .Timer currentTimer = (org .eclipse .microprofile .metrics .Timer ) metricValue ;
475518 if (cmm .getComputationType ().equals (Constants .DURATION )) {
476- Integer currentIntValueInNanos = currentTimer .getElapsedTime ()
477- .getNano ();
478- currentValue = (currentIntValueInNanos .doubleValue ()) * Constants .NANOSECONDCONVERSION ;
519+ Duration currentDur = currentTimer .getElapsedTime ();
520+ if (currentDur != null ) {
521+ metricNum = currentDur .getNano ();
522+ currentValue = (metricNum .doubleValue ()) * Constants .NANOSECONDCONVERSION ; // to seconds.
523+ }
479524 } else {
480525 // Get Total Counter Value for Timer
481- Long currentLongValue = currentTimer .getCount ();
482- currentValue = currentLongValue .doubleValue ();
526+ metricNum = currentTimer .getCount ();
483527 }
484528 }
485- diff = cmm .getDifference (currentValue );
486- return diff ;
529+
530+ if (metricNum != null && currentValue == null ) {
531+ // If the metricValue is present and the currentValue is not already set.
532+ currentValue = metricNum .doubleValue ();
533+ }
534+
535+ return currentValue ;
487536 }
488537
489538 public MetricRegistry getMetricRegistry (String scope ) {
@@ -495,7 +544,9 @@ public void calculateEWMAValue(MetricID computedMetricID, double duration, doubl
495544 double computedVal = 0.0 ;
496545
497546 // Calculate the new computed metric.
498- computedVal = duration / totalCount ;
547+ // If the duration or the totalCount is a negative value, set the computedValue to be -1.0
548+ // Should only happen when calculating the gc.time.per.cycle.
549+ computedVal = ((duration < 0.0 ) || (totalCount < 0.0 )) ? -1.0 : (duration / totalCount );
499550
500551 // Only the computed metricIDs that require EWMA will be passed into this method.
501552 EWMA ewmaObj = (EWMA ) finalComputedMetricsMap .get (computedMetricID );
@@ -505,16 +556,13 @@ public void calculateEWMAValue(MetricID computedMetricID, double duration, doubl
505556 double alpha = calculateAlpha (EWMA_MOVING_WINDOW_INTERVAL ); // 5 min moving window
506557 ewmaObj = new EWMA (alpha );
507558
508- // EWMA[0] will be equal directly to the initially calculated frequency
509- double initialValue = (duration == 0.0 || totalCount == 0.0 )
510- ? 0.0
511- : computedVal ;
559+ // EWMA[0] will be equal directly to the initially calculated value.
560+ double initialValue = (duration == 0.0 || totalCount == 0.0 ) ? 0.0 : computedVal ;
512561 ewmaObj .updateNewValue (initialValue );
513562 } else {
514563 if ((duration == 0.0 || totalCount == 0.0 )) {
515564 // If nothing changed during the current sampling period, get the previously calculated EWMA value and feed it into it again.
516- if (TraceComponent .isAnyTracingEnabled ()
517- && tc .isDebugEnabled ()) {
565+ if (TraceComponent .isAnyTracingEnabled () && tc .isDebugEnabled ()) {
518566 Tr .debug (tc , "Idling - There were no new data in the sampling period, getting the previously calculated EWMA value to feed into it again." );
519567 }
520568 ewmaObj .updateNewValue (ewmaObj .getAveragedValue ());
0 commit comments