19 package org.sleuthkit.autopsy.healthmonitor;
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.sql.Connection;
25 import java.sql.DriverManager;
26 import java.sql.PreparedStatement;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.sql.Statement;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.GregorianCalendar;
36 import java.util.UUID;
37 import java.util.concurrent.ScheduledThreadPoolExecutor;
38 import java.util.concurrent.TimeUnit;
39 import java.util.concurrent.atomic.AtomicBoolean;
40 import java.util.logging.Level;
41 import java.util.Random;
42 import org.apache.commons.dbcp2.BasicDataSource;
51 import org.
sleuthkit.datamodel.CaseDbSchemaVersionNumber;
70 private final static AtomicBoolean
isEnabled =
new AtomicBoolean(
false);
86 timingInfoMap =
new HashMap<>();
90 userInfoList =
new ArrayList<>();
94 hostName = java.net.InetAddress.getLocalHost().getHostName();
95 }
catch (java.net.UnknownHostException ex) {
97 hostName = UUID.randomUUID().toString();
98 logger.log(Level.SEVERE,
"Unable to look up host name - falling back to UUID " + hostName, ex);
102 username = System.getProperty(
"user.name");
105 updateFromGlobalEnabledStatus();
118 synchronized static HealthMonitor getInstance() throws HealthMonitorException {
119 if (instance == null) {
135 logger.log(Level.INFO,
"Activating Servies Health Monitor");
141 throw new HealthMonitorException(
"Multi user mode is not enabled - can not activate health monitor");
147 throw new HealthMonitorException(
"Error getting database lock");
161 if (
getVersion().compareTo(CURRENT_DB_SCHEMA_VERSION) < 0) {
166 throw new HealthMonitorException(
"Error releasing database lock", ex);
170 timingInfoMap.clear();
171 userInfoList.clear();
179 logger.log(Level.INFO,
"Upgrading Health Monitor database");
180 CaseDbSchemaVersionNumber currentSchema =
getVersion();
184 throw new HealthMonitorException(
"Error getting database connection");
186 ResultSet resultSet = null;
188 try (Statement statement = conn.createStatement()) {
189 conn.setAutoCommit(
false);
197 if (currentSchema.compareTo(
new CaseDbSchemaVersionNumber(1, 1)) < 0) {
200 statement.execute(
"CREATE TABLE IF NOT EXISTS user_data ("
201 +
"id SERIAL PRIMARY KEY,"
202 +
"host text NOT NULL,"
203 +
"timestamp bigint NOT NULL,"
204 +
"event_type int NOT NULL,"
205 +
"is_examiner boolean NOT NULL,"
206 +
"case_name text NOT NULL"
212 if (currentSchema.compareTo(
new CaseDbSchemaVersionNumber(1, 2)) < 0) {
214 resultSet = statement.executeQuery(
"SELECT column_name " +
215 "FROM information_schema.columns " +
216 "WHERE table_name='user_data' and column_name='username'");
217 if (! resultSet.next()) {
219 statement.execute(
"ALTER TABLE user_data ADD COLUMN username text");
224 statement.execute(
"UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMajor() +
"' WHERE name='SCHEMA_VERSION'");
225 statement.execute(
"UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMinor() +
"' WHERE name='SCHEMA_MINOR_VERSION'");
228 logger.log(Level.INFO,
"Health Monitor database upgraded to version {0}", CURRENT_DB_SCHEMA_VERSION.toString());
229 }
catch (SQLException ex) {
232 }
catch (SQLException ex2) {
233 logger.log(Level.SEVERE,
"Rollback error");
235 throw new HealthMonitorException(
"Error upgrading database", ex);
237 if (resultSet != null) {
240 }
catch (SQLException ex2) {
241 logger.log(Level.SEVERE,
"Error closing result set");
246 }
catch (SQLException ex) {
247 logger.log(Level.SEVERE,
"Error closing connection.", ex);
262 logger.log(Level.INFO,
"Deactivating Servies Health Monitor");
265 timingInfoMap.clear();
279 healthMonitorOutputTimer =
new ScheduledThreadPoolExecutor(1,
new ThreadFactoryBuilder().setNameFormat(
"health_monitor_timer").build());
280 healthMonitorOutputTimer.scheduleWithFixedDelay(
new PeriodicHealthMonitorTask(), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES);
287 if (healthMonitorOutputTimer != null) {
298 static synchronized void startUpIfEnabled() throws HealthMonitorException {
308 static synchronized void shutdown() throws HealthMonitorException {
320 static synchronized void setEnabled(
boolean enabled)
throws HealthMonitorException {
321 if (enabled == isEnabled.get()) {
333 if (isEnabled.get()) {
337 isEnabled.set(
false);
354 if (isEnabled.get()) {
368 if (isEnabled.get() && (metric != null)) {
372 }
catch (HealthMonitorException ex) {
374 logger.log(Level.SEVERE,
"Error adding timing metric", ex);
391 if (isEnabled.get() && (metric != null)) {
394 metric.normalize(normalization);
396 }
catch (HealthMonitorException ex) {
398 logger.log(Level.SEVERE,
"Error adding timing metric", ex);
413 synchronized (
this) {
420 if (timingInfoMap.containsKey(metric.getName())) {
421 timingInfoMap.get(metric.getName()).addMetric(metric);
423 timingInfoMap.put(metric.getName(),
new TimingInfo(metric));
434 UserData userInfo =
new UserData(eventType);
435 synchronized (
this) {
436 userInfoList.add(userInfo);
453 List<Image> images = skCase.getImages();
457 long normalization = images.size();
458 if (images.isEmpty()) {
460 }
else if (images.size() == 1) {
462 }
else if (images.size() < 10) {
471 }
catch (TskCoreException ex) {
472 throw new HealthMonitorException(
"Error running getImages()", ex);
492 Map<String, TimingInfo> timingMapCopy;
493 List<UserData> userDataCopy;
497 synchronized (
this) {
498 if (!isEnabled.get()) {
505 timingInfoMap.clear();
508 userInfoList.clear();
512 if (timingMapCopy.keySet().isEmpty() && userDataCopy.isEmpty()) {
516 logger.log(Level.INFO,
"Writing health monitor metrics to database");
521 throw new HealthMonitorException(
"Error getting database lock");
526 throw new HealthMonitorException(
"Error getting database connection");
530 String addTimingInfoSql =
"INSERT INTO timing_data (name, host, timestamp, count, average, max, min) VALUES (?, ?, ?, ?, ?, ?, ?)";
531 String addUserInfoSql =
"INSERT INTO user_data (host, username, timestamp, event_type, is_examiner, case_name) VALUES (?, ?, ?, ?, ?, ?)";
532 try (PreparedStatement timingStatement = conn.prepareStatement(addTimingInfoSql);
533 PreparedStatement userStatement = conn.prepareStatement(addUserInfoSql)) {
535 for (String name : timingMapCopy.keySet()) {
538 timingStatement.setString(1, name);
539 timingStatement.setString(2, hostName);
540 timingStatement.setLong(3, System.currentTimeMillis());
541 timingStatement.setLong(4, info.getCount());
542 timingStatement.setDouble(5, info.getAverage());
543 timingStatement.setDouble(6, info.getMax());
544 timingStatement.setDouble(7, info.getMin());
546 timingStatement.execute();
549 for (UserData userInfo : userDataCopy) {
550 userStatement.setString(1, hostName);
551 userStatement.setString(2, username);
552 userStatement.setLong(3, userInfo.getTimestamp());
553 userStatement.setInt(4, userInfo.getEventType().getEventValue());
554 userStatement.setBoolean(5, userInfo.isExaminerNode());
555 userStatement.setString(6, userInfo.getCaseName());
556 userStatement.execute();
559 }
catch (SQLException ex) {
560 throw new HealthMonitorException(
"Error saving metric data to database", ex);
564 }
catch (SQLException ex) {
565 logger.log(Level.SEVERE,
"Error closing Connection.", ex);
569 throw new HealthMonitorException(
"Error releasing database lock", ex);
585 Class.forName(
"org.postgresql.Driver");
587 try (Connection connection = DriverManager.getConnection(
"jdbc:postgresql://" + db.getHost() +
":" + db.getPort() +
"/postgres", db.getUserName(), db.getPassword());
588 Statement statement = connection.createStatement();) {
589 String createCommand =
"SELECT 1 AS result FROM pg_database WHERE datname='" + DATABASE_NAME +
"'";
590 rs = statement.executeQuery(createCommand);
600 throw new HealthMonitorException(
"Failed check for health monitor database", ex);
614 Class.forName(
"org.postgresql.Driver");
615 try (Connection connection = DriverManager.getConnection(
"jdbc:postgresql://" + db.getHost() +
":" + db.getPort() +
"/postgres", db.getUserName(), db.getPassword());
616 Statement statement = connection.createStatement();) {
617 String createCommand =
"CREATE DATABASE \"" + DATABASE_NAME +
"\" OWNER \"" + db.getUserName() +
"\"";
618 statement.execute(createCommand);
620 logger.log(Level.INFO,
"Created new health monitor database " + DATABASE_NAME);
622 throw new HealthMonitorException(
"Failed to delete health monitor database", ex);
634 connectionSettingsInUse = db;
636 connectionPool =
new BasicDataSource();
637 connectionPool.setDriverClassName(
"org.postgresql.Driver");
639 StringBuilder connectionURL =
new StringBuilder();
640 connectionURL.append(
"jdbc:postgresql://");
641 connectionURL.append(db.getHost());
642 connectionURL.append(
":");
643 connectionURL.append(db.getPort());
644 connectionURL.append(
"/");
645 connectionURL.append(DATABASE_NAME);
647 connectionPool.setUrl(connectionURL.toString());
648 connectionPool.setUsername(db.getUserName());
649 connectionPool.setPassword(db.getPassword());
652 connectionPool.setInitialSize(3);
653 connectionPool.setMaxIdle(CONN_POOL_SIZE);
654 connectionPool.setValidationQuery(
"SELECT version()");
656 throw new HealthMonitorException(
"Error loading database configuration", ex);
667 synchronized (
this) {
668 if (connectionPool != null) {
669 connectionPool.close();
670 connectionPool = null;
673 }
catch (SQLException ex) {
674 throw new HealthMonitorException(
"Failed to close existing database connections.", ex);
685 private Connection
connect() throws HealthMonitorException {
686 synchronized (
this) {
687 if (connectionPool == null) {
693 return connectionPool.getConnection();
694 }
catch (SQLException ex) {
695 throw new HealthMonitorException(
"Error getting connection from connection pool.", ex);
710 throw new HealthMonitorException(
"Error getting database connection");
712 ResultSet resultSet = null;
714 try (Statement statement = conn.createStatement()) {
715 resultSet = statement.executeQuery(
"SELECT value FROM db_info WHERE name='SCHEMA_VERSION'");
716 return resultSet.next();
717 }
catch (SQLException ex) {
721 if (resultSet != null) {
724 }
catch (SQLException ex) {
725 logger.log(Level.SEVERE,
"Error closing result set", ex);
730 }
catch (SQLException ex) {
731 logger.log(Level.SEVERE,
"Error closing Connection.", ex);
742 static boolean monitorIsEnabled() {
743 return isEnabled.get();
752 synchronized void updateFromGlobalEnabledStatus() throws HealthMonitorException {
754 boolean previouslyEnabled = monitorIsEnabled();
757 if (!UserPreferences.getIsMultiUserModeEnabled()) {
758 isEnabled.set(
false);
760 if (previouslyEnabled) {
769 isEnabled.set(
false);
771 if (previouslyEnabled) {
779 if (previouslyEnabled && (connectionSettingsInUse != null)) {
781 CaseDbConnectionInfo currentSettings = UserPreferences.getDatabaseConnectionInfo();
782 if (!(connectionSettingsInUse.getUserName().equals(currentSettings.getUserName())
783 && connectionSettingsInUse.getPassword().equals(currentSettings.getPassword())
784 && connectionSettingsInUse.getPort().equals(currentSettings.getPort())
785 && connectionSettingsInUse.getHost().equals(currentSettings.getHost()))) {
788 }
catch (UserPreferencesException ex) {
789 throw new HealthMonitorException(
"Error reading database connection info", ex);
794 if (currentlyEnabled != previouslyEnabled) {
795 if (!currentlyEnabled) {
796 isEnabled.set(
false);
815 try (Connection conn =
connect();
816 Statement statement = conn.createStatement();
817 ResultSet resultSet = statement.executeQuery(
"SELECT value FROM db_info WHERE name='MONITOR_ENABLED'")) {
819 if (resultSet.next()) {
820 return (resultSet.getBoolean(
"value"));
822 throw new HealthMonitorException(
"No enabled status found in database");
823 }
catch (SQLException ex) {
824 throw new HealthMonitorException(
"Error initializing database", ex);
835 try (Connection conn =
connect();
836 Statement statement = conn.createStatement();) {
837 statement.execute(
"UPDATE db_info SET value='" + status +
"' WHERE name='MONITOR_ENABLED'");
838 }
catch (SQLException ex) {
839 throw new HealthMonitorException(
"Error setting enabled status", ex);
850 private CaseDbSchemaVersionNumber
getVersion() throws HealthMonitorException {
853 throw new HealthMonitorException(
"Error getting database connection");
855 ResultSet resultSet = null;
857 try (Statement statement = conn.createStatement()) {
858 int minorVersion = 0;
859 int majorVersion = 0;
860 resultSet = statement.executeQuery(
"SELECT value FROM db_info WHERE name='SCHEMA_MINOR_VERSION'");
861 if (resultSet.next()) {
862 String minorVersionStr = resultSet.getString(
"value");
864 minorVersion = Integer.parseInt(minorVersionStr);
865 }
catch (NumberFormatException ex) {
866 throw new HealthMonitorException(
"Bad value for schema minor version (" + minorVersionStr +
") - database is corrupt");
870 resultSet = statement.executeQuery(
"SELECT value FROM db_info WHERE name='SCHEMA_VERSION'");
871 if (resultSet.next()) {
872 String majorVersionStr = resultSet.getString(
"value");
874 majorVersion = Integer.parseInt(majorVersionStr);
875 }
catch (NumberFormatException ex) {
876 throw new HealthMonitorException(
"Bad value for schema version (" + majorVersionStr +
") - database is corrupt");
880 return new CaseDbSchemaVersionNumber(majorVersion, minorVersion);
881 }
catch (SQLException ex) {
882 throw new HealthMonitorException(
"Error initializing database", ex);
884 if (resultSet != null) {
887 }
catch (SQLException ex) {
888 logger.log(Level.SEVERE,
"Error closing result set", ex);
893 }
catch (SQLException ex) {
894 logger.log(Level.SEVERE,
"Error closing Connection.", ex);
907 throw new HealthMonitorException(
"Error getting database connection");
910 try (Statement statement = conn.createStatement()) {
911 conn.setAutoCommit(
false);
913 statement.execute(
"CREATE TABLE IF NOT EXISTS timing_data ("
914 +
"id SERIAL PRIMARY KEY,"
915 +
"name text NOT NULL,"
916 +
"host text NOT NULL,"
917 +
"timestamp bigint NOT NULL,"
918 +
"count bigint NOT NULL,"
919 +
"average double precision NOT NULL,"
920 +
"max double precision NOT NULL,"
921 +
"min double precision NOT NULL"
924 statement.execute(
"CREATE TABLE IF NOT EXISTS db_info ("
925 +
"id SERIAL PRIMARY KEY NOT NULL,"
926 +
"name text NOT NULL,"
927 +
"value text NOT NULL"
930 statement.execute(
"CREATE TABLE IF NOT EXISTS user_data ("
931 +
"id SERIAL PRIMARY KEY,"
932 +
"host text NOT NULL,"
933 +
"timestamp bigint NOT NULL,"
934 +
"event_type int NOT NULL,"
935 +
"is_examiner BOOLEAN NOT NULL,"
936 +
"case_name text NOT NULL,"
940 statement.execute(
"INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() +
"')");
941 statement.execute(
"INSERT INTO db_info (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() +
"')");
942 statement.execute(
"INSERT INTO db_info (name, value) VALUES ('MONITOR_ENABLED', 'true')");
945 }
catch (SQLException ex) {
948 }
catch (SQLException ex2) {
949 logger.log(Level.SEVERE,
"Rollback error");
951 throw new HealthMonitorException(
"Error initializing database", ex);
955 }
catch (SQLException ex) {
956 logger.log(Level.SEVERE,
"Error closing connection.", ex);
965 static final class PeriodicHealthMonitorTask
implements Runnable {
981 getInstance().updateFromGlobalEnabledStatus();
982 if (monitorIsEnabled()) {
986 }
catch (HealthMonitorException ex) {
987 logger.log(Level.SEVERE,
"Error recording health monitor metrics", ex);
994 switch (
Case.
Events.valueOf(evt.getPropertyName())) {
997 if ((null == evt.getNewValue()) && (evt.getOldValue() instanceof
Case)) {
1001 }
else if ((null == evt.getOldValue()) && (evt.getNewValue() instanceof
Case)) {
1014 void populateDatabaseWithSampleData(
int nDays,
int nNodes,
boolean createVerificationData)
throws HealthMonitorException {
1016 if (!isEnabled.get()) {
1017 throw new HealthMonitorException(
"Can't populate database - monitor not enabled");
1023 throw new HealthMonitorException(
"Error getting database lock");
1026 String[] metricNames = {
"Disk Reads: Hash calculation",
"Database: getImages query",
"Solr: Index chunk",
"Solr: Connectivity check",
1027 "Central Repository: Notable artifact query",
"Central Repository: Bulk insert"};
1029 Random rand =
new Random();
1031 long maxTimestamp = System.currentTimeMillis();
1032 long millisPerHour = 1000 * 60 * 60;
1033 long minTimestamp = maxTimestamp - (nDays * (millisPerHour * 24));
1035 Connection conn = null;
1039 throw new HealthMonitorException(
"Error getting database connection");
1042 try (Statement statement = conn.createStatement()) {
1044 statement.execute(
"DELETE FROM timing_data");
1045 }
catch (SQLException ex) {
1046 logger.log(Level.SEVERE,
"Error clearing timing data", ex);
1051 String addTimingInfoSql =
"INSERT INTO timing_data (name, host, timestamp, count, average, max, min) VALUES (?, ?, ?, ?, ?, ?, ?)";
1052 try (PreparedStatement statement = conn.prepareStatement(addTimingInfoSql)) {
1054 for (String metricName : metricNames) {
1056 long baseIndex = rand.nextInt(900) + 100;
1057 int multiplier = rand.nextInt(5);
1058 long minIndexTimeNanos;
1059 switch (multiplier) {
1061 minIndexTimeNanos = baseIndex;
1064 minIndexTimeNanos = baseIndex * 1000;
1067 minIndexTimeNanos = baseIndex * 1000 * 1000;
1071 long maxIndexTimeOverMin = minIndexTimeNanos * 3;
1073 for (
int node = 0; node < nNodes; node++) {
1075 String host =
"testHost" + node;
1078 double maxCount = nDays * 24 + 1;
1081 for (
long timestamp = minTimestamp + rand.nextInt(1000 * 60 * 55); timestamp < maxTimestamp; timestamp += millisPerHour) {
1088 double slowNodeMultiplier = 1.0;
1089 if ((maxCount - count) <= 3 * 24) {
1090 slowNodeMultiplier += (3 - (maxCount - count) / 24) * 0.33;
1093 if (!createVerificationData) {
1096 int outlierVal = rand.nextInt(30);
1097 long randVal = rand.nextLong();
1101 if (outlierVal < 2) {
1102 aveTime = minIndexTimeNanos + maxIndexTimeOverMin + randVal % maxIndexTimeOverMin;
1103 }
else if (outlierVal == 2) {
1104 aveTime = (minIndexTimeNanos / 2) + randVal % (minIndexTimeNanos / 2);
1105 }
else if (outlierVal < 17) {
1106 aveTime = minIndexTimeNanos + randVal % (maxIndexTimeOverMin / 2);
1108 aveTime = minIndexTimeNanos + randVal % maxIndexTimeOverMin;
1112 aveTime = aveTime * slowNodeMultiplier;
1118 Calendar thisDate =
new GregorianCalendar();
1119 thisDate.setTimeInMillis(timestamp);
1120 int day = thisDate.get(Calendar.DAY_OF_MONTH);
1121 aveTime = day * 1000000;
1124 statement.setString(1, metricName);
1125 statement.setString(2, host);
1126 statement.setLong(3, timestamp);
1127 statement.setLong(4, 0);
1128 statement.setDouble(5, aveTime / 1000000);
1129 statement.setDouble(6, 0);
1130 statement.setDouble(7, 0);
1132 statement.execute();
1136 }
catch (SQLException ex) {
1137 throw new HealthMonitorException(
"Error saving metric data to database", ex);
1144 }
catch (SQLException ex) {
1145 logger.log(Level.SEVERE,
"Error closing Connection.", ex);
1149 }
catch (CoordinationService.CoordinationServiceException ex) {
1150 throw new HealthMonitorException(
"Error releasing database lock", ex);
1164 Map<String, List<DatabaseTimingResult>> getTimingMetricsFromDatabase(
long timeRange)
throws HealthMonitorException {
1169 if (!isEnabled.get()) {
1170 throw new HealthMonitorException(
"Health Monitor is not enabled");
1174 long minimumTimestamp = System.currentTimeMillis() - timeRange;
1178 throw new HealthMonitorException(
"Error getting database lock");
1183 throw new HealthMonitorException(
"Error getting database connection");
1186 Map<String, List<DatabaseTimingResult>> resultMap =
new HashMap<>();
1188 try (Statement statement = conn.createStatement();
1189 ResultSet resultSet = statement.executeQuery(
"SELECT * FROM timing_data WHERE timestamp > " + minimumTimestamp)) {
1191 while (resultSet.next()) {
1192 String name = resultSet.getString(
"name");
1193 DatabaseTimingResult timingResult =
new DatabaseTimingResult(resultSet);
1195 if (resultMap.containsKey(name)) {
1196 resultMap.get(name).add(timingResult);
1198 List<DatabaseTimingResult> resultList =
new ArrayList<>();
1199 resultList.add(timingResult);
1200 resultMap.put(name, resultList);
1204 }
catch (SQLException ex) {
1205 throw new HealthMonitorException(
"Error reading timing metrics from database", ex);
1209 }
catch (SQLException ex) {
1210 logger.log(Level.SEVERE,
"Error closing Connection.", ex);
1213 }
catch (CoordinationService.CoordinationServiceException ex) {
1214 throw new HealthMonitorException(
"Error getting database lock", ex);
1227 List<UserData> getUserMetricsFromDatabase(
long timeRange)
throws HealthMonitorException {
1232 if (!isEnabled.get()) {
1233 throw new HealthMonitorException(
"Health Monitor is not enabled");
1237 long minimumTimestamp = System.currentTimeMillis() - timeRange;
1241 throw new HealthMonitorException(
"Error getting database lock");
1244 List<UserData> resultList =
new ArrayList<>();
1246 try (Connection conn =
connect();
1247 Statement statement = conn.createStatement();
1248 ResultSet resultSet = statement.executeQuery(
"SELECT * FROM user_data WHERE timestamp > " + minimumTimestamp)) {
1250 while (resultSet.next()) {
1251 resultList.add(
new UserData(resultSet));
1254 }
catch (SQLException ex) {
1255 throw new HealthMonitorException(
"Error reading user metrics from database", ex);
1257 }
catch (CoordinationService.CoordinationServiceException ex) {
1258 throw new HealthMonitorException(
"Error getting database lock", ex);
1277 throw new HealthMonitorException(
"Error acquiring database lock");
1279 throw new HealthMonitorException(
"Error acquiring database lock", ex);
1299 throw new HealthMonitorException(
"Error acquiring database lock");
1301 throw new HealthMonitorException(
"Error acquiring database lock");
1316 UserEvent(
int value) {
1325 int getEventValue() {
1338 static UserEvent valueOf(
int value)
throws HealthMonitorException {
1339 for (UserEvent v : UserEvent.values()) {
1340 if (v.value == value) {
1344 throw new HealthMonitorException(
"Can not create UserEvent from unknown value " + value);
1353 boolean caseIsOpen() {
1354 return (this.equals(CASE_OPEN));
1363 boolean userIsLoggedIn() {
1366 return (!this.equals(LOG_OFF));
1374 static class UserData
implements Comparable<UserData> {
1376 private final UserEvent eventType;
1377 private long timestamp;
1378 private final boolean isExaminer;
1379 private final String hostname;
1381 private String caseName;
1389 private UserData(UserEvent eventType) {
1390 this.eventType = eventType;
1391 this.timestamp = System.currentTimeMillis();
1392 this.isExaminer = (UserPreferences.SelectedMode.STANDALONE == UserPreferences.getMode());
1398 this.caseName = Case.getCurrentCaseThrows().getDisplayName();
1399 }
catch (NoCurrentCaseException ex) {
1413 UserData(ResultSet resultSet)
throws SQLException, HealthMonitorException {
1414 this.timestamp = resultSet.getLong(
"timestamp");
1415 this.hostname = resultSet.getString(
"host");
1416 this.eventType = UserEvent.valueOf(resultSet.getInt(
"event_type"));
1417 this.isExaminer = resultSet.getBoolean(
"is_examiner");
1418 this.caseName = resultSet.getString(
"case_name");
1419 this.username = resultSet.getString(
"username");
1420 if (this.username == null) {
1433 static UserData createDummyUserData(
long timestamp) {
1434 UserData userData =
new UserData(UserEvent.CASE_CLOSE);
1435 userData.timestamp = timestamp;
1444 long getTimestamp() {
1453 String getHostname() {
1462 UserEvent getEventType() {
1471 boolean isExaminerNode() {
1480 String getCaseName() {
1489 String getUserName() {
1494 public int compareTo(UserData otherData) {
1495 return Long.compare(getTimestamp(), otherData.getTimestamp());
1515 sum = metric.getDuration();
1516 max = metric.getDuration();
1517 min = metric.getDuration();
1530 void addMetric(
TimingMetric metric)
throws HealthMonitorException {
1534 sum += metric.getDuration();
1537 if (max < metric.getDuration()) {
1538 max = metric.getDuration();
1542 if (min > metric.getDuration()) {
1543 min = metric.getDuration();
1552 double getAverage() {
1588 static class DatabaseTimingResult {
1590 private final long timestamp;
1591 private final String hostname;
1592 private final long count;
1593 private final double average;
1594 private final double max;
1595 private final double min;
1597 DatabaseTimingResult(ResultSet resultSet)
throws SQLException {
1598 this.timestamp = resultSet.getLong(
"timestamp");
1599 this.hostname = resultSet.getString(
"host");
1600 this.count = resultSet.getLong(
"count");
1601 this.average = resultSet.getDouble(
"average");
1602 this.max = resultSet.getDouble(
"max");
1603 this.min = resultSet.getDouble(
"min");
1611 long getTimestamp() {
1620 double getAverage() {
1656 String getHostName() {
void writeCurrentStateToDatabase()
BasicDataSource connectionPool
CoordinationService.Lock getSharedDbLock()
CaseDbSchemaVersionNumber getVersion()
static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
static void recordMetrics()
synchronized void startTimer()
static final String DATABASE_NAME
synchronized void deactivateMonitorLocally()
void performDatabaseQuery()
static boolean getIsMultiUserModeEnabled()
static CaseDbConnectionInfo getDatabaseConnectionInfo()
final Map< String, TimingInfo > timingInfoMap
boolean databaseIsInitialized()
CaseDbConnectionInfo connectionSettingsInUse
static TimingMetric getTimingMetric(String name)
boolean getGlobalEnabledStatusFromDB()
static final Logger logger
static void shutDownTaskExecutor(ExecutorService executor)
void initializeDatabaseSchema()
synchronized void activateMonitorLocally()
ScheduledThreadPoolExecutor healthMonitorOutputTimer
void setGlobalEnabledStatusInDB(boolean status)
void addTimingMetric(TimingMetric metric)
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static final int CONN_POOL_SIZE
SleuthkitCase getSleuthkitCase()
static void addPropertyChangeListener(PropertyChangeListener listener)
void setupConnectionPool()
static final long DATABASE_WRITE_INTERVAL
static final AtomicBoolean isEnabled
static void submitTimingMetric(TimingMetric metric)
static HealthMonitor instance
final List< UserData > userInfoList
void shutdownConnections()
synchronized static Logger getLogger(String name)
synchronized void stopTimer()
void upgradeDatabaseSchema()
CoordinationService.Lock getExclusiveDbLock()
static Case getCurrentCaseThrows()
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static synchronized CoordinationService getInstance()
static void submitNormalizedTimingMetric(TimingMetric metric, long normalization)
void gatherTimerBasedMetrics()
void propertyChange(PropertyChangeEvent evt)
void addUserEvent(UserEvent eventType)