|
@@ -20,7 +20,6 @@
|
|
|
|
|
|
package password.pwm.svc.stats;
|
|
|
|
|
|
-import org.apache.commons.csv.CSVPrinter;
|
|
|
import password.pwm.PwmApplication;
|
|
|
import password.pwm.PwmConstants;
|
|
|
import password.pwm.bean.DomainID;
|
|
@@ -33,25 +32,21 @@ import password.pwm.util.DailySummaryJob;
|
|
|
import password.pwm.util.EventRateMeter;
|
|
|
import password.pwm.util.java.CollectorUtil;
|
|
|
import password.pwm.util.java.EnumUtil;
|
|
|
-import password.pwm.util.java.PwmUtil;
|
|
|
import password.pwm.util.java.StringUtil;
|
|
|
import password.pwm.util.java.TimeDuration;
|
|
|
import password.pwm.util.localdb.LocalDB;
|
|
|
import password.pwm.util.localdb.LocalDBException;
|
|
|
import password.pwm.util.logging.PwmLogger;
|
|
|
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.OutputStream;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.time.Instant;
|
|
|
-import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
-import java.util.Locale;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.Optional;
|
|
|
+import java.util.SortedSet;
|
|
|
import java.util.TimerTask;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
@@ -69,28 +64,16 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
|
|
|
private static final String DB_VALUE_VERSION = "1";
|
|
|
|
|
|
- public static final String KEY_CURRENT = "CURRENT";
|
|
|
- public static final String KEY_CUMULATIVE = "CUMULATIVE";
|
|
|
-
|
|
|
private LocalDB localDB;
|
|
|
|
|
|
- private DailyKey currentDailyKey = DailyKey.forToday();
|
|
|
- private DailyKey initialDailyKey = DailyKey.forToday();
|
|
|
+ private StatisticsBundleKey currentDailyKey = StatisticsBundleKey.forToday();
|
|
|
+ private StatisticsBundleKey initialDailyKey = StatisticsBundleKey.forToday();
|
|
|
|
|
|
private final StatisticsBundle statsCurrent = new StatisticsBundle();
|
|
|
private final Map<EpsKey, EventRateMeter> epsMeterMap;
|
|
|
private StatisticsBundle statsDaily = new StatisticsBundle();
|
|
|
- private StatisticsBundle statsCummulative = new StatisticsBundle();
|
|
|
-
|
|
|
+ private StatisticsBundle statsCumulative = new StatisticsBundle();
|
|
|
|
|
|
- private final Map<String, StatisticsBundle> cachedStoredStats = new LinkedHashMap<>()
|
|
|
- {
|
|
|
- @Override
|
|
|
- protected boolean removeEldestEntry( final Map.Entry<String, StatisticsBundle> eldest )
|
|
|
- {
|
|
|
- return this.size() > 50;
|
|
|
- }
|
|
|
- };
|
|
|
|
|
|
public StatisticsService( )
|
|
|
{
|
|
@@ -105,99 +88,96 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
{
|
|
|
statsCurrent.incrementValue( statistic );
|
|
|
statsDaily.incrementValue( statistic );
|
|
|
- statsCummulative.incrementValue( statistic );
|
|
|
+ statsCumulative.incrementValue( statistic );
|
|
|
}
|
|
|
|
|
|
public void updateAverageValue( final AvgStatistic statistic, final long value )
|
|
|
{
|
|
|
statsCurrent.updateAverageValue( statistic, value );
|
|
|
statsDaily.updateAverageValue( statistic, value );
|
|
|
- statsCummulative.updateAverageValue( statistic, value );
|
|
|
+ statsCumulative.updateAverageValue( statistic, value );
|
|
|
}
|
|
|
|
|
|
- public Map<String, String> getStatHistory( final Statistic statistic, final int days )
|
|
|
+ public Map<StatisticsBundleKey, String> getStatHistory( final Statistic statistic, final int days )
|
|
|
{
|
|
|
- final Map<String, String> returnMap = new LinkedHashMap<>();
|
|
|
- DailyKey loopKey = currentDailyKey;
|
|
|
+ final Map<StatisticsBundleKey, String> returnMap = new LinkedHashMap<>();
|
|
|
+ StatisticsBundleKey loopKey = currentDailyKey;
|
|
|
int counter = days;
|
|
|
while ( counter > 0 )
|
|
|
{
|
|
|
- final StatisticsBundle bundle = getStatBundleForKey( loopKey.toString() );
|
|
|
- if ( bundle != null )
|
|
|
+ final StatisticsBundleKey finalKey = loopKey;
|
|
|
+ getStatBundleForKey( loopKey ).ifPresent( bundle ->
|
|
|
{
|
|
|
- final String key = loopKey.toString();
|
|
|
final String value = bundle.getStatistic( statistic );
|
|
|
- returnMap.put( key, value );
|
|
|
- }
|
|
|
+ returnMap.put( finalKey, value );
|
|
|
+ } );
|
|
|
+
|
|
|
loopKey = loopKey.previous();
|
|
|
counter--;
|
|
|
}
|
|
|
return returnMap;
|
|
|
}
|
|
|
|
|
|
- public StatisticsBundle getStatBundleForKey( final String key )
|
|
|
+ public StatisticsBundle getCumulativeBundle()
|
|
|
{
|
|
|
- if ( key == null || key.length() < 1 || KEY_CUMULATIVE.equals( key ) )
|
|
|
- {
|
|
|
- return statsCummulative;
|
|
|
- }
|
|
|
+ return getStatBundleForKey( StatisticsBundleKey.CUMULATIVE ).orElseThrow();
|
|
|
+ }
|
|
|
|
|
|
- if ( KEY_CURRENT.equals( key ) )
|
|
|
+ public StatisticsBundle getCurrentBundle()
|
|
|
+ {
|
|
|
+ return getStatBundleForKey( StatisticsBundleKey.CURRENT ).orElseThrow();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Optional<StatisticsBundle> getStatBundleForKey( final StatisticsBundleKey key )
|
|
|
+ {
|
|
|
+ Objects.requireNonNull( key );
|
|
|
+
|
|
|
+ if ( StatisticsBundleKey.CUMULATIVE == key )
|
|
|
{
|
|
|
- return statsCurrent;
|
|
|
+ return Optional.of( statsCumulative );
|
|
|
}
|
|
|
|
|
|
- if ( Objects.equals( currentDailyKey.toString(), key ) )
|
|
|
+ if ( StatisticsBundleKey.CURRENT == key )
|
|
|
{
|
|
|
- return statsDaily;
|
|
|
+ return Optional.of( statsCurrent );
|
|
|
}
|
|
|
|
|
|
- if ( cachedStoredStats.containsKey( key ) )
|
|
|
+ if ( Objects.equals( currentDailyKey, key ) )
|
|
|
{
|
|
|
- return cachedStoredStats.get( key );
|
|
|
+ return Optional.of( statsDaily );
|
|
|
}
|
|
|
|
|
|
if ( localDB == null )
|
|
|
{
|
|
|
- return null;
|
|
|
+ return Optional.empty();
|
|
|
}
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- final Optional<String> storedStat = localDB.get( LocalDB.DB.PWM_STATS, key );
|
|
|
- final StatisticsBundle returnBundle;
|
|
|
- returnBundle = storedStat.map( StatisticsBundle::input ).orElseGet( StatisticsBundle::new );
|
|
|
- cachedStoredStats.put( key, returnBundle );
|
|
|
- return returnBundle;
|
|
|
+ final Optional<String> storedStat = localDB.get( LocalDB.DB.PWM_STATS, key.toString() );
|
|
|
+ return storedStat.map( StatisticsBundle::input );
|
|
|
}
|
|
|
catch ( final LocalDBException e )
|
|
|
{
|
|
|
LOGGER.error( () -> "error retrieving stored stat for " + key + ": " + e.getMessage() );
|
|
|
}
|
|
|
|
|
|
- return null;
|
|
|
+ return Optional.empty();
|
|
|
}
|
|
|
|
|
|
- public Map<DailyKey, String> getAvailableKeys( final Locale locale )
|
|
|
+ StatisticsBundleKey getCurrentDailyKey()
|
|
|
{
|
|
|
- final Map<DailyKey, String> returnMap = new LinkedHashMap<>();
|
|
|
+ return currentDailyKey;
|
|
|
+ }
|
|
|
|
|
|
- // if no historical data then we're done
|
|
|
- if ( currentDailyKey.equals( initialDailyKey ) )
|
|
|
- {
|
|
|
- return returnMap;
|
|
|
- }
|
|
|
+ StatisticsBundleKey getInitialDailyKey()
|
|
|
+ {
|
|
|
+ return initialDailyKey;
|
|
|
+ }
|
|
|
|
|
|
- DailyKey loopKey = currentDailyKey;
|
|
|
- int safetyCounter = 0;
|
|
|
- while ( !loopKey.equals( initialDailyKey ) && safetyCounter < 5000 )
|
|
|
- {
|
|
|
- final String display = loopKey.toString();
|
|
|
- returnMap.put( loopKey, display );
|
|
|
- loopKey = loopKey.previous();
|
|
|
- safetyCounter++;
|
|
|
- }
|
|
|
- return returnMap;
|
|
|
+ public SortedSet<StatisticsBundleKey> allKeys()
|
|
|
+ {
|
|
|
+ return StatisticsUtils.allKeys( this );
|
|
|
}
|
|
|
|
|
|
public String toString( )
|
|
@@ -238,7 +218,7 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- statsCummulative = StatisticsBundle.input( storedCumulativeBundleSir.get() );
|
|
|
+ statsCumulative = StatisticsBundle.input( storedCumulativeBundleSir.get() );
|
|
|
}
|
|
|
catch ( final Exception e )
|
|
|
{
|
|
@@ -249,11 +229,11 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
|
|
|
{
|
|
|
final Optional<String> storedInitialString = localDB.get( LocalDB.DB.PWM_STATS, DB_KEY_INITIAL_DAILY_KEY );
|
|
|
- storedInitialString.ifPresent( s -> initialDailyKey = new DailyKey( s ) );
|
|
|
+ storedInitialString.ifPresent( s -> initialDailyKey = StatisticsBundleKey.fromString( s ) );
|
|
|
}
|
|
|
|
|
|
{
|
|
|
- currentDailyKey = DailyKey.forToday();
|
|
|
+ currentDailyKey = StatisticsBundleKey.forToday();
|
|
|
final Optional<String> storedDailyStr = localDB.get( LocalDB.DB.PWM_STATS, currentDailyKey.toString() );
|
|
|
storedDailyStr.ifPresent( s -> statsDaily = StatisticsBundle.input( s ) );
|
|
|
}
|
|
@@ -288,7 +268,7 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
try
|
|
|
{
|
|
|
final Map<String, String> dbData = new LinkedHashMap<>();
|
|
|
- dbData.put( DB_KEY_CUMULATIVE, statsCummulative.output() );
|
|
|
+ dbData.put( DB_KEY_CUMULATIVE, statsCumulative.output() );
|
|
|
dbData.put( currentDailyKey.toString(), statsDaily.output() );
|
|
|
localDB.putAll( LocalDB.DB.PWM_STATS, dbData );
|
|
|
}
|
|
@@ -309,7 +289,7 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
|
|
|
private void resetDailyStats( )
|
|
|
{
|
|
|
- currentDailyKey = DailyKey.forToday();
|
|
|
+ currentDailyKey = StatisticsBundleKey.forToday();
|
|
|
statsDaily = new StatisticsBundle();
|
|
|
LOGGER.debug( () -> "reset daily statistics" );
|
|
|
}
|
|
@@ -369,58 +349,6 @@ public class StatisticsService extends AbstractPwmService implements PwmService
|
|
|
return epsMeterMap.get( epsKey ).rawEps();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- public int outputStatsToCsv( final OutputStream outputStream, final Locale locale, final boolean includeHeader )
|
|
|
- throws IOException
|
|
|
- {
|
|
|
- LOGGER.trace( () -> "beginning output stats to csv process" );
|
|
|
- final Instant startTime = Instant.now();
|
|
|
-
|
|
|
- final StatisticsService statsManger = getPwmApplication().getStatisticsManager();
|
|
|
- final CSVPrinter csvPrinter = PwmUtil.makeCsvPrinter( outputStream );
|
|
|
-
|
|
|
- if ( includeHeader )
|
|
|
- {
|
|
|
- final List<String> headers = Statistic.asSet().stream()
|
|
|
- .map( stat -> stat.getLabel( locale ) )
|
|
|
- .collect( Collectors.toList() );
|
|
|
-
|
|
|
- headers.add( "KEY" );
|
|
|
- headers.add( "YEAR" );
|
|
|
- headers.add( "DAY" );
|
|
|
-
|
|
|
- csvPrinter.printRecord( headers );
|
|
|
- }
|
|
|
-
|
|
|
- int counter = 0;
|
|
|
- final Map<DailyKey, String> keys = statsManger.getAvailableKeys( PwmConstants.DEFAULT_LOCALE );
|
|
|
- for ( final DailyKey loopKey : keys.keySet() )
|
|
|
- {
|
|
|
- counter++;
|
|
|
- final StatisticsBundle bundle = statsManger.getStatBundleForKey( loopKey.toString() );
|
|
|
-
|
|
|
- final List<String> lineOutput = new ArrayList<>( Statistic.asSet().size() );
|
|
|
-
|
|
|
- lineOutput.add( loopKey.toString() );
|
|
|
- lineOutput.add( String.valueOf( loopKey.getYear() ) );
|
|
|
- lineOutput.add( String.valueOf( loopKey.getDay() ) );
|
|
|
-
|
|
|
- lineOutput.addAll( EnumUtil.enumStream( Statistic.class )
|
|
|
- .map( bundle::getStatistic )
|
|
|
- .collect( Collectors.toList() ) );
|
|
|
-
|
|
|
- csvPrinter.printRecord( lineOutput );
|
|
|
- }
|
|
|
-
|
|
|
- csvPrinter.flush();
|
|
|
- {
|
|
|
- final int finalCounter = counter;
|
|
|
- LOGGER.trace( () -> "completed output stats to csv process; output " + finalCounter + " records in "
|
|
|
- + TimeDuration.compactFromCurrent( startTime ) );
|
|
|
- }
|
|
|
- return counter;
|
|
|
- }
|
|
|
-
|
|
|
@Override
|
|
|
public ServiceInfoBean serviceInfo( )
|
|
|
{
|