You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ji...@apache.org on 2019/08/28 03:02:00 UTC
[incubator-iotdb] branch log_tool updated: add comments
This is an automated email from the ASF dual-hosted git repository.
jiangtian pushed a commit to branch log_tool
in repository https://gitbox.apache.org/repos/asf/incubator-iotdb.git
The following commit(s) were added to refs/heads/log_tool by this push:
new 22b29d6 add comments
22b29d6 is described below
commit 22b29d63d6767c37a2470ac5a505f5b69c9d6caf
Author: jt <jt...@163.com>
AuthorDate: Wed Aug 28 10:52:08 2019 +0800
add comments
---
.../resources/tools/logAnalyze/default.log.pattern | 12 +-
.../logAnalyze/plans/flushTimeConsumption.plan | 23 +++-
.../apache/iotdb/db/tools/logvisual/LogEntry.java | 41 ++++++
.../apache/iotdb/db/tools/logvisual/LogFilter.java | 53 +++++---
.../apache/iotdb/db/tools/logvisual/LogParser.java | 11 ++
.../iotdb/db/tools/logvisual/LogVisualizer.java | 141 +++++++++++++--------
.../iotdb/db/tools/logvisual/PatternLogParser.java | 12 ++
.../db/tools/logvisual/TimeSeriesStatistics.java | 27 +---
.../iotdb/db/tools/logvisual/VisualUtils.java | 6 +-
.../db/tools/logvisual/VisualizationPlan.java | 35 ++++-
.../db/tools/logvisual/conf/PropertyKeys.java | 18 ++-
.../db/tools/logvisual/gui/ClosableComboTab.java | 7 +-
.../iotdb/db/tools/logvisual/gui/ClosableTab.java | 5 +-
.../db/tools/logvisual/gui/FileSelectionBox.java | 13 +-
.../db/tools/logvisual/gui/LabeledComboBox.java | 8 +-
.../iotdb/db/tools/logvisual/gui/LoadLogBox.java | 29 +++--
...gVisualizeGui.java => LogVisualizationGui.java} | 46 ++++++-
.../iotdb/db/tools/logvisual/gui/MainPanel.java | 30 ++++-
.../iotdb/db/tools/logvisual/gui/PlanBox.java | 35 ++++-
.../db/tools/logvisual/gui/PlanDetailPanel.java | 23 +++-
.../db/tools/logvisual/gui/ResultPlotTab.java | 4 +
.../db/tools/logvisual/gui/ResultStatisticTab.java | 10 +-
22 files changed, 427 insertions(+), 162 deletions(-)
diff --git a/server/src/assembly/resources/tools/logAnalyze/default.log.pattern b/server/src/assembly/resources/tools/logAnalyze/default.log.pattern
index 9cf7f9f..57fdf42 100644
--- a/server/src/assembly/resources/tools/logAnalyze/default.log.pattern
+++ b/server/src/assembly/resources/tools/logAnalyze/default.log.pattern
@@ -17,10 +17,20 @@
# under the License.
#
+# Pattern defines how a log looks like. A log must be exactly one line.
pattern=([^\\[]*)(\\[.*])(\\s\\w+\\s)([^:]*:\\d+)(\\s-\\s)(.*)
+# The index of the group which represents the date that the log is generated, one based
date_index=1
+# How the date is formatted
+date_pattern=yyyy-MM-dd hh:mm:ss,SSS
+# The index of the group which represents the thread name, one based
thread_name_index=2
+# The index of the group which represents the log level, one based
level_index=3
+# The index of the group which represents the position of the code (class name and line number)
+# that generated the log, one based
code_location_index=4
+# The index of the group which represents the message in the log, one based
content_index=6
-date_pattern=yyyy-MM-dd hh:mm:ss,SSS
+
+
diff --git a/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan b/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan
index ccf1b11..d88232d 100644
--- a/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan
+++ b/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan
@@ -17,19 +17,34 @@
# under the License.
#
+# the name of the this plan
name=flushTimeConsumption
-content_pattern=Storage group (.*) memtable (.*) flushing a memtable has finished! Time consumption: (\\d+)ms
+# how are the targeted logs formatted and what fields are interesting and should be grouped
+content_pattern=Storage group (.*) memtable (.*) flushing a memtable has finished! Time consumption: (.*)ms
-# if you add more, use comma to separate
+# the following 3 properties are all one-based, if there are more than one measurements or tags, use
+# comma to separate the positions
+# the position of the groups in the pattern that will be plotted as values, must be real values
measurement_positions=3
+# the name of each measurement
legends=Time
+# the position of the groups in the pattern that will be used to group the logs, each log group will
+# be plotted separately
tag_positions=1
-# min_level=info
-# thread_name_white_list=pool-1-IoTDB-JDBC-CLient-thread-5
+# only logs with levels higher or equal to this will be visualized, one of DEBUG,INFO,WARN,ERROR
+# min_level=INFO
+# only logs whose threads are in the list will be visualized, comma separated
+# thread_name_white_list=pool-1-IoTDB-JDBC-Client-thread-5
+# only logs whose classes are in the list will be visualized, comma separated
# class_name_white_list=org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor
+# only logs whose line numbers are in the list will be visualized, comma separated
# line_num_white_list=392
+# the pattern of start_date and end_date, only after this is set will start_date and end_date become
+# valid
date_pattern=yyyy-MM-dd hh:mm:ss
+# only logs generated within [start_date, end_date] will be visualized and the dates must be
+# parsable by date_pattern
start_date=2019-08-21 09:00:00
end_date=2019-08-22 09:00:00
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogEntry.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogEntry.java
index 18b7634..8d8e1df 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogEntry.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogEntry.java
@@ -4,17 +4,58 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
+/**
+ * LogEntry is a parsed log event.
+ * We will take the log:
+ * "2019-08-21 09:57:16,552 [pool-4-IoTDB-Flush-ServerServiceImpl-thread-4] INFO org.apache
+ * .iotdb.db.engine.flush.MemTableFlushTask:95 - Storage group root.perform.group_6 memtable org
+ * .apache.iotdb.db.engine.memtable.PrimitiveMemTable@66 flushing a memtable has finished! Time
+ * consumption: 9942ms"
+ * as a running example.
+ */
public class LogEntry {
+ // if a visualization plan has no tag, this tag will be the tag of all events
private static List<String> DEFAULT_TAG = Collections.EMPTY_LIST;
+ /**
+ * The time when the log is logged.
+ * "2019-08-21 09:57:16,552" in the example.
+ */
private Date date;
+ /**
+ * The name of the thread which has written the log.
+ * "pool-4-IoTDB-Flush-ServerServiceImpl-thread-4" in the example.
+ */
private String threadName;
+ /**
+ * The level of the log, one of "DEBUG", "INFO", "WARN", "ERROR".
+ * "INFO" in the example.
+ */
private LogLevel logLevel;
+ /**
+ * The class and line of code that generated this log.
+ * "org.apache.iotdb.db.engine.flush.MemTableFlushTask:95" in the example.
+ */
private CodeLocation codeLocation;
+ /**
+ * The message contained in the log.
+ * "Storage group root.perform.group_6 memtable org
+ * .apache.iotdb.db.engine.memtable.PrimitiveMemTable@66 flushing a memtable has finished! Time
+ * consumption: 9942ms" in the example.
+ */
private String logContent;
+ /**
+ * tags are a list of strings cut out of the logContent to group the logs into different groups.
+ * This is specified by a given visualization plan.
+ */
private List<String> tags = DEFAULT_TAG;
+
+ /**
+ * measurements are real values occurred in the logContent that should be plotted.
+ * This is also specified by a given visualization plan.
+ */
private List<Double> measurements;
LogEntry(Date date, String threadName,
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogFilter.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogFilter.java
index 662ccde..fdc37e5 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogFilter.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogFilter.java
@@ -10,29 +10,38 @@ import java.util.Date;
import java.util.Properties;
import org.apache.iotdb.db.tools.logvisual.LogEntry.LogLevel;
+/**
+ * LogFilter filters log events by its level, threadName, className, lineNum and date.
+ */
public class LogFilter {
- // optional, only logs with levels equal to or higher than this will be analyzed
- private LogLevel minLevel = LogLevel.DEBUG;
- // optional, only threads, classes, lines in the lists are analyzed. When unset, all logs will
+
+ /**
+ * optional, only logs with levels equal to or higher than this will be analyzed
+ */
+ private LogLevel minLevel;
+
+ // optional, only threads, classes, lines in the lists are analyzed. When unset, all logs will be
// analyzed. comma-separated
private String[] threadNameWhiteList;
private String[] classNameWhiteList;
private int[] lineNumWhiteList;
- // optional, only time ranges within the interval will be analyzed
- // if startDate or endDate is set, datePattern must be set too
- private DateFormat datePartten;
+
+ /**
+ * optional, only time ranges within the interval will be analyzed
+ * if startDate or endDate is set, datePattern must be set too
+ */
+ private DateFormat datePattern;
private Date startDate = new Date(Long.MIN_VALUE);
private Date endDate = new Date(Long.MAX_VALUE);
- public LogFilter() {
+ LogFilter() {
minLevel = LogLevel.DEBUG;
}
- public LogFilter(Properties properties) throws IOException {
+ LogFilter(Properties properties) throws IOException {
minLevel = LogLevel.valueOf(properties.getProperty(MIN_LEVEL.getPropertyName(), minLevel.name()));
- String threadNameWhiteListStr = properties.getProperty(THREAD_NAME_WHITE_LIST.getPropertyName
- ());
+ String threadNameWhiteListStr = properties.getProperty(THREAD_NAME_WHITE_LIST.getPropertyName());
if (threadNameWhiteListStr != null) {
threadNameWhiteList = threadNameWhiteListStr.trim().split(",");
}
@@ -47,11 +56,13 @@ public class LogFilter {
String datePatternStr = properties.getProperty(DATE_PATTERN.getPropertyName());
if (datePatternStr != null) {
- this.datePartten = new SimpleDateFormat(datePatternStr.trim());
+ this.datePattern = new SimpleDateFormat(datePatternStr.trim());
+
+ // only when date pattern is set should we parse the start date and end date
String startDateStr = properties.getProperty(START_DATE.getPropertyName());
if (startDateStr != null) {
try {
- startDate = datePartten.parse(startDateStr.trim());
+ startDate = datePattern.parse(startDateStr.trim());
} catch (ParseException e) {
throw new IOException(e);
}
@@ -60,7 +71,7 @@ public class LogFilter {
String endDatePattern = properties.getProperty(END_DATE.getPropertyName());
if (startDateStr != null) {
try {
- endDate = datePartten.parse(endDatePattern.trim());
+ endDate = datePattern.parse(endDatePattern.trim());
} catch (ParseException e) {
throw new IOException(e);
}
@@ -119,8 +130,8 @@ public class LogFilter {
return lineNumWhiteList;
}
- public DateFormat getDatePatten() {
- return datePartten;
+ public DateFormat getDatePattern() {
+ return datePattern;
}
public Date getStartDate() {
@@ -147,8 +158,8 @@ public class LogFilter {
this.lineNumWhiteList = lineNumWhiteList;
}
- public void setDatePartten(DateFormat datePartten) {
- this.datePartten = datePartten;
+ public void setDatePattern(DateFormat datePattern) {
+ this.datePattern = datePattern;
}
public void setStartDate(Date startDate) {
@@ -172,13 +183,13 @@ public class LogFilter {
(lineNumWhiteList));
}
if (startDate != null) {
- properties.put(START_DATE.propertyName, datePartten.format(startDate));
+ properties.put(START_DATE.propertyName, datePattern.format(startDate));
}
if (endDate != null) {
- properties.put(END_DATE.propertyName, datePartten.format(endDate));
+ properties.put(END_DATE.propertyName, datePattern.format(endDate));
}
- if (datePartten != null) {
- properties.put(DATE_PATTERN.propertyName, ((SimpleDateFormat) datePartten).toPattern());
+ if (datePattern != null) {
+ properties.put(DATE_PATTERN.propertyName, ((SimpleDateFormat) datePattern).toPattern());
}
}
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogParser.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogParser.java
index 2675085..356f17d 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogParser.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogParser.java
@@ -2,6 +2,9 @@ package org.apache.iotdb.db.tools.logvisual;
import java.io.IOException;
+/**
+ * LogParser works as an iterator of logs.
+ */
public interface LogParser {
/**
@@ -9,7 +12,15 @@ public interface LogParser {
*/
LogEntry next() throws IOException;
+ /**
+ * Release resources such as file streams.
+ * @throws IOException
+ */
void close() throws IOException;
+ /**
+ * Start the parse from the beginning. Must be called before the first call to next().
+ * @throws IOException
+ */
void reset() throws IOException;
}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogVisualizer.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogVisualizer.java
index f897fb1..f94ac5e 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogVisualizer.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogVisualizer.java
@@ -14,7 +14,6 @@ import java.util.Map.Entry;
import java.util.Properties;
import org.apache.iotdb.db.tools.logvisual.exceptions.AnalyzeException;
import org.apache.iotdb.db.tools.logvisual.exceptions.NoLogFileLoadedException;
-import org.apache.iotdb.db.tools.logvisual.exceptions.NoSuchPlanException;
import org.apache.iotdb.db.tools.logvisual.exceptions.UnmatchedContentException;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
@@ -26,24 +25,65 @@ import org.jfree.data.time.TimeSeriesCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
+/**
+ * LogVisualizer is the backend of this tool, which parses, groups and plots the logs according
+ * to a given visualization plan.
+ * The general procedure of one visualization would be:
+ * 1. call setLogFile() and setPropertyFile() to set those files;
+ * 2. call loadLogParser() to create a logParser;
+ * 3. call executePlan() to execute a visualization plan.
+ * 4. call getCharts() and getStatisticMap() to get the results.
+ */
public class LogVisualizer {
private static final Logger logger = LoggerFactory.getLogger(LogVisualizer.class);
private LogParser logParser;
+ /**
+ * logCache stores the logs that satisfies the filter of the plan being executed.
+ */
private List<LogEntry> logCache = new ArrayList<>();
+ /**
+ * logGroups stores the logs by their their tags (specified by the plan).
+ */
+ private Map<List<String>, List<LogEntry>> logGroups = new HashMap<>();
+ /**
+ * the plans currently loaded into memory.
+ */
private Map<String, VisualizationPlan> plans = new HashMap<>();
- private Map<List<String>, List<LogEntry>> logGroups = new HashMap<>();
+ /**
+ * the timeseries plots generated for each group of logs.
+ */
private Map<String, JFreeChart> charts;
+ /**
+ * the statistics (count, mean, min, max) of each group of logs.
+ */
private Map<String, List<TimeSeriesStatistics>> statisticsMap;
+ /**
+ * the file that defines how to parse the logs in the log file.
+ */
private File parserPropertyFile;
+ /**
+ * the file that contains the logs to be visualized.
+ */
private File logFile;
- private void clearLogs() {
- logCache.clear();
+ public static void main(String[] args) throws IOException {
+ LogVisualizer visualizer = new LogVisualizer();
+ visualizer.setParserPropertyFile(new File("E:\\codestore\\incubator-iotdb\\server\\src\\assembly"
+ + "\\resources"
+ + "\\tools\\logAnalyze\\default.log.pattern"));
+ visualizer.setLogFile( new File("C:\\Users\\admin\\Desktop\\logs\\log-all-2019-08-21.0.log"));
+ visualizer.loadLogParser();
+ visualizer.loadPlan("E:\\codestore\\incubator-iotdb\\server\\src\\assembly\\resources\\tools\\logAnalyze\\plans\\flushTimeConsumption.plan");
+ }
+
+ private void clearLogGroups() {
+ // log cache is cleared after logGroups are generated, so we do not clear it here
logGroups.clear();
}
@@ -54,6 +94,8 @@ public class LogVisualizer {
if (logFile == null) {
throw new IOException("Log file unset!");
}
+ // close the previous
+ close();
String propertyFilePath = parserPropertyFile.getPath();
String logFilePath = logFile.getPath();
Properties properties = new Properties();
@@ -65,8 +107,10 @@ public class LogVisualizer {
}
public void close() throws IOException {
- logParser.close();
- logParser = null;
+ if (logParser != null) {
+ logParser.close();
+ logParser = null;
+ }
}
public void loadPlans(File[] planFiles) throws IOException {
@@ -75,7 +119,7 @@ public class LogVisualizer {
}
}
- public void loadPlan(File file) throws IOException {
+ private void loadPlan(File file) throws IOException {
if (!file.exists()) {
return;
}
@@ -95,19 +139,12 @@ public class LogVisualizer {
return plans.values();
}
- public void executePlan(String planName) throws AnalyzeException {
- VisualizationPlan plan = plans.get(planName);
- if (plan == null) {
- throw new NoSuchPlanException(planName);
- }
- executePlan(plan);
- }
-
public void executePlan(VisualizationPlan plan) throws AnalyzeException {
if (logParser == null) {
throw new NoLogFileLoadedException();
}
try {
+ // read the logs fom the beginning
logParser.reset();
} catch (IOException e) {
throw new AnalyzeException(e);
@@ -117,11 +154,16 @@ public class LogVisualizer {
Map<String,TimeSeriesCollection> taggedTimeSeries = createTimeSeries(plan);
charts = drawCharts(taggedTimeSeries, plan);
statisticsMap = genStatisticMap(taggedTimeSeries);
+ clearLogGroups();
}
+ /**
+ * Read all logs from the logParser and filter unwanted or malformed ones.
+ * @param plan
+ * @throws AnalyzeException
+ */
private void collectLogs(VisualizationPlan plan) throws AnalyzeException {
LogFilter logFilter = plan.getLogFilter();
- clearLogs();
try {
LogEntry logEntry;
readLogs:
@@ -133,6 +175,7 @@ public class LogVisualizer {
}
switch (logFilter.filter(logEntry)) {
+ // all logs must be time-ordered, so on meeting a log that is too new we can end the loop
case BEYOND_END_TIME:
break readLogs;
case REJECT:
@@ -144,13 +187,14 @@ public class LogVisualizer {
} catch (IOException e) {
throw new AnalyzeException(e);
}
- logger.info("Collected {} logs", logCache.size());
+ logger.info("Collected {} logs from {}", logCache.size(), logFile.getPath());
}
private void groupLogs() {
for (LogEntry logEntry : logCache) {
logGroups.computeIfAbsent(logEntry.getTags(), tag -> new ArrayList<>()).add(logEntry);
}
+ logCache.clear();
logger.info("Found {} different tags", logGroups.size());
}
@@ -159,6 +203,7 @@ public class LogVisualizer {
for (Entry<List<String>, List<LogEntry>> entry : logGroups.entrySet()) {
List<String> tags = entry.getKey();
List<LogEntry> logs = entry.getValue();
+ // create a tag string for the log group as the key of the timeseries
String concatenatedTag;
if (tags.isEmpty()) {
concatenatedTag = plan.getName();
@@ -170,27 +215,32 @@ public class LogVisualizer {
builder.append(" ");
concatenatedTag = builder.toString();
}
- TimeSeriesCollection tagTimeseries = new TimeSeriesCollection();
+
+ TimeSeriesCollection timeseriesGroup = new TimeSeriesCollection();
if (plan.getMeasurementPositions() != null) {
+ // the measurements are given, create a timeseries for each measurement
String[] legends = plan.getLegends();
for (String legend : legends) {
+ // name each timeseries with the legend of each measurement
TimeSeries timeSeries = new TimeSeries(concatenatedTag + legend);
- tagTimeseries.addSeries(timeSeries);
+ timeseriesGroup.addSeries(timeSeries);
}
+ // use the values in each log to build the timeseries
for (LogEntry logEntry : logs) {
List<Double> values = logEntry.getMeasurements();
for (int i = 0; i < values.size(); i++) {
- tagTimeseries.getSeries(i).addOrUpdate(new Millisecond(logEntry.getDate()), values.get(i));
+ timeseriesGroup.getSeries(i).addOrUpdate(new Millisecond(logEntry.getDate()), values.get(i));
}
}
} else {
- TimeSeries happenedInstance = new TimeSeries("HappenedInstance");
+ // the measurement are not given, just record the time when each log happened
+ TimeSeries happenedInstance = new TimeSeries(concatenatedTag + "HappenedInstance");
for (LogEntry logEntry : logs) {
happenedInstance.addOrUpdate(new Millisecond(logEntry.getDate()), 1.0);
}
- tagTimeseries.addSeries(happenedInstance);
+ timeseriesGroup.addSeries(happenedInstance);
}
- ret.put(concatenatedTag, tagTimeseries);
+ ret.put(concatenatedTag, timeseriesGroup);
}
return ret;
}
@@ -201,30 +251,21 @@ public class LogVisualizer {
for (Entry<String, TimeSeriesCollection> entry : taggedTimeSeries.entrySet()) {
String tag = entry.getKey();
TimeSeriesCollection timeSeriesList = entry.getValue();
+ // contain the start time of the timeseries in the x-axis name
Date startDate = new Date((long) timeSeriesList.getDomainBounds(true).getLowerBound());
- if (plan.getMeasurementPositions() != null) {
- // a real-valued timeseries, draw a curve
- JFreeChart chart = ChartFactory.createTimeSeriesChart(tag, "time-"+ startDate, "value",
- timeSeriesList);
- XYPlot xyPlot = chart.getXYPlot();
- XYLineAndShapeRenderer xyLineAndShapeRenderer = ((XYLineAndShapeRenderer) xyPlot
- .getRenderer());
- xyLineAndShapeRenderer.setDefaultShapesVisible(true);
- xyLineAndShapeRenderer.setDefaultShapesFilled(true);
-
- charts.put(tag, chart);
- } else {
- JFreeChart chart = ChartFactory.createTimeSeriesChart(tag, "time-"+ startDate, "value",
- timeSeriesList);
- XYPlot xyPlot = chart.getXYPlot();
- XYLineAndShapeRenderer xyLineAndShapeRenderer = ((XYLineAndShapeRenderer) xyPlot
- .getRenderer());
- xyLineAndShapeRenderer.setDefaultShapesVisible(true);
- xyLineAndShapeRenderer.setDefaultShapesFilled(true);
+ JFreeChart chart = ChartFactory.createTimeSeriesChart(tag, "time-"+ startDate, "value",
+ timeSeriesList);
+ XYPlot xyPlot = chart.getXYPlot();
+ XYLineAndShapeRenderer xyLineAndShapeRenderer = ((XYLineAndShapeRenderer) xyPlot
+ .getRenderer());
+ // show the origin data points in the plot
+ xyLineAndShapeRenderer.setDefaultShapesVisible(true);
+ xyLineAndShapeRenderer.setDefaultShapesFilled(true);
+ if (plan.getMeasurementPositions() == null) {
+ // do not draw lines if we only record the time instances of the logs
xyLineAndShapeRenderer.setDefaultLinesVisible(false);
-
- charts.put(tag, chart);
}
+ charts.put(tag, chart);
}
return charts;
}
@@ -245,6 +286,7 @@ public class LogVisualizer {
taggedTimeSeries) {
Map<String, List<TimeSeriesStatistics>> ret = new HashMap<>();
for (Entry<String, TimeSeriesCollection> timeSeriesCollectionEntry : taggedTimeSeries.entrySet()) {
+ // calculate the statistics of the logs in each group
String tag = timeSeriesCollectionEntry.getKey();
TimeSeriesCollection timeSeriesCollection = timeSeriesCollectionEntry.getValue();
List<TimeSeriesStatistics> seriesStatistics = new ArrayList<>();
@@ -260,15 +302,4 @@ public class LogVisualizer {
public Map<String, List<TimeSeriesStatistics>> getStatisticsMap() {
return statisticsMap;
}
-
- public static void main(String[] args) throws IOException, AnalyzeException {
- LogVisualizer visualizer = new LogVisualizer();
- visualizer.setParserPropertyFile(new File("E:\\codestore\\incubator-iotdb\\server\\src\\assembly"
- + "\\resources"
- + "\\tools\\logAnalyze\\default.log.pattern"));
- visualizer.setLogFile( new File("C:\\Users\\admin\\Desktop\\logs\\log-all-2019-08-21.0.log"));
- visualizer.loadLogParser();
- visualizer.loadPlan("E:\\codestore\\incubator-iotdb\\server\\src\\assembly\\resources\\tools\\logAnalyze\\plans\\flushTimeConsumption.plan");
- visualizer.executePlan("flushTimeConsumption");
- }
}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/PatternLogParser.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/PatternLogParser.java
index 68021cf..47a714a 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/PatternLogParser.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/PatternLogParser.java
@@ -24,6 +24,18 @@ import org.apache.iotdb.db.tools.logvisual.LogEntry.LogLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * PatternLogParser parse logs according to a property file which defines how a log looks like
+ * and what fields in the log are interesting.
+ * A running example of the property file:
+ * pattern=([^\\[]*)(\\[.*])(\\s\\w+\\s)([^:]*:\\d+)(\\s-\\s)(.*)
+ * date_index=1
+ * thread_name_index=2
+ * level_index=3
+ * code_location_index=4
+ * content_index=6
+ * date_pattern=yyyy-MM-dd hh:mm:ss,SSS
+ */
public class PatternLogParser implements LogParser{
private static final Logger logger = LoggerFactory.getLogger(LogParser.class);
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/TimeSeriesStatistics.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/TimeSeriesStatistics.java
index 1bdcdee..71d36b5 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/TimeSeriesStatistics.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/TimeSeriesStatistics.java
@@ -23,6 +23,9 @@ import java.util.Date;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesDataItem;
+/**
+ * TimeSeriesStatistics store the the count, mean, max, min of both time and measurements.
+ */
public class TimeSeriesStatistics {
public static final String[] HEADER = new String[] {
@@ -69,30 +72,6 @@ public class TimeSeriesStatistics {
return size;
}
- public double getMeanInterval() {
- return meanInterval;
- }
-
- public long getMaxInterval() {
- return maxInterval;
- }
-
- public long getMinInterval() {
- return minInterval;
- }
-
- public double getMeanVal() {
- return meanVal;
- }
-
- public double getMaxVal() {
- return maxVal;
- }
-
- public double getMinVal() {
- return minVal;
- }
-
public Object[] toArray() {
Object[] ret = new Object[HEADER.length];
int i = 0;
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualUtils.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualUtils.java
index cc56f69..0f61d5e 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualUtils.java
@@ -25,9 +25,9 @@ public class VisualUtils {
throw new UnsupportedOperationException("Initializing a util class");
}
- public static int[] parseIntArray(String intarrayStr) {
- if (intarrayStr != null) {
- String[] intStrs = intarrayStr.split(",");
+ public static int[] parseIntArray(String intArrayStr) {
+ if (intArrayStr != null) {
+ String[] intStrs = intArrayStr.split(",");
int[] ints = new int[intStrs.length];
for (int i = 0; i < ints.length; i++) {
ints[i] = Integer.parseInt(intStrs[i]);
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizationPlan.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizationPlan.java
index 66f6643..4447eed 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizationPlan.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizationPlan.java
@@ -14,6 +14,26 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.iotdb.db.tools.logvisual.exceptions.UnmatchedContentException;
+/**
+ * VisualizationPlan defines what fields (by using groups in regexp) should be plotted as value,
+ * what fields should be used to group the logs and what logs should be filtered.
+ * An example plan:
+ name=flushTimeConsumption
+ content_pattern=Storage group (.*) memtable (.*) flushing a memtable has finished! Time consumption: (.*)ms
+
+ measurement_positions=3
+ legends=Time
+ tag_positions=1
+
+ min_level=INFO
+ thread_name_white_list=pool-1-IoTDB-JDBC-Client-thread-5
+ class_name_white_list=org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor
+ line_num_white_list=392
+
+ date_pattern=yyyy-MM-dd hh:mm:ss
+ start_date=2019-08-21 09:00:00
+ end_date=2019-08-22 09:00:00
+ */
public class VisualizationPlan {
// optional, this will be used as the title of the figure.
private String name;
@@ -39,6 +59,7 @@ public class VisualizationPlan {
private LogFilter logFilter;
+ // where the plan is stored
private String planFilePath;
public VisualizationPlan() {
@@ -73,13 +94,21 @@ public class VisualizationPlan {
logFilter = new LogFilter(properties);
}
+ /**
+ * parse the content in a LogEntry using contentPattern and store the parsed fields back to the
+ * entry.
+ * @param logEntry
+ * @throws UnmatchedContentException
+ */
public void parseContents(LogEntry logEntry) throws UnmatchedContentException {
Matcher matcher = contentPattern.matcher(logEntry.getLogContent());
if (!matcher.matches()) {
throw new UnmatchedContentException(logEntry.getLogContent(), contentPattern.pattern());
}
+
String[] matchedValues = new String[matcher.groupCount()];
for (int i = 1; i <= matcher.groupCount(); i++) {
+ // group(0) is the whole string
matchedValues[i - 1] = matcher.group(i);
}
if (tagPositions != null) {
@@ -141,11 +170,7 @@ public class VisualizationPlan {
public void setTagPositions(int[] tagPositions) {
this.tagPositions = tagPositions;
}
-
- public void setLogFilter(LogFilter logFilter) {
- this.logFilter = logFilter;
- }
-
+
public void setPlanFilePath(String planFilePath) {
this.planFilePath = planFilePath;
}
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/conf/PropertyKeys.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/conf/PropertyKeys.java
index 593f65f..b6e24d8 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/conf/PropertyKeys.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/conf/PropertyKeys.java
@@ -19,9 +19,23 @@
package org.apache.iotdb.db.tools.logvisual.conf;
+/**
+ * PropertyKeys holds the keys of properties that store the paths chosen by the user the last
+ * time he uses this tool for convenience.
+ */
public enum PropertyKeys {
- DEFAULT_PARSER_FILE_PATH("parser_properties_path"), DEFAULT_LOG_FILE_PATH
- ("log_path"), DEFAULT_PLAN_PATH("plans_path");
+ /**
+ * The last path of the parser property file chosen by the user.
+ */
+ DEFAULT_PARSER_FILE_PATH("parser_properties_path"),
+ /**
+ * The last path of the log file chosen by the user.
+ */
+ DEFAULT_LOG_FILE_PATH("log_path"),
+ /**
+ * The last path of the visualization plan file chosen by the user.
+ */
+ DEFAULT_PLAN_PATH("plans_path");
private String key;
PropertyKeys(String key) {
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableComboTab.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableComboTab.java
index 227f973..7525001 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableComboTab.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableComboTab.java
@@ -21,11 +21,14 @@ package org.apache.iotdb.db.tools.logvisual.gui;
import java.util.Map;
-public abstract class ClosableComboTab extends ClosableTab {
+/**
+ * ClosableComboTab is a ClosableTab with a comboBox.
+ */
+abstract class ClosableComboTab extends ClosableTab {
private LabeledComboBox comboBox;
- public ClosableComboTab(String name, Map comboItems, TabCloseCallBack tabCloseCallBack) {
+ ClosableComboTab(String name, Map comboItems, TabCloseCallBack tabCloseCallBack) {
super(name, tabCloseCallBack);
comboBox = new LabeledComboBox(comboItems, this::onItemSelected, "Please select an item to "
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableTab.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableTab.java
index 956d974..44a8932 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableTab.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ClosableTab.java
@@ -25,6 +25,9 @@ import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JPanel;
+/**
+ * ClosableTab is a JPanel with a "close" button.
+ */
abstract class ClosableTab extends JPanel {
private JButton closeTabButton;
@@ -47,6 +50,6 @@ abstract class ClosableTab extends JPanel {
}
public interface TabCloseCallBack {
- void call(String name);
+ void call(String tabName);
}
}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/FileSelectionBox.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/FileSelectionBox.java
index 5a6db5d..4f6f268 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/FileSelectionBox.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/FileSelectionBox.java
@@ -30,14 +30,19 @@ import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
-public class FileSelectionBox extends Box{
+/**
+ * FileSelectionBox is a combination of a JLabel (showing the name of this component), a
+ * JTextField (showing the path of the chosen file) and a JButton (on clicking it the user can
+ * choose a single file).
+ */
+class FileSelectionBox extends Box{
private JLabel panelName;
private JTextField filePathField;
private JButton selectFileButton;
- FilePathBoxSelectionCallBack callBack;
+ private FilePathBoxSelectionCallBack callBack;
- public FileSelectionBox(String name, FilePathBoxSelectionCallBack callBack, String
+ FileSelectionBox(String name, FilePathBoxSelectionCallBack callBack, String
defaultFilePath) {
super(BoxLayout.X_AXIS);
this.callBack = callBack;
@@ -63,6 +68,7 @@ public class FileSelectionBox extends Box{
add(selectFileButton);
if (defaultFilePath != null) {
+ // select the default file if provided
File defaultFile = new File(defaultFilePath);
if (!defaultFile.exists()) {
JOptionPane.showMessageDialog(this, panelName.getText() + ":default file " +
@@ -78,6 +84,7 @@ public class FileSelectionBox extends Box{
JFileChooser fileChooser = new JFileChooser();
int status = fileChooser.showOpenDialog(this);
if (status == JFileChooser.APPROVE_OPTION) {
+ // only one file is allowed
File chosenFile = fileChooser.getSelectedFile();
callBack.call(chosenFile);
filePathField.setText(chosenFile.getPath());
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LabeledComboBox.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LabeledComboBox.java
index 2852a6e..e587a7f 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LabeledComboBox.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LabeledComboBox.java
@@ -28,12 +28,15 @@ import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
-public class LabeledComboBox<K, V> extends Box {
+/**
+ * LabeledComboBox is JComboBox with a label, using a map as the data source of the ComboBox.
+ */
+class LabeledComboBox<K, V> extends Box {
private ComboBoxModel comboBoxModel;
private JComboBox comboBox;
- public LabeledComboBox(Map<K, V> itemMap, ComboSelectedCallback callback, String labelText) {
+ LabeledComboBox(Map<K, V> itemMap, ComboSelectedCallback callback, String labelText) {
super(BoxLayout.Y_AXIS);
JLabel label = new JLabel(labelText);
@@ -54,6 +57,7 @@ public class LabeledComboBox<K, V> extends Box {
}
public interface ComboSelectedCallback {
+ // this methods accepts the value instead of the key in the itemMap
void call(Object value);
}
}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LoadLogBox.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LoadLogBox.java
index e91191e..43174d1 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LoadLogBox.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LoadLogBox.java
@@ -29,33 +29,34 @@ import javax.swing.JLabel;
import javax.swing.JOptionPane;
import org.apache.iotdb.db.tools.logvisual.LogVisualizer;
-public class LoadLogBox extends Box{
+/**
+ * LoadLogBox contains a JButton to load a log parser into memory to prepare for visualization
+ * and a JLabel to show whether the loading of the log parser is successful.
+ */
+class LoadLogBox extends Box{
private JLabel status;
private JButton loadLogButton;
-
private LogVisualizer visualizer;
- public LoadLogBox(LogVisualizer visualizer) {
+ LoadLogBox(LogVisualizer visualizer) {
super(BoxLayout.Y_AXIS);
this.visualizer = visualizer;
status = new JLabel("No logs are loaded");
loadLogButton = new JButton("Load logs");
- loadLogButton.addActionListener(new AbstractAction() {
- @Override
- public void actionPerformed(ActionEvent e) {
- try {
- visualizer.loadLogParser();
- status.setText("Logs are successfully loaded");
- } catch (IOException e1) {
- JOptionPane.showMessageDialog(LoadLogBox.this, "Cannot load logs: " + e1);
- }
- }
- });
+ loadLogButton.addActionListener(this::onLoadLog);
add(status);
add(loadLogButton);
}
+ private void onLoadLog(ActionEvent e) {
+ try {
+ visualizer.loadLogParser();
+ status.setText("Logs are successfully loaded");
+ } catch (IOException e1) {
+ JOptionPane.showMessageDialog(LoadLogBox.this, "Cannot load logs: " + e1);
+ }
+ }
}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LogVisualizeGui.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LogVisualizationGui.java
similarity index 73%
rename from server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LogVisualizeGui.java
rename to server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LogVisualizationGui.java
index 570d84c..ba6b599 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LogVisualizeGui.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/LogVisualizationGui.java
@@ -22,26 +22,59 @@ import org.apache.iotdb.db.tools.logvisual.LogVisualizer;
import org.apache.iotdb.db.tools.logvisual.TimeSeriesStatistics;
import org.jfree.chart.JFreeChart;
-public class LogVisualizeGui {
+/**
+ * LogVisualizationGui provides a graphic way wo manipulate visualization plans and view the
+ * results of visualization.
+ */
+public class LogVisualizationGui {
- private final String DEFAULT_PROPERTY = "visual.config";
+ /**
+ * if a config file is not provided, this will be used as a default config file saving the
+ * paths use chooses for the next usage.
+ */
+ private final String DEFAULT_CONFIG = "visual.config";
private final int DEFAULT_HEIGHT = 600;
private final int DEFAULT_WIDTH = 800;
private LogVisualizer visualizer;
+
+ /**
+ * mainFrame is the main and only window of this gui.
+ */
private JFrame mainFrame;
+ /**
+ * tabbedPane is the only direct component of mainFrame, which consists of one mainPanel and
+ * many result panels.
+ */
private JTabbedPane tabbedPane;
+ /**
+ * mainPanel provide gui of loading log files and visualization plans and running visualization
+ * plans to generate results panels.
+ */
private MainPanel mainPanel;
+ /**
+ * Each time a visualization plan is executed, two tabs will be generated, one (the
+ * ResultPlotTab) contains the
+ * timeseries plot of the visualized logs and the other (the ResultStatisticTab) contains a table
+ * showing the statistics of the observed logs.
+ * They will be store in the following maps with keys {planName}-plot and {planName}-statistic
+ * respectively.
+ */
private Map<String, ResultPlotTab> resultPlotPanels = new HashMap<>();
private Map<String, ResultStatisticTab> resultTablePanels = new HashMap<>();
+
+ /**
+ * properties contain most recently chosen files, so when the next time the user use this tool,
+ * he will not need to set all from the beginning.
+ */
private String propertyFilePath;
private Properties properties;
- public LogVisualizeGui(String propertyFilePath) throws IOException {
+ private LogVisualizationGui(String propertyFilePath) throws IOException {
properties = new Properties();
if (propertyFilePath == null) {
- propertyFilePath = DEFAULT_PROPERTY;
+ propertyFilePath = DEFAULT_CONFIG;
}
this.propertyFilePath = propertyFilePath;
File propertyFile = new File(propertyFilePath);
@@ -70,6 +103,7 @@ public class LogVisualizeGui {
private void onPlanExecuted(String planName, Map<String, JFreeChart> charts, Map<String,
List<TimeSeriesStatistics>> statisticMap) {
+ // create a tab to display the timeseries plots, may replace the old one
String tabName = planName + "-plot";
ResultPlotTab resultPlotTab = new ResultPlotTab(tabName, charts, this::onTabClose);
ResultPlotTab oldPlotTab = resultPlotPanels.get(tabName);
@@ -79,6 +113,7 @@ public class LogVisualizeGui {
resultPlotPanels.put(tabName, resultPlotTab);
tabbedPane.add(resultPlotTab);
+ // create a tab to display the log statistics, may replace the old one
tabName = planName + "-statistics";
ResultStatisticTab resultStatisticTab = new ResultStatisticTab(tabName, statisticMap,
this::onTabClose);
@@ -91,6 +126,7 @@ public class LogVisualizeGui {
}
private void onPropertyChange(String key, String value) {
+ // when the user chooses a new file, the properties should be updated and persisted
properties.put(key, value);
try (FileWriter writer = new FileWriter(propertyFilePath);
BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
@@ -121,7 +157,7 @@ public class LogVisualizeGui {
if (args.length > 0) {
propertyFilePath = args[0];
}
- LogVisualizeGui gui = new LogVisualizeGui(propertyFilePath);
+ LogVisualizationGui gui = new LogVisualizationGui(propertyFilePath);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/MainPanel.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/MainPanel.java
index 3755c0a..8ff49ca 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/MainPanel.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/MainPanel.java
@@ -23,24 +23,44 @@ import java.io.File;
import java.util.Properties;
import javax.swing.JPanel;
import org.apache.iotdb.db.tools.logvisual.LogVisualizer;
-import org.apache.iotdb.db.tools.logvisual.VisualizationPlan;
import org.apache.iotdb.db.tools.logvisual.conf.PropertyKeys;
-import org.apache.iotdb.db.tools.logvisual.gui.LogVisualizeGui.PropertyChangeCallback;
+import org.apache.iotdb.db.tools.logvisual.gui.LogVisualizationGui.PropertyChangeCallback;
import org.apache.iotdb.db.tools.logvisual.gui.PlanBox.ExecutePlanCallback;
-public class MainPanel extends JPanel {
+/**
+ * MainPanel provides components that enable the user to choose log file to be visualized,
+ * manipulate or execute visualization plans.
+ */
+class MainPanel extends JPanel {
+ /**
+ * to select a log file that will be visualized.
+ */
private FileSelectionBox logFileSelectionBox;
+ /**
+ * to select a file that describe how to parse the logs.
+ */
private FileSelectionBox parserPropertyBox;
+ /**
+ * to generate a log parser and prepare to load the logs.
+ */
private LoadLogBox loadLogBox;
+ /**
+ * to provide means of manipulating the visualizations plans and execute them.
+ */
private PlanBox planBox;
-
+ /**
+ * A backend that actually performs the visualization.
+ */
private LogVisualizer visualizer;
+ /**
+ * When a user choose a new file, call this to remember the choice.
+ */
private PropertyChangeCallback propertyChangeCallback;
- public MainPanel(LogVisualizer logVisualizer, ExecutePlanCallback executePlanCallback,
+ MainPanel(LogVisualizer logVisualizer, ExecutePlanCallback executePlanCallback,
Properties properties, PropertyChangeCallback propertyChangeCallback) {
this.visualizer = logVisualizer;
this.propertyChangeCallback = propertyChangeCallback;
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanBox.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanBox.java
index 3e381ea..986869b 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanBox.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanBox.java
@@ -43,27 +43,36 @@ import org.apache.iotdb.db.tools.logvisual.TimeSeriesStatistics;
import org.apache.iotdb.db.tools.logvisual.VisualizationPlan;
import org.apache.iotdb.db.tools.logvisual.conf.PropertyKeys;
import org.apache.iotdb.db.tools.logvisual.exceptions.AnalyzeException;
-import org.apache.iotdb.db.tools.logvisual.gui.LogVisualizeGui.PropertyChangeCallback;
+import org.apache.iotdb.db.tools.logvisual.gui.LogVisualizationGui.PropertyChangeCallback;
import org.jfree.chart.JFreeChart;
-public class PlanBox extends Box{
+/**
+ * PlanBox provides interfaces to save, load, create, delete and execute visualization plans.
+ */
+@SuppressWarnings("unused") // ignore the event parameter
+class PlanBox extends Box{
private JLabel panelName;
private JButton loadPlanButton;
private JButton executePlanButton;
private JButton savePlanButton;
private JButton createPlanButton;
private JButton deletePlanButton;
+
+ // display of plans
private JScrollPane scrollPane;
private DefaultListModel<VisualizationPlan> planListModel;
private JList planList;
private PlanDetailPanel planDetailPanel;
+ // plan execution backend
private LogVisualizer visualizer;
+ // call this to create tabs to display the results when a plan is executed
private ExecutePlanCallback executePlanCallback;
+ // call this to remember the choice when the user has loaded new plans
private PropertyChangeCallback propertyChangeCallback;
- public PlanBox(LogVisualizer visualizer, ExecutePlanCallback executePlanCallback, String defaultPlanPath,
+ PlanBox(LogVisualizer visualizer, ExecutePlanCallback executePlanCallback, String defaultPlanPath,
PropertyChangeCallback propertyChangeCallback) {
super(BoxLayout.X_AXIS);
@@ -104,7 +113,6 @@ public class PlanBox extends Box{
vBox.add(executePlanButton);
vBox.add(Box.createGlue());
add(vBox);
- setAlignmentY(0.5f);
planDetailPanel = new PlanDetailPanel();
planDetailPanel.setPreferredSize(new Dimension(400, 300));
@@ -118,14 +126,17 @@ public class PlanBox extends Box{
deletePlanButton.addActionListener(this::onDeletePlan);
if (defaultPlanPath != null) {
+ // load default plans if given
String[] defaultPaths = defaultPlanPath.split(";");
File[] defaultPlanFiles = new File[defaultPaths.length];
for (int i = 0; i < defaultPaths.length; i++) {
defaultPlanFiles[i] = new File(defaultPaths[i]);
}
try {
+ // read the plans from the disk
visualizer.loadPlans(defaultPlanFiles);
Collection<VisualizationPlan> planList = visualizer.listPlans();
+ // show plans in the gui
updatePlan(planList);
} catch (IOException e1) {
JOptionPane.showMessageDialog(this, "Cannot load plan: " + e1);
@@ -137,14 +148,18 @@ public class PlanBox extends Box{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(true);
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+ // let the user choose plan files or directories that contain visualization plans
int status = fileChooser.showOpenDialog(this);
if (status == JFileChooser.APPROVE_OPTION) {
File[] chosenFiles = fileChooser.getSelectedFiles();
try {
+ // read plans from disk
visualizer.loadPlans(chosenFiles);
Collection<VisualizationPlan> planList = visualizer.listPlans();
+ // display the plans in the panel
updatePlan(planList);
+ // save the paths so that the tool can load the plans automatically next time
if (chosenFiles.length > 0) {
StringBuilder builder = new StringBuilder(chosenFiles[0].getPath());
for (int i = 1; i < chosenFiles.length; i++) {
@@ -153,7 +168,7 @@ public class PlanBox extends Box{
propertyChangeCallback.call(PropertyKeys.DEFAULT_PLAN_PATH.getKey(), builder.toString());
}
} catch (IOException e1) {
- JOptionPane.showMessageDialog(this, "Cannot load plan: " + e1);
+ JOptionPane.showMessageDialog(this, "Cannot load plan: " + e1.getMessage());
}
}
}
@@ -175,7 +190,9 @@ public class PlanBox extends Box{
} catch (AnalyzeException e1) {
JOptionPane.showMessageDialog(this, "Cannot execute plan: " + e1.getMessage());
}
+ // timeseries plots of each measurement in the visualization plan
Map<String, JFreeChart> charts = visualizer.getCharts();
+ // statistics (count, mean, max, min) of each measurement
Map<String, List<TimeSeriesStatistics>> statisticMap = visualizer.getStatisticsMap();
executePlanCallback.call(plan.getName(), charts, statisticMap);
}
@@ -185,24 +202,30 @@ public class PlanBox extends Box{
if (plan == null) {
return;
}
+ // update the display of the panel according to the new plan
planDetailPanel.setPlan(plan);
}
private void onPlanSave(ActionEvent e) {
+ // update the content of the plan according to the text fields
planDetailPanel.updatePlan();
}
private void onCreatePlan(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
+ // let the user to choose a place for the new plan
int status = fileChooser.showOpenDialog(this);
if (status == JFileChooser.APPROVE_OPTION) {
File chosenFile = fileChooser.getSelectedFile();
VisualizationPlan plan = new VisualizationPlan();
plan.setPlanFilePath(chosenFile.getPath());
+ // the name of the file will also be the name of the plan
plan.setName(chosenFile.getName());
+ // a default plan matches every thing
plan.setContentPattern(Pattern.compile(".*"));
planListModel.addElement(plan);
+ // the new plan will be focused on
planList.setSelectedIndex(planListModel.getSize() - 1);
planDetailPanel.setPlan(plan);
}
@@ -220,11 +243,13 @@ public class PlanBox extends Box{
File file = new File(plan.getPlanFilePath());
file.delete();
planListModel.removeElement(plan);
+ // update the display since the deleted one is always the one being displayed
planDetailPanel.setPlan(null);
}
}
public interface ExecutePlanCallback {
+ // call this to create new tabs to show the results after the plan is executed
void call(String planName, Map<String, JFreeChart> charts, Map<String,
List<TimeSeriesStatistics>> statisticMap);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanDetailPanel.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanDetailPanel.java
index f932760..4a13fb0 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanDetailPanel.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/PlanDetailPanel.java
@@ -30,12 +30,13 @@ import org.apache.iotdb.db.tools.logvisual.LogEntry.LogLevel;
import org.apache.iotdb.db.tools.logvisual.VisualUtils;
import org.apache.iotdb.db.tools.logvisual.VisualizationPlan;
+/**
+ * PlanDetailPanel displays the information of a visualization plan.
+ */
public class PlanDetailPanel extends JScrollPane {
private VisualizationPlan plan;
- private Box box;
-
private JTextField nameField = new JTextField();
private JTextField patternField = new JTextField();
private JTextField measurementsField = new JTextField();
@@ -49,7 +50,7 @@ public class PlanDetailPanel extends JScrollPane {
private JTextField startDateField = new JTextField();
private JTextField endDateField = new JTextField();
- public PlanDetailPanel() {
+ PlanDetailPanel() {
super(null, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
setBorder(BorderFactory.createTitledBorder("Plan detail"));
@@ -84,6 +85,10 @@ public class PlanDetailPanel extends JScrollPane {
setViewportView(box);
}
+ /**
+ * Set the currently displayed plan. If it is null, clean the display.
+ * @param plan
+ */
public void setPlan(VisualizationPlan plan) {
this.plan = plan;
updateFields();
@@ -131,8 +136,8 @@ public class PlanDetailPanel extends JScrollPane {
if (plan.getLogFilter().getLineNumWhiteList() != null) {
lineNumField.setText(VisualUtils.intArrayToString(plan.getLogFilter().getLineNumWhiteList()));
}
- if (plan.getLogFilter().getDatePatten() != null) {
- SimpleDateFormat datePatten = (SimpleDateFormat) plan.getLogFilter().getDatePatten();
+ if (plan.getLogFilter().getDatePattern() != null) {
+ SimpleDateFormat datePatten = (SimpleDateFormat) plan.getLogFilter().getDatePattern();
datePatternField.setText(datePatten.toPattern());
if (plan.getLogFilter().getStartDate() != null) {
startDateField.setText(datePatten.format(plan.getLogFilter().getStartDate()));
@@ -143,6 +148,9 @@ public class PlanDetailPanel extends JScrollPane {
}
}
+ /**
+ * Update the current displayed plan after it is modified amd save it to a file.
+ */
public void updatePlan() {
if (plan == null) {
return;
@@ -161,6 +169,7 @@ public class PlanDetailPanel extends JScrollPane {
String startDate = startDateField.getText();
String endDate = endDateField.getText();
+ // validate the fields
if (name.matches("\\s*")) {
JOptionPane.showMessageDialog(this, "Name cannot be empty");
return;
@@ -221,13 +230,13 @@ public class PlanDetailPanel extends JScrollPane {
plan.getLogFilter().setMinLevel(LogLevel.valueOf(logLevel));
SimpleDateFormat simpleDateFormat = datePattern != null ? new SimpleDateFormat(datePattern) :
null;
- plan.getLogFilter().setDatePartten(simpleDateFormat);
+ plan.getLogFilter().setDatePattern(simpleDateFormat);
plan.getLogFilter().setStartDate(startDate != null ? simpleDateFormat.parse(startDate) : null);
plan.getLogFilter().setEndDate(endDate != null ? simpleDateFormat.parse(endDate) : null);
plan.saveAsFile();
} catch (Exception e) {
- JOptionPane.showMessageDialog(this, e.toString());
+ JOptionPane.showMessageDialog(this, e.getMessage());
}
}
}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultPlotTab.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultPlotTab.java
index fa293e0..8c31aca 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultPlotTab.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultPlotTab.java
@@ -25,6 +25,9 @@ import javax.swing.BorderFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
+/**
+ * ResultPlotTab shows the timeseries-plot of the log events.
+ */
class ResultPlotTab extends ClosableComboTab {
private ChartPanel chartPanel;
@@ -39,6 +42,7 @@ class ResultPlotTab extends ClosableComboTab {
chartPanel.setSize(800, 480);
add(chartPanel);
}
+
void onItemSelected(Object chart){
chartPanel.setChart((JFreeChart) chart);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultStatisticTab.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultStatisticTab.java
index f764c7c..a8544d0 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultStatisticTab.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/gui/ResultStatisticTab.java
@@ -19,14 +19,11 @@
package org.apache.iotdb.db.tools.logvisual.gui;
-import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
-import javax.swing.BorderFactory;
import javax.swing.Box;
-import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
@@ -34,6 +31,9 @@ import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.apache.iotdb.db.tools.logvisual.TimeSeriesStatistics;
+/**
+ * ResultStatisticTab shows the statistic (count, mean, min, max) of the log events.
+ */
class ResultStatisticTab extends ClosableTab {
private TableModel tableModel;
@@ -46,8 +46,11 @@ class ResultStatisticTab extends ClosableTab {
table = new JTable();
Box box = Box.createVerticalBox();
+ // the header and the data should be added separately
box.add(table.getTableHeader());
box.add(table);
+
+ // provides a scroll bar for many series
JScrollPane scrollPane = new JScrollPane(box);
scrollPane.setLocation(0, 100);
scrollPane.setSize(800, 600);
@@ -65,6 +68,7 @@ class ResultStatisticTab extends ClosableTab {
}
tableModel = new DefaultTableModel(data, header);
table.setModel(tableModel);
+ // enable sort by column
table.setRowSorter(new TableRowSorter<>(tableModel));
}
}
\ No newline at end of file