Skip to content

Commit 88a8c0e

Browse files
author
kevinbin
committed
Add subcommand slowlog. Used to capture slow logs.
1 parent 33f5d7f commit 88a8c0e

File tree

4 files changed

+195
-8
lines changed

4 files changed

+195
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Available Commands:
1515
help Help about any command
1616
monitor A MySQL monitor like iostat
1717
rbr2sbr Convert a rbr binary log to sbr format
18+
slowlog Capture MySQL slow log
1819
1920
Flags:
2021
-h, --help help for mysqlbox

cmd/mon.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -263,19 +263,12 @@ func getGtid(db *sql.DB) string {
263263

264264
func rename(f string) {
265265
if _, err := os.Stat(f); err == nil {
266-
n := f + "_" + time.Now().Format("20160102150405")
266+
n := f + "_" + time.Now().Format("20060102150405")
267267
os.Rename(f, n)
268268
fmt.Printf("Output file: %s \n", n)
269269
}
270270
}
271271

272-
func mysqlConnect() *sql.DB {
273-
dsn := fmt.Sprintf("%s:%s@(%s:%d)/", dbUser, dbPassWd, dbHost, dbPort)
274-
db, err := sql.Open("mysql", dsn)
275-
ifErrWithLog(err)
276-
return db
277-
}
278-
279272
func monitor() {
280273
c := make(chan os.Signal, 2)
281274
signal.Notify(c, os.Interrupt, syscall.SIGTERM)

cmd/root.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121
package cmd
2222

2323
import (
24+
"archive/tar"
25+
"compress/gzip"
26+
"database/sql"
2427
"fmt"
2528
"github.com/logrusorgru/aurora"
2629
"github.com/spf13/cobra"
30+
"io"
2731
"log"
2832
"os"
2933
"strconv"
@@ -107,3 +111,55 @@ func ifErrWithLog(err error) {
107111
log.Fatal(err)
108112
}
109113
}
114+
115+
func mysqlConnect() *sql.DB {
116+
dsn := fmt.Sprintf("%s:%s@(%s:%d)/", dbUser, dbPassWd, dbHost, dbPort)
117+
db, err := sql.Open("mysql", dsn)
118+
ifErrWithLog(err)
119+
return db
120+
}
121+
122+
func tarIt(src string, des string) error {
123+
fi, err := os.Stat(src)
124+
if err != nil {
125+
return err
126+
}
127+
128+
fw, err := os.Create(des)
129+
if err != nil {
130+
return err
131+
}
132+
defer fw.Close()
133+
134+
gzw := gzip.NewWriter(fw)
135+
136+
defer gzw.Close()
137+
138+
tw := tar.NewWriter(gzw)
139+
defer tw.Close()
140+
141+
// create a new dir/file header
142+
header, err := tar.FileInfoHeader(fi, fi.Name())
143+
if err != nil {
144+
return err
145+
}
146+
147+
// write the header
148+
if err := tw.WriteHeader(header); err != nil {
149+
return err
150+
}
151+
152+
// open files for taring
153+
f, err := os.Open(src)
154+
defer f.Close()
155+
if err != nil {
156+
return err
157+
}
158+
159+
// copy file data into tar writer
160+
if _, err := io.Copy(tw, f); err != nil {
161+
return err
162+
}
163+
164+
return nil
165+
}

cmd/slowlog.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright © 2017 Hong Bin <[email protected]>
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package cmd
22+
23+
import (
24+
"database/sql"
25+
"fmt"
26+
"github.com/spf13/cobra"
27+
"os"
28+
"os/signal"
29+
"path"
30+
"path/filepath"
31+
"strconv"
32+
// "strings"
33+
"syscall"
34+
"time"
35+
)
36+
37+
// slowlogCmd represents the slowlog command
38+
var slowlogCmd = &cobra.Command{
39+
Use: "slowlog",
40+
Short: "Capture MySQL slow log ",
41+
Long: `Custom query time threshold, capture MySQL slow log
42+
For example: Set query time threshold 0.1s, capture slow log for 60 seconds.
43+
$ mysqldba slowlog -u user -p pass -d /path -l 60 -t 0.1`,
44+
Run: func(cmd *cobra.Command, args []string) {
45+
run()
46+
},
47+
}
48+
49+
func run() {
50+
db := mysqlConnect()
51+
res := getLogConfig(db)
52+
getSlowLog(db, res)
53+
54+
}
55+
56+
var (
57+
dirPath string
58+
duration int
59+
queryTime float32
60+
)
61+
62+
func init() {
63+
RootCmd.AddCommand(slowlogCmd)
64+
65+
slowlogCmd.Flags().IntVarP(&duration, "long", "l", 60, "How long is capture")
66+
slowlogCmd.Flags().Float32VarP(&queryTime, "time", "t", 0, "Long query time threshold")
67+
slowlogCmd.Flags().StringVarP(&dirPath, "dir", "d", ".", "Specify save directory")
68+
}
69+
70+
func getLogConfig(db *sql.DB) map[string]string {
71+
var r = make(map[string]string)
72+
rows, err := db.Query(globalVariableSQL)
73+
ifErrWithLog(err)
74+
defer rows.Close()
75+
76+
for rows.Next() {
77+
var n, v string
78+
err := rows.Scan(&n, &v)
79+
ifErrWithLog(err)
80+
r[n] = v
81+
}
82+
err = rows.Err()
83+
ifErrWithLog(err)
84+
return r
85+
}
86+
87+
func restoreOption(db *sql.DB, r map[string]string) {
88+
89+
sql, err := strconv.ParseFloat(r["long_query_time"], 32)
90+
ifErrWithLog(err)
91+
92+
_, err = db.Exec("SET GLOBAL LONG_QUERY_TIME=?", sql)
93+
ifErrWithLog(err)
94+
95+
db.Exec("SET GLOBAL SLOW_QUERY_LOG=?", r["slow_query_log"])
96+
db.Exec("SET GLOBAL LOG_OUTPUT=?", r["log_output"])
97+
}
98+
99+
func getSlowLog(db *sql.DB, r map[string]string) {
100+
101+
c := make(chan os.Signal, 2)
102+
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
103+
go func() {
104+
<-c
105+
fmt.Println("Configuration restored. Exiting..")
106+
restoreOption(db, r)
107+
108+
os.Exit(1)
109+
}()
110+
111+
fmt.Printf("Original configuration \n slow_query_log: %v\n long_query_time: %v\n log_output: %v\n", r["slow_query_log"], r["long_query_time"], r["log_output"])
112+
113+
_, err := db.Exec("SET GLOBAL SLOW_QUERY_LOG=ON")
114+
ifErrWithLog(err)
115+
db.Exec("SET GLOBAL LOG_OUTPUT=FILE")
116+
db.Exec("FLUSH SLOW LOGS")
117+
db.Exec("SET GLOBAL LONG_QUERY_TIME=?", queryTime)
118+
var slowLogFile string
119+
if path.Dir(r["slow_query_log_file"]) == "." {
120+
slowLogFile = fmt.Sprintf("%s%s", r["datadir"], r["slow_query_log_file"])
121+
} else {
122+
slowLogFile = r["slow_query_log_file"]
123+
}
124+
125+
for i := 1; i <= duration; i++ {
126+
fmt.Fprintf(os.Stdout, "Query is recording to %v %vs.\r", slowLogFile, i)
127+
time.Sleep(time.Second)
128+
}
129+
130+
restoreOption(db, r)
131+
132+
tarFile := fmt.Sprintf("%s/%s_%vs_%s.tar.gz", dirPath, filepath.Base(r["slow_query_log_file"]), queryTime, time.Now().Format("20060102150405"))
133+
err = tarIt(slowLogFile, tarFile)
134+
ifErrWithLog(err)
135+
fmt.Printf("\nConfiguration restored. Archived to %v\n", tarFile)
136+
137+
}

0 commit comments

Comments
 (0)