TD-797: add log support

This commit is contained in:
Bomin Zhang 2020-07-02 16:06:47 +08:00
parent 72ddf72b01
commit 96acc6c284
2 changed files with 55 additions and 16 deletions

View File

@ -5,7 +5,7 @@ Stress test tool for TDengine. It run a set of test cases randomly and show stat
## COMMAND LINE ## COMMAND LINE
``` bash ``` bash
$ ./stress [-h=<localhost>] [-P=<0>] [-d=<test>] [-u=<root>] [-p=<taosdata>] [-c=<4>] [-f=<true>] [path_or_sql] $ ./stress [-h=<localhost>] [-P=<0>] [-d=<test>] [-u=<root>] [-p=<taosdata>] [-c=<4>] [-f=<true>] [-l=<logPath>] [path_or_sql]
``` ```
* **-h**: host name or IP address of TDengine server (default: localhost). * **-h**: host name or IP address of TDengine server (default: localhost).
@ -14,6 +14,7 @@ $ ./stress [-h=<localhost>] [-P=<0>] [-d=<test>] [-u=<root>] [-p=<taosdata>] [-c
* **-p**: password (default: taosdata). * **-p**: password (default: taosdata).
* **-c**: concurrency, number of concurrent goroutines for query (default: 4). * **-c**: concurrency, number of concurrent goroutines for query (default: 4).
* **-f**: fetch data or not (default: true). * **-f**: fetch data or not (default: true).
* **-l**: log file path (default: no log).
* **path_or_sql**: a SQL statement or path of a JSON file which contains the test cases (default: cases.json). * **path_or_sql**: a SQL statement or path of a JSON file which contains the test cases (default: cases.json).
## TEST CASE FILE ## TEST CASE FILE
@ -66,7 +67,6 @@ Placeholders of `sql` are replaced by arguments in `args` at runtime. There are
``` ```
00:00:08 | TOTAL REQ | TOTAL TIME(us) | TOTAL AVG(us) | REQUEST | TIME(us) | AVERAGE(us) | 00:00:08 | TOTAL REQ | TOTAL TIME(us) | TOTAL AVG(us) | REQUEST | TIME(us) | AVERAGE(us) |
-----------------------------------------------------------------------------------------------
TOTAL | 3027 | 26183890 | 8650.11 | 287 | 3060935 | 10665.28 | TOTAL | 3027 | 26183890 | 8650.11 | 287 | 3060935 | 10665.28 |
SUCCESS | 3027 | 26183890 | 8650.11 | 287 | 3060935 | 10665.28 | SUCCESS | 3027 | 26183890 | 8650.11 | 287 | 3060935 | 10665.28 |
FAIL | 0 | 0 | 0.00 | 0 | 0 | 0.00 | FAIL | 0 | 0 | 0.00 | 0 | 0 | 0.00 |

View File

@ -121,9 +121,11 @@ var (
password string password string
fetch bool fetch bool
chLog chan string
wgLog sync.WaitGroup
startAt time.Time startAt time.Time
shouldStop int64 shouldStop int64
wg sync.WaitGroup wgTest sync.WaitGroup
stat statitics stat statitics
totalWeight int totalWeight int
cases []testCase cases []testCase
@ -204,7 +206,7 @@ func selectTestCase() *testCase {
} }
func runTest() { func runTest() {
defer wg.Done() defer wgTest.Done()
db, e := sql.Open("taosSql", fmt.Sprintf("%s:%s@tcp(%s:%v)/%s", user, password, host, port, database)) db, e := sql.Open("taosSql", fmt.Sprintf("%s:%s@tcp(%s:%v)/%s", user, password, host, port, database))
if e != nil { if e != nil {
fmt.Printf("failed to connect to database: %s\n", e.Error()) fmt.Printf("failed to connect to database: %s\n", e.Error())
@ -232,6 +234,9 @@ func runTest() {
duration := time.Now().Sub(start).Microseconds() duration := time.Now().Sub(start).Microseconds()
if e != nil { if e != nil {
if chLog != nil {
chLog <- str + ": " + e.Error()
}
atomic.AddInt64(&stat.failed, 1) atomic.AddInt64(&stat.failed, 1)
atomic.AddInt64(&stat.failedDuration, duration) atomic.AddInt64(&stat.failedDuration, duration)
} else { } else {
@ -254,9 +259,8 @@ func getStatPrinter() func(tm time.Time) {
current.failedDuration = atomic.LoadInt64(&stat.failedDuration) current.failedDuration = atomic.LoadInt64(&stat.failedDuration)
seconds := int64(tm.Sub(startAt).Seconds()) seconds := int64(tm.Sub(startAt).Seconds())
format := "\033K %02v:%02v:%02v | TOTAL REQ | TOTAL TIME(us) | TOTAL AVG(us) | REQUEST | TIME(us) | AVERAGE(us) |\n" format := "\033[47;30m %02v:%02v:%02v | TOTAL REQ | TOTAL TIME(us) | TOTAL AVG(us) | REQUEST | TIME(us) | AVERAGE(us) |\033[0m\n"
fmt.Printf(format, seconds/3600, seconds%3600/60, seconds%60) fmt.Printf(format, seconds/3600, seconds%3600/60, seconds%60)
fmt.Println("-----------------------------------------------------------------------------------------------")
tr := current.succeeded + current.failed tr := current.succeeded + current.failed
td := current.succeededDuration + current.failedDuration td := current.succeededDuration + current.failedDuration
@ -269,7 +273,7 @@ func getStatPrinter() func(tm time.Time) {
if r > 0 { if r > 0 {
a = float64(d) / float64(r) a = float64(d) / float64(r)
} }
format = "\033[K TOTAL | %9v | %14v | %13.2f | %7v | %10v | % 13.2f |\n" format = " TOTAL | %9v | %14v | %13.2f | %7v | %10v | % 13.2f |\n"
fmt.Printf(format, tr, td, ta, r, d, a) fmt.Printf(format, tr, td, ta, r, d, a)
tr = current.succeeded tr = current.succeeded
@ -283,7 +287,7 @@ func getStatPrinter() func(tm time.Time) {
if r > 0 { if r > 0 {
a = float64(d) / float64(r) a = float64(d) / float64(r)
} }
format = "\033[K SUCCESS | \033[32m%9v\033[0m | \033[32m%14v\033[0m | \033[32m%13.2f\033[0m | \033[32m%7v\033[0m | \033[32m%10v\033[0m | \033[32m%13.2f\033[0m |\n" format = " SUCCESS | \033[32m%9v\033[0m | \033[32m%14v\033[0m | \033[32m%13.2f\033[0m | \033[32m%7v\033[0m | \033[32m%10v\033[0m | \033[32m%13.2f\033[0m |\n"
fmt.Printf(format, tr, td, ta, r, d, a) fmt.Printf(format, tr, td, ta, r, d, a)
tr = current.failed tr = current.failed
@ -297,7 +301,7 @@ func getStatPrinter() func(tm time.Time) {
if r > 0 { if r > 0 {
a = float64(d) / float64(r) a = float64(d) / float64(r)
} }
format = "\033[K FAIL | \033[31m%9v\033[0m | \033[31m%14v\033[0m | \033[31m%13.2f\033[0m | \033[31m%7v\033[0m | \033[31m%10v\033[0m | \033[31m%13.2f\033[0m |\n" format = " FAIL | \033[31m%9v\033[0m | \033[31m%14v\033[0m | \033[31m%13.2f\033[0m | \033[31m%7v\033[0m | \033[31m%10v\033[0m | \033[31m%13.2f\033[0m |\n"
fmt.Printf(format, tr, td, ta, r, d, a) fmt.Printf(format, tr, td, ta, r, d, a)
last = current last = current
@ -305,8 +309,35 @@ func getStatPrinter() func(tm time.Time) {
} }
} }
func startLogger(path string) error {
if len(path) == 0 {
return nil
}
f, e := os.Create(path)
if e != nil {
return e
}
chLog = make(chan string, 100)
wgLog.Add(1)
go func() {
for s := range chLog {
if f != nil {
f.WriteString(s)
f.WriteString("\n")
}
}
f.Close()
wgLog.Done()
}()
return nil
}
func main() { func main() {
var concurrency uint var concurrency uint
var logPath string
flag.StringVar(&host, "h", "localhost", "host name or IP address of TDengine server") flag.StringVar(&host, "h", "localhost", "host name or IP address of TDengine server")
flag.UintVar(&port, "P", 0, "port (default 0)") flag.UintVar(&port, "P", 0, "port (default 0)")
flag.StringVar(&database, "d", "test", "database name") flag.StringVar(&database, "d", "test", "database name")
@ -314,8 +345,14 @@ func main() {
flag.StringVar(&password, "p", "taosdata", "password") flag.StringVar(&password, "p", "taosdata", "password")
flag.BoolVar(&fetch, "f", true, "fetch result or not") flag.BoolVar(&fetch, "f", true, "fetch result or not")
flag.UintVar(&concurrency, "c", 4, "concurrency, number of goroutines for query") flag.UintVar(&concurrency, "c", 4, "concurrency, number of goroutines for query")
flag.StringVar(&logPath, "l", "", "path of log file (default: no log)")
flag.Parse() flag.Parse()
if e := startLogger(logPath); e != nil {
fmt.Println("failed to open log file:", e.Error())
return
}
pathOrSQL := flag.Arg(0) pathOrSQL := flag.Arg(0)
if len(pathOrSQL) == 0 { if len(pathOrSQL) == 0 {
pathOrSQL = "cases.json" pathOrSQL = "cases.json"
@ -327,16 +364,14 @@ func main() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
fmt.Println() fmt.Printf("\nSERVER: %s DATABASE: %s CONCURRENCY: %d FETCH DATA: %v\n\n", host, database, concurrency, fetch)
fmt.Printf("SERVER: %s DATABASE: %s CONCURRENCY: %d FETCH DATA: %v\n", host, database, concurrency, fetch)
fmt.Println()
startAt = time.Now() startAt = time.Now()
printStat := getStatPrinter() printStat := getStatPrinter()
printStat(startAt) printStat(startAt)
for i := uint(0); i < concurrency; i++ { for i := uint(0); i < concurrency; i++ {
wg.Add(1) wgTest.Add(1)
go runTest() go runTest()
} }
@ -352,16 +387,20 @@ LOOP:
case <-interrupt: case <-interrupt:
break LOOP break LOOP
case tm := <-ticker.C: case tm := <-ticker.C:
fmt.Print("\033[5A") fmt.Print("\033[4A")
printStat(tm) printStat(tm)
} }
} }
atomic.StoreInt64(&shouldStop, 1) atomic.StoreInt64(&shouldStop, 1)
fmt.Print("\033[100D'Ctrl + C' received, Waiting started query to stop...") fmt.Print("\033[100D'Ctrl + C' received, Waiting started query to stop...")
wgTest.Wait()
wg.Wait() if chLog != nil {
fmt.Print("\033[5A\033[100D") close(chLog)
wgLog.Wait()
}
fmt.Print("\033[4A\033[100D")
printStat(time.Now()) printStat(time.Now())
fmt.Println() fmt.Println()
} }