@@ -69,8 +69,17 @@ if ! output=$(get_mysql_status 2>&1); then
6969 fi
7070fi
7171
72- # Handle CTRL+C and restore cursor on exit
73- trap " echo -e '\nExiting MySQL Monitor. Goodbye!'; printf '\033[?25h'; exit" SIGINT
72+ tput smcup 2> /dev/null || true
73+
74+ # Handle CTRL+C and other term signals to restore screen and cursor before exit.
75+ cleanup () {
76+ printf ' \033[?25h' # Show the cursor
77+ tput rmcup 2> /dev/null || true
78+ echo -e " \nExiting MySQL Monitor. Goodbye!"
79+ exit
80+ }
81+
82+ trap cleanup SIGINT SIGTERM
7483
7584# Hide the cursor to reduce flicker
7685printf ' \033[?25l'
@@ -79,35 +88,28 @@ printf '\033[?25l'
7988printf " \033[H\033[J"
8089
8190while true ; do
82- # ====== Added Code Start: Check Terminal Size ======
83- # Retrieve current terminal size
84- read rows cols <<< $( stty size)
85-
86- # Check if terminal size is below minimum requirements
91+
92+ # Read current terminal size
93+ read rows cols <<< " $(stty size)"
94+
95+ # If terminal size is below minimum, show a warning in the center
8796 if [[ $rows -lt $MIN_ROWS || $cols -lt $MIN_COLS ]]; then
88- # Clear the screen
8997 printf " \033[H\033[J"
90-
91- # Define warning message
92- WARNING_MSG=" ❗ Terminal size too small. Please resize the window to at least ${MIN_COLS} columns and ${MIN_ROWS} rows. ❗"
93-
94- # Calculate the position to center the message
98+ WARNING_MSG=" ❗ Terminal size too small. Resize to at least ${MIN_COLS} x${MIN_ROWS} . ❗"
9599 msg_length=${# WARNING_MSG}
96100 msg_row=$(( rows / 2 ))
97101 msg_col=$(( (cols - msg_length) / 2 ))
98-
99- # Ensure message does not go negative
100- if [[ $msg_col -lt 0 ]]; then
101- msg_col=0
102+ (( msg_col < 0 )) && msg_col=0
103+
104+ # Move cursor to the calculated position and print warning in bold red
105+ printf " \033[${msg_row} ;${msg_col} H\033[1;31m%s\033[0m\n" " $WARNING_MSG "
106+
107+ read -t 2 -n 1 -r key
108+ if [[ $key == " q" || $key == " Q" ]]; then
109+ cleanup
102110 fi
103-
104- # Move cursor to the calculated position and print the message
105- printf " \033[${msg_row} ;${msg_col} H\033[1;31m%s\033[0m\n" " $WARNING_MSG " # Bold red text
106-
107- # Skip the rest of the loop iteration
108111 continue
109112 fi
110- # ====== Added Code End: Check Terminal Size ======
111113
112114 # Initialize an empty variable to hold all output
113115 output=" "
@@ -117,7 +119,6 @@ while true; do
117119
118120 # Capture MySQL status and append to output
119121 mysql_data=$( get_mysql_status | awk '
120- # Converts seconds to a human-readable "x d y h z m" etc.
121122 function prettyTime(sec) {
122123 years = int(sec / 31536000); sec %= 31536000
123124 months = int(sec / 2592000); sec %= 2592000
@@ -139,19 +140,18 @@ while true; do
139140 # Converts large numbers into user-friendly formats (K, M, B, T)
140141 function formatNumber(num) {
141142 if (num >= 1e12) {
142- return sprintf("%.2fT", num / 1e12) # Trillions
143+ return sprintf("%.2fT", num / 1e12)
143144 } else if (num >= 1e9) {
144- return sprintf("%.2fB", num / 1e9) # Billions
145+ return sprintf("%.2fB", num / 1e9)
145146 } else if (num >= 1e6) {
146- return sprintf("%.2fM", num / 1e6) # Millions
147+ return sprintf("%.2fM", num / 1e6)
147148 } else if (num >= 1e3) {
148- return sprintf("%.2fK", num / 1e3) # Thousands
149+ return sprintf("%.2fK", num / 1e3)
149150 } else {
150- return num # Small numbers stay as is
151+ return num
151152 }
152153 }
153154
154- # Convert values in MB to either XX MB or XX GB.
155155 function shortSizeMB(mb) {
156156 if (mb >= 1024) {
157157 gb = mb / 1024
@@ -162,7 +162,6 @@ while true; do
162162 }
163163
164164 BEGIN {
165- # Short descriptions for variables
166165 desc["Aborted_clients"] = "Failed client connections."
167166 desc["Aborted_connects"] = "Failed MySQL server connections."
168167 desc["Connections"] = "Total connection attempts."
@@ -203,18 +202,12 @@ while true; do
203202 desc["Threads_running"] = "Threads currently running queries."
204203 desc["Uptime"] = ""
205204
206- # Additional Metrics descriptions
207- # Moved "Queries per Second"
208- # desc["Queries per Second"] = "Should match traffic/app changes."
209205 desc["InnoDB Buffer Pool Free"] = "Zero/low? = innodb_buffer_pool_size."
210206 desc["InnoDB Buffer Pool Hit Ratio"] = "High QPS? Aim for high hit ratio."
211207 desc["Thread Cache Hit Ratio"] = "Faster connection handling. > 90%."
212208 desc["Table Cache Hit Ratio"] = "Faster table access speeds. > 90%."
213209 desc["Temp tables created on disk"] = "Keep this low! Disk I/O is slow."
214210
215- # Define additional metrics labels
216- # Removed "Queries per Second" from additional_labels
217- # additional_labels[1] = "Queries per Second"
218211 additional_labels[1] = "InnoDB Buffer Pool Free"
219212 additional_labels[2] = "InnoDB Buffer Pool Hit Ratio"
220213 additional_labels[3] = "Thread Cache Hit Ratio"
@@ -229,149 +222,132 @@ while true; do
229222 }
230223
231224 END {
232- # Build a list of keys
233225 count = 0
234226 for (v in data) {
235227 count++
236228 keys[count] = v
237229 }
238- asort(keys) # Sort the array of keys
230+ asort(keys)
239231
240- # Prepare the values
241- # InnoDB Buffer Pool
242232 ibp_size_mb = ""
243233 if ("Innodb_buffer_pool_size" in data) {
244234 ibp_size_mb = data["Innodb_buffer_pool_size"] / (1024 * 1024)
245235 }
246- # Estimate free pages in MB
236+
247237 ibp_free_mb = ""
248238 if ("Innodb_buffer_pool_pages_free" in data) {
249239 ibp_free_mb = data["Innodb_buffer_pool_pages_free"] * 16 / 1024
250240 }
251241
252- # Queries per second (QPS)
253242 qps = ""
254243 if (("Questions" in data) && ("Uptime" in data) && (data["Uptime"] > 0)) {
255244 qps = data["Questions"] / data["Uptime"]
256245 }
257246
258- # Temp table disk ratio
259247 tmp_disk_ratio = ""
260248 if (("Created_tmp_disk_tables" in data) && ("Created_tmp_tables" in data) && (data["Created_tmp_tables"] > 0)) {
261249 tmp_disk_ratio = 100 * data["Created_tmp_disk_tables"] / data["Created_tmp_tables"]
262250 }
263251
264- # Thread Cache Hit Ratio
265252 thread_cache_ratio = ""
266253 if (("Threads_created" in data) && ("Connections" in data) && (data["Connections"] > 0)) {
267254 thread_cache_ratio = 100 * (1 - (data["Threads_created"] / data["Connections"]))
268255 }
269256
270- # Correct Table Cache Hit Ratio Calculation
271257 table_cache_ratio = ""
272- if (("Table_open_cache_hits" in data) && ("Table_open_cache_misses" in data) && (data["Table_open_cache_hits"] + data["Table_open_cache_misses"] > 0)) {
258+ if (("Table_open_cache_hits" in data) && ("Table_open_cache_misses" in data) &&
259+ (data["Table_open_cache_hits"] + data["Table_open_cache_misses"] > 0)) {
273260 table_cache_ratio = 100 * (data["Table_open_cache_hits"] / (data["Table_open_cache_hits"] + data["Table_open_cache_misses"]))
274261 }
275262
276- # InnoDB Buffer Pool Hit Ratio, clamped to 0% if negative
277263 ibp_efficiency = ""
278- if (("Innodb_buffer_pool_read_requests" in data) && ("Innodb_buffer_pool_reads" in data) && (data["Innodb_buffer_pool_read_requests"] > 0)) {
264+ if (("Innodb_buffer_pool_read_requests" in data) && ("Innodb_buffer_pool_reads" in data) &&
265+ (data["Innodb_buffer_pool_read_requests"] > 0)) {
279266 temp_ratio = 100 * (1 - (data["Innodb_buffer_pool_reads"] / data["Innodb_buffer_pool_read_requests"]))
280267 if (temp_ratio < 0) {
281268 temp_ratio = 0
282269 }
283270 ibp_efficiency = temp_ratio
284271 }
285272
286- # Calculate column widths dynamically based on content
287273 col1_width = 0
288274 col2_width = 0
289275 col3_width = 0
290276
291277 for (v in data) {
292- if (length(v) > col1_width) col1_width = length(v) # Longest Metric (varName)
293- if (length(data[v]) > col2_width) col2_width = length(data[v]) # Longest Value (val)
294- if (length(desc[v]) > col3_width) col3_width = length(desc[v]) # Longest Description (explanation)
278+ if (length(v) > col1_width) col1_width = length(v)
279+ if (length(data[v]) > col2_width) col2_width = length(data[v])
280+ if (length(desc[v]) > col3_width) col3_width = length(desc[v])
295281 }
296282
297- # Include additional metrics labels in column width calculation
298283 for (i in additional_labels) {
299284 label_length = length(additional_labels[i])
300285 if (label_length > col1_width) {
301286 col1_width = label_length
302287 }
303288 }
304289
305- # Ensure minimum widths for readability
306290 col1_width = (col1_width > 15 ? col1_width : 15)
307291 col2_width = (col2_width > 10 ? col2_width : 10)
308292 col3_width = (col3_width > 25 ? col3_width : 25)
309293
310- # Initialize output within AWK
311294 output = ""
312295
313- # Print the data with adjusted widths
314296 for (i=1; i<=count; i++) {
315297 varName = keys[i]
316298 explanation = (varName in desc) ? desc[varName] : "No description available"
317299
318300 if (varName == "Uptime") {
319- val = prettyTime(data[varName]) # Format Uptime
320- # Append the note "(Wait 24h for accuracy)" to the varName
301+ val = prettyTime(data[varName])
321302 output = output sprintf("%-" col1_width "s | %s %s\n", varName " (Wait 24h for accuracy)", val, explanation)
322- # Skip further processing for Uptime
323303 continue
324304 }
325305
326306 val = formatNumber(data[varName])
327307
328- # New: Append QPS to "Questions"
329308 if (varName == "Questions" && qps != "") {
330309 qps_formatted = sprintf("(%.2f QPS)", qps)
331310 varName = varName " " qps_formatted
332311 }
333312
334- # Highlight specific values if needed
335313 if (varName == "Innodb_buffer_pool_pages_free" && data[varName] == 0) {
336- output = output sprintf("\033[0;31m%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\033[0m\n", varName, val, explanation)
314+ output = output sprintf("\033[0;31m%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\033[0m\n",
315+ varName, val, explanation)
337316 } else {
338- output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", varName, val, explanation)
317+ output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n",
318+ varName, val, explanation)
339319 }
340320 }
341321
342- # Additional Metrics section
343322 output = output sprintf("------------------------------- MySQL Health Metrics --------------------------------\n")
344323
345- # Moved Queries per Second
346- # Removed the following block to eliminate the separate "Queries per Second" line
347- # if (qps != "") {
348- # output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", \
349- # "Queries per Second", sprintf("%.2f QPS", qps), desc["Queries per Second"])
350- # }
351-
352324 if (ibp_free_mb != "") {
353- output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", \
354- "InnoDB Buffer Pool Free", shortSizeMB(ibp_free_mb), desc["InnoDB Buffer Pool Free"])
325+ output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n",
326+ "InnoDB Buffer Pool Free", shortSizeMB(ibp_free_mb), desc["InnoDB Buffer Pool Free"])
355327 }
356328
357329 if (ibp_efficiency != "") {
358- output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", \
359- "InnoDB Buffer Pool Hit Ratio", sprintf("%.1f%%", ibp_efficiency), desc["InnoDB Buffer Pool Hit Ratio"])
330+ output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n",
331+ "InnoDB Buffer Pool Hit Ratio", sprintf("%.1f%%", ibp_efficiency),
332+ desc["InnoDB Buffer Pool Hit Ratio"])
360333 }
361334
362335 if (thread_cache_ratio != "") {
363- output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", \
364- "Thread Cache Hit Ratio", sprintf("%.1f%%", thread_cache_ratio), desc["Thread Cache Hit Ratio"])
336+ output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n",
337+ "Thread Cache Hit Ratio", sprintf("%.1f%%", thread_cache_ratio),
338+ desc["Thread Cache Hit Ratio"])
365339 }
366340
367341 if (table_cache_ratio != "") {
368- output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", \
369- "Table Cache Hit Ratio", sprintf("%.1f%%", table_cache_ratio), desc["Table Cache Hit Ratio"])
342+ output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n",
343+ "Table Cache Hit Ratio", sprintf("%.1f%%", table_cache_ratio),
344+ desc["Table Cache Hit Ratio"])
370345 }
371346
372347 if (tmp_disk_ratio != "") {
373- output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n", \
374- "Temp tables created on disk", sprintf("%.1f%%", tmp_disk_ratio), desc["Temp tables created on disk"])
348+ output = output sprintf("%-" col1_width "s | %-" col2_width "s | %-" col3_width "s\n",
349+ "Temp tables created on disk", sprintf("%.1f%%", tmp_disk_ratio),
350+ desc["Temp tables created on disk"])
375351 }
376352
377353 print output
@@ -390,39 +366,30 @@ while true; do
390366 mem_free_bytes=${mem_array[2]}
391367 mem_avail_bytes=${mem_array[3]}
392368
393- # Convert bytes to gigabytes with two decimal places
394369 mem_total_gb=$( awk " BEGIN {printf \" %.2f\" , $mem_total_bytes / 1024 / 1024 / 1024}" )
395370 mem_used_gb=$( awk " BEGIN {printf \" %.2f\" , $mem_used_bytes / 1024 / 1024 / 1024}" )
396371 mem_free_gb=$( awk " BEGIN {printf \" %.2f\" , $mem_free_bytes / 1024 / 1024 / 1024}" )
397372 mem_avail_gb=$( awk " BEGIN {printf \" %.2f\" , $mem_avail_bytes / 1024 / 1024 / 1024}" )
398373
399- # Calculate available memory percentage with floating-point precision
400374 avail_mem_percentage=$( awk " BEGIN {printf \" %.2f\" , 100 * $mem_avail_bytes / $mem_total_bytes }" )
401-
402- # Determine if available memory is below 10%
403375 is_low_mem=$( awk " BEGIN {print ($avail_mem_percentage < 10)}" )
404376
405377 if (( is_low_mem )) ; then
406378 mem_info=" Total: ${mem_total_gb} GB, Used: ${mem_used_gb} GB, Free: ${mem_free_gb} GB, Available: ${mem_avail_gb} GB \033[0;31m(Warning!: ${avail_mem_percentage} %%)\033[0m"
407379 else
408380 mem_info=" Total: ${mem_total_gb} GB, Used: ${mem_used_gb} GB, Free: ${mem_free_gb} GB, Available: ${mem_avail_gb} GB"
409381 fi
410- # Append mem_info with an empty line before it
411- output+=$' \n ' " ${mem_info} " $' \n '
412382
413- # Add title followed by a newline
383+ output+= $' \n ' " ${mem_info} " $' \n '
414384 output+=" ${TITLE} " $' \n '
415385
416386 # Move cursor to top-left and print all output at once
387+ printf " \033[H\033[J" # Clear screen each time to avoid partial lines.
417388 printf " \033[H%s" " $output "
418389
419- # Read user input with timeout
390+ # Wait for user input with timeout
420391 read -t " $INTERVAL " -n 1 -r key
421392 if [[ $key == " q" || $key == " Q" ]]; then
422- echo -e " \nQuitting MySQL Monitor. Goodbye!"
423- break
393+ cleanup
424394 fi
425395done
426-
427- # Restore the cursor before exiting (in case loop is exited without SIGINT)
428- printf ' \033[?25h'
0 commit comments