Skip to content

Commit 49bed63

Browse files
Added TODOs and the parser function that populates event.CommentMetadata
1 parent cddb610 commit 49bed63

File tree

3 files changed

+107
-12
lines changed

3 files changed

+107
-12
lines changed

event.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@ package slowlog
1212
// event is expected to define the query and Query_time metric. Other metrics
1313
// and metadata vary according to MySQL version, distro, and configuration.
1414
type Event struct {
15-
Offset uint64 // byte offset in file at which event starts
16-
Ts string // raw timestamp of event
17-
Admin bool // true if Query is admin command
18-
Query string // SQL query or admin command
19-
User string
20-
Host string
21-
Db string
22-
TimeMetrics map[string]float64 // *_time and *_wait metrics
23-
NumberMetrics map[string]uint64 // most metrics
24-
BoolMetrics map[string]bool // yes/no metrics
25-
RateType string // Percona Server rate limit type
26-
RateLimit uint // Percona Server rate limit value
15+
Offset uint64 // byte offset in file at which event starts
16+
Ts string // raw timestamp of event
17+
Admin bool // true if Query is admin command
18+
Query string // SQL query or admin command
19+
User string
20+
Host string
21+
Db string
22+
TimeMetrics map[string]float64 // *_time and *_wait metrics
23+
NumberMetrics map[string]uint64 // most metrics
24+
BoolMetrics map[string]bool // yes/no metrics
25+
RateType string // Percona Server rate limit type
26+
RateLimit uint // Percona Server rate limit value
27+
CommentMetadata map[string]string
2728
}
2829

2930
// NewEvent returns a new Event with initialized metric maps.

metrics.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type Metrics struct {
1414
TimeMetrics map[string]*TimeStats `json:",omitempty"`
1515
NumberMetrics map[string]*NumberStats `json:",omitempty"`
1616
BoolMetrics map[string]*BoolStats `json:",omitempty"`
17+
// TODO(Yash): Add another metrics for metadata.
1718
}
1819

1920
// TimeStats are microsecond-based metrics like Query_time and Lock_time.
@@ -26,6 +27,8 @@ type TimeStats struct {
2627
P95 float64 `json:",omitempty"` // 95th percentile
2728
Max float64 `json:",omitempty"`
2829
outlierSum float64
30+
// TODO(Yash) timeTakeToTraceId as map. Move this to Metrics class or create another struct parallel to TImeMetrics, NumberMetrics, BoolMetrics
31+
// traceIdOfMaxSql
2932
}
3033

3134
// NumberStats are integer-based metrics like Rows_sent and Merge_passes.
@@ -72,6 +75,7 @@ func (m *Metrics) AddEvent(e Event, outlier bool) {
7275
stats.Sum += val
7376
}
7477
stats.vals = append(stats.vals, float64(val))
78+
// TODO(Yash): Add traceIdProto to the map defined in TimeMetrics
7579
}
7680

7781
for metric, val := range e.NumberMetrics {
@@ -133,6 +137,7 @@ func (m *Metrics) Finalize(rateLimit uint) {
133137

134138
// Update sum last because avg ^ needs the original value.
135139
s.Sum = (s.Sum * float64(rateLimit)) + s.outlierSum
140+
//TODO(Yash): Do timeTakenToTraceIdProto[s.Max] to know the traceId and populate traceIdOfMaxSql
136141
}
137142

138143
for _, s := range m.NumberMetrics {

parser.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package slowlog
99

1010
import (
1111
"bufio"
12+
"bytes"
1213
"errors"
1314
"fmt"
1415
"io"
@@ -51,6 +52,7 @@ var metricsRe = regexp.MustCompile(`(\w+): (\S+|\z)`)
5152
var adminRe = regexp.MustCompile(`command: (.+)`)
5253
var setRe = regexp.MustCompile(`^SET (?:last_insert_id|insert_id|timestamp)`)
5354
var useRe = regexp.MustCompile(`^(?i)use `)
55+
var blockCommentsRe = regexp.MustCompile("/\\*[\\w\\W]*?(?=\\*/)\\*/")
5456

5557
// FileParser represents a file-based Parser. This is the canonical Parser
5658
// because the slow log is a file.
@@ -396,6 +398,92 @@ func (p *FileParser) parseAdmin(line string) {
396398
}
397399
}
398400

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+
399487
func (p *FileParser) sendEvent(inHeader bool, inQuery bool) {
400488
if Debug {
401489
log.Println("send event")
@@ -421,6 +509,7 @@ func (p *FileParser) sendEvent(inHeader bool, inQuery bool) {
421509
// Clean up the event.
422510
p.event.Db = strings.TrimSuffix(p.event.Db, ";\n")
423511
p.event.Query = strings.TrimSuffix(p.event.Query, ";")
512+
p.event.CommentMetadata = parseComments(p.event.Query)
424513

425514
// Send the event. This will block.
426515
select {

0 commit comments

Comments
 (0)