diff --git a/stats.go b/stats.go index adcf6ea..3da0a2a 100644 --- a/stats.go +++ b/stats.go @@ -21,9 +21,73 @@ import ( "unsafe" ) +// Semantics represents the semantics of a counter ('c', 'g', 'b', 'q', '?'). +type Semantics int + +const ( + SemanticsUnknown Semantics = iota + SemanticsCounter // 'c' + SemanticsGauge // 'g' + SemanticsBitmap // 'b' + SemanticsBoolean // 'q' +) + +// Flags represents the display format of a counter ('i', 'B', 'b', 'q', 'd', '?'). +type Flags int + +const ( + FlagsUnknown Flags = iota + FlagsInteger // 'i' + FlagsBytes // 'B' + FlagsBitmap // 'b' + FlagsBoolean // 'q' + FlagsDuration // 'd' +) + +func semanticsFromC(c C.int) Semantics { + switch c { + case 'c': + return SemanticsCounter + case 'g': + return SemanticsGauge + case 'b': + return SemanticsBitmap + case 'q': + return SemanticsBoolean + default: + return SemanticsUnknown + } +} + +func flagsFromC(c C.int) Flags { + switch c { + case 'i': + return FlagsInteger + case 'B': + return FlagsBytes + case 'b': + return FlagsBitmap + case 'q': + return FlagsBoolean + case 'd': + return FlagsDuration + default: + return FlagsUnknown + } +} + +// Counter represents a Varnish statistic counter with metadata. +type Counter struct { + SDesc string `json:"description"` + LDesc string `json:"ldesc"` + Value uint64 `json:"value"` + Semantics Semantics `json:"semantics"` + Flags Flags `json:"flags"` +} + // Stats returns a map with all stat counters and their values. -func (v *Varnish) Stats() map[string]uint64 { - items := make(map[string]uint64) +func (v *Varnish) Stats() map[string]Counter { + items := make(map[string]Counter) handle := ptrHandles.track(items) defer ptrHandles.untrack(handle) C.VSC_Iter(v.vsc, v.vsm, @@ -36,8 +100,11 @@ func (v *Varnish) Stats() map[string]uint64 { // 0 and false otherwise. func (v *Varnish) Stat(s string) (uint64, bool) { stats := v.Stats() - value, ok := stats[s] - return value, ok + c, ok := stats[s] + if !ok { + return 0, false + } + return c.Value, true } // do_list_cb() @@ -49,11 +116,16 @@ func listCallback(handle unsafe.Pointer, pt *C.struct_VSC_point) C.int { return 1 } name := C.GoString(pt.name) - value := *(*uint64)(unsafe.Pointer(pt.ptr)) - items, ok := priv.(map[string]uint64) + items, ok := priv.(map[string]Counter) if !ok { return 1 } - items[name] = value + items[name] = Counter{ + SDesc: C.GoString(pt.sdesc), + LDesc: C.GoString(pt.ldesc), + Value: *(*uint64)(unsafe.Pointer(pt.ptr)), + Semantics: semanticsFromC(pt.semantics), + Flags: flagsFromC(pt.format), + } return 0 } diff --git a/vago_test.go b/vago_test.go index 2fe11ae..d98da25 100644 --- a/vago_test.go +++ b/vago_test.go @@ -1,6 +1,7 @@ package vago import ( + "fmt" "strings" "sync" "testing" @@ -112,6 +113,15 @@ func TestStats(t *testing.T) { if len(items) == 0 { t.Fatal("Expected map with elements") } + for name, counter := range items { + if counter.SDesc == "" { + t.Errorf("%s: expected non-empty SDesc", name) + } + if counter.LDesc == "" { + t.Errorf("%s: expected non-empty LDesc", name) + } + break + } } func TestStatFail(t *testing.T) { @@ -133,7 +143,22 @@ func TestStatOK(t *testing.T) { t.Fatal("Expected nil") } defer v.Close() - if _, ok := v.Stat("MAIN.uptime"); !ok { - t.Fatal("Expected some value") + stats := v.Stats() + fmt.Printf("Stats: %v\n", stats) + counter, ok := stats["MAIN.uptime"] + if !ok { + t.Fatal("Expected MAIN.uptime") + } + if counter.SDesc == "" { + t.Error("Expected non-empty SDesc") + } + if counter.LDesc == "" { + t.Error("Expected non-empty LDesc") + } + if counter.Semantics != SemanticsCounter { + t.Errorf("Expected SemanticsCounter, got %v", counter.Semantics) + } + if counter.Flags != FlagsDuration { + t.Errorf("Expected FlagsDuration, got %v", counter.Flags) } }