Skip to content

Commit

Permalink
alternative API
Browse files Browse the repository at this point in the history
- retrieving collectors by name string: `counter("foo").inc()` and
  `gauge("bar").set(2)`, with implicit object creation
- cleanup benchmarks and add their compilation to the "test" task
  • Loading branch information
stefantalpalaru authored and zah committed Jul 20, 2019
1 parent 169c27d commit 97b4e26
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 52 deletions.
8 changes: 6 additions & 2 deletions benchmarks/bench_collectors.nim
Expand Up @@ -4,19 +4,23 @@ import
proc main(nb_samples: Natural) =
warmup()

bench("create a counter and increment it 3 times with different values", 0):
var res: float64

bench("create a counter and increment it 3 times with different values", res):
declareCounter counter1, "help"
counter1.inc()
counter1.inc(2)
counter1.inc(2.1)
res = counter1.value
counter1.unregister()

let labelValues = @["a", "b"]
bench("create a counter with 2 labels and increment it 3 times with different values", 0):
bench("create a counter with 2 labels and increment it 3 times with different values", res):
declareCounter counter2, "help", @["foo", "bar"]
counter2.inc(labelValues = labelValues)
counter2.inc(2, labelValues)
counter2.inc(2.1, labelValues)
res = counter2.value(labelValues)
counter2.unregister()

when isMainModule:
Expand Down
28 changes: 28 additions & 0 deletions metrics.nim
Expand Up @@ -34,6 +34,11 @@ type
Registry* = ref object of RootObj
lock*: Lock
collectors*: OrderedSet[Collector]
# Only used by the secondary API to retrieve collectors by name. No need to
# distinguish between collectors with the same name and different labels,
# here. We'll just return the last registered collector with that name for
# something like `counter("foo_bar")`.
collectorsByName*: Table[string, Collector]

RegistrationError* = object of Exception

Expand Down Expand Up @@ -131,6 +136,7 @@ proc newRegistry*(): Registry =
result.lock.initLock()
# TODO: remove this set initialisation after porting to Nim-0.20.x
result.collectors.init()
result.collectorsByName = initTable[string, Collector]()

var defaultRegistry* = newRegistry()

Expand All @@ -141,6 +147,7 @@ proc register*(collector: Collector, registry = defaultRegistry) =
raise newException(RegistrationError, "Collector already registered.")

registry.collectors.incl(collector)
registry.collectorsByName[collector.name] = collector

proc unregister*(collector: Collector, registry = defaultRegistry) =
when defined(metrics):
Expand All @@ -149,6 +156,7 @@ proc unregister*(collector: Collector, registry = defaultRegistry) =
raise newException(RegistrationError, "Collector not registered.")

registry.collectors.excl(collector)
registry.collectorsByName.del(collector.name)

