-
Notifications
You must be signed in to change notification settings - Fork 31
/
main.go
153 lines (135 loc) · 5.63 KB
/
main.go
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// ps-top - Top like program which collects information from MySQL's
// performance_schema database.
package main
import (
"flag"
"fmt"
"os"
"runtime/pprof"
"github.com/howeyc/gopass"
"github.com/sjmudd/ps-top/app"
"github.com/sjmudd/ps-top/connector"
"github.com/sjmudd/ps-top/log"
"github.com/sjmudd/ps-top/model/filter"
"github.com/sjmudd/ps-top/utils"
"github.com/sjmudd/ps-top/version"
)
var (
connectorFlags connector.Config
// command line flags
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
flagAnonymise = flag.Bool("anonymise", false, "Anonymise hostname, user, db and table names (default: false)")
flagAskpass = flag.Bool("askpass", false, "Ask for password interactively")
flagDatabaseFilter = flag.String("database-filter", "", "Optional comma-separated filter of database names")
flagDebug = flag.Bool("debug", false, "Enabling debug logging")
flagHelp = flag.Bool("help", false, "Provide some help for "+utils.ProgName)
flagInterval = flag.Int("interval", 1, "Set the initial poll interval (default 1 second)")
flagVersion = flag.Bool("version", false, "Show the version of "+utils.ProgName)
flagView = flag.String("view", "", "Provide view to show when starting "+utils.ProgName+" (default: table_io_latency)")
getPasswdFunc = gopass.GetPasswd // to allow me to test
)
func usage() {
lines := []string{
utils.ProgName + " - " + utils.Copyright,
"",
"Top-like program to show MySQL activity by using information collected",
"from performance_schema.",
"",
"Usage: " + utils.ProgName + " <options>",
"",
"Options:",
"--anonymise=<true|false> Anonymise hostname, user, db and table names",
"--askpass Request password to be provided interactively",
"--database-filter=db1[,db2,db3,...] Optional database names to filter on, default ''",
"--defaults-file=/path/to/defaults.file Connect to MySQL using given defaults-file, default ~/.my.cnf",
"--help Show this help message",
"--host=<hostname> MySQL host to connect to",
"--interval=<seconds> Set the default poll interval (in seconds)",
"--password=<password> Password to use when connecting",
"--port=<port> MySQL port to connect to",
"--socket=<path> MySQL path of the socket to connect to",
"--user=<user> User to connect with",
"--use-environment Connect to MySQL using a go dsn collected from MYSQL_DSN e.g. MYSQL_DSN='test_user:test_pass@tcp(127.0.0.1:3306)/performance_schema'",
"--version Show the version",
"--view=<view> Determine the view you want to see when " + utils.ProgName + " starts (default: table_io_latency)",
" Possible values: table_io_latency table_io_ops file_io_latency table_lock_latency user_latency mutex_latency stages_latency",
}
for _, line := range lines {
fmt.Println(line)
}
}
// askPass asks for a password interactively from the user and returns it.
func askPass() (string, error) {
fmt.Printf("Password: ")
pass, err := getPasswdFunc()
if err != nil {
return "", err
}
stringPassword := string(pass) // converting from []char to string may not be perfect
return stringPassword, nil
}
// getConfig collects the configuration from the command line arguments
func getConnectorConfig() connector.Config {
defaultsFile := flag.String("defaults-file", "", "Define the defaults file to read")
host := flag.String("host", "", "Provide the hostname of the MySQL to connect to")
password := flag.String("password", "", "Provide the password when connecting to the MySQL server")
port := flag.Int("port", 0, "Provide the port number of the MySQL to connect to (default: 3306)") /* Port is deliberately 0 here, defaults to 3306 elsewhere */
socket := flag.String("socket", "", "Provide the path to the local MySQL server to connect to")
user := flag.String("user", "", "Provide the username to connect with to MySQL (default: $USER)")
useEnvironment := flag.Bool("use-environment", false, "Use the environment variable MYSQL_DSN (go dsn) to connect with to MySQL")
flag.Parse()
return connector.Config{
DefaultsFile: defaultsFile,
Host: host,
Password: password,
Port: port,
Socket: socket,
User: user,
UseEnvironment: useEnvironment,
}
}
func main() {
connectorFlags = getConnectorConfig()
// Enable logging if requested or PSTOP_DEBUG=1
log.SetupLogging(*flagDebug || os.Getenv("PSTOP_DEBUG") == "1", utils.ProgName+".log")
log.Printf("Starting %v version %v", utils.ProgName, version.Version)
if *flagAskpass {
password, err := askPass()
if err != nil {
fmt.Printf("Failed to read password: %v\n", err)
return
}
connectorFlags.Password = &password
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
if *flagHelp {
usage()
return
}
if *flagVersion {
fmt.Println(utils.ProgName + " version " + version.Version)
return
}
app, err := app.NewApp(
connectorFlags,
app.Settings{
Anonymise: *flagAnonymise,
Filter: filter.NewDatabaseFilter(*flagDatabaseFilter),
Interval: *flagInterval,
ViewName: *flagView,
})
if err != nil {
log.Fatalf("Failed to start %s: %s", utils.ProgName, err)
}
defer app.Cleanup()
app.Run()
}