Browse Source

Add GCCollector (#1)

pull/2/head
Fredrik Ekre 2 years ago committed by GitHub
parent
commit
0c0de999ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 20
      README.md
  3. 71
      src/Prometheus.jl
  4. 37
      test/runtests.jl

3
CHANGELOG.md

@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
First stable release of Prometheus.jl: First stable release of Prometheus.jl:
- Supported collectors: Counter, Gauge, Summary - Supported basic collectors: Counter, Gauge, Summary
- GCCollector for metrics about allocations and garbage collection
- Support for default and custom collector registries - Support for default and custom collector registries
- Support for metric labeling - Support for metric labeling
- Support for exposing metrics to file and over HTTP - Support for exposing metrics to file and over HTTP

20
README.md

@ -28,13 +28,20 @@
3. Visit <http://localhost:8000> in your browser. You will see something like the following 3. Visit <http://localhost:8000> in your browser. You will see something like the following
``` ```
# HELP gc_alloc_bytes_total Total number of allocated bytes
# TYPE gc_alloc_bytes_total counter
gc_alloc_bytes_total 365578814
[...]
# HELP request_count Number of handled requests # HELP request_count Number of handled requests
# TYPE request_count counter # TYPE request_count counter
request_count 1 request_count 1
``` ```
which is how the counter is presented when Prometheus scrapes the metrics. The default output contains metrics about allocations and garbage collections (see
Every time you refresh, the counter will increment its value. [GCCollector](#gccollector)), as well as the request counter that we added ourselves.
`close(server)` will shutdown the server. Every time you refresh, the counter will increment its value. `close(server)` will
shutdown the server.
## Collectors ## Collectors
@ -66,6 +73,13 @@ See <https://prometheus.io/docs/concepts/metric_types/#summary> for details.
Supported methods: Supported methods:
- `Prometheus.observe(summary, v)`: record the observed value `v`. - `Prometheus.observe(summary, v)`: record the observed value `v`.
### GCCollector
A collector that exports metrics about allocations and garbage collection (for example
number of allocations, number of bytes allocated, time spent in garbage collection, etc).
These metrics have the `gc_` prefix in their name.
## Labels ## Labels
See <https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels> for details. See <https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels> for details.

71
src/Prometheus.jl

@ -69,8 +69,6 @@ struct CollectorRegistry
end end
end end
const DEFAULT_REGISTRY = CollectorRegistry()
function register(reg::CollectorRegistry, collector::Collector) function register(reg::CollectorRegistry, collector::Collector)
existing_names = Set{String}() # TODO: Cache existing_names in the registry? existing_names = Set{String}() # TODO: Cache existing_names in the registry?
@lock reg.lock begin @lock reg.lock begin
@ -401,6 +399,71 @@ function collect!(metrics::Vector, family::Family{C}) where C
return metrics return metrics
end end
############################
# GCCollector <: Collector #
############################
mutable struct GCCollector <: Collector
function GCCollector(registry::Union{CollectorRegistry, Nothing}=DEFAULT_REGISTRY)
gcc = new()
if registry !== nothing
register(registry, gcc)
end
return gcc
end
end
function metric_names(::GCCollector)
return (
"gc_alloc_total", "gc_free_total", "gc_alloc_bytes_total",
"gc_live_bytes", "gc_seconds_total", "gc_collections_total",
)
end
function collect!(metrics::Vector, ::GCCollector)
# See base/timing.jl
gc_num = Base.gc_num()
gc_live_bytes = Base.gc_live_bytes()
# Push all the metrics
push!(metrics,
Metric(
"counter", "gc_alloc_total", "Total number of allocations (calls to malloc, realloc, etc)",
LabelNames(["type"]),
[
Sample(nothing, LabelValues(["bigalloc"]), gc_num.bigalloc),
Sample(nothing, LabelValues(["malloc"]), gc_num.malloc),
Sample(nothing, LabelValues(["poolalloc"]), gc_num.poolalloc),
Sample(nothing, LabelValues(["realloc"]), gc_num.realloc),
],
),
Metric(
"counter", "gc_free_total", "Total number of calls to free()",
nothing, Sample(nothing, nothing, gc_num.freecall),
),
Metric(
"counter", "gc_alloc_bytes_total", "Total number of allocated bytes", nothing,
Sample(nothing, nothing, Base.gc_total_bytes(gc_num)),
),
Metric(
"gauge", "gc_live_bytes", "Current number of live bytes", nothing,
Sample(nothing, nothing, gc_live_bytes),
),
Metric(
"counter", "gc_seconds_total", "Total time spent in garbage collection", nothing,
Sample(nothing, nothing, gc_num.total_time / 10^9), # [ns] to [s]
),
Metric(
"counter", "gc_collections_total", "Total number of calls to garbage collection",
LabelNames(["type"]),
[
Sample(nothing, LabelValues(["full"]), gc_num.full_sweep),
Sample(nothing, LabelValues(["minor"]), gc_num.pause - gc_num.full_sweep),
],
),
)
return metrics
end
############## ##############
# Exposition # # Exposition #
@ -563,4 +626,8 @@ function expose(http::HTTP.Stream, reg::CollectorRegistry = DEFAULT_REGISTRY; co
return return
end end
# Default registry and collectors
const DEFAULT_REGISTRY = CollectorRegistry()
const GC_COLLECTOR = GCCollector(DEFAULT_REGISTRY)
end # module Prometheus end # module Prometheus

37
test/runtests.jl

@ -281,6 +281,43 @@ end
""" """
end end
@testset "Prometheus.GCCollector" begin
r = Prometheus.CollectorRegistry()
c = Prometheus.GCCollector(r)
@test c in r.collectors
# Record before and after stats and test that the metrics are in between
old_stats = Base.gc_num()
x = zeros(1024^2); x = nothing; GC.gc(); GC.gc()
metrics = Prometheus.collect(c)
x = zeros(1024^2); x = nothing; GC.gc(); GC.gc()
new_stats = Base.gc_num()
@test length(metrics) == 6
gc_alloc_total = metrics[findfirst(x -> x.metric_name == "gc_alloc_total", metrics)]
@test old_stats.bigalloc <= gc_alloc_total.samples[1].value <= new_stats.bigalloc
@test old_stats.malloc <= gc_alloc_total.samples[2].value <= new_stats.malloc
@test old_stats.poolalloc <= gc_alloc_total.samples[3].value <= new_stats.poolalloc
@test old_stats.realloc <= gc_alloc_total.samples[4].value <= new_stats.realloc
gc_free_total = metrics[findfirst(x -> x.metric_name == "gc_free_total", metrics)]
@test old_stats.freecall <= gc_free_total.samples.value <= new_stats.freecall
gc_alloc_bytes_total = metrics[findfirst(x -> x.metric_name == "gc_alloc_bytes_total", metrics)]
@test Base.gc_total_bytes(old_stats) <= gc_alloc_bytes_total.samples.value <= Base.gc_total_bytes(new_stats)
gc_seconds_total = metrics[findfirst(x -> x.metric_name == "gc_seconds_total", metrics)]
@test old_stats.total_time / 10^9 <= gc_seconds_total.samples.value <= new_stats.total_time / 10^9
# Prometheus.expose_metric(...)
str = sprint(Prometheus.expose_metric, gc_alloc_total)
@test occursin(
r"""
# HELP gc_alloc_total Total number of allocations \(calls to malloc, realloc, etc\)
# TYPE gc_alloc_total counter
gc_alloc_total{type="bigalloc"} \d+
gc_alloc_total{type="malloc"} \d+
gc_alloc_total{type="poolalloc"} \d+
gc_alloc_total{type="realloc"} \d+
""",
sprint(Prometheus.expose_metric, gc_alloc_total),
)
end
@testset "Prometheus.expose(::Union{String, IO})" begin @testset "Prometheus.expose(::Union{String, IO})" begin
r = Prometheus.DEFAULT_REGISTRY r = Prometheus.DEFAULT_REGISTRY
empty!(r.collectors) empty!(r.collectors)

Loading…
Cancel
Save