@@ -39,7 +39,9 @@ import (
39
39
var DumpConfig = Dump {}
40
40
41
41
type Dump struct {
42
- AuditLogPaths []string
42
+ AuditLogPaths []string
43
+ AuditLogTable string
44
+
43
45
AuditLogUnescape bool
44
46
OutputDDLDir string
45
47
OutputQueryDir string
@@ -60,6 +62,7 @@ type Dump struct {
60
62
QueryStates []string
61
63
OnlySelect bool
62
64
Strict bool
65
+ From , To string
63
66
64
67
Clean bool
65
68
}
@@ -147,7 +150,10 @@ func init() {
147
150
pFlags .StringSliceVar (& DumpConfig .QueryStates , "query-states" , []string {}, "Dump queries with states, like 'ok', 'eof' and 'err'" )
148
151
pFlags .BoolVar (& DumpConfig .OnlySelect , "only-select" , true , "Only dump SELECT queries" )
149
152
pFlags .BoolVarP (& DumpConfig .Strict , "strict" , "s" , false , "Filter out sqls that can't be parsed" )
150
- pFlags .StringSliceVar (& DumpConfig .AuditLogPaths , "audit-logs" , nil , "Audit log paths, either local path or ssh://xxx" )
153
+ pFlags .StringVar (& DumpConfig .From , "from" , "" , "Dump queries from this time, like '2006-01-02 15:04:05'" )
154
+ pFlags .StringVar (& DumpConfig .To , "to" , "" , "Dump queries to this time, like '2006-01-02 16:04:05'" )
155
+ pFlags .StringSliceVar (& DumpConfig .AuditLogPaths , "audit-logs" , nil , "Scan query from audit log files, either local path or 'ssh://xxx'" )
156
+ pFlags .StringVar (& DumpConfig .AuditLogTable , "audit-log-table" , "" , "Scan query from audit log table, like 'audit_db.audit_tbl'" )
151
157
pFlags .BoolVar (& DumpConfig .AuditLogUnescape , "audit-log-unescape" , true , "Unescape '\\ n', '\\ t' and '\\ r' in audit log" )
152
158
pFlags .StringVar (& DumpConfig .AuditLogEncoding , "audit-log-encoding" , "auto" , "Audit log encoding, like utf8, gbk, ..." )
153
159
pFlags .StringVar (& DumpConfig .SSHAddress , "ssh-address" , "" , "SSH address for downloading audit log, default is 'root@{db_host}:22'" )
@@ -168,10 +174,25 @@ func completeDumpConfig() error {
168
174
DumpConfig .OutputQueryDir = filepath .Join (GlobalConfig .OutputDir , "sql" )
169
175
DumpConfig .LocalAuditLogCacheDir = filepath .Join (GlobalConfig .DataDir , "auditlog" )
170
176
177
+ if DumpConfig .AuditLogTable != "" && ! strings .Contains (DumpConfig .AuditLogTable , "." ) {
178
+ return errors .New ("Need to specific database in '--audit-log-table', like 'audit_db.audit_tbl'" )
179
+ }
180
+
171
181
if DumpConfig .QueryMinDuration_ > 0 {
172
182
DumpConfig .QueryMinDurationMs = int (DumpConfig .QueryMinDuration_ .Milliseconds ())
173
183
}
174
184
185
+ if DumpConfig .From != "" {
186
+ if _ , err := time .Parse (time .DateTime , DumpConfig .From ); err != nil {
187
+ return err
188
+ }
189
+ }
190
+ if DumpConfig .To != "" {
191
+ if _ , err := time .Parse (time .DateTime , DumpConfig .To ); err != nil {
192
+ return err
193
+ }
194
+ }
195
+
175
196
GlobalConfig .DBs , GlobalConfig .Tables = lo .Uniq (GlobalConfig .DBs ), lo .Uniq (GlobalConfig .Tables )
176
197
dbs , tables := GlobalConfig .DBs , GlobalConfig .Tables
177
198
if DumpConfig .DumpSchema && len (dbs ) == 0 {
@@ -336,11 +357,57 @@ func outputSchemas(schemas []*src.DBSchema) error {
336
357
}
337
358
338
359
func dumpQueries (ctx context.Context ) ([][]string , error ) {
360
+ opts := src.AuditLogScanOpts {
361
+ DBs : GlobalConfig .DBs ,
362
+ QueryMinDurationMs : DumpConfig .QueryMinDurationMs ,
363
+ QueryStates : DumpConfig .QueryStates ,
364
+ Unique : DumpConfig .QueryOutputMode == "unique" ,
365
+ UniqueNormalize : DumpConfig .QueryUniqueNormalize ,
366
+ Unescape : DumpConfig .AuditLogUnescape ,
367
+ OnlySelect : DumpConfig .OnlySelect ,
368
+ Strict : DumpConfig .Strict ,
369
+ From : DumpConfig .From ,
370
+ To : DumpConfig .To ,
371
+ }
372
+
373
+ if DumpConfig .AuditLogTable != "" {
374
+ return dumpQueriesFromTable (ctx , opts )
375
+ }
376
+ return dumpQueriesFromFile (ctx , opts )
377
+ }
378
+
379
+ func dumpQueriesFromTable (ctx context.Context , opts src.AuditLogScanOpts ) ([][]string , error ) {
380
+ if opts .From == "" || opts .To == "" {
381
+ return nil , errors .New ("Must specific both '--from' and '--to' when dumping from audit log table" )
382
+ }
383
+ if opts .Unique {
384
+ return nil , errors .New ("Not yet support '--query-output-mode=unique' with '--audit-log-table'" )
385
+ }
386
+
387
+ dbTable := strings .SplitN (DumpConfig .AuditLogTable , "." , 2 )
388
+ dbname , table := dbTable [0 ], dbTable [1 ]
389
+
390
+ db , err := connectDB (dbname )
391
+ if err != nil {
392
+ return nil , err
393
+ }
394
+
395
+ logrus .Infof ("Dumping queries from audit log table '%s'...\n " , DumpConfig .AuditLogTable )
396
+
397
+ sqls , err := src .GetDBAuditLogs (ctx , db , dbname , table , opts , GlobalConfig .Parallel )
398
+ if err != nil {
399
+ logrus .Errorf ("Extract queries from audit logs table failed, %v\n " , err )
400
+ return nil , err
401
+ }
402
+ return [][]string {sqls }, nil
403
+ }
404
+
405
+ func dumpQueriesFromFile (ctx context.Context , opts src.AuditLogScanOpts ) ([][]string , error ) {
339
406
auditLogs := DumpConfig .AuditLogPaths
340
407
if len (auditLogs ) == 0 {
341
408
sshUrl , err := chooseRemoteAuditLog (ctx )
342
409
if err != nil {
343
- return nil , fmt .Errorf ("Please specific audit log path by --audit-logs, error: %v" , err )
410
+ return nil , fmt .Errorf ("Please specific audit log files by ' --audit-logs' or table by '--audit-log-table' , error: %v" , err )
344
411
}
345
412
auditLogs = []string {sshUrl }
346
413
}
@@ -372,23 +439,16 @@ func dumpQueries(ctx context.Context) ([][]string, error) {
372
439
auditLogFiles = append (auditLogFiles , localPaths ... )
373
440
}
374
441
375
- logrus .Infoln ("Dumping queries from audit logs ..." )
442
+ logrus .Infoln ("Dumping queries from audit log files ..." )
376
443
377
444
queries , err := src .ExtractQueriesFromAuditLogs (
378
- GlobalConfig .DBs ,
379
445
auditLogFiles ,
380
446
DumpConfig .AuditLogEncoding ,
381
- DumpConfig .QueryMinDurationMs ,
382
- DumpConfig .QueryStates ,
447
+ opts ,
383
448
GlobalConfig .Parallel ,
384
- DumpConfig .QueryOutputMode == "unique" ,
385
- DumpConfig .QueryUniqueNormalize ,
386
- DumpConfig .AuditLogUnescape ,
387
- DumpConfig .OnlySelect ,
388
- DumpConfig .Strict ,
389
449
)
390
450
if err != nil {
391
- logrus .Errorf ("Extract queries from audit logs failed, %v\n " , err )
451
+ logrus .Errorf ("Extract queries from audit logs file failed, %v\n " , err )
392
452
return nil , err
393
453
}
394
454
@@ -466,7 +526,7 @@ func outputQueryFileNameFormat(total int) string {
466
526
}
467
527
468
528
func chooseRemoteAuditLog (ctx context.Context ) (string , error ) {
469
- conn , err := connectDB (GlobalConfig . DBs [ 0 ] )
529
+ conn , err := connectDB ("information_schema" )
470
530
if err != nil {
471
531
return "" , err
472
532
}
@@ -494,7 +554,7 @@ func chooseRemoteAuditLog(ctx context.Context) (string, error) {
494
554
return "" , errors .New ("No audit log found on remote server" )
495
555
}
496
556
497
- choosed , err := src .Choose ("Choose audit log on remote server to dump: " , auditLogs )
557
+ choosed , err := src .Choose ("Choose audit log on remote server to dump" , auditLogs )
498
558
if err != nil {
499
559
return "" , err
500
560
}
@@ -551,7 +611,7 @@ func expandSSHPath(remotePath string) (string, error) {
551
611
552
612
func connectDB (db string ) (* sqlx.DB , error ) {
553
613
if db == "" {
554
- return nil , fmt .Errorf ("database name is required, please use --db flag " )
614
+ return nil , fmt .Errorf ("database name is required" )
555
615
}
556
616
return src .NewDB (GlobalConfig .DBHost , GlobalConfig .DBPort , GlobalConfig .DBUser , GlobalConfig .DBPassword , db )
557
617
}
0 commit comments