From e7f60e5a455e3437b5f961be1a97e13170b5c2e7 Mon Sep 17 00:00:00 2001 From: Marty Schoch Date: Tue, 1 Dec 2015 16:53:13 -0500 Subject: [PATCH] allow escaping the dot character in paths using a backslash Occasionally expvar output contains keys that are hostnames, IP addresses, or filenames that contain the "." character. For example: { "bleve": { "bootDuration": 16559, "indexes": { "bench.bleve": { "index": { "analysis_time": 10889841135, "batches": 145, "deletes": 0, "errors": 0, "index_time": 21277401883, "lookup_queue_len": 0, "updates": 14500 }, "search_time": 0, "searches": 0 } } } ... } I can now chart the lookup_queue_len value using the var: bleve.indexes.bench\.bleve.index.lookup_queue_len This partially addresses #11 (still does not escape colon character). --- var.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- var_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/var.go b/var.go index 2c3dc32..8dff7ed 100644 --- a/var.go +++ b/var.go @@ -38,7 +38,7 @@ const ( // Example: "mem:memstats.Alloc" => []string{"memstats", "Alloc"} func (v VarName) ToSlice() []string { start := strings.IndexRune(string(v), ':') + 1 - slice := strings.FieldsFunc(string(v)[start:], func(r rune) bool { return r == '.' }) + slice := DottedFieldsToSliceEscaped(string(v)[start:]) return slice } @@ -128,3 +128,47 @@ func roundDuration(d time.Duration) time.Duration { } return d } + +func DottedFieldsToSliceEscaped(s string) []string { + rv := make([]string, 0) + lastSlash := false + curr := "" + for _, r := range s { + // base case, dot not after slash + if !lastSlash && r == '.' { + if len(curr) > 0 { + rv = append(rv, curr) + curr = "" + } + continue + } else if !lastSlash { + // any character not after slash + curr += string(r) + if r == '\\' { + lastSlash = true + } else { + lastSlash = false + } + continue + } else if r == '\\' { + // last was slash, and so is this + lastSlash = false // 2 slashes = 0 + // we already appended a single slash on first + continue + } else if r == '.' { + // we see \. but already appended \ last time + // replace it with . + curr = curr[:len(curr)-1] + "." + lastSlash = false + } else { + // \ and any other character, ignore + curr += string(r) + lastSlash = false + continue + } + } + if len(curr) > 0 { + rv = append(rv, curr) + } + return rv +} diff --git a/var_test.go b/var_test.go index ddc084d..3d56bf1 100644 --- a/var_test.go +++ b/var_test.go @@ -44,4 +44,49 @@ func TestVarName(t *testing.T) { if kind != KindDuration { t.Fatalf("Expecting kind to be %v, but got: %v", KindDuration, kind) } + + // single \. escapes the dot + v = VarName(`bleve.indexes.bench\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench.bleve" || + slice[3] != "index" || slice[4] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // double \\. escapes backslash, not dot + v = VarName(`bleve.indexes.bench\\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 6 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\" || + slice[3] != "bleve" || slice[4] != "index" || slice[5] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // triple \\\. escapes backslash then dot + v = VarName(`bleve.indexes.bench\\\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\.bleve" || + slice[3] != "index" || slice[4] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // quadruple \\\\. escapes two backslashes, not dot + v = VarName(`bleve.indexes.bench\\\\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 6 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\\\" || + slice[3] != "bleve" || slice[4] != "index" || slice[5] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // unsupported \x passes through unaltered + v = VarName(`bleve.indexes.bench\xbleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\xbleve" || + slice[3] != "index" || slice[4] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } }