19 package org.sleuthkit.autopsy.healthmonitor;
21 import java.awt.Container;
22 import java.awt.Cursor;
23 import java.awt.Dimension;
25 import java.util.HashSet;
26 import java.util.HashMap;
28 import java.util.Arrays;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
33 import java.io.BufferedWriter;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.OutputStreamWriter;
38 import javax.swing.Box;
39 import javax.swing.JButton;
40 import javax.swing.JDialog;
41 import javax.swing.JComboBox;
42 import javax.swing.JSeparator;
43 import javax.swing.JCheckBox;
44 import javax.swing.JLabel;
45 import javax.swing.JPanel;
46 import javax.swing.JScrollPane;
47 import javax.swing.BorderFactory;
48 import javax.swing.BoxLayout;
49 import java.awt.GridLayout;
50 import java.nio.charset.StandardCharsets;
51 import java.nio.file.Paths;
52 import java.text.DateFormat;
53 import java.text.SimpleDateFormat;
54 import java.util.Date;
55 import java.util.Collections;
56 import java.util.logging.Level;
57 import java.util.stream.Collectors;
58 import javax.swing.JFileChooser;
59 import org.openide.util.NbBundle;
95 timingData =
new HashMap<>();
96 userData =
new ArrayList<>();
97 parentWindow = parent;
103 @NbBundle.Messages({
"HealthMonitorDashboard.display.errorCreatingDashboard=Error creating health monitor dashboard",
104 "HealthMonitorDashboard.display.dashboardTitle=Health Monitor"})
117 }
catch (HealthMonitorException ex) {
118 logger.log(Level.SEVERE,
"Error creating panels for health monitor dashboard", ex);
124 JPanel mainPanel =
new JPanel();
125 mainPanel.setLayout(
new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
128 mainPanel.add(timingPanel);
131 mainPanel.add(userPanel);
134 File adminFile =
new File(ADMIN_ACCESS_FILE_PATH);
135 if(adminFile.exists()) {
136 mainPanel.add(adminPanel);
140 dialog =
new JDialog();
141 dialog.setTitle(Bundle.HealthMonitorDashboard_display_dashboardTitle());
142 dialog.add(mainPanel);
144 dialog.setLocationRelativeTo(parentWindow);
145 dialog.setVisible(
true);
153 if (dialog != null) {
154 dialog.setVisible(
false);
183 @NbBundle.Messages({
"HealthMonitorDashboard.createTimingPanel.noData=No data to display - monitor is not enabled",
184 "HealthMonitorDashboard.createTimingPanel.timingMetricsTitle=Timing Metrics"})
190 JPanel emptyTimingMetricPanel =
new JPanel();
191 emptyTimingMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle()));
192 emptyTimingMetricPanel.add(
new JLabel(
" "));
193 emptyTimingMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_noData()));
195 return emptyTimingMetricPanel;
198 JPanel timingMetricPanel =
new JPanel();
199 timingMetricPanel.setLayout(
new BoxLayout(timingMetricPanel, BoxLayout.PAGE_AXIS));
200 timingMetricPanel.setBorder(BorderFactory.createEtchedBorder());
203 JLabel timingMetricTitle =
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle());
204 timingMetricPanel.add(timingMetricTitle);
205 timingMetricPanel.add(
new JSeparator());
209 timingMetricPanel.add(
new JSeparator());
212 timingGraphPanel =
new JPanel();
213 timingGraphPanel.setLayout(
new GridLayout(0,2));
217 JScrollPane scrollPane =
new JScrollPane(timingGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
218 timingMetricPanel.add(scrollPane);
219 timingMetricPanel.revalidate();
220 timingMetricPanel.repaint();
222 return timingMetricPanel;
229 @NbBundle.Messages({
"HealthMonitorDashboard.createTimingControlPanel.filterByHost=Filter by host",
230 "HealthMonitorDashboard.createTimingControlPanel.maxDays=Max days to display",
231 "HealthMonitorDashboard.createTimingControlPanel.skipOutliers=Do not plot outliers",
232 "HealthMonitorDashboard.createTimingControlPanel.showTrendLine=Show trend line"})
234 JPanel timingControlPanel =
new JPanel();
238 return timingControlPanel;
242 String[] dateOptionStrings = Arrays.stream(
DateRange.values()).map(e -> e.getLabel()).toArray(String[]::
new);
243 timingDateComboBox =
new JComboBox<>(dateOptionStrings);
247 timingDateComboBox.addActionListener(
new ActionListener() {
249 public void actionPerformed(ActionEvent arg0) {
252 }
catch (HealthMonitorException ex) {
253 logger.log(Level.SEVERE,
"Error updating timing metric panel", ex);
259 Set<String> hostNameSet =
new HashSet<>();
260 for(String metricType:timingData.keySet()) {
262 hostNameSet.add(result.getHostName());
267 timingHostComboBox =
new JComboBox<>(hostNameSet.toArray(
new String[hostNameSet.size()]));
270 timingHostComboBox.addActionListener(
new ActionListener() {
272 public void actionPerformed(ActionEvent arg0) {
274 if((timingHostCheckBox != null) && timingHostCheckBox.isSelected()) {
277 }
catch (HealthMonitorException ex) {
278 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
284 timingHostCheckBox =
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_filterByHost());
285 timingHostCheckBox.setSelected(
false);
286 timingHostComboBox.setEnabled(
false);
289 timingHostCheckBox.addActionListener(
new ActionListener() {
291 public void actionPerformed(ActionEvent arg0) {
293 timingHostComboBox.setEnabled(timingHostCheckBox.isSelected());
295 }
catch (HealthMonitorException ex) {
296 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
302 timingShowTrendLineCheckBox =
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_showTrendLine());
303 timingShowTrendLineCheckBox.setSelected(
true);
306 timingShowTrendLineCheckBox.addActionListener(
new ActionListener() {
308 public void actionPerformed(ActionEvent arg0) {
311 }
catch (HealthMonitorException ex) {
312 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
318 timingSkipOutliersCheckBox =
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_skipOutliers());
319 timingSkipOutliersCheckBox.setSelected(
false);
322 timingSkipOutliersCheckBox.addActionListener(
new ActionListener() {
324 public void actionPerformed(ActionEvent arg0) {
327 }
catch (HealthMonitorException ex) {
328 logger.log(Level.SEVERE,
"Error populating timing metric panel", ex);
334 timingControlPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingControlPanel_maxDays()));
335 timingControlPanel.add(timingDateComboBox);
338 timingControlPanel.add(Box.createHorizontalStrut(100));
341 timingControlPanel.add(timingHostCheckBox);
342 timingControlPanel.add(timingHostComboBox);
345 timingControlPanel.add(Box.createHorizontalStrut(100));
348 timingControlPanel.add(this.timingShowTrendLineCheckBox);
351 timingControlPanel.add(Box.createHorizontalStrut(100));
354 timingControlPanel.add(this.timingSkipOutliersCheckBox);
356 return timingControlPanel;
363 @NbBundle.Messages({
"HealthMonitorDashboard.updateTimingMetricGraphs.noData=No data to display"})
367 timingGraphPanel.removeAll();
369 if(timingData.keySet().isEmpty()) {
371 timingGraphPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_updateTimingMetricGraphs_noData()));
375 for(String metricName:timingData.keySet()) {
379 if(timingDateComboBox.getSelectedItem() != null) {
381 long threshold = System.currentTimeMillis() - selectedDateRange.
getTimestampRange();
382 intermediateTimingDataForDisplay = timingData.get(metricName).stream()
383 .filter(t -> t.getTimestamp() > threshold)
384 .collect(Collectors.toList());
386 intermediateTimingDataForDisplay = timingData.get(metricName);
392 String hostToDisplay = null;
393 if(timingHostCheckBox.isSelected() && (timingHostComboBox.getSelectedItem() != null)) {
394 hostToDisplay = timingHostComboBox.getSelectedItem().toString();
398 TimingMetricGraphPanel singleTimingGraphPanel =
new TimingMetricGraphPanel(intermediateTimingDataForDisplay,
399 hostToDisplay,
true, metricName, timingSkipOutliersCheckBox.isSelected(), timingShowTrendLineCheckBox.isSelected());
400 singleTimingGraphPanel.setPreferredSize(
new Dimension(700,200));
402 timingGraphPanel.add(singleTimingGraphPanel);
404 timingGraphPanel.revalidate();
405 timingGraphPanel.repaint();
413 @NbBundle.Messages({
"HealthMonitorDashboard.createUserPanel.noData=No data to display - monitor is not enabled",
414 "HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics"})
418 JPanel emptyUserMetricPanel =
new JPanel();
419 emptyUserMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle()));
420 emptyUserMetricPanel.add(
new JLabel(
" "));
421 emptyUserMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_noData()));
423 return emptyUserMetricPanel;
426 JPanel userMetricPanel =
new JPanel();
427 userMetricPanel.setLayout(
new BoxLayout(userMetricPanel, BoxLayout.PAGE_AXIS));
428 userMetricPanel.setBorder(BorderFactory.createEtchedBorder());
431 JLabel userMetricTitle =
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle());
432 userMetricPanel.add(userMetricTitle);
433 userMetricPanel.add(
new JSeparator());
437 userMetricPanel.add(
new JSeparator());
440 userGraphPanel =
new JPanel();
441 userGraphPanel.setLayout(
new GridLayout(0,2));
445 JScrollPane scrollPane =
new JScrollPane(userGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
446 userMetricPanel.add(scrollPane);
447 userMetricPanel.revalidate();
448 userMetricPanel.repaint();
450 return userMetricPanel;
457 @NbBundle.Messages({
"HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display",
458 "HealthMonitorDashboard.createUserControlPanel.userReportButton=Generate Report",
459 "HealthMonitorDashboard.createUserControlPanel.reportError=Error generating report",
460 "# {0} - Report file name",
461 "HealthMonitorDashboard.createUserControlPanel.reportDone=Report saved to: {0}"})
463 JPanel userControlPanel =
new JPanel();
467 return userControlPanel;
471 String[] dateOptionStrings = Arrays.stream(
DateRange.values()).map(e -> e.getLabel()).toArray(String[]::
new);
472 userDateComboBox =
new JComboBox<>(dateOptionStrings);
476 userDateComboBox.addActionListener(
new ActionListener() {
478 public void actionPerformed(ActionEvent arg0) {
481 }
catch (HealthMonitorException ex) {
482 logger.log(Level.SEVERE,
"Error updating user metric panel", ex);
488 userControlPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserControlPanel_maxDays()));
489 userControlPanel.add(userDateComboBox);
492 JButton reportButton =
new JButton(Bundle.HealthMonitorDashboard_createUserControlPanel_userReportButton());
495 reportButton.addActionListener(
new ActionListener() {
497 public void actionPerformed(ActionEvent arg0) {
498 JFileChooser reportFileChooser =
new JFileChooser();
499 reportFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
501 final DateFormat csvTimestampFormat =
new SimpleDateFormat(
"yyyyMMdd_HHmmss");
502 String fileName =
"UserReport_" + csvTimestampFormat.format(
new Date())+
".csv";
503 reportFileChooser.setSelectedFile(
new File(fileName));
505 int returnVal = reportFileChooser.showSaveDialog(userControlPanel);
506 if (returnVal == JFileChooser.APPROVE_OPTION) {
508 File selectedFile = reportFileChooser.getSelectedFile();
511 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
514 }
catch (HealthMonitorException ex) {
515 logger.log(Level.SEVERE,
"Error generating report", ex);
518 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
523 userControlPanel.add(reportButton);
525 return userControlPanel;
536 final DateFormat timestampFormat =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
538 try (BufferedWriter writer =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(reportFile), StandardCharsets.UTF_8))) {
540 writer.write(
"Case open,Case close,Duration,Host,User,Case name");
545 Collections.sort(dataForReport);
549 for (
int caseOpenIndex = 0; caseOpenIndex < dataForReport.size() - 1; caseOpenIndex++) {
558 for (
int nextEventIndex = caseOpenIndex + 1; nextEventIndex < dataForReport.size(); nextEventIndex++) {
561 if ( nextEvent.getHostname().equals(caseOpenEvent.getHostname())
562 && nextEvent.getUserName().equals(caseOpenEvent.getUserName())) {
563 nextEventAfterCaseOpen = nextEvent;
569 String caseOpenTime = timestampFormat.format(caseOpenEvent.getTimestamp());
577 String caseCloseTime =
"";
578 String duration =
"";
579 if (nextEventAfterCaseOpen != null
581 && nextEventAfterCaseOpen.getCaseName().equals(caseOpenEvent.getCaseName())) {
582 caseCloseTime = timestampFormat.format(nextEventAfterCaseOpen.getTimestamp());
583 duration =
getDuration(caseOpenEvent.getTimestamp(), nextEventAfterCaseOpen.getTimestamp());
586 String host = caseOpenEvent.getHostname();
587 String user = caseOpenEvent.getUserName();
588 String caseName = caseOpenEvent.getCaseName();
590 String csvEntry = caseOpenTime +
"," + caseCloseTime +
"," + duration +
"," + host +
"," + user +
",\"" + caseName +
"\"";
591 writer.write(csvEntry);
594 }
catch (IOException ex) {
595 throw new HealthMonitorException(
"Error writing to output file " + reportFile.getAbsolutePath(), ex);
609 long durationInSeconds = (end - start) / 1000;
610 long second = durationInSeconds % 60;
611 long minute = (durationInSeconds / 60) % 60;
612 long hours = durationInSeconds / (60 * 60);
614 return String.format(
"%d:%02d:%02d", hours, minute, second);
621 @NbBundle.Messages({
"HealthMonitorDashboard.updateUserMetricGraphs.noData=No data to display"})
625 userGraphPanel.removeAll();
627 if(userData.isEmpty()) {
629 userGraphPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_updateUserMetricGraphs_noData()));
638 long timestampThreshold;
639 if(userDateComboBox.getSelectedItem() != null) {
641 timestampThreshold = System.currentTimeMillis() - selectedDateRange.
getTimestampRange();
648 UserMetricGraphPanel caseGraphPanel =
new UserMetricGraphPanel(userData, timestampThreshold,
true);
649 caseGraphPanel.setPreferredSize(
new Dimension(700,200));
651 UserMetricGraphPanel logonGraphPanel =
new UserMetricGraphPanel(userData, timestampThreshold,
false);
652 logonGraphPanel.setPreferredSize(
new Dimension(700,200));
654 userGraphPanel.add(caseGraphPanel);
655 userGraphPanel.add(logonGraphPanel);
656 userGraphPanel.revalidate();
657 userGraphPanel.repaint();
665 @NbBundle.Messages({
"HealthMonitorDashboard.createAdminPanel.enableButton=Enable monitor",
666 "HealthMonitorDashboard.createAdminPanel.disableButton=Disable monitor"})
669 JPanel adminPanel =
new JPanel();
670 adminPanel.setBorder(BorderFactory.createEtchedBorder());
673 JButton enableButton =
new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_enableButton());
674 JButton disableButton =
new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_disableButton());
677 enableButton.setEnabled(! isEnabled);
678 disableButton.setEnabled(isEnabled);
681 enableButton.addActionListener(
new ActionListener() {
683 public void actionPerformed(ActionEvent arg0) {
685 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
688 }
catch (HealthMonitorException ex) {
689 logger.log(Level.SEVERE,
"Error enabling monitoring", ex);
691 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
697 disableButton.addActionListener(
new ActionListener() {
699 public void actionPerformed(ActionEvent arg0) {
701 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
704 }
catch (HealthMonitorException ex) {
705 logger.log(Level.SEVERE,
"Error disabling monitoring", ex);
707 dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
713 adminPanel.add(enableButton);
714 adminPanel.add(Box.createHorizontalStrut(25));
715 adminPanel.add(disableButton);
723 @NbBundle.Messages({
"HealthMonitorDashboard.DateRange.oneMonth=One month",
724 "HealthMonitorDashboard.DateRange.twoWeeks=Two weeks",
725 "HealthMonitorDashboard.DateRange.oneWeek=One week",
726 "HealthMonitorDashboard.DateRange.oneDay=One day"})
728 ONE_DAY(Bundle.HealthMonitorDashboard_DateRange_oneDay(), 1),
729 ONE_WEEK(Bundle.HealthMonitorDashboard_DateRange_oneWeek(), 7),
730 TWO_WEEKS(Bundle.HealthMonitorDashboard_DateRange_twoWeeks(), 14),
731 ONE_MONTH(Bundle.HealthMonitorDashboard_DateRange_oneMonth(), 31);
735 private static final long MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
739 this.numberOfDays = numberOfDays;
757 if (numberOfDays > 0) {
758 return numberOfDays * MILLISECONDS_PER_DAY;
760 return Long.MAX_VALUE;
770 long maxRange = Long.MIN_VALUE;
772 if (dateRange.getTimestampRange() > maxRange) {
773 maxRange = dateRange.getTimestampRange();
781 if (dateRange.label.equalsIgnoreCase(text)) {
JPanel createTimingPanel()
JCheckBox timingSkipOutliersCheckBox
final Container parentWindow
static DateRange fromLabel(String text)
HealthMonitorDashboard(Container parent)
JPanel createTimingControlPanel()
static final String ADMIN_ACCESS_FILE_NAME
static void setHealthMonitorReportPath(String reportPath)
JPanel createAdminPanel()
JCheckBox timingShowTrendLineCheckBox
DateRange(String label, long numberOfDays)
void updateTimingMetricGraphs()
JCheckBox timingHostCheckBox
static final String ADMIN_ACCESS_FILE_PATH
JPanel createUserControlPanel()
static String getHealthMonitorReportPath()
JComboBox< String > timingHostComboBox
JComboBox< String > timingDateComboBox
JComboBox< String > userDateComboBox
void updateUserMetricGraphs()
synchronized static Logger getLogger(String name)
static final Logger logger
static long getMaximumTimestampRange()
void generateCSVUserReport(File reportFile)
static void info(String message)
String getDuration(long start, long end)
static void error(String message)