19package org.sleuthkit.autopsy.healthmonitor;
21import java.awt.Container;
22import java.awt.Cursor;
23import java.awt.Dimension;
25import java.util.HashSet;
26import java.util.HashMap;
28import java.util.Arrays;
29import java.util.ArrayList;
31import java.awt.event.ActionEvent;
32import java.awt.event.ActionListener;
33import java.io.BufferedWriter;
35import java.io.FileOutputStream;
36import java.io.IOException;
37import java.io.OutputStreamWriter;
38import javax.swing.Box;
39import javax.swing.JButton;
40import javax.swing.JDialog;
41import javax.swing.JComboBox;
42import javax.swing.JSeparator;
43import javax.swing.JCheckBox;
44import javax.swing.JLabel;
45import javax.swing.JPanel;
46import javax.swing.JScrollPane;
47import javax.swing.BorderFactory;
48import javax.swing.BoxLayout;
49import java.awt.GridLayout;
50import java.nio.charset.StandardCharsets;
51import java.nio.file.Paths;
52import java.text.DateFormat;
53import java.text.SimpleDateFormat;
55import java.util.Collections;
56import java.util.logging.Level;
57import java.util.stream.Collectors;
58import javax.swing.JFileChooser;
59import org.openide.util.NbBundle;
60import org.sleuthkit.autopsy.core.UserPreferences;
61import org.sleuthkit.autopsy.coreutils.Logger;
62import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
63import org.sleuthkit.autopsy.coreutils.PlatformUtil;
64import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
98 timingData =
new HashMap<>();
99 userData =
new ArrayList<>();
107 @NbBundle.Messages({
"HealthMonitorDashboard.display.errorCreatingDashboard=Error creating health monitor dashboard",
108 "HealthMonitorDashboard.display.dashboardTitle=Health Monitor"})
121 }
catch (HealthMonitorException ex) {
122 logger.log(Level.SEVERE,
"Error creating panels for health monitor dashboard", ex);
128 JPanel mainPanel =
new JPanel();
129 mainPanel.setLayout(
new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
132 mainPanel.add(timingPanel);
135 mainPanel.add(userPanel);
139 if(adminFile.exists()) {
140 mainPanel.add(adminPanel);
145 dialog.setTitle(Bundle.HealthMonitorDashboard_display_dashboardTitle());
187 @NbBundle.Messages({
"HealthMonitorDashboard.createTimingPanel.noData=No data to display - monitor is not enabled",
188 "HealthMonitorDashboard.createTimingPanel.timingMetricsTitle=Timing Metrics"})
194 JPanel emptyTimingMetricPanel =
new JPanel();
195 emptyTimingMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle()));
196 emptyTimingMetricPanel.add(
new JLabel(
" "));
197 emptyTimingMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_noData()));
199 return emptyTimingMetricPanel;
202 JPanel timingMetricPanel =
new JPanel();
203 timingMetricPanel.setLayout(
new BoxLayout(timingMetricPanel, BoxLayout.PAGE_AXIS));
204 timingMetricPanel.setBorder(BorderFactory.createEtchedBorder());
207 JLabel timingMetricTitle =
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle());
208 timingMetricPanel.add(timingMetricTitle);
209 timingMetricPanel.add(
new JSeparator());
213 timingMetricPanel.add(
new JSeparator());
221 JScrollPane scrollPane =
new JScrollPane(
timingGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
222 timingMetricPanel.add(scrollPane);
223 timingMetricPanel.revalidate();
224 timingMetricPanel.repaint();
226 return timingMetricPanel;
233 @NbBundle.Messages({
"HealthMonitorDashboard.createTimingControlPanel.filterByHost=Filter by host",
234 "HealthMonitorDashboard.createTimingControlPanel.maxDays=Max days to display",
235 "HealthMonitorDashboard.createTimingControlPanel.skipOutliers=Do not plot outliers",
236 "HealthMonitorDashboard.createTimingControlPanel.showTrendLine=Show trend line"})
238 JPanel timingControlPanel =
new JPanel();
242 return timingControlPanel;
246 String[] dateOptionStrings = Arrays.stream(
DateRange.values()).map(e -> e.getLabel()).toArray(String[]::new);
253 public void actionPerformed(ActionEvent arg0) {
256 }
catch (HealthMonitorException ex) {
257 logger.log(Level.SEVERE,
"Error updating timing metric panel", ex);
263 Set<String> hostNameSet =
new HashSet<>();
264 for(String metricType:timingData.keySet()) {
266 hostNameSet.add(result.getHostName());
271 timingHostComboBox =
new JComboBox<>(hostNameSet.toArray(
new String[hostNameSet.size()]));
276 public void actionPerformed(ActionEvent arg0) {
281 }
catch (HealthMonitorException ex) {
282 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
288 timingHostCheckBox =
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_filterByHost());
295 public void actionPerformed(ActionEvent arg0) {
299 }
catch (HealthMonitorException ex) {
300 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
312 public void actionPerformed(ActionEvent arg0) {
315 }
catch (HealthMonitorException ex) {
316 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
328 public void actionPerformed(ActionEvent arg0) {
331 }
catch (HealthMonitorException ex) {
332 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
338 timingControlPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingControlPanel_maxDays()));
342 timingControlPanel.add(Box.createHorizontalStrut(100));
349 timingControlPanel.add(Box.createHorizontalStrut(100));
352 timingControlPanel.add(this.timingShowTrendLineCheckBox);
355 timingControlPanel.add(Box.createHorizontalStrut(100));
358 timingControlPanel.add(this.timingSkipOutliersCheckBox);
360 return timingControlPanel;
367 @NbBundle.Messages({
"HealthMonitorDashboard.updateTimingMetricGraphs.noData=No data to display"})
373 if(timingData.keySet().isEmpty()) {
375 timingGraphPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_updateTimingMetricGraphs_noData()));
379 for(String metricName:timingData.keySet()) {
385 long threshold = System.currentTimeMillis() - selectedDateRange.
getTimestampRange();
386 intermediateTimingDataForDisplay = timingData.get(metricName).stream()
387 .filter(t -> t.getTimestamp() > threshold)
388 .collect(Collectors.toList());
390 intermediateTimingDataForDisplay = timingData.get(metricName);
396 String hostToDisplay =
null;
402 TimingMetricGraphPanel singleTimingGraphPanel =
new TimingMetricGraphPanel(intermediateTimingDataForDisplay,
404 singleTimingGraphPanel.setPreferredSize(
new Dimension(700,200));
417 @NbBundle.Messages({
"HealthMonitorDashboard.createUserPanel.noData=No data to display - monitor is not enabled",
418 "HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics"})
422 JPanel emptyUserMetricPanel =
new JPanel();
423 emptyUserMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle()));
424 emptyUserMetricPanel.add(
new JLabel(
" "));
425 emptyUserMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_noData()));
427 return emptyUserMetricPanel;
430 JPanel userMetricPanel =
new JPanel();
431 userMetricPanel.setLayout(
new BoxLayout(userMetricPanel, BoxLayout.PAGE_AXIS));
432 userMetricPanel.setBorder(BorderFactory.createEtchedBorder());
435 JLabel userMetricTitle =
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle());
436 userMetricPanel.add(userMetricTitle);
437 userMetricPanel.add(
new JSeparator());
441 userMetricPanel.add(
new JSeparator());
449 JScrollPane scrollPane =
new JScrollPane(
userGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
450 userMetricPanel.add(scrollPane);
451 userMetricPanel.revalidate();
452 userMetricPanel.repaint();
454 return userMetricPanel;
461 @NbBundle.Messages({
"HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display",
462 "HealthMonitorDashboard.createUserControlPanel.userReportButton=Generate Report",
463 "HealthMonitorDashboard.createUserControlPanel.reportError=Error generating report",
464 "# {0} - Report file name",
465 "HealthMonitorDashboard.createUserControlPanel.reportDone=Report saved to: {0}"})
467 JPanel userControlPanel =
new JPanel();
471 return userControlPanel;
475 String[] dateOptionStrings = Arrays.stream(
DateRange.values()).map(e -> e.getLabel()).toArray(String[]::new);
482 public void actionPerformed(ActionEvent arg0) {
485 }
catch (HealthMonitorException ex) {
486 logger.log(Level.SEVERE,
"Error updating user metric panel", ex);
492 userControlPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserControlPanel_maxDays()));
496 JButton reportButton =
new JButton(Bundle.HealthMonitorDashboard_createUserControlPanel_userReportButton());
499 reportButton.addActionListener(
new ActionListener() {
501 public void actionPerformed(ActionEvent arg0) {
503 reportFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
505 final DateFormat csvTimestampFormat =
new SimpleDateFormat(
"yyyyMMdd_HHmmss");
506 String fileName =
"UserReport_" + csvTimestampFormat.format(
new Date())+
".csv";
507 reportFileChooser.setSelectedFile(
new File(fileName));
509 int returnVal = reportFileChooser.showSaveDialog(userControlPanel);
510 if (returnVal == JFileChooser.APPROVE_OPTION) {
512 File selectedFile = reportFileChooser.getSelectedFile();
515 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
518 }
catch (HealthMonitorException ex) {
519 logger.log(Level.SEVERE,
"Error generating report", ex);
522 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
527 userControlPanel.add(reportButton);
529 return userControlPanel;
540 final DateFormat timestampFormat =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
542 try (BufferedWriter writer =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(reportFile), StandardCharsets.UTF_8))) {
544 writer.write(
"Case open,Case close,Duration,Host,User,Case name");
549 Collections.sort(dataForReport);
553 for (
int caseOpenIndex = 0; caseOpenIndex < dataForReport.size() - 1; caseOpenIndex++) {
560 HealthMonitor.UserData caseOpenEvent = dataForReport.get(caseOpenIndex);
561 HealthMonitor.UserData nextEventAfterCaseOpen =
null;
562 for (
int nextEventIndex = caseOpenIndex + 1; nextEventIndex < dataForReport.size(); nextEventIndex++) {
563 HealthMonitor.UserData nextEvent = dataForReport.get(nextEventIndex);
565 if ( nextEvent.getHostname().equals(caseOpenEvent.getHostname())
566 && nextEvent.getUserName().equals(caseOpenEvent.getUserName())) {
567 nextEventAfterCaseOpen = nextEvent;
573 String caseOpenTime = timestampFormat.format(caseOpenEvent.getTimestamp());
581 String caseCloseTime =
"";
582 String duration =
"";
583 if (nextEventAfterCaseOpen !=
null
585 && nextEventAfterCaseOpen.getCaseName().equals(caseOpenEvent.getCaseName())) {
586 caseCloseTime = timestampFormat.format(nextEventAfterCaseOpen.getTimestamp());
587 duration =
getDuration(caseOpenEvent.getTimestamp(), nextEventAfterCaseOpen.getTimestamp());
590 String host = caseOpenEvent.getHostname();
591 String user = caseOpenEvent.getUserName();
592 String caseName = caseOpenEvent.getCaseName();
594 String csvEntry = caseOpenTime +
"," + caseCloseTime +
"," + duration +
"," + host +
"," + user +
",\"" + caseName +
"\"";
595 writer.write(csvEntry);
598 }
catch (IOException ex) {
599 throw new HealthMonitorException(
"Error writing to output file " + reportFile.getAbsolutePath(), ex);
613 long durationInSeconds = (end - start) / 1000;
614 long second = durationInSeconds % 60;
615 long minute = (durationInSeconds / 60) % 60;
616 long hours = durationInSeconds / (60 * 60);
618 return String.format(
"%d:%02d:%02d", hours, minute, second);
625 @NbBundle.Messages({
"HealthMonitorDashboard.updateUserMetricGraphs.noData=No data to display"})
631 if(userData.isEmpty()) {
633 userGraphPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_updateUserMetricGraphs_noData()));
642 long timestampThreshold;
645 timestampThreshold = System.currentTimeMillis() - selectedDateRange.
getTimestampRange();
652 UserMetricGraphPanel caseGraphPanel =
new UserMetricGraphPanel(userData, timestampThreshold,
true);
653 caseGraphPanel.setPreferredSize(
new Dimension(700,200));
655 UserMetricGraphPanel logonGraphPanel =
new UserMetricGraphPanel(userData, timestampThreshold,
false);
656 logonGraphPanel.setPreferredSize(
new Dimension(700,200));
669 @NbBundle.Messages({
"HealthMonitorDashboard.createAdminPanel.enableButton=Enable monitor",
670 "HealthMonitorDashboard.createAdminPanel.disableButton=Disable monitor"})
673 JPanel adminPanel =
new JPanel();
674 adminPanel.setBorder(BorderFactory.createEtchedBorder());
677 JButton enableButton =
new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_enableButton());
678 JButton disableButton =
new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_disableButton());
681 enableButton.setEnabled(! isEnabled);
682 disableButton.setEnabled(isEnabled);
685 enableButton.addActionListener(
new ActionListener() {
687 public void actionPerformed(ActionEvent arg0) {
689 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
692 }
catch (HealthMonitorException ex) {
693 logger.log(Level.SEVERE,
"Error enabling monitoring", ex);
695 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
701 disableButton.addActionListener(
new ActionListener() {
703 public void actionPerformed(ActionEvent arg0) {
705 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
708 }
catch (HealthMonitorException ex) {
709 logger.log(Level.SEVERE,
"Error disabling monitoring", ex);
711 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
717 adminPanel.add(enableButton);
718 adminPanel.add(Box.createHorizontalStrut(25));
719 adminPanel.add(disableButton);
727 @NbBundle.Messages({
"HealthMonitorDashboard.DateRange.oneMonth=One month",
728 "HealthMonitorDashboard.DateRange.twoWeeks=Two weeks",
729 "HealthMonitorDashboard.DateRange.oneWeek=One week",
730 "HealthMonitorDashboard.DateRange.oneDay=One day"})
732 ONE_DAY(Bundle.HealthMonitorDashboard_DateRange_oneDay(), 1),
733 ONE_WEEK(Bundle.HealthMonitorDashboard_DateRange_oneWeek(), 7),
734 TWO_WEEKS(Bundle.HealthMonitorDashboard_DateRange_twoWeeks(), 14),
735 ONE_MONTH(Bundle.HealthMonitorDashboard_DateRange_oneMonth(), 31);
764 return Long.MAX_VALUE;
774 long maxRange = Long.MIN_VALUE;
776 if (dateRange.getTimestampRange() > maxRange) {
777 maxRange = dateRange.getTimestampRange();
785 if (dateRange.label.equalsIgnoreCase(text)) {
static String getHealthMonitorReportPath()
static void setHealthMonitorReportPath(String reportPath)
synchronized static Logger getLogger(String name)
static void error(String message)
static void info(String message)
HealthMonitorDashboard(Container parent)
String getDuration(long start, long end)
JPanel createUserControlPanel()
JComboBox< String > userDateComboBox
JCheckBox timingShowTrendLineCheckBox
JPanel createTimingPanel()
JComboBox< String > timingHostComboBox
JPanel createAdminPanel()
JCheckBox timingHostCheckBox
static final String ADMIN_ACCESS_FILE_PATH
final Container parentWindow
void generateCSVUserReport(File reportFile)
JPanel createTimingControlPanel()
JComboBox< String > timingDateComboBox
static final String ADMIN_ACCESS_FILE_NAME
void updateTimingMetricGraphs()
static final Logger logger
final JFileChooserFactory chooserHelper
JCheckBox timingSkipOutliersCheckBox
void updateUserMetricGraphs()
DateRange(String label, long numberOfDays)
static final long MILLISECONDS_PER_DAY
static long getMaximumTimestampRange()
static DateRange fromLabel(String text)