├── images
├── 下断点.png
├── 悬浮提示.png
├── 搜索.png
├── 更新.png
├── 清空.png
├── 数据库连接失败.png
└── 数据库连接成功.png
├── .gitignore
├── src
└── main
│ └── java
│ └── com
│ ├── ResultTask.java
│ ├── MainApp.java
│ ├── main.fxml
│ ├── Util.java
│ └── MainController.java
├── README.md
└── pom.xml
/images/下断点.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/下断点.png
--------------------------------------------------------------------------------
/images/悬浮提示.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/悬浮提示.png
--------------------------------------------------------------------------------
/images/搜索.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/搜索.png
--------------------------------------------------------------------------------
/images/更新.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/更新.png
--------------------------------------------------------------------------------
/images/清空.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/清空.png
--------------------------------------------------------------------------------
/images/数据库连接失败.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/数据库连接失败.png
--------------------------------------------------------------------------------
/images/数据库连接成功.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fupinglee/MySQLMonitor/HEAD/images/数据库连接成功.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 | config.properties
22 | .idea
23 | src/test
24 | .csv
25 | target/
26 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
27 | hs_err_pid*
--------------------------------------------------------------------------------
/src/main/java/com/ResultTask.java:
--------------------------------------------------------------------------------
1 | package com;
2 |
3 | public class ResultTask {
4 |
5 | private int index;
6 | private String date;
7 | private String sql;
8 |
9 | public int getIndex() {
10 | return index;
11 | }
12 |
13 | public void setIndex(int index) {
14 | this.index = index;
15 | }
16 |
17 | public String getDate() {
18 | return date;
19 | }
20 |
21 | public void setDate(String date) {
22 | this.date = date;
23 | }
24 |
25 | public String getSql() {
26 | return sql;
27 | }
28 |
29 | public void setSql(String sql) {
30 | this.sql = sql;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/MainApp.java:
--------------------------------------------------------------------------------
1 | package com;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Scene;
6 | import javafx.scene.layout.AnchorPane;
7 | import javafx.stage.Stage;
8 |
9 | public class MainApp extends Application {
10 |
11 | public void start(Stage primaryStage) throws Exception {
12 |
13 | AnchorPane rootLayout = FXMLLoader.load(getClass().getResource("main.fxml"));
14 | Scene scene = new Scene(rootLayout,1000,600);
15 |
16 | primaryStage.setScene(scene);
17 | primaryStage.setTitle("MySQL监控工具");
18 | primaryStage.setResizable(false);
19 | // primaryStage.getIcons().add();
20 |
21 | primaryStage.setOnCloseRequest(event -> System.exit(0));
22 | primaryStage.show();
23 | primaryStage.centerOnScreen();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MySQLMonitor
2 | MySQL实时监控工具(代码审计、黑盒测试辅助工具)
3 |
4 | ## 使用
5 | 1.自行打包使用
6 |
7 | ```
8 | git clone https://github.com/fupinglee/MySQLMonitor
9 | cd MySQLMonitor
10 | mvn clean package -DskipTests=true
11 | ```
12 | 打开target下的jar文件即可执行
13 |
14 | 2.直接下载使用
15 |
16 | https://github.com/fupinglee/MySQLMonitor/releases
17 | ## 使用说明
18 |
19 | ### 数据库连接
20 | 数据库连接失败,下断、更新、清空等按钮不可用
21 | 
22 |
23 | 数据库连接成功,下断按钮可以使用,更新、清空等按钮不可用
24 | 
25 |
26 |
27 | ### 下断
28 | 下断点后可以更新和清空
29 | 
30 |
31 | ### 更新
32 | 点击更新查看执行的SQL语句
33 | 
34 |
35 | ### 搜索
36 | 在搜索框里输入内容可以对所需要的sql语句进行过滤
37 | 
38 |
39 | ### 清空
40 | 清空按钮清空表格里面的内容
41 | 
42 |
43 | ### 其他
44 | 单击选中一行后,鼠标移动可以悬浮显示该行的sql语句
45 | 
46 | >双击可以复制sql语句到剪贴板上
47 |
--------------------------------------------------------------------------------
/src/main/java/com/main.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com
8 | MySQLMonitor
9 | 1.1
10 | jar
11 |
12 | MySQLMonitor
13 | 监控MySQL数据库
14 |
15 |
16 |
17 | UTF-8
18 |
19 |
20 |
21 |
22 |
23 | org.apache.maven.plugins
24 | maven-compiler-plugin
25 | 3.8.1
26 |
27 | 1.8
28 | 1.8
29 |
30 |
31 |
32 | maven-assembly-plugin
33 |
34 | ${project.artifactId}-${project.version}
35 | false
36 |
37 |
38 | com.MainApp
39 |
40 |
41 |
42 | jar-with-dependencies
43 |
44 |
45 |
46 |
47 | make-assembly
48 | package
49 |
50 | single
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | false
59 | src/main/java
60 |
61 | **/*.fxml
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | mysql
70 | mysql-connector-java
71 | 8.0.21
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/main/java/com/Util.java:
--------------------------------------------------------------------------------
1 | package com;
2 |
3 | import java.sql.*;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Date;
6 |
7 | public class Util {
8 |
9 | public static String ftime() {
10 | SimpleDateFormat ftime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
11 | return ftime.format(new Date());
12 | }
13 |
14 | public static String getEcho(String password){
15 |
16 | String echo = "";
17 |
18 | if (password != null && !password.equals("")){
19 |
20 | int length = password.length();
21 | StringBuffer stringBuffer = new StringBuffer();
22 | for (int i=0;i tableCol_sql;
51 |
52 | @FXML
53 | private TableColumn tableCol_date;
54 | @FXML
55 | private TableColumn tableCol_id;
56 | @FXML
57 | private TextField textField_filter;
58 | @FXML
59 | private TableView tableView;
60 |
61 |
62 | private String date;
63 |
64 |
65 | private String dbpass;
66 |
67 | private ObservableList list = FXCollections.observableArrayList();
68 | private Connection conn;
69 |
70 | @FXML
71 | private Button btn_clear;
72 |
73 | @FXML
74 | private Label label_date;
75 |
76 | @FXML
77 | private Label label_state;
78 | private ChangeListener changeListener;
79 | @FXML
80 | public void initialize() {
81 | initConfigComponents();
82 | }
83 |
84 | static {
85 | try {
86 | Tooltip obj = new Tooltip();
87 |
88 | Class> clazz = obj.getClass().getDeclaredClasses()[1];
89 | if(!clazz.getName().contains("TooltipBehavior")){
90 | clazz = obj.getClass().getDeclaredClasses()[0];
91 | }
92 | Constructor> constructor = clazz.getDeclaredConstructor(
93 | Duration.class,
94 | Duration.class,
95 | Duration.class,
96 | boolean.class);
97 | constructor.setAccessible(true);
98 | Object tooltipBehavior = constructor.newInstance(
99 | new Duration(250), //open
100 | new Duration(50000), //visible
101 | new Duration(200), //close
102 | false);
103 | Field fieldBehavior = obj.getClass().getDeclaredField("BEHAVIOR");
104 | fieldBehavior.setAccessible(true);
105 | fieldBehavior.set(obj, tooltipBehavior);
106 | }
107 | catch (Exception e) {
108 | System.out.println("error:"+e);
109 | }
110 | }
111 | private int index;
112 | private void initConfigComponents() {
113 |
114 |
115 |
116 | testConn.setOnAction(event -> conn = conn());
117 |
118 | btn_clear.setDisable(true);
119 | btn_update.setDisable(true);
120 | btn_logOn.setDisable(true);
121 | tableCol_id.setCellValueFactory(new PropertyValueFactory("index"));
122 | tableCol_id.setStyle( "-fx-alignment: CENTER;");
123 |
124 |
125 |
126 | tableCol_sql.setCellValueFactory(new PropertyValueFactory("sql"));
127 | tableCol_date.setCellValueFactory(new PropertyValueFactory("date"));
128 |
129 |
130 | cb_showPass.setSelected(true);
131 | dbpass = textField_password.textProperty().get().trim();
132 |
133 |
134 | textField_password.textProperty().addListener((observable, oldValue, newValue) -> {
135 |
136 | if (cb_showPass.isSelected()) {
137 | dbpass = newValue;
138 | } else {
139 | dbpass = oldValue;
140 | }
141 | });
142 |
143 | cb_showPass.setOnAction(event -> {
144 | textField_password.setEditable(true);
145 | if (cb_showPass.isSelected()) {
146 | textField_password.setEditable(true);
147 | textField_password.setText(dbpass);
148 | } else {
149 | textField_password.setEditable(false);
150 | textField_password.setText(Util.getEcho(dbpass));
151 | }
152 | });
153 |
154 | btn_logOn.setOnAction(event -> {
155 | if (conn != null) {
156 | date = Util.ftime();
157 | label_date.setText("下断时间:"+date);
158 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "断点成功")));
159 | try {
160 | conn.prepareStatement("SET global general_log=on").executeUpdate();
161 | conn.prepareStatement("SET GLOBAL log_output='table'").executeUpdate();
162 | } catch (SQLException throwables) {
163 | throwables.printStackTrace();
164 | }
165 | index = 0;
166 | btn_clear.setDisable(false);
167 | btn_update.setDisable(false);
168 | } else {
169 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "数据库未连接或连接超时")));
170 | showAlert(Alert.AlertType.ERROR, "错误", "数据库未连接或连接超时");
171 | btn_clear.setDisable(true);
172 | btn_update.setDisable(true);
173 | }
174 |
175 | });
176 | FilteredList filteredList = new FilteredList<>(list, p -> true);
177 |
178 |
179 | changeListener = (observable, oldValue, newValue) -> {
180 | filteredList.setPredicate(resultTask -> {
181 | if (newValue == null || newValue.isEmpty()) {
182 | return true;
183 | }
184 | if (resultTask.getSql().toLowerCase().contains(newValue)) {
185 | return true;
186 | }
187 | return false;
188 | });
189 | tableView.setItems(filteredList);
190 | };
191 | btn_update.setOnAction(event -> {
192 | if(tableView.getItems().size()>0){
193 | clear(changeListener);
194 | }
195 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "查询成功")));
196 | try {
197 | Statement stmt = conn.createStatement();
198 |
199 | String logSql = "select * from mysql.general_log where command_type =\"Query\" OR command_type =\"Execute\" order by event_time desc limit 2";
200 |
201 | logSql = "select date_format(event_time,'%Y-%m-%d %H:%i:%S') as event_date ,argument from general_log where command_type='Query' and argument not like '/* mysql-conne%%' and argument not like 'SET auto%%'and argument not like 'SET sql_mo%%'and argument not like 'select event_time,argument from%%' and event_time>'" + date + "'";
202 |
203 | ResultSet log = stmt.executeQuery(logSql);
204 |
205 | ResultTask resultTask;
206 | while (log.next()) {
207 | String sql = log.getString("argument");
208 | String event_time = log.getString("event_date");
209 | if (!sql.equals(logSql)) {
210 | index ++;
211 | resultTask = new ResultTask();
212 | resultTask.setIndex(index);
213 | resultTask.setDate(event_time);
214 | resultTask.setSql(sql);
215 | list.add(resultTask);
216 | }
217 | }
218 | } catch (SQLException e) {
219 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "查询出错,"+e.getMessage())));
220 | }
221 |
222 | tableView.setItems(list);
223 | });
224 |
225 | btn_clear.setOnAction(event -> {
226 | clear(changeListener);
227 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "清除成功")));
228 | });
229 | textField_filter.textProperty().addListener(changeListener);
230 |
231 |
232 | tableView.setRowFactory(param -> {
233 | TableRow row = new TableRow();
234 |
235 | row.setOnMouseClicked(event -> {
236 | if(!row.isEmpty()){
237 | if( event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2){
238 |
239 | Clipboard clipboard = Clipboard.getSystemClipboard();
240 | ClipboardContent content = new ClipboardContent();
241 | content.putString(row.getItem().getSql());
242 | clipboard.setContent(content);
243 |
244 | } else if ( event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) {
245 |
246 | row.setEditable(true);
247 | }else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
248 |
249 | final Tooltip tt = new Tooltip();
250 | tt.setStyle("-fx-font: normal bold 13 Langdon; "
251 | + "-fx-base: #AE3522; "
252 | + "-fx-text-fill: orange;");
253 |
254 | tt.setText(row.getItem().getSql());
255 | tt.setWrapText(true);
256 | tt.setMaxWidth(300);
257 |
258 | row.setTooltip(tt);
259 |
260 | }
261 | }
262 |
263 | });
264 | return row;
265 | });
266 | }
267 |
268 | private void clear(ChangeListener changeListener){
269 | index = 0;
270 | tableView.setItems(list);
271 | textField_filter.textProperty().removeListener(changeListener);
272 | textField_filter.clear();
273 | tableView.getItems().clear();
274 | textField_filter.textProperty().addListener(changeListener);
275 | }
276 | public Connection conn() {
277 | String dbHost;
278 | int dbPort;
279 | String dbUser;
280 | dbHost = textField_host.textProperty().get().trim();
281 | dbPort = Integer.parseInt(textField_port.textProperty().get().trim());
282 | dbUser = textField_user.textProperty().get().trim();
283 | btn_clear.setDisable(true);
284 | btn_update.setDisable(true);
285 | btn_logOn.setDisable(true);
286 | clear(changeListener);
287 | Connection conn = Util.getConn(dbHost, dbPort, dbUser, dbpass);
288 | if (conn != null) {
289 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "数据库连接成功")));
290 | showAlert(Alert.AlertType.INFORMATION, "提示", "数据库连接成功");
291 |
292 | btn_logOn.setDisable(false);
293 | } else {
294 | label_state.setText((String.format("[%s]:%s", Util.ftime(), "数据库连接失败")));
295 | showAlert(Alert.AlertType.ERROR, "错误", "数据库连接失败");
296 |
297 | }
298 |
299 | return conn;
300 | }
301 |
302 |
303 | private void showAlert(Alert.AlertType type, String title, String message) {
304 | Alert alert = new Alert(type);
305 | alert.setTitle(title);
306 | alert.setHeaderText(null);
307 | alert.setContentText(message);
308 | alert.showAndWait();
309 | }
310 | }
--------------------------------------------------------------------------------