mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Testing graphs for dashboard.
This commit is contained in:
parent
fc53bff61a
commit
a673dff63d
@ -27,6 +27,8 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@ -53,7 +55,7 @@ public class ServicesHealthMonitor {
|
|||||||
private final static String DATABASE_NAME = "ServicesHealthMonitor";
|
private final static String DATABASE_NAME = "ServicesHealthMonitor";
|
||||||
private final static String MODULE_NAME = "ServicesHealthMonitor";
|
private final static String MODULE_NAME = "ServicesHealthMonitor";
|
||||||
private final static String IS_ENABLED_KEY = "is_enabled";
|
private final static String IS_ENABLED_KEY = "is_enabled";
|
||||||
private final static long DATABASE_WRITE_INTERVAL = 60; // Minutes
|
private final static long DATABASE_WRITE_INTERVAL = 1; // Minutes TODO - put back to an hour
|
||||||
public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
|
public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
|
||||||
= new CaseDbSchemaVersionNumber(1, 0);
|
= new CaseDbSchemaVersionNumber(1, 0);
|
||||||
|
|
||||||
@ -111,6 +113,9 @@ public class ServicesHealthMonitor {
|
|||||||
|
|
||||||
logger.log(Level.INFO, "Activating Servies Health Monitor");
|
logger.log(Level.INFO, "Activating Servies Health Monitor");
|
||||||
|
|
||||||
|
// Make sure there are no left over connections to an old database
|
||||||
|
shutdownConnections();
|
||||||
|
|
||||||
if (!UserPreferences.getIsMultiUserModeEnabled()) {
|
if (!UserPreferences.getIsMultiUserModeEnabled()) {
|
||||||
throw new HealthMonitorException("Multi user mode is not enabled - can not activate services health monitor");
|
throw new HealthMonitorException("Multi user mode is not enabled - can not activate services health monitor");
|
||||||
}
|
}
|
||||||
@ -523,7 +528,7 @@ public class ServicesHealthMonitor {
|
|||||||
ResultSet resultSet = null;
|
ResultSet resultSet = null;
|
||||||
|
|
||||||
try (Statement statement = conn.createStatement()) {
|
try (Statement statement = conn.createStatement()) {
|
||||||
int minorVersion = 0;
|
int minorVersion = 0;
|
||||||
int majorVersion = 0;
|
int majorVersion = 0;
|
||||||
resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='SCHEMA_MINOR_VERSION'");
|
resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='SCHEMA_MINOR_VERSION'");
|
||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
@ -636,6 +641,76 @@ public class ServicesHealthMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all timing metrics currently stored in the database. This also converts
|
||||||
|
* the times to milliseconds (from nanoseconds).
|
||||||
|
* @return A map with metric name mapped to a list of data
|
||||||
|
* @throws HealthMonitorException
|
||||||
|
*/
|
||||||
|
Map<String, List<DatabaseTimingResult>> getTimingMetricsFromDatabase() throws HealthMonitorException {
|
||||||
|
|
||||||
|
// Make sure the monitor is enabled. It could theoretically get disabled after this
|
||||||
|
// check but it doesn't seem worth holding a lock to ensure that it doesn't since that
|
||||||
|
// may slow down ingest.
|
||||||
|
if(! isEnabled.get()) {
|
||||||
|
throw new HealthMonitorException("Health Monitor is not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
CoordinationService.Lock lock = getSharedDbLock();
|
||||||
|
if(lock == null) {
|
||||||
|
throw new HealthMonitorException("Error getting database lock");
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
Connection conn = connect();
|
||||||
|
if(conn == null) {
|
||||||
|
throw new HealthMonitorException("Error getting database connection");
|
||||||
|
}
|
||||||
|
ResultSet resultSet = null;
|
||||||
|
|
||||||
|
Map<String, List<DatabaseTimingResult>> resultMap = new HashMap<>();
|
||||||
|
|
||||||
|
try (Statement statement = conn.createStatement()) {
|
||||||
|
|
||||||
|
resultSet = statement.executeQuery("SELECT * FROM timing_data");
|
||||||
|
while (resultSet.next()) {
|
||||||
|
String name = resultSet.getString("name");
|
||||||
|
DatabaseTimingResult timingResult = new DatabaseTimingResult(resultSet);
|
||||||
|
|
||||||
|
if(resultMap.containsKey(name)) {
|
||||||
|
resultMap.get(name).add(timingResult);
|
||||||
|
} else {
|
||||||
|
List<DatabaseTimingResult> resultList = new ArrayList<>();
|
||||||
|
resultList.add(timingResult);
|
||||||
|
resultMap.put(name, resultList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
throw new HealthMonitorException("Error reading timing metrics from database", ex);
|
||||||
|
} finally {
|
||||||
|
if(resultSet != null) {
|
||||||
|
try {
|
||||||
|
resultSet.close();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error closing result set", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
conn.close();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error closing Connection.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
lock.release();
|
||||||
|
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||||
|
throw new HealthMonitorException("Error releasing database lock", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an exclusive lock for the health monitor database.
|
* Get an exclusive lock for the health monitor database.
|
||||||
* Acquire this before creating, initializing, or updating the database schema.
|
* Acquire this before creating, initializing, or updating the database schema.
|
||||||
@ -753,4 +828,73 @@ public class ServicesHealthMonitor {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for retrieving timing metrics from the database to display to the user.
|
||||||
|
* All times will be in milliseconds.
|
||||||
|
*/
|
||||||
|
static class DatabaseTimingResult {
|
||||||
|
private long timestamp; // Time the metric was recorded
|
||||||
|
private long count; // Number of metrics collected
|
||||||
|
private double average; // Average of the durations collected (milliseconds)
|
||||||
|
private double max; // Maximum value found (milliseconds)
|
||||||
|
private double min; // Minimum value found (milliseconds)
|
||||||
|
|
||||||
|
// TODO - maybe delete this
|
||||||
|
DatabaseTimingResult(long timestamp, long count, double average, double max, double min) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.count = count;
|
||||||
|
this.average = average;
|
||||||
|
this.max = max;
|
||||||
|
this.min = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseTimingResult(ResultSet resultSet) throws SQLException {
|
||||||
|
this.timestamp = resultSet.getLong("timestamp");
|
||||||
|
this.count = resultSet.getLong("count");
|
||||||
|
this.average = resultSet.getLong("average") / 1000000;
|
||||||
|
this.max = resultSet.getLong("max") / 1000000;
|
||||||
|
this.min = resultSet.getLong("min") / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp for when the metric was recorded
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the average duration
|
||||||
|
* @return average duration (milliseconds)
|
||||||
|
*/
|
||||||
|
double getAverage() {
|
||||||
|
return average;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum duration
|
||||||
|
* @return maximum duration (milliseconds)
|
||||||
|
*/
|
||||||
|
double getMax() {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the minimum duration
|
||||||
|
* @return minimum duration (milliseconds)
|
||||||
|
*/
|
||||||
|
double getMin() {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of metrics collected
|
||||||
|
* @return number of metrics collected
|
||||||
|
*/
|
||||||
|
long getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2018 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.healthmonitor;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.Stroke;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import org.sleuthkit.autopsy.healthmonitor.ServicesHealthMonitor.DatabaseTimingResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class TimingMetricGraphPanel extends JPanel {
|
||||||
|
|
||||||
|
private int width = 800;
|
||||||
|
private int heigth = 400;
|
||||||
|
private int padding = 25;
|
||||||
|
private int labelPadding = 25;
|
||||||
|
private Color lineColor = new Color(44, 102, 230, 180);
|
||||||
|
private Color pointColor = new Color(100, 100, 100, 180);
|
||||||
|
private Color gridColor = new Color(200, 200, 200, 200);
|
||||||
|
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
|
||||||
|
private int pointWidth = 4;
|
||||||
|
private int numberYDivisions = 10;
|
||||||
|
private List<DatabaseTimingResult> timingResults;
|
||||||
|
|
||||||
|
TimingMetricGraphPanel(List<DatabaseTimingResult> timingResults) {
|
||||||
|
this.timingResults = timingResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getMaxScore() {
|
||||||
|
// Find the highest of the max values
|
||||||
|
double maxScore = Double.MIN_VALUE;
|
||||||
|
for (DatabaseTimingResult score : timingResults) {
|
||||||
|
maxScore = Math.max(maxScore, score.getMax());
|
||||||
|
}
|
||||||
|
return maxScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getMinScore() {
|
||||||
|
// Find the highest of the max values
|
||||||
|
double minScore = Double.MAX_VALUE;
|
||||||
|
for (DatabaseTimingResult score : timingResults) {
|
||||||
|
minScore = Math.min(minScore, score.getMin());
|
||||||
|
}
|
||||||
|
return minScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
double maxScore = getMaxScore();
|
||||||
|
double minScore = getMinScore();
|
||||||
|
|
||||||
|
|
||||||
|
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (timingResults.size() - 1); // TODO: make this based on timestamps
|
||||||
|
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (maxScore - minScore);
|
||||||
|
|
||||||
|
//List<Point> graphPoints = new ArrayList<>();
|
||||||
|
List<Point> averageGraphPoints = new ArrayList<>();
|
||||||
|
List<Point> maxGraphPoints = new ArrayList<>();
|
||||||
|
List<Point> minGraphPoints = new ArrayList<>();
|
||||||
|
for (int i = 0; i < timingResults.size(); i++) {
|
||||||
|
int x1 = (int) (i * xScale + padding + labelPadding);
|
||||||
|
int yAve = (int) ((maxScore - timingResults.get(i).getAverage()) * yScale + padding);
|
||||||
|
int yMax = (int) ((maxScore - timingResults.get(i).getMax()) * yScale + padding);
|
||||||
|
int yMin = (int) ((maxScore - timingResults.get(i).getMin()) * yScale + padding);
|
||||||
|
averageGraphPoints.add(new Point(x1, yAve));
|
||||||
|
maxGraphPoints.add(new Point(x1, yMax));
|
||||||
|
minGraphPoints.add(new Point(x1, yMin));
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw white background
|
||||||
|
g2.setColor(Color.WHITE);
|
||||||
|
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding);
|
||||||
|
g2.setColor(Color.BLACK);
|
||||||
|
|
||||||
|
// create hatch marks and grid lines for y axis.
|
||||||
|
for (int i = 0; i < numberYDivisions + 1; i++) {
|
||||||
|
int x0 = padding + labelPadding;
|
||||||
|
int x1 = pointWidth + padding + labelPadding;
|
||||||
|
int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
|
||||||
|
int y1 = y0;
|
||||||
|
if (timingResults.size() > 0) {
|
||||||
|
g2.setColor(gridColor);
|
||||||
|
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
|
||||||
|
g2.setColor(Color.BLACK);
|
||||||
|
String yLabel = ((int) ((getMinScore() + (maxScore - minScore) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
|
||||||
|
FontMetrics metrics = g2.getFontMetrics();
|
||||||
|
int labelWidth = metrics.stringWidth(yLabel);
|
||||||
|
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
|
||||||
|
}
|
||||||
|
g2.drawLine(x0, y0, x1, y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and for x axis
|
||||||
|
for (int i = 0; i < timingResults.size(); i++) {
|
||||||
|
if (timingResults.size() > 1) {
|
||||||
|
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (timingResults.size() - 1) + padding + labelPadding;
|
||||||
|
int x1 = x0;
|
||||||
|
int y0 = getHeight() - padding - labelPadding;
|
||||||
|
int y1 = y0 - pointWidth;
|
||||||
|
if ((i % ((int) ((timingResults.size() / 20.0)) + 1)) == 0) {
|
||||||
|
g2.setColor(gridColor);
|
||||||
|
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
|
||||||
|
g2.setColor(Color.BLACK);
|
||||||
|
String xLabel = i + "";
|
||||||
|
FontMetrics metrics = g2.getFontMetrics();
|
||||||
|
int labelWidth = metrics.stringWidth(xLabel);
|
||||||
|
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
|
||||||
|
}
|
||||||
|
g2.drawLine(x0, y0, x1, y1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create x and y axes
|
||||||
|
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
|
||||||
|
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding);
|
||||||
|
|
||||||
|
Stroke oldStroke = g2.getStroke();
|
||||||
|
g2.setColor(lineColor);
|
||||||
|
g2.setStroke(GRAPH_STROKE);
|
||||||
|
for (int i = 0; i < averageGraphPoints.size() - 1; i++) {
|
||||||
|
int x1 = averageGraphPoints.get(i).x;
|
||||||
|
int y1 = averageGraphPoints.get(i).y;
|
||||||
|
int x2 = averageGraphPoints.get(i + 1).x;
|
||||||
|
int y2 = averageGraphPoints.get(i + 1).y;
|
||||||
|
g2.drawLine(x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
g2.setColor(new Color(10, 10, 200));
|
||||||
|
for (int i = 0; i < maxGraphPoints.size() - 1; i++) {
|
||||||
|
int x1 = maxGraphPoints.get(i).x;
|
||||||
|
int y1 = maxGraphPoints.get(i).y;
|
||||||
|
int x2 = maxGraphPoints.get(i + 1).x;
|
||||||
|
int y2 = maxGraphPoints.get(i + 1).y;
|
||||||
|
g2.drawLine(x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
g2.setColor(new Color(10, 200, 20));
|
||||||
|
for (int i = 0; i < minGraphPoints.size() - 1; i++) {
|
||||||
|
int x1 = minGraphPoints.get(i).x;
|
||||||
|
int y1 = minGraphPoints.get(i).y;
|
||||||
|
int x2 = minGraphPoints.get(i + 1).x;
|
||||||
|
int y2 = minGraphPoints.get(i + 1).y;
|
||||||
|
g2.drawLine(x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
g2.setStroke(oldStroke);
|
||||||
|
g2.setColor(pointColor);
|
||||||
|
/*for (int i = 0; i < graphPoints.size(); i++) {
|
||||||
|
int x = graphPoints.get(i).x - pointWidth / 2;
|
||||||
|
int y = graphPoints.get(i).y - pointWidth / 2;
|
||||||
|
int ovalW = pointWidth;
|
||||||
|
int ovalH = pointWidth;
|
||||||
|
g2.fillOval(x, y, ovalW, ovalH);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user