proc collect*(registry: Registry): OrderedTable[Collector, Metrics] =
when defined(metrics):
Expand Down Expand Up @@ -221,6 +229,17 @@ template declarePublicCounter*(identifier: untyped,
else:
type identifier* = IgnoredCollector

# alternative API
proc counter*(name: string, labels: Labels = @[], registry = defaultRegistry): Counter | IgnoredCollector =
when defined(metrics):
# TODO: return IgnoredCollector when fine-grained filtering is enabled and
# `name` matches the excluded pattern
if name in registry.collectorsByName:
result = registry.collectorsByName[name].Counter
else:
# no support for custom help strings in this API
result = newCounter(name, "", labels, registry)

proc incCounter(counter: Counter, amount: int64|float64 = 1, labelValues: Labels = @[]) =
when defined(metrics):
var timestamp = getTime().toMilliseconds()
Expand Down Expand Up @@ -303,6 +322,15 @@ template declareGauge*(identifier: untyped,
else:
type identifier = IgnoredCollector

# alternative API
proc gauge*(name: string, labels: Labels = @[], registry = defaultRegistry): Gauge | IgnoredCollector =
when defined(metrics):
if name in registry.collectorsByName:
result = registry.collectorsByName[name].Gauge
else:
# no support for custom help strings in this API
result = newGauge(name, "", labels, registry)

template declarePublicGauge*(identifier: untyped,
help: static string,
labels: Labels = @[],
Expand Down
2 changes: 2 additions & 0 deletions metrics.nimble
Expand Up @@ -30,6 +30,8 @@ task test, "Main tests":
# build it with metrics disabled, first
buildBinary "main_tests", "tests/", "-f"
test "main_tests"
buildBinary "bench_collectors", "benchmarks/", "-f"
buildBinary "bench_collectors", "benchmarks/", "-f -d:metrics"

task test_chronicles, "Chronicles tests":
buildBinary "chronicles_tests", "tests/", "-f"
Expand Down
18 changes: 9 additions & 9 deletions tests/chronicles_tests.nim
Expand Up @@ -11,16 +11,16 @@ import chronicles, tables, unittest,
suite "logging":
test "info":
var registry = newRegistry()
declareCounter counter, "help", registry = registry
counter.inc()
info "counter", counter
declareCounter lcounter, "l help", @["foo", "bar"], registry
declareCounter myCounter, "help", registry = registry
myCounter.inc()
info "myCounter", myCounter
declareCounter lCounter, "l help", @["foo", "bar"], registry
let labelValues = @["a", "x \"y\" \n\\z"]
lcounter.inc(4.5, labelValues = labelValues)
info "lcounter", lcounter
declareGauge gauge, "help", registry = registry
gauge.set(9.5)
info "gauge", gauge
lCounter.inc(4.5, labelValues = labelValues)
info "lCounter", lCounter
declareGauge myGauge, "help", registry = registry
myGauge.set(9.5)
info "myGauge", myGauge
when defined(metrics):
for collector, metricsTable in registry.collect():
for labels, metrics in metricsTable:
Expand Down
97 changes: 56 additions & 41 deletions tests/main_tests.nim
Expand Up @@ -24,18 +24,32 @@ proc gcSafetyTest* {.gcsafe.} = # The test is succesful if this proc compiles
suite "counter":
setup:
var registry = newRegistry()
declareCounter counter, "help", registry = registry
declareCounter myCounter, "help", registry = registry

test "increment":
check(counter.value == 0)
counter.inc()
check(counter.value == 1)
counter.inc(7)
check(counter.value == 8)
counter.inc(0.5)
check(counter.value == 8.5)
check(myCounter.value == 0)
myCounter.inc()
check(myCounter.value == 1)
myCounter.inc(7)
check(myCounter.value == 8)
myCounter.inc(0.5)
check(myCounter.value == 8.5)
expect ValueError:
counter.inc(-1)
myCounter.inc(-1)

# alternative API
counter("myCounter", registry = registry).inc()
check(counter("myCounter", registry = registry).value == 9.5)
check(myCounter.value == 9.5)
counter("myNewCounter", registry = registry).inc()
check(counter("myNewCounter", registry = registry).value == 1)
# default registry
counter("foo_bar").inc()
check(counter("foo_bar").value == 1)
counter("foo_bar").inc(0.5)
check(counter("foo_bar").value == 1.5)
expect ObjectConversionError:
gauge("foo_bar").inc()

test "exceptions":
proc f(switch: bool) =
Expand All @@ -45,61 +59,62 @@ suite "counter":
raise newException(IndexError, "exc2")

expect IndexError:
counter.countExceptions(ValueError):
myCounter.countExceptions(ValueError):
f(false)
check(counter.value == 0)
check(myCounter.value == 0)

expect ValueError:
counter.countExceptions(ValueError):
myCounter.countExceptions(ValueError):
f(true)
check(counter.value == 1)
check(myCounter.value == 1)

expect IndexError:
counter.countExceptions:
myCounter.countExceptions:
f(false)
check(counter.value == 2)
check(myCounter.value == 2)

counter.countExceptions:
myCounter.countExceptions:
discard
check(counter.value == 2)
# echo counter.toTextLines().join("\n")
check(myCounter.value == 2)
# echo myCounter.toTextLines().join("\n")

test "labels":
declareCounter lcounter, "l help", @["foo", "bar"], registry
declareCounter lCounter, "l help", @["foo", "bar"], registry
expect KeyError:
discard lcounter.value
discard lCounter.value

# you can't access a labelled value before it was initialised
expect KeyError:
discard lcounter.value(@["a", "x"])
discard lCounter.value(@["a", "x"])

let labelValues = @["a", "x \"y\" \n\\z"]
lcounter.inc(labelValues = labelValues)
check(lcounter.value(labelValues) == 1)
lCounter.inc(labelValues = labelValues)
check(lCounter.value(labelValues) == 1)
# echo registry.toText()

suite "gauge":
setup:
var registry = newRegistry()
declareGauge gauge, "help", registry = registry
declareGauge myGauge, "help", registry = registry

test "basic":
check(gauge.value == 0)
gauge.inc()
check(gauge.value == 1)
gauge.dec(3)
check(gauge.value == -2.0) # weird Nim bug if it's "-2"
gauge.dec(0.1)
check(gauge.value == -2.1)
gauge.set(9.5)
check(gauge.value == 9.5)
gauge.set(1)
check(gauge.value == 1)
check(myGauge.value == 0)
myGauge.inc()
check(myGauge.value == 1)
myGauge.dec(3)
check(myGauge.value == -2.0) # weird Nim bug if it's "-2"
myGauge.dec(0.1)
check(myGauge.value == -2.1)
myGauge.set(9.5)
check(myGauge.value == 9.5)
myGauge.set(1)
check(myGauge.value == 1)
check(gauge("myGauge", registry = registry).value == 1)

test "in progress":
gauge.trackInProgress:
check(gauge.value == 1)
check(gauge.value == 0)
myGauge.trackInProgress:
check(myGauge.value == 1)
check(myGauge.value == 0)

declareGauge lgauge, "help", @["foobar"], registry = registry
let labelValues = @["b"]
Expand All @@ -109,10 +124,10 @@ suite "gauge":
# echo registry.toText()

test "timing":
gauge.time:
myGauge.time:
sleep(1000)
check(gauge.value == 0)
check(gauge.value >= 1) # may be 2 inside a macOS Travis job
check(myGauge.value == 0)
check(myGauge.value >= 1) # may be 2 inside a macOS Travis job
# echo registry.toText()

test "timing with labels":
Expand Down

0 comments on commit 97b4e26

Please sign in to comment.