@@ -9,6 +9,7 @@ package slowlog
9
9
10
10
import (
11
11
"bufio"
12
+ "bytes"
12
13
"errors"
13
14
"fmt"
14
15
"io"
@@ -51,6 +52,7 @@ var metricsRe = regexp.MustCompile(`(\w+): (\S+|\z)`)
51
52
var adminRe = regexp .MustCompile (`command: (.+)` )
52
53
var setRe = regexp .MustCompile (`^SET (?:last_insert_id|insert_id|timestamp)` )
53
54
var useRe = regexp .MustCompile (`^(?i)use ` )
55
+ var blockCommentsRe = regexp .MustCompile ("/\\ *[\\ w\\ W]*?(?=\\ */)\\ */" )
54
56
55
57
// FileParser represents a file-based Parser. This is the canonical Parser
56
58
// because the slow log is a file.
@@ -396,6 +398,92 @@ func (p *FileParser) parseAdmin(line string) {
396
398
}
397
399
}
398
400
401
+ // Tested here: https://play.golang.org/p/u7XZnxu2LLL
402
+ func parseComments (queryWithComments string ) map [string ]string {
403
+ startIndex := strings .Index (queryWithComments , "/*" )
404
+ if startIndex != - 1 {
405
+ endIndex := strings .LastIndex (queryWithComments , "*/" )
406
+ sqlComments := queryWithComments [startIndex :endIndex ]
407
+ return parseKeyValuePairToMap (sqlComments )
408
+ }
409
+ return map [string ]string {}
410
+ }
411
+
412
+ // Copied verbatim from https://gist.github.com/alexisvisco/4b846978c9346e4eaf618bb632c0693a
413
+ func parseKeyValuePairToMap (msg string ) map [string ]string {
414
+
415
+ type kv struct {
416
+ key , val string
417
+ }
418
+
419
+ var pair * kv = nil
420
+ pairs := make (map [string ]string )
421
+ buf := bytes .NewBuffer ([]byte {})
422
+
423
+ var (
424
+ escape = false
425
+ garbage = false
426
+ quoted = false
427
+ )
428
+
429
+ completePair := func (buffer * bytes.Buffer , pair * kv ) kv {
430
+ if pair != nil {
431
+ return kv {pair .key , buffer .String ()}
432
+ } else {
433
+ return kv {buffer .String (), "" }
434
+ }
435
+ }
436
+
437
+ for _ , c := range msg {
438
+ if ! quoted && c == ' ' {
439
+ if buf .Len () != 0 {
440
+ if ! garbage {
441
+ p := completePair (buf , pair )
442
+ pairs [p .key ] = p .val
443
+ pair = nil
444
+ }
445
+ buf .Reset ()
446
+ }
447
+ garbage = false
448
+ } else if ! quoted && c == '=' {
449
+ if buf .Len () != 0 {
450
+ pair = & kv {key : buf .String (), val : "" }
451
+ buf .Reset ()
452
+ } else {
453
+ garbage = true
454
+ }
455
+ } else if quoted && c == '\\' {
456
+ escape = true
457
+ } else if c == '"' {
458
+ if escape {
459
+ buf .WriteRune (c )
460
+ escape = false
461
+ } else {
462
+ quoted = ! quoted
463
+ }
464
+ } else {
465
+ if escape {
466
+ buf .WriteRune ('\\' )
467
+ escape = false
468
+ }
469
+ buf .WriteRune (c )
470
+ }
471
+ }
472
+
473
+ if ! garbage {
474
+ p := completePair (buf , pair )
475
+ pairs [p .key ] = p .val
476
+ }
477
+
478
+ // Remove any key value pair where either of them is a blank string
479
+ for key , value := range pairs {
480
+ if key == "" || value == "" {
481
+ delete (pairs , key )
482
+ }
483
+ }
484
+ return pairs
485
+ }
486
+
399
487
func (p * FileParser ) sendEvent (inHeader bool , inQuery bool ) {
400
488
if Debug {
401
489
log .Println ("send event" )
@@ -421,6 +509,7 @@ func (p *FileParser) sendEvent(inHeader bool, inQuery bool) {
421
509
// Clean up the event.
422
510
p .event .Db = strings .TrimSuffix (p .event .Db , ";\n " )
423
511
p .event .Query = strings .TrimSuffix (p .event .Query , ";" )
512
+ p .event .CommentMetadata = parseComments (p .event .Query )
424
513
425
514
// Send the event. This will block.
426
515
select {
0 commit comments