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/23 04:55:33 UTC
[incubator-iotdb] 01/01: add a framework
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
commit 406cbbd1d7e1d5f4fd1870b43744505b7e20cc30
Author: jt <jt...@163.com>
AuthorDate: Fri Aug 23 12:45:36 2019 +0800
add a framework
---
server/pom.xml | 6 +
.../resources/tools/logAnalyze/default.log.pattern | 7 +
.../logAnalyze/plans/flushTimeConsumption.plan | 16 ++
.../apache/iotdb/db/tools/logvisual/LogEntry.java | 85 ++++++++
.../apache/iotdb/db/tools/logvisual/LogFilter.java | 114 +++++++++++
.../apache/iotdb/db/tools/logvisual/LogParser.java | 15 ++
.../iotdb/db/tools/logvisual/LogVisualizer.java | 213 +++++++++++++++++++++
.../iotdb/db/tools/logvisual/PatternLogParser.java | 110 +++++++++++
.../iotdb/db/tools/logvisual/VisualizePlan.java | 144 ++++++++++++++
.../logvisual/exceptions/AnalyzeException.java | 19 ++
.../exceptions/NoLogFileLoadedException.java | 8 +
.../logvisual/exceptions/NoSuchPlanException.java | 8 +
.../exceptions/UnmatchedContentException.java | 8 +
13 files changed, 753 insertions(+)
diff --git a/server/pom.xml b/server/pom.xml
index 9055d28..13f6ebb 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -85,6 +85,12 @@
<artifactId>powermock-api-mockito2</artifactId>
<scope>test</scope>
</dependency>
+ <!-- https://mvnrepository.com/artifact/org.jfree/jfreechart -->
+ <dependency>
+ <groupId>org.jfree</groupId>
+ <artifactId>jfreechart</artifactId>
+ <version>1.5.0</version>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/server/src/assembly/resources/tools/logAnalyze/default.log.pattern b/server/src/assembly/resources/tools/logAnalyze/default.log.pattern
new file mode 100644
index 0000000..fccbd91
--- /dev/null
+++ b/server/src/assembly/resources/tools/logAnalyze/default.log.pattern
@@ -0,0 +1,7 @@
+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
diff --git a/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan b/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan
new file mode 100644
index 0000000..f18e0d6
--- /dev/null
+++ b/server/src/assembly/resources/tools/logAnalyze/plans/flushTimeConsumption.plan
@@ -0,0 +1,16 @@
+name=flushTimeConsumption
+content_pattern=Storage group (.*) memtable (.*) flushing a memtable has finished! Time consumption: (\\d+)ms
+
+# if you add more, use comma to separate
+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
\ 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
new file mode 100644
index 0000000..7146113
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogEntry.java
@@ -0,0 +1,85 @@
+package org.apache.iotdb.db.tools.logvisual;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+public class LogEntry {
+
+ private static List<String> DEFAULT_TAG = Collections.EMPTY_LIST;
+
+ private Date date;
+ private String threadName;
+ private LogLevel logLevel;
+ private CodeLocation codeLocation;
+ private String logContent;
+
+ private List<String> tags = DEFAULT_TAG;
+ private List<Double> measurements;
+
+ LogEntry(Date date, String threadName,
+ LogLevel logLevel, CodeLocation codeLocation, String logContent) {
+ this.date = date;
+ this.threadName = threadName;
+ this.logLevel = logLevel;
+ this.codeLocation = codeLocation;
+ this.logContent = logContent;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public String getThreadName() {
+ return threadName;
+ }
+
+ public LogLevel getLogLevel() {
+ return logLevel;
+ }
+ public CodeLocation getCodeLocation() {
+ return codeLocation;
+ }
+
+ public String getLogContent() {
+ return logContent;
+ }
+
+ public List<String> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<String> tags) {
+ this.tags = tags;
+ }
+
+ public List<Double> getMeasurements() {
+ return measurements;
+ }
+
+ public void setMeasurements(List<Double> measurements) {
+ this.measurements = measurements;
+ }
+
+ enum LogLevel {
+ DEBUG, INFO, WARN, ERROR
+ }
+
+ static class CodeLocation {
+ private String className;
+ private int lineNum;
+
+ CodeLocation(String className, int lineNum) {
+ this.className = className;
+ this.lineNum = lineNum;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public int getLineNum() {
+ return lineNum;
+ }
+ }
+}
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
new file mode 100644
index 0000000..054fa6e
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogFilter.java
@@ -0,0 +1,114 @@
+package org.apache.iotdb.db.tools.logvisual;
+
+import static org.apache.iotdb.db.tools.logvisual.LogFilter.FilterProperties.*;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.DATE_PATTERN;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+import org.apache.iotdb.db.tools.logvisual.LogEntry.LogLevel;
+
+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
+ // analyzed. comma-separated
+ private List<String> threadNameWhiteList;
+ private List<String> classNameWhiteList;
+ private List<Integer> 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;
+ private Date startDate = new Date(Long.MIN_VALUE);
+ private Date endDate = new Date(Long.MAX_VALUE);
+
+ public LogFilter(Properties properties) throws IOException {
+ minLevel = LogLevel.valueOf(properties.getProperty(MIN_LEVEL.getPropertyName(), minLevel.name()));
+
+ String threadNameWhiteListStr = properties.getProperty(THREAD_NAME_WHITE_LIST.getPropertyName
+ ());
+ if (threadNameWhiteListStr != null) {
+ threadNameWhiteList = Arrays.asList(threadNameWhiteListStr.trim().split(","));
+ }
+
+ String classNameWhiteListStr = properties.getProperty(CLASS_NAME_WHITE_LIST.getPropertyName());
+ if (classNameWhiteListStr != null) {
+ classNameWhiteList = Arrays.asList(classNameWhiteListStr.trim().split(","));
+ }
+
+ String lineNumWhiteListStr = properties.getProperty(LINE_NUM_WHITE_LIST.getPropertyName());
+ if (lineNumWhiteListStr != null) {
+ String[] lineNumWhiteListStrs = lineNumWhiteListStr.trim().split(",");
+ lineNumWhiteList = new ArrayList<>();
+ for (int i = 0; i < lineNumWhiteListStrs.length; i++) {
+ lineNumWhiteList.add(Integer.parseInt(lineNumWhiteListStrs[i]));
+ }
+ }
+
+ String datePatternStr = properties.getProperty(DATE_PATTERN.getPropertyName());
+ if (datePatternStr != null) {
+ this.datePartten = new SimpleDateFormat(datePatternStr.trim());
+ String startDateStr = properties.getProperty(START_DATE.getPropertyName());
+ if (startDateStr != null) {
+ try {
+ startDate = datePartten.parse(startDateStr.trim());
+ } catch (ParseException e) {
+ throw new IOException(e);
+ }
+ }
+
+ String endDatePattern = properties.getProperty(END_DATE.getPropertyName());
+ if (startDateStr != null) {
+ try {
+ endDate = datePartten.parse(endDatePattern.trim());
+ } catch (ParseException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ }
+
+ public FilterFeedBack filter(LogEntry entry) {
+ if (entry.getLogLevel().ordinal() < minLevel.ordinal() ||
+ (threadNameWhiteList != null && !threadNameWhiteList.contains(entry.getThreadName())) ||
+ (classNameWhiteList != null && !classNameWhiteList.contains(entry.getCodeLocation()
+ .getClassName())) ||
+ (lineNumWhiteList != null && !lineNumWhiteList.contains(entry.getCodeLocation().getLineNum
+ ())) ||
+ (startDate != null && entry.getDate().before(startDate))) {
+ return FilterFeedBack.REJECT;
+ }
+
+ if (endDate != null && entry.getDate().after(endDate)) {
+ return FilterFeedBack.BEYOND_END_TIME;
+ }
+ return FilterFeedBack.OK;
+ }
+
+ enum FilterProperties {
+ MIN_LEVEL("min_level"), THREAD_NAME_WHITE_LIST("thread_name_white_list"), CLASS_NAME_WHITE_LIST(
+ "class_name_white_list"),
+ LINE_NUM_WHITE_LIST("line_num_white_list"), START_DATE("start_date"), END_DATE("end_date"),
+ DATE_PATTERN("date_pattern");
+
+ private String propertyName;
+
+ FilterProperties(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+ }
+
+ enum FilterFeedBack {
+ OK, REJECT, BEYOND_END_TIME
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2675085
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogParser.java
@@ -0,0 +1,15 @@
+package org.apache.iotdb.db.tools.logvisual;
+
+import java.io.IOException;
+
+public interface LogParser {
+
+ /**
+ * return the next LogEntry or null if there is no more logs.
+ */
+ LogEntry next() throws IOException;
+
+ void close() 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
new file mode 100644
index 0000000..bf9b660
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/LogVisualizer.java
@@ -0,0 +1,213 @@
+package org.apache.iotdb.db.tools.logvisual;
+
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+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;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.time.Millisecond;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LogVisualizer {
+
+ private static final Logger logger = LoggerFactory.getLogger(LogVisualizer.class);
+ private static final String STR_HAPPENED_INSTANCE = "HappenedInstance";
+
+ private LogParser logParser;
+ private List<LogEntry> logCache = new ArrayList<>();
+
+ private Map<String, VisualizePlan> plans = new HashMap<>();
+ private Map<List<String>, List<LogEntry>> logGroups = new HashMap<>();
+
+ private void clearLogs() {
+ logCache.clear();
+ logGroups.clear();
+ }
+
+ public void loadLogParser(String propertyFilePath, String logFilePath)
+ throws IOException {
+ Properties properties = new Properties();
+ try (FileInputStream inputStream = new FileInputStream(propertyFilePath);
+ BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
+ properties.load(bufferedInputStream);
+ }
+ logParser = new PatternLogParser(properties, logFilePath);
+ }
+
+ public void close() throws IOException {
+ logParser.close();
+ logParser = null;
+ }
+
+ public void loadPlan(String planFilePath) throws IOException {
+ try (FileInputStream reader = new FileInputStream(planFilePath);
+ BufferedInputStream bufferedInputStream = new BufferedInputStream(reader)) {
+ Properties properties = new Properties();
+ properties.load(bufferedInputStream);
+ VisualizePlan plan = new VisualizePlan(properties);
+ plans.put(plan.getName(), plan);
+ }
+ }
+
+ public Collection<String> listPlans() {
+ return plans.keySet();
+ }
+
+ public void executePlan(String planName) throws AnalyzeException {
+ VisualizePlan plan = plans.get(planName);
+ if (plan == null) {
+ throw new NoSuchPlanException(planName);
+ }
+ if (logParser == null) {
+ throw new NoLogFileLoadedException();
+ }
+ try {
+ logParser.reset();
+ } catch (IOException e) {
+ throw new AnalyzeException(e);
+ }
+ collectLogs(plan);
+ groupLogs();
+ Map<String,TimeSeriesCollection> taggedTimeSeries = createTimeSeries(plan);
+ List<JFreeChart> charts = drawCharts(taggedTimeSeries, plan);
+ showCharts(charts.subList(0,10));
+ }
+
+ private void collectLogs(VisualizePlan plan) throws AnalyzeException {
+ LogFilter logFilter = plan.getLogFilter();
+ clearLogs();
+ try {
+ LogEntry logEntry;
+ readLogs:
+ while ((logEntry = logParser.next()) != null) {
+ try {
+ plan.parseContents(logEntry);
+ } catch (UnmatchedContentException e) {
+ continue;
+ }
+
+ switch (logFilter.filter(logEntry)) {
+ case BEYOND_END_TIME:
+ break readLogs;
+ case REJECT:
+ break;
+ case OK:
+ logCache.add(logEntry);
+ }
+ }
+ } catch (IOException e) {
+ throw new AnalyzeException(e);
+ }
+ logger.info("Collected {} logs", logCache.size());
+ }
+
+ private void groupLogs() {
+ for (LogEntry logEntry : logCache) {
+ logGroups.computeIfAbsent(logEntry.getTags(), tag -> new ArrayList<>()).add(logEntry);
+ }
+ logger.info("Found {} different tags", logGroups.size());
+ }
+
+ private Map<String, TimeSeriesCollection> createTimeSeries(VisualizePlan plan) {
+ Map<String, TimeSeriesCollection> ret = new HashMap<>();
+ for (Entry<List<String>, List<LogEntry>> entry : logGroups.entrySet()) {
+ List<String> tags = entry.getKey();
+ List<LogEntry> logs = entry.getValue();
+ String concatenatedTag;
+ if (tags.isEmpty()) {
+ concatenatedTag = plan.getName();
+ } else {
+ StringBuilder builder = new StringBuilder(plan.getName() + "-" + tags.get(0));
+ for (int i = 1; i < tags.size(); i++) {
+ builder.append(",").append(tags.get(i));
+ }
+ builder.append(" ");
+ concatenatedTag = builder.toString();
+ }
+ TimeSeriesCollection tagTimeseries = new TimeSeriesCollection();
+ if (plan.getMeasurementPositions() != null) {
+ String[] legends = plan.getLegends();
+ for (String legend : legends) {
+ tagTimeseries.addSeries(new TimeSeries(concatenatedTag + legend));
+ }
+ 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));
+ }
+ }
+ } else {
+ TimeSeries happenedInstance = new TimeSeries("HappenedInstance");
+ for (LogEntry logEntry : logs) {
+ happenedInstance.add(new Millisecond(logEntry.getDate()), 1.0);
+ }
+ }
+ ret.put(concatenatedTag, tagTimeseries);
+ }
+ return ret;
+ }
+
+ private List<JFreeChart> drawCharts(Map<String, TimeSeriesCollection> taggedTimeSeries, VisualizePlan plan) {
+ List<JFreeChart> charts = new ArrayList<>();
+ for (Entry<String, TimeSeriesCollection> entry : taggedTimeSeries.entrySet()) {
+ String tag = entry.getKey();
+ TimeSeriesCollection timeSeriesList = entry.getValue();
+ if (plan.getMeasurementPositions() != null) {
+ // a real-valued timeseries, draw a curve
+ JFreeChart chart = ChartFactory.createTimeSeriesChart(tag, "time", "value", timeSeriesList);
+ XYPlot xyPlot = chart.getXYPlot();
+ ((XYLineAndShapeRenderer) xyPlot.getRenderer()).setDefaultShapesFilled(true);
+ charts.add(chart);
+ } else {
+ // a binary timeseries, draw a scatter plot
+ JFreeChart chart = ChartFactory.createScatterPlot(tag, "time", "", timeSeriesList);
+ charts.add(chart);
+ }
+ }
+ return charts;
+ }
+
+ private void showCharts(List<JFreeChart> charts) {
+ for (JFreeChart chart : charts) {
+ BufferedImage image = chart.createBufferedImage(800, 600);
+ JFrame frame = new JFrame();
+ frame.add(new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ g.drawImage(image, 0, 0, null);
+ }
+ });
+ frame.setVisible(true);
+ frame.setSize(880, 660);
+ }
+ }
+
+ public static void main(String[] args) throws IOException, AnalyzeException {
+ LogVisualizer visualizer = new LogVisualizer();
+ visualizer.loadLogParser("E:\\codestore\\incubator-iotdb\\server\\src\\assembly\\resources"
+ + "\\tools\\logAnalyze\\default.log.pattern",
+ "C:\\Users\\admin\\Desktop\\logs\\log-all-2019-08-21.0.log");
+ 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
new file mode 100644
index 0000000..93d793c
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/PatternLogParser.java
@@ -0,0 +1,110 @@
+package org.apache.iotdb.db.tools.logvisual;
+
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.CODE_LOCATION_INDEX;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.CONTENT_INDEX;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.DATE_INDEX;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.DATE_PATTERN;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.LEVEL_INDEX;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.PATTERN;
+import static org.apache.iotdb.db.tools.logvisual.PatternLogParser.PatternProperties.THREAD_NAME_INDEX;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.iotdb.db.tools.logvisual.LogEntry.CodeLocation;
+import org.apache.iotdb.db.tools.logvisual.LogEntry.LogLevel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PatternLogParser implements LogParser{
+ private static final Logger logger = LoggerFactory.getLogger(LogParser.class);
+
+ private Pattern pattern;
+ private int dateIndex;
+ private int threadNameIndex;
+ private int levelIndex;
+ private int codeLocationIndex;
+ private int contentIndex;
+
+ private String logFilePath;
+ private BufferedReader reader;
+ private DateFormat dateFormat;
+
+ PatternLogParser(Properties properties, String logFilePath) throws IOException {
+ this.pattern = Pattern.compile(properties.getProperty(PATTERN.getPropertyName()));
+ this.dateIndex = Integer.parseInt(properties.getProperty(DATE_INDEX.getPropertyName()));
+ this.threadNameIndex = Integer.parseInt(properties.getProperty(THREAD_NAME_INDEX
+ .getPropertyName()));
+ this.levelIndex = Integer.parseInt(properties.getProperty(LEVEL_INDEX.getPropertyName()));
+ this.codeLocationIndex = Integer.parseInt(properties.getProperty(CODE_LOCATION_INDEX
+ .getPropertyName()));
+ this.contentIndex = Integer.parseInt(properties.getProperty(CONTENT_INDEX.getPropertyName()));
+ this.dateFormat = new SimpleDateFormat(properties.getProperty(DATE_PATTERN.getPropertyName()));
+ this.logFilePath = logFilePath;
+ }
+
+ @Override
+ public LogEntry next() throws IOException {
+ String line = reader.readLine();
+ if (line == null) {
+ return null;
+ }
+
+ Matcher matcher = pattern.matcher(line);
+ if (!matcher.matches()) {
+ logger.error("Unrecognizable log: {}", line);
+ return null;
+ }
+ Date date;
+ try {
+ date = dateFormat.parse(matcher.group(dateIndex));
+ } catch (ParseException e) {
+ logger.error("Incorrect time format in {}", e);
+ return null;
+ }
+ String threadName = matcher.group(threadNameIndex).trim();
+ LogLevel logLevel = LogLevel.valueOf(matcher.group(levelIndex).trim());
+ String[] codeLocationStr = matcher.group(codeLocationIndex).split(":");
+ CodeLocation codeLocation = new CodeLocation(codeLocationStr[0].trim(), Integer.parseInt
+ (codeLocationStr[1]));
+ String content = matcher.group(contentIndex).trim();
+ return new LogEntry(date, threadName, logLevel, codeLocation, content);
+ }
+
+ @Override
+ public void close() throws IOException {
+ reader.close();
+ }
+
+ @Override
+ public void reset() throws IOException {
+ if (reader != null) {
+ reader.close();
+ }
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(logFilePath)));
+ }
+
+ enum PatternProperties {
+ PATTERN("pattern"), DATE_INDEX("date_index"), THREAD_NAME_INDEX("thread_name_index"),
+ LEVEL_INDEX("level_index"), CODE_LOCATION_INDEX("code_location_index"), CONTENT_INDEX
+ ("content_index"), DATE_PATTERN("date_pattern");
+
+ private String propertyName;
+
+ PatternProperties(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizePlan.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizePlan.java
new file mode 100644
index 0000000..431f305
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/VisualizePlan.java
@@ -0,0 +1,144 @@
+package org.apache.iotdb.db.tools.logvisual;
+
+import static org.apache.iotdb.db.tools.logvisual.VisualizePlan.PlanProperties.*;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.iotdb.db.tools.logvisual.exceptions.UnmatchedContentException;
+
+public class VisualizePlan {
+ // optional, this will be used as the title of the figure.
+ private String name;
+
+ // required, a regex that will capture the logs to be analyzed, the interesting values
+ // (measurements and group-by tags) should be surrounded with bracelets e.g.:
+ // contentPattern = location:(.*) temperature:(.*) pressure:(.*)
+ private Pattern contentPattern;
+ // the following 3 are optional, if not set, then it means this plan only cares whether this
+ // event happens or not and draws a scatter plot, other wise it will capture the given
+ // measurements and draw curves. Only numeric measurements are supported currently.
+ // if one of first 2 is set, the other must be set
+ // comma-separated
+ // e.g.:
+ // measurementPositions = 2,3
+ // legends = temperature,pressure
+ // tagPositions = 1
+ // then the logs will be grouped-by their locations and for each group, there will be two
+ // curves describing temperature and pressure respectively
+ private int[] measurementPositions;
+ private String[] legends;
+ private int[] tagPositions;
+
+ private LogFilter logFilter;
+
+ public VisualizePlan(Properties properties) throws IOException {
+ this.name = properties.getProperty(NAME.getPropertyName(), "untitled");
+ String patternStr = properties.getProperty(CONTENT_PATTERN.getPropertyName());
+ if (patternStr == null) {
+ throw new IOException("Bad plan, content pattern unset");
+ }
+ this.contentPattern = Pattern.compile(patternStr);
+
+ String measurementPositionsStr = properties.getProperty(MEASUREMENT_POSITIONS.getPropertyName());
+ if (measurementPositionsStr != null) {
+ String[] measurePosStrs = measurementPositionsStr.split(",");
+ measurementPositions = new int[measurePosStrs.length];
+ for (int i = 0; i < measurementPositions.length; i++) {
+ measurementPositions[i] = Integer.parseInt(measurePosStrs[i]);
+ }
+ }
+
+ String legendStr = properties.getProperty(LEGENDS.getPropertyName());
+ if (legendStr != null) {
+ legends = legendStr.split(",");
+ }
+
+ String groupByPositionsStr = properties.getProperty(TAG_POSITIONS.getPropertyName());
+ if (groupByPositionsStr != null) {
+ String[] groupByPosStrs = groupByPositionsStr.split(",");
+ tagPositions = new int[groupByPosStrs.length];
+ for (int i = 0; i < tagPositions.length; i++) {
+ tagPositions[i] = Integer.parseInt(groupByPosStrs[i]);
+ }
+ }
+
+ logFilter = new LogFilter(properties);
+ }
+
+ 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++) {
+ matchedValues[i - 1] = matcher.group(i);
+ }
+ if (tagPositions != null) {
+ List<String> tags = new ArrayList<>();
+ for (int pos : tagPositions) {
+ tags.add(matchedValues[pos-1]);
+ }
+ logEntry.setTags(tags);
+ }
+ if (measurementPositions != null) {
+ List<Double> measurements = new ArrayList<>();
+ for (int pos : measurementPositions) {
+ measurements.add(Double.parseDouble(matchedValues[pos-1]));
+ }
+ logEntry.setMeasurements(measurements);
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Pattern getContentPattern() {
+ return contentPattern;
+ }
+
+ public int[] getMeasurementPositions() {
+ return measurementPositions;
+ }
+
+ public String[] getLegends() {
+ return legends;
+ }
+
+ public int[] getTagPositions() {
+ return tagPositions;
+ }
+
+ public LogFilter getLogFilter() {
+ return logFilter;
+ }
+
+ enum PlanProperties {
+ NAME("name"), CONTENT_PATTERN("content_pattern"), MEASUREMENT_POSITIONS(
+ "measurement_positions"),
+ LEGENDS("legends"), TAG_POSITIONS("tag_positions");
+
+ private String propertyName;
+
+ PlanProperties(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ Properties properties = new Properties();
+ properties.load(new FileInputStream("E:\\codestore\\incubator-iotdb\\server\\src\\assembly\\resources\\tools\\logAnalyze\\plans\\flushTimeConsumption.plan"));
+
+ VisualizePlan plan = new VisualizePlan(properties);
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/AnalyzeException.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/AnalyzeException.java
new file mode 100644
index 0000000..0bc72c0
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/AnalyzeException.java
@@ -0,0 +1,19 @@
+package org.apache.iotdb.db.tools.logvisual.exceptions;
+
+public class AnalyzeException extends Exception {
+
+ public AnalyzeException() {
+ }
+
+ public AnalyzeException(String message) {
+ super(message);
+ }
+
+ public AnalyzeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AnalyzeException(Throwable cause) {
+ super(cause);
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/NoLogFileLoadedException.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/NoLogFileLoadedException.java
new file mode 100644
index 0000000..54338f3
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/NoLogFileLoadedException.java
@@ -0,0 +1,8 @@
+package org.apache.iotdb.db.tools.logvisual.exceptions;
+
+public class NoLogFileLoadedException extends AnalyzeException {
+
+ public NoLogFileLoadedException() {
+ super("No log file is loaded, please load a log file first");
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/NoSuchPlanException.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/NoSuchPlanException.java
new file mode 100644
index 0000000..32c04fc
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/NoSuchPlanException.java
@@ -0,0 +1,8 @@
+package org.apache.iotdb.db.tools.logvisual.exceptions;
+
+public class NoSuchPlanException extends AnalyzeException {
+
+ public NoSuchPlanException(String planName) {
+ super(String.format("No such plan %s", planName));
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/UnmatchedContentException.java b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/UnmatchedContentException.java
new file mode 100644
index 0000000..9c015a9
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/tools/logvisual/exceptions/UnmatchedContentException.java
@@ -0,0 +1,8 @@
+package org.apache.iotdb.db.tools.logvisual.exceptions;
+
+public class UnmatchedContentException extends AnalyzeException {
+
+ public UnmatchedContentException(String content, String pattern) {
+ super(String.format("%s cannot match %s", content, pattern));
+ }
+}
\ No newline at end of